12118f387SNathan Whitehorn /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31de7b4b8SPedro F. Giffuni * 42118f387SNathan Whitehorn * Copyright (c) 2011 Nathan Whitehorn 582ac9f2bSDevin Teske * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org> 62118f387SNathan Whitehorn * All rights reserved. 72118f387SNathan Whitehorn * 82118f387SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 92118f387SNathan Whitehorn * modification, are permitted provided that the following conditions 102118f387SNathan Whitehorn * are met: 112118f387SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 122118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 132118f387SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 142118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 152118f387SNathan Whitehorn * documentation and/or other materials provided with the distribution. 162118f387SNathan Whitehorn * 172118f387SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 182118f387SNathan Whitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 192118f387SNathan Whitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 202118f387SNathan Whitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 212118f387SNathan Whitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 222118f387SNathan Whitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 232118f387SNathan Whitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 242118f387SNathan Whitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 252118f387SNathan Whitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 262118f387SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272118f387SNathan Whitehorn * SUCH DAMAGE. 282118f387SNathan Whitehorn */ 292118f387SNathan Whitehorn 3082ac9f2bSDevin Teske #include <sys/cdefs.h> 3182ac9f2bSDevin Teske __FBSDID("$FreeBSD$"); 3282ac9f2bSDevin Teske 3346cc2c02SNathan Whitehorn #include <sys/param.h> 34263660c0SAlfonso Siciliano 3582ac9f2bSDevin Teske #include <archive.h> 3682ac9f2bSDevin Teske #include <ctype.h> 3718f20d5dSAlfonso Siciliano #include <bsddialog.h> 3818f20d5dSAlfonso Siciliano #include <bsddialog_progressview.h> 3982ac9f2bSDevin Teske #include <err.h> 402118f387SNathan Whitehorn #include <errno.h> 412118f387SNathan Whitehorn #include <limits.h> 4218f20d5dSAlfonso Siciliano #include <signal.h> 4382ac9f2bSDevin Teske #include <stdio.h> 4482ac9f2bSDevin Teske #include <stdlib.h> 4582ac9f2bSDevin Teske #include <string.h> 4682ac9f2bSDevin Teske #include <unistd.h> 472118f387SNathan Whitehorn 48bd1df636SDevin Teske /* Data to process */ 49*f9549860SBaptiste Daroussin static const char *distdir = NULL; 504ff1fc3bSDevin Teske static struct archive *archive = NULL; 51bd1df636SDevin Teske 52bd1df636SDevin Teske /* Function prototypes */ 534ff1fc3bSDevin Teske static void sig_int(int sig); 5482ac9f2bSDevin Teske static int count_files(const char *file); 5518f20d5dSAlfonso Siciliano static int extract_files(struct bsddialog_fileminibar *file); 56bd1df636SDevin Teske 5718f20d5dSAlfonso Siciliano #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__)) 582118f387SNathan Whitehorn 592118f387SNathan Whitehorn int 602118f387SNathan Whitehorn main(void) 612118f387SNathan Whitehorn { 62bd1df636SDevin Teske char *chrootdir; 63bd1df636SDevin Teske char *distributions; 64dd8ca6b2SBaptiste Daroussin char *distribs, *distrib; 6582ac9f2bSDevin Teske int retval; 6618f20d5dSAlfonso Siciliano size_t minibar_size = sizeof(struct bsddialog_fileminibar); 6718f20d5dSAlfonso Siciliano unsigned int nminibars; 6818f20d5dSAlfonso Siciliano struct bsddialog_fileminibar *dists; 6918f20d5dSAlfonso Siciliano struct bsddialog_progviewconf pvconf; 7018f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 714ff1fc3bSDevin Teske struct sigaction act; 7282ac9f2bSDevin Teske char error[PATH_MAX + 512]; 73bfd258f7SNathan Whitehorn 74bd1df636SDevin Teske if ((distributions = getenv("DISTRIBUTIONS")) == NULL) 7582ac9f2bSDevin Teske errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); 76bd1df636SDevin Teske if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) 77*f9549860SBaptiste Daroussin distdir = ""; 78dd8ca6b2SBaptiste Daroussin if ((distribs = strdup(distributions)) == NULL) 79dd8ca6b2SBaptiste Daroussin errx(EXIT_FAILURE, "memory error"); 80bfd258f7SNathan Whitehorn 8118f20d5dSAlfonso Siciliano if (bsddialog_init() == BSDDIALOG_ERROR) 82263660c0SAlfonso Siciliano errx(EXIT_FAILURE, "Error libbsdialog: %s", 83263660c0SAlfonso Siciliano bsddialog_geterror()); 8418f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 85263660c0SAlfonso Siciliano bsddialog_backtitle(&conf, "FreeBSD Installer"); 8618f20d5dSAlfonso Siciliano bsddialog_infobox(&conf, 87263660c0SAlfonso Siciliano "Checking distribution archives.\nPlease wait...", 4, 35); 8857bda1b6SNathan Whitehorn 8918f20d5dSAlfonso Siciliano /* Parse $DISTRIBUTIONS */ 9018f20d5dSAlfonso Siciliano nminibars = 0; 9118f20d5dSAlfonso Siciliano dists = NULL; 92dd8ca6b2SBaptiste Daroussin while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) { 93dd8ca6b2SBaptiste Daroussin if (strlen(distrib) == 0) 94bd1df636SDevin Teske continue; 95bd1df636SDevin Teske 96bd1df636SDevin Teske /* Allocate a new struct for the distribution */ 9718f20d5dSAlfonso Siciliano dists = realloc(dists, (nminibars + 1) * minibar_size); 9818f20d5dSAlfonso Siciliano if (dists == NULL) 99bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 100bd1df636SDevin Teske 10118f20d5dSAlfonso Siciliano /* Set file path */ 102dd8ca6b2SBaptiste Daroussin dists[nminibars].path = distrib; 103bd1df636SDevin Teske 10418f20d5dSAlfonso Siciliano /* Set mini bar label */ 10518f20d5dSAlfonso Siciliano dists[nminibars].label = strrchr(dists[nminibars].path, '/'); 10618f20d5dSAlfonso Siciliano if (dists[nminibars].label == NULL) 10718f20d5dSAlfonso Siciliano dists[nminibars].label = dists[nminibars].path; 108bd1df636SDevin Teske 109bd1df636SDevin Teske /* Set initial length in files (-1 == error) */ 11018f20d5dSAlfonso Siciliano dists[nminibars].size = count_files(dists[nminibars].path); 11118f20d5dSAlfonso Siciliano if (dists[nminibars].size < 0) { 11218f20d5dSAlfonso Siciliano bsddialog_end(); 113bd1df636SDevin Teske return (EXIT_FAILURE); 114bd1df636SDevin Teske } 115bd1df636SDevin Teske 116263660c0SAlfonso Siciliano /* Set initial status and implicitly miniperc to pending */ 117263660c0SAlfonso Siciliano dists[nminibars].status = BSDDIALOG_MG_PENDING; 11818f20d5dSAlfonso Siciliano 11918f20d5dSAlfonso Siciliano /* Set initial read */ 12018f20d5dSAlfonso Siciliano dists[nminibars].read = 0; 12118f20d5dSAlfonso Siciliano 12218f20d5dSAlfonso Siciliano nminibars += 1; 123bd1df636SDevin Teske } 124bd1df636SDevin Teske 125bd1df636SDevin Teske /* Optionally chdir(2) into $BSDINSTALL_CHROOT */ 126bd1df636SDevin Teske chrootdir = getenv("BSDINSTALL_CHROOT"); 127bd1df636SDevin Teske if (chrootdir != NULL && chdir(chrootdir) != 0) { 12882ac9f2bSDevin Teske snprintf(error, sizeof(error), 129bd1df636SDevin Teske "Could not change to directory %s: %s\n", 130bd1df636SDevin Teske chrootdir, strerror(errno)); 131*f9549860SBaptiste Daroussin conf.title = "Error"; 13218f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, error, 0, 0); 13318f20d5dSAlfonso Siciliano bsddialog_end(); 13482ac9f2bSDevin Teske return (EXIT_FAILURE); 13557bda1b6SNathan Whitehorn } 13657bda1b6SNathan Whitehorn 1374ff1fc3bSDevin Teske /* Set cleanup routine for Ctrl-C action */ 1384ff1fc3bSDevin Teske act.sa_handler = sig_int; 1394ff1fc3bSDevin Teske sigaction(SIGINT, &act, 0); 1402118f387SNathan Whitehorn 141263660c0SAlfonso Siciliano conf.title = "Archive Extraction"; 142263660c0SAlfonso Siciliano conf.auto_minwidth = 40; 14318f20d5dSAlfonso Siciliano pvconf.callback = extract_files; 14418f20d5dSAlfonso Siciliano pvconf.refresh = 1; 145263660c0SAlfonso Siciliano pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec."; 14618f20d5dSAlfonso Siciliano bsddialog_total_progview = 0; 14718f20d5dSAlfonso Siciliano bsddialog_interruptprogview = bsddialog_abortprogview = false; 14818f20d5dSAlfonso Siciliano retval = bsddialog_progressview(&conf, 149263660c0SAlfonso Siciliano "\nExtracting distribution files...\n", 0, 0, 150263660c0SAlfonso Siciliano &pvconf, nminibars, dists); 151263660c0SAlfonso Siciliano 152263660c0SAlfonso Siciliano if (retval == BSDDIALOG_ERROR) { 153263660c0SAlfonso Siciliano fprintf(stderr, "progressview error: %s\n", 154263660c0SAlfonso Siciliano bsddialog_geterror()); 155263660c0SAlfonso Siciliano } 15657bda1b6SNathan Whitehorn 15718f20d5dSAlfonso Siciliano bsddialog_end(); 15818f20d5dSAlfonso Siciliano 159dd8ca6b2SBaptiste Daroussin free(distribs); 16018f20d5dSAlfonso Siciliano free(dists); 1612118f387SNathan Whitehorn 1622118f387SNathan Whitehorn return (retval); 1632118f387SNathan Whitehorn } 1642118f387SNathan Whitehorn 1654ff1fc3bSDevin Teske static void 1664ff1fc3bSDevin Teske sig_int(int sig __unused) 1674ff1fc3bSDevin Teske { 16818f20d5dSAlfonso Siciliano bsddialog_interruptprogview = true; 1694ff1fc3bSDevin Teske } 1704ff1fc3bSDevin Teske 171bd1df636SDevin Teske /* 172bd1df636SDevin Teske * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST 173bd1df636SDevin Teske * if it exists, otherwise uses archive(3) to read the archive file. 174bd1df636SDevin Teske */ 1752118f387SNathan Whitehorn static int 17646cc2c02SNathan Whitehorn count_files(const char *file) 17746cc2c02SNathan Whitehorn { 17882ac9f2bSDevin Teske static FILE *manifest = NULL; 179bd1df636SDevin Teske char *p; 18082ac9f2bSDevin Teske int file_count; 18182ac9f2bSDevin Teske int retval; 182bd1df636SDevin Teske size_t span; 18346cc2c02SNathan Whitehorn struct archive_entry *entry; 18482ac9f2bSDevin Teske char line[512]; 18582ac9f2bSDevin Teske char path[PATH_MAX]; 18682ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 18718f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 18846cc2c02SNathan Whitehorn 18946cc2c02SNathan Whitehorn if (manifest == NULL) { 190bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/MANIFEST", distdir); 19146cc2c02SNathan Whitehorn manifest = fopen(path, "r"); 19246cc2c02SNathan Whitehorn } 19346cc2c02SNathan Whitehorn 19446cc2c02SNathan Whitehorn if (manifest != NULL) { 195896a9484SNathan Whitehorn rewind(manifest); 19646cc2c02SNathan Whitehorn while (fgets(line, sizeof(line), manifest) != NULL) { 197bd1df636SDevin Teske p = &line[0]; 198bd1df636SDevin Teske span = strcspn(p, "\t") ; 199bd1df636SDevin Teske if (span < 1 || strncmp(p, file, span) != 0) 20046cc2c02SNathan Whitehorn continue; 20146cc2c02SNathan Whitehorn 20246cc2c02SNathan Whitehorn /* 20346cc2c02SNathan Whitehorn * We're at the right manifest line. The file count is 20446cc2c02SNathan Whitehorn * in the third element 20546cc2c02SNathan Whitehorn */ 206bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 207bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 208bd1df636SDevin Teske if (span > 0) { 209bd1df636SDevin Teske file_count = (int)strtol(p, (char **)NULL, 10); 210bd1df636SDevin Teske if (file_count == 0 && errno == EINVAL) 211bd1df636SDevin Teske continue; 212bd1df636SDevin Teske return (file_count); 213bd1df636SDevin Teske } 21446cc2c02SNathan Whitehorn } 21546cc2c02SNathan Whitehorn } 21646cc2c02SNathan Whitehorn 217bd1df636SDevin Teske /* 218bd1df636SDevin Teske * Either no manifest, or manifest didn't mention this archive. 219bd1df636SDevin Teske * Use archive(3) to read the archive, counting files within. 220bd1df636SDevin Teske */ 22118f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 222bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 223bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 224bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 225263660c0SAlfonso Siciliano conf.title = "Extract Error"; 22618f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 227bd1df636SDevin Teske return (-1); 228bd1df636SDevin Teske } 22946cc2c02SNathan Whitehorn archive_read_support_format_all(archive); 230ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 231bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file); 23282ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 23382ac9f2bSDevin Teske if (retval != ARCHIVE_OK) { 23446cc2c02SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 23546cc2c02SNathan Whitehorn "Error while extracting %s: %s\n", file, 23646cc2c02SNathan Whitehorn archive_error_string(archive)); 237263660c0SAlfonso Siciliano conf.title = "Extract Error"; 23818f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 2394ff1fc3bSDevin Teske archive = NULL; 24046cc2c02SNathan Whitehorn return (-1); 24146cc2c02SNathan Whitehorn } 24246cc2c02SNathan Whitehorn 24346cc2c02SNathan Whitehorn file_count = 0; 24446cc2c02SNathan Whitehorn while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) 24546cc2c02SNathan Whitehorn file_count++; 24646cc2c02SNathan Whitehorn archive_read_free(archive); 2474ff1fc3bSDevin Teske archive = NULL; 24846cc2c02SNathan Whitehorn 24946cc2c02SNathan Whitehorn return (file_count); 25046cc2c02SNathan Whitehorn } 25146cc2c02SNathan Whitehorn 25246cc2c02SNathan Whitehorn static int 25318f20d5dSAlfonso Siciliano extract_files(struct bsddialog_fileminibar *file) 2542118f387SNathan Whitehorn { 25582ac9f2bSDevin Teske int retval; 2562118f387SNathan Whitehorn struct archive_entry *entry; 25782ac9f2bSDevin Teske char path[PATH_MAX]; 25882ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 25918f20d5dSAlfonso Siciliano struct bsddialog_conf conf; 26018f20d5dSAlfonso Siciliano 26118f20d5dSAlfonso Siciliano bsddialog_initconf(&conf); 2622118f387SNathan Whitehorn 2634ff1fc3bSDevin Teske /* Open the archive if necessary */ 2644ff1fc3bSDevin Teske if (archive == NULL) { 265bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 266bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 267bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 268263660c0SAlfonso Siciliano conf.title = "Extract Error"; 26918f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 27018f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 2714ff1fc3bSDevin Teske return (-1); 2722118f387SNathan Whitehorn } 2732118f387SNathan Whitehorn archive_read_support_format_all(archive); 274ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 275bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file->path); 27682ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 277bd1df636SDevin Teske if (retval != 0) { 278bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 27918f20d5dSAlfonso Siciliano "Error opening %s: %s\n", file->label, 280bd1df636SDevin Teske archive_error_string(archive)); 281263660c0SAlfonso Siciliano conf.title = "Extract Error"; 28218f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 283263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED; 28418f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 2854ff1fc3bSDevin Teske return (-1); 2864ff1fc3bSDevin Teske } 287bd1df636SDevin Teske } 2882118f387SNathan Whitehorn 2894ff1fc3bSDevin Teske /* Read the next archive header */ 2904ff1fc3bSDevin Teske retval = archive_read_next_header(archive, &entry); 2912118f387SNathan Whitehorn 2924ff1fc3bSDevin Teske /* If that went well, perform the extraction */ 2934ff1fc3bSDevin Teske if (retval == ARCHIVE_OK) 29482ac9f2bSDevin Teske retval = archive_read_extract(archive, entry, 2952118f387SNathan Whitehorn ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | 2962118f387SNathan Whitehorn ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | 2972118f387SNathan Whitehorn ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); 2982118f387SNathan Whitehorn 2994ff1fc3bSDevin Teske /* Test for either EOF or error */ 3004ff1fc3bSDevin Teske if (retval == ARCHIVE_EOF) { 3012118f387SNathan Whitehorn archive_read_free(archive); 3024ff1fc3bSDevin Teske archive = NULL; 303263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_DONE; /*Done*/; 3044ff1fc3bSDevin Teske return (100); 305a5837f2cSNathan Whitehorn } else if (retval != ARCHIVE_OK && 306a5837f2cSNathan Whitehorn !(retval == ARCHIVE_WARN && 307a5837f2cSNathan Whitehorn strcmp(archive_error_string(archive), "Can't restore time") == 0)) { 308a5837f2cSNathan Whitehorn /* 309a5837f2cSNathan Whitehorn * Print any warning/error messages except inability to set 310a5837f2cSNathan Whitehorn * ctime/mtime, which is not fatal, or even interesting, 311a5837f2cSNathan Whitehorn * for our purposes. Would be nice if this were a libarchive 312a5837f2cSNathan Whitehorn * option. 313a5837f2cSNathan Whitehorn */ 3144ff1fc3bSDevin Teske snprintf(errormsg, sizeof(errormsg), 31518f20d5dSAlfonso Siciliano "Error while extracting %s: %s\n", file->label, 3164ff1fc3bSDevin Teske archive_error_string(archive)); 317263660c0SAlfonso Siciliano conf.title = "Extract Error"; 31818f20d5dSAlfonso Siciliano bsddialog_msgbox(&conf, errormsg, 0, 0); 319263660c0SAlfonso Siciliano file->status = BSDDIALOG_MG_FAILED; /* Failed */ 32018f20d5dSAlfonso Siciliano bsddialog_abortprogview = true; 3214ff1fc3bSDevin Teske return (-1); 3222118f387SNathan Whitehorn } 3232118f387SNathan Whitehorn 32418f20d5dSAlfonso Siciliano bsddialog_total_progview++; 3254ff1fc3bSDevin Teske file->read++; 3264ff1fc3bSDevin Teske 3274ff1fc3bSDevin Teske /* Calculate [overall] percentage of completion (if possible) */ 32818f20d5dSAlfonso Siciliano if (file->size >= 0) 32918f20d5dSAlfonso Siciliano return (file->read * 100 / file->size); 3304ff1fc3bSDevin Teske else 3314ff1fc3bSDevin Teske return (-1); 3322118f387SNathan Whitehorn } 333