12118f387SNathan Whitehorn /*- 22118f387SNathan Whitehorn * Copyright (c) 2011 Nathan Whitehorn 382ac9f2bSDevin Teske * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org> 42118f387SNathan Whitehorn * All rights reserved. 52118f387SNathan Whitehorn * 62118f387SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 72118f387SNathan Whitehorn * modification, are permitted provided that the following conditions 82118f387SNathan Whitehorn * are met: 92118f387SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 102118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 112118f387SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 122118f387SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 132118f387SNathan Whitehorn * documentation and/or other materials provided with the distribution. 142118f387SNathan Whitehorn * 152118f387SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 162118f387SNathan Whitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 172118f387SNathan Whitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 182118f387SNathan Whitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 192118f387SNathan Whitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 202118f387SNathan Whitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 212118f387SNathan Whitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 222118f387SNathan Whitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 232118f387SNathan Whitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 242118f387SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 252118f387SNathan Whitehorn * SUCH DAMAGE. 262118f387SNathan Whitehorn */ 272118f387SNathan Whitehorn 2882ac9f2bSDevin Teske #include <sys/cdefs.h> 2982ac9f2bSDevin Teske __FBSDID("$FreeBSD$"); 3082ac9f2bSDevin Teske 3146cc2c02SNathan Whitehorn #include <sys/param.h> 3282ac9f2bSDevin Teske #include <archive.h> 3382ac9f2bSDevin Teske #include <ctype.h> 3482ac9f2bSDevin Teske #include <dialog.h> 35*4ff1fc3bSDevin Teske #include <dpv.h> 3682ac9f2bSDevin Teske #include <err.h> 372118f387SNathan Whitehorn #include <errno.h> 382118f387SNathan Whitehorn #include <limits.h> 3982ac9f2bSDevin Teske #include <stdio.h> 4082ac9f2bSDevin Teske #include <stdlib.h> 4182ac9f2bSDevin Teske #include <string.h> 4282ac9f2bSDevin Teske #include <unistd.h> 432118f387SNathan Whitehorn 44bd1df636SDevin Teske /* Data to process */ 45bd1df636SDevin Teske static char *distdir = NULL; 46*4ff1fc3bSDevin Teske static struct archive *archive = NULL; 47*4ff1fc3bSDevin Teske static struct dpv_file_node *dists = NULL; 48bd1df636SDevin Teske 49bd1df636SDevin Teske /* Function prototypes */ 50*4ff1fc3bSDevin Teske static void sig_int(int sig); 5182ac9f2bSDevin Teske static int count_files(const char *file); 52*4ff1fc3bSDevin Teske static int extract_files(struct dpv_file_node *file, int out); 53bd1df636SDevin Teske 54bd1df636SDevin Teske #if __FreeBSD_version <= 1000008 /* r232154: bump for libarchive update */ 55bd1df636SDevin Teske #define archive_read_support_filter_all(x) \ 56bd1df636SDevin Teske archive_read_support_compression_all(x) 57bd1df636SDevin Teske #endif 58bd1df636SDevin Teske 59bd1df636SDevin Teske #define _errx(...) (end_dialog(), errx(__VA_ARGS__)) 602118f387SNathan Whitehorn 612118f387SNathan Whitehorn int 622118f387SNathan Whitehorn main(void) 632118f387SNathan Whitehorn { 64bd1df636SDevin Teske char *chrootdir; 65bd1df636SDevin Teske char *distributions; 6682ac9f2bSDevin Teske int retval; 67*4ff1fc3bSDevin Teske size_t config_size = sizeof(struct dpv_config); 68*4ff1fc3bSDevin Teske size_t file_node_size = sizeof(struct dpv_file_node); 69bd1df636SDevin Teske size_t span; 70*4ff1fc3bSDevin Teske struct dpv_config *config; 71*4ff1fc3bSDevin Teske struct dpv_file_node *dist = dists; 72*4ff1fc3bSDevin Teske static char backtitle[] = "FreeBSD Installer"; 73*4ff1fc3bSDevin Teske static char title[] = "Archive Extraction"; 74*4ff1fc3bSDevin Teske static char aprompt[] = "\n Overall Progress:"; 75*4ff1fc3bSDevin Teske static char pprompt[] = "Extracting distribution files...\n"; 76*4ff1fc3bSDevin Teske struct sigaction act; 7782ac9f2bSDevin Teske char error[PATH_MAX + 512]; 78bfd258f7SNathan Whitehorn 79bd1df636SDevin Teske if ((distributions = getenv("DISTRIBUTIONS")) == NULL) 8082ac9f2bSDevin Teske errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); 81bd1df636SDevin Teske if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) 82bd1df636SDevin Teske distdir = __DECONST(char *, ""); 83bfd258f7SNathan Whitehorn 84bd1df636SDevin Teske /* Initialize dialog(3) */ 8557bda1b6SNathan Whitehorn init_dialog(stdin, stdout); 86*4ff1fc3bSDevin Teske dialog_vars.backtitle = backtitle; 8757bda1b6SNathan Whitehorn dlg_put_backtitle(); 8857bda1b6SNathan Whitehorn 89bd1df636SDevin Teske dialog_msgbox("", 90bd1df636SDevin Teske "Checking distribution archives.\nPlease wait...", 4, 35, FALSE); 91bd1df636SDevin Teske 92bd1df636SDevin Teske /* 93*4ff1fc3bSDevin Teske * Parse $DISTRIBUTIONS into dpv(3) linked-list 94bd1df636SDevin Teske */ 95bd1df636SDevin Teske while (*distributions != '\0') { 96bd1df636SDevin Teske span = strcspn(distributions, "\t\n\v\f\r "); 97bd1df636SDevin Teske if (span < 1) { /* currently on whitespace */ 98bd1df636SDevin Teske distributions++; 99bd1df636SDevin Teske continue; 100bd1df636SDevin Teske } 101bd1df636SDevin Teske 102bd1df636SDevin Teske /* Allocate a new struct for the distribution */ 103bd1df636SDevin Teske if (dist == NULL) { 104bd1df636SDevin Teske if ((dist = calloc(1, file_node_size)) == NULL) 105bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 106bd1df636SDevin Teske dists = dist; 107bd1df636SDevin Teske } else { 108bd1df636SDevin Teske dist->next = calloc(1, file_node_size); 109bd1df636SDevin Teske if (dist->next == NULL) 110bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 111bd1df636SDevin Teske dist = dist->next; 112bd1df636SDevin Teske } 113bd1df636SDevin Teske 114bd1df636SDevin Teske /* Set path */ 115bd1df636SDevin Teske if ((dist->path = malloc(span + 1)) == NULL) 116bd1df636SDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 117bd1df636SDevin Teske snprintf(dist->path, span + 1, "%s", distributions); 118bd1df636SDevin Teske dist->path[span] = '\0'; 119bd1df636SDevin Teske 120bd1df636SDevin Teske /* Set display name */ 121bd1df636SDevin Teske dist->name = strrchr(dist->path, '/'); 122bd1df636SDevin Teske if (dist->name == NULL) 123bd1df636SDevin Teske dist->name = dist->path; 124bd1df636SDevin Teske 125bd1df636SDevin Teske /* Set initial length in files (-1 == error) */ 126bd1df636SDevin Teske dist->length = count_files(dist->path); 127bd1df636SDevin Teske if (dist->length < 0) { 128bd1df636SDevin Teske end_dialog(); 129bd1df636SDevin Teske return (EXIT_FAILURE); 130bd1df636SDevin Teske } 131bd1df636SDevin Teske 132bd1df636SDevin Teske distributions += span; 133bd1df636SDevin Teske } 134bd1df636SDevin Teske 135bd1df636SDevin Teske /* Optionally chdir(2) into $BSDINSTALL_CHROOT */ 136bd1df636SDevin Teske chrootdir = getenv("BSDINSTALL_CHROOT"); 137bd1df636SDevin Teske if (chrootdir != NULL && chdir(chrootdir) != 0) { 13882ac9f2bSDevin Teske snprintf(error, sizeof(error), 139bd1df636SDevin Teske "Could not change to directory %s: %s\n", 140bd1df636SDevin Teske chrootdir, strerror(errno)); 14157bda1b6SNathan Whitehorn dialog_msgbox("Error", error, 0, 0, TRUE); 14257bda1b6SNathan Whitehorn end_dialog(); 14382ac9f2bSDevin Teske return (EXIT_FAILURE); 14457bda1b6SNathan Whitehorn } 14557bda1b6SNathan Whitehorn 146*4ff1fc3bSDevin Teske /* Set cleanup routine for Ctrl-C action */ 147*4ff1fc3bSDevin Teske act.sa_handler = sig_int; 148*4ff1fc3bSDevin Teske sigaction(SIGINT, &act, 0); 1492118f387SNathan Whitehorn 150*4ff1fc3bSDevin Teske /* 151*4ff1fc3bSDevin Teske * Hand off to dpv(3) 152*4ff1fc3bSDevin Teske */ 153*4ff1fc3bSDevin Teske if ((config = calloc(1, config_size)) == NULL) 154*4ff1fc3bSDevin Teske _errx(EXIT_FAILURE, "Out of memory!"); 155*4ff1fc3bSDevin Teske config->backtitle = backtitle; 156*4ff1fc3bSDevin Teske config->title = title; 157*4ff1fc3bSDevin Teske config->pprompt = pprompt; 158*4ff1fc3bSDevin Teske config->aprompt = aprompt; 159*4ff1fc3bSDevin Teske config->options |= DPV_WIDE_MODE; 160*4ff1fc3bSDevin Teske config->label_size = -1; 161*4ff1fc3bSDevin Teske config->action = extract_files; 162*4ff1fc3bSDevin Teske config->status_solo = 163*4ff1fc3bSDevin Teske "%10lli files read @ %'9.1f files/sec."; 164*4ff1fc3bSDevin Teske config->status_many = 165*4ff1fc3bSDevin Teske "%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]"; 16657bda1b6SNathan Whitehorn end_dialog(); 167*4ff1fc3bSDevin Teske retval = dpv(config, dists); 16857bda1b6SNathan Whitehorn 169*4ff1fc3bSDevin Teske dpv_free(); 170bd1df636SDevin Teske while ((dist = dists) != NULL) { 171bd1df636SDevin Teske dists = dist->next; 172bd1df636SDevin Teske if (dist->path != NULL) 173bd1df636SDevin Teske free(dist->path); 174bd1df636SDevin Teske free(dist); 175bd1df636SDevin Teske } 1762118f387SNathan Whitehorn 1772118f387SNathan Whitehorn return (retval); 1782118f387SNathan Whitehorn } 1792118f387SNathan Whitehorn 180*4ff1fc3bSDevin Teske static void 181*4ff1fc3bSDevin Teske sig_int(int sig __unused) 182*4ff1fc3bSDevin Teske { 183*4ff1fc3bSDevin Teske dpv_interrupt = TRUE; 184*4ff1fc3bSDevin Teske } 185*4ff1fc3bSDevin Teske 186bd1df636SDevin Teske /* 187bd1df636SDevin Teske * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST 188bd1df636SDevin Teske * if it exists, otherwise uses archive(3) to read the archive file. 189bd1df636SDevin Teske */ 1902118f387SNathan Whitehorn static int 19146cc2c02SNathan Whitehorn count_files(const char *file) 19246cc2c02SNathan Whitehorn { 19382ac9f2bSDevin Teske static FILE *manifest = NULL; 194bd1df636SDevin Teske char *p; 19582ac9f2bSDevin Teske int file_count; 19682ac9f2bSDevin Teske int retval; 197bd1df636SDevin Teske size_t span; 19846cc2c02SNathan Whitehorn struct archive_entry *entry; 19982ac9f2bSDevin Teske char line[512]; 20082ac9f2bSDevin Teske char path[PATH_MAX]; 20182ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 20246cc2c02SNathan Whitehorn 20346cc2c02SNathan Whitehorn if (manifest == NULL) { 204bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/MANIFEST", distdir); 20546cc2c02SNathan Whitehorn manifest = fopen(path, "r"); 20646cc2c02SNathan Whitehorn } 20746cc2c02SNathan Whitehorn 20846cc2c02SNathan Whitehorn if (manifest != NULL) { 209896a9484SNathan Whitehorn rewind(manifest); 21046cc2c02SNathan Whitehorn while (fgets(line, sizeof(line), manifest) != NULL) { 211bd1df636SDevin Teske p = &line[0]; 212bd1df636SDevin Teske span = strcspn(p, "\t") ; 213bd1df636SDevin Teske if (span < 1 || strncmp(p, file, span) != 0) 21446cc2c02SNathan Whitehorn continue; 21546cc2c02SNathan Whitehorn 21646cc2c02SNathan Whitehorn /* 21746cc2c02SNathan Whitehorn * We're at the right manifest line. The file count is 21846cc2c02SNathan Whitehorn * in the third element 21946cc2c02SNathan Whitehorn */ 220bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 221bd1df636SDevin Teske span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); 222bd1df636SDevin Teske if (span > 0) { 223bd1df636SDevin Teske file_count = (int)strtol(p, (char **)NULL, 10); 224bd1df636SDevin Teske if (file_count == 0 && errno == EINVAL) 225bd1df636SDevin Teske continue; 226bd1df636SDevin Teske return (file_count); 227bd1df636SDevin Teske } 22846cc2c02SNathan Whitehorn } 22946cc2c02SNathan Whitehorn } 23046cc2c02SNathan Whitehorn 231bd1df636SDevin Teske /* 232bd1df636SDevin Teske * Either no manifest, or manifest didn't mention this archive. 233bd1df636SDevin Teske * Use archive(3) to read the archive, counting files within. 234bd1df636SDevin Teske */ 235bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 236bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 237bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 238bd1df636SDevin Teske dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 239bd1df636SDevin Teske return (-1); 240bd1df636SDevin Teske } 24146cc2c02SNathan Whitehorn archive_read_support_format_all(archive); 242ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 243bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file); 24482ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 24582ac9f2bSDevin Teske if (retval != ARCHIVE_OK) { 24646cc2c02SNathan Whitehorn snprintf(errormsg, sizeof(errormsg), 24746cc2c02SNathan Whitehorn "Error while extracting %s: %s\n", file, 24846cc2c02SNathan Whitehorn archive_error_string(archive)); 24946cc2c02SNathan Whitehorn dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 250*4ff1fc3bSDevin Teske archive = NULL; 25146cc2c02SNathan Whitehorn return (-1); 25246cc2c02SNathan Whitehorn } 25346cc2c02SNathan Whitehorn 25446cc2c02SNathan Whitehorn file_count = 0; 25546cc2c02SNathan Whitehorn while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) 25646cc2c02SNathan Whitehorn file_count++; 25746cc2c02SNathan Whitehorn archive_read_free(archive); 258*4ff1fc3bSDevin Teske archive = NULL; 25946cc2c02SNathan Whitehorn 26046cc2c02SNathan Whitehorn return (file_count); 26146cc2c02SNathan Whitehorn } 26246cc2c02SNathan Whitehorn 26346cc2c02SNathan Whitehorn static int 264*4ff1fc3bSDevin Teske extract_files(struct dpv_file_node *file, int out __unused) 2652118f387SNathan Whitehorn { 26682ac9f2bSDevin Teske int retval; 2672118f387SNathan Whitehorn struct archive_entry *entry; 26882ac9f2bSDevin Teske char path[PATH_MAX]; 26982ac9f2bSDevin Teske char errormsg[PATH_MAX + 512]; 2702118f387SNathan Whitehorn 271*4ff1fc3bSDevin Teske /* Open the archive if necessary */ 272*4ff1fc3bSDevin Teske if (archive == NULL) { 273bd1df636SDevin Teske if ((archive = archive_read_new()) == NULL) { 274bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 275bd1df636SDevin Teske "Error: %s\n", archive_error_string(NULL)); 276bd1df636SDevin Teske dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 277*4ff1fc3bSDevin Teske dpv_abort = 1; 278*4ff1fc3bSDevin Teske return (-1); 2792118f387SNathan Whitehorn } 2802118f387SNathan Whitehorn archive_read_support_format_all(archive); 281ebb8fc42SMartin Matuska archive_read_support_filter_all(archive); 282bd1df636SDevin Teske snprintf(path, sizeof(path), "%s/%s", distdir, file->path); 28382ac9f2bSDevin Teske retval = archive_read_open_filename(archive, path, 4096); 284bd1df636SDevin Teske if (retval != 0) { 285bd1df636SDevin Teske snprintf(errormsg, sizeof(errormsg), 286bd1df636SDevin Teske "Error opening %s: %s\n", file->name, 287bd1df636SDevin Teske archive_error_string(archive)); 288bd1df636SDevin Teske dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 289*4ff1fc3bSDevin Teske file->status = DPV_STATUS_FAILED; 290*4ff1fc3bSDevin Teske dpv_abort = 1; 291*4ff1fc3bSDevin Teske return (-1); 292*4ff1fc3bSDevin Teske } 293bd1df636SDevin Teske } 2942118f387SNathan Whitehorn 295*4ff1fc3bSDevin Teske /* Read the next archive header */ 296*4ff1fc3bSDevin Teske retval = archive_read_next_header(archive, &entry); 2972118f387SNathan Whitehorn 298*4ff1fc3bSDevin Teske /* If that went well, perform the extraction */ 299*4ff1fc3bSDevin Teske if (retval == ARCHIVE_OK) 30082ac9f2bSDevin Teske retval = archive_read_extract(archive, entry, 3012118f387SNathan Whitehorn ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | 3022118f387SNathan Whitehorn ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | 3032118f387SNathan Whitehorn ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); 3042118f387SNathan Whitehorn 305*4ff1fc3bSDevin Teske /* Test for either EOF or error */ 306*4ff1fc3bSDevin Teske if (retval == ARCHIVE_EOF) { 3072118f387SNathan Whitehorn archive_read_free(archive); 308*4ff1fc3bSDevin Teske archive = NULL; 309*4ff1fc3bSDevin Teske file->status = DPV_STATUS_DONE; 310*4ff1fc3bSDevin Teske return (100); 311*4ff1fc3bSDevin Teske } else if (retval != ARCHIVE_OK) { 312*4ff1fc3bSDevin Teske snprintf(errormsg, sizeof(errormsg), 313*4ff1fc3bSDevin Teske "Error while extracting %s: %s\n", file->name, 314*4ff1fc3bSDevin Teske archive_error_string(archive)); 315*4ff1fc3bSDevin Teske dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 316*4ff1fc3bSDevin Teske file->status = DPV_STATUS_FAILED; 317*4ff1fc3bSDevin Teske dpv_abort = 1; 318*4ff1fc3bSDevin Teske return (-1); 3192118f387SNathan Whitehorn } 3202118f387SNathan Whitehorn 321*4ff1fc3bSDevin Teske dpv_overall_read++; 322*4ff1fc3bSDevin Teske file->read++; 323*4ff1fc3bSDevin Teske 324*4ff1fc3bSDevin Teske /* Calculate [overall] percentage of completion (if possible) */ 325*4ff1fc3bSDevin Teske if (file->length >= 0) 326*4ff1fc3bSDevin Teske return (file->read * 100 / file->length); 327*4ff1fc3bSDevin Teske else 328*4ff1fc3bSDevin Teske return (-1); 3292118f387SNathan Whitehorn } 330