/*- * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include static int extract_files(struct dpv_file_node *file, int out); static int count_files(const char *file); static struct archive *archive = NULL; int main(void) { char *diststring; int retval, ndists = 0; struct dpv_file_node *file_list = NULL, *curfile = file_list; struct dpv_config *config; size_t span, file_node_size = sizeof(struct dpv_file_node); if (getenv("DISTRIBUTIONS") == NULL) { fprintf(stderr, "DISTRIBUTIONS variable is not set\n"); return (1); } diststring = strdup(getenv("DISTRIBUTIONS")); init_dialog(stdin, stdout); dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); dlg_put_backtitle(); dialog_msgbox("", "Checking distribution archives.\nPlease wait...", 4, 35, FALSE); while (*diststring != '\0') { span = strcspn(diststring, "\t\n\v\f\r "); if (span < 1) { /* currently on whitespace */ diststring++; continue; } ndists++; if (curfile == NULL) { if ((curfile = calloc(1, file_node_size)) == NULL) exit(1); file_list = curfile; } else { if ((curfile->next = calloc(1, file_node_size)) == NULL) exit(1); curfile = curfile->next; } if ((curfile->path = malloc(span + 1)) == NULL) exit(1); snprintf(curfile->path, span + 1, "%s", diststring); curfile->path[span] = '\0'; curfile->name = strrchr(curfile->path, '/'); if (curfile->name == NULL) curfile->name = curfile->path; curfile->length = count_files(curfile->path); if (curfile->length < 0) { end_dialog(); return (1); } diststring += span; } if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) { char error[512]; sprintf(error, "Could could change to directory %s: %s\n", getenv("BSDINSTALL_DISTDIR"), strerror(errno)); dialog_msgbox("Error", error, 0, 0, TRUE); end_dialog(); return (1); } if ((config = calloc(1, sizeof(struct dpv_config))) == NULL) exit(1); config->backtitle = __DECONST(char *, "FreeBSD Installer"); config->title = __DECONST(char *, "Archive Extraction"); config->pprompt = __DECONST(char *, "Extracting distribution files...\n"); config->aprompt = __DECONST(char *, "\n Overall Progress:"); config->options |= DPV_WIDE_MODE; config->label_size = -1; config->action = extract_files; config->status_solo = "%10lli files read @ %'9.1f files/sec."; config->status_many = "%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]"; end_dialog(); retval = dpv(config, file_list); dpv_free(); return (retval); } static int count_files(const char *file) { struct archive_entry *entry; static FILE *manifest = NULL; char path[MAXPATHLEN]; char errormsg[512]; int file_count, err; if (manifest == NULL) { sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR")); manifest = fopen(path, "r"); } if (manifest != NULL) { char line[512]; char *tok1, *tok2; rewind(manifest); while (fgets(line, sizeof(line), manifest) != NULL) { tok2 = line; tok1 = strsep(&tok2, "\t"); if (tok1 == NULL || strcmp(tok1, file) != 0) continue; /* * We're at the right manifest line. The file count is * in the third element */ tok1 = strsep(&tok2, "\t"); tok1 = strsep(&tok2, "\t"); if (tok1 != NULL) return atoi(tok1); } } /* Either we didn't have a manifest, or this archive wasn't there */ archive = archive_read_new(); archive_read_support_format_all(archive); archive_read_support_filter_all(archive); sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file); err = archive_read_open_filename(archive, path, 4096); if (err != ARCHIVE_OK) { snprintf(errormsg, sizeof(errormsg), "Error while extracting %s: %s\n", file, archive_error_string(archive)); dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); archive = NULL; return (-1); } file_count = 0; while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) file_count++; archive_read_free(archive); archive = NULL; return (file_count); } static int extract_files(struct dpv_file_node *file, int out __unused) { char path[PATH_MAX]; struct archive_entry *entry; char errormsg[512]; int err; if (archive == NULL) { if ((archive = archive_read_new()) == NULL) { snprintf(errormsg, sizeof(errormsg), "Error: %s\n", archive_error_string(NULL)); dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); dpv_abort = 1; return (-1); } archive_read_support_format_all(archive); archive_read_support_filter_all(archive); sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file->path); err = archive_read_open_filename(archive, path, 4096); } err = archive_read_next_header(archive, &entry); if (err == ARCHIVE_OK) err = archive_read_extract(archive, entry, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); if (err == ARCHIVE_EOF) { archive_read_free(archive); archive = NULL; file->status = DPV_STATUS_DONE; return (100); } else if (err != ARCHIVE_OK) { snprintf(errormsg, sizeof(errormsg), "Error while extracting %s: %s\n", file->name, archive_error_string(archive)); dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); file->status = DPV_STATUS_FAILED; dpv_abort = 1; return (-1); } overall_read++; file->read++; if (file->length >= 0) return (file->read * 100 / file->length); else return (-1); }