1 /*- 2 * Copyright (c) 2011 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/param.h> 30 #include <stdio.h> 31 #include <errno.h> 32 #include <limits.h> 33 #include <archive.h> 34 #include <dialog.h> 35 36 static int extract_files(int nfiles, const char **files); 37 38 int 39 main(void) 40 { 41 char *diststring; 42 const char **dists; 43 int i, retval, ndists = 0; 44 45 if (getenv("DISTRIBUTIONS") == NULL) { 46 fprintf(stderr, "DISTRIBUTIONS variable is not set\n"); 47 return (1); 48 } 49 50 diststring = strdup(getenv("DISTRIBUTIONS")); 51 for (i = 0; diststring[i] != 0; i++) 52 if (isspace(diststring[i]) && !isspace(diststring[i+1])) 53 ndists++; 54 ndists++; /* Last one */ 55 56 dists = calloc(ndists, sizeof(const char *)); 57 if (dists == NULL) { 58 fprintf(stderr, "Out of memory!\n"); 59 free(diststring); 60 return (1); 61 } 62 63 for (i = 0; i < ndists; i++) 64 dists[i] = strsep(&diststring, " \t"); 65 66 init_dialog(stdin, stdout); 67 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); 68 dlg_put_backtitle(); 69 70 if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) { 71 char error[512]; 72 sprintf(error, "Could could change to directory %s: %s\n", 73 getenv("BSDINSTALL_DISTDIR"), strerror(errno)); 74 dialog_msgbox("Error", error, 0, 0, TRUE); 75 end_dialog(); 76 return (1); 77 } 78 79 retval = extract_files(ndists, dists); 80 81 end_dialog(); 82 83 free(diststring); 84 free(dists); 85 86 return (retval); 87 } 88 89 static int 90 count_files(const char *file) 91 { 92 struct archive *archive; 93 struct archive_entry *entry; 94 static FILE *manifest = NULL; 95 char path[MAXPATHLEN]; 96 char errormsg[512]; 97 int file_count, err; 98 99 if (manifest == NULL) { 100 sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR")); 101 manifest = fopen(path, "r"); 102 } 103 104 if (manifest != NULL) { 105 char line[512]; 106 char *tok1, *tok2; 107 108 rewind(manifest); 109 while (fgets(line, sizeof(line), manifest) != NULL) { 110 tok2 = line; 111 tok1 = strsep(&tok2, "\t"); 112 if (tok1 == NULL || strcmp(tok1, file) != 0) 113 continue; 114 115 /* 116 * We're at the right manifest line. The file count is 117 * in the third element 118 */ 119 tok1 = strsep(&tok2, "\t"); 120 tok1 = strsep(&tok2, "\t"); 121 if (tok1 != NULL) 122 return atoi(tok1); 123 } 124 } 125 126 /* Either we didn't have a manifest, or this archive wasn't there */ 127 archive = archive_read_new(); 128 archive_read_support_format_all(archive); 129 archive_read_support_filter_all(archive); 130 sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file); 131 err = archive_read_open_filename(archive, path, 4096); 132 if (err != ARCHIVE_OK) { 133 snprintf(errormsg, sizeof(errormsg), 134 "Error while extracting %s: %s\n", file, 135 archive_error_string(archive)); 136 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 137 return (-1); 138 } 139 140 file_count = 0; 141 while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) 142 file_count++; 143 archive_read_free(archive); 144 145 return (file_count); 146 } 147 148 static int 149 extract_files(int nfiles, const char **files) 150 { 151 const char *items[nfiles*2]; 152 char path[PATH_MAX]; 153 int archive_files[nfiles]; 154 int total_files, current_files, archive_file; 155 struct archive *archive; 156 struct archive_entry *entry; 157 char errormsg[512]; 158 char status[8]; 159 int i, err, progress, last_progress; 160 161 err = 0; 162 progress = 0; 163 164 /* Make the transfer list for dialog */ 165 for (i = 0; i < nfiles; i++) { 166 items[i*2] = strrchr(files[i], '/'); 167 if (items[i*2] != NULL) 168 items[i*2]++; 169 else 170 items[i*2] = files[i]; 171 items[i*2 + 1] = "Pending"; 172 } 173 174 dialog_msgbox("", 175 "Checking distribution archives.\nPlease wait...", 0, 0, FALSE); 176 177 /* Count all the files */ 178 total_files = 0; 179 for (i = 0; i < nfiles; i++) { 180 archive_files[i] = count_files(files[i]); 181 if (archive_files[i] < 0) 182 return (-1); 183 total_files += archive_files[i]; 184 } 185 186 current_files = 0; 187 188 for (i = 0; i < nfiles; i++) { 189 archive = archive_read_new(); 190 archive_read_support_format_all(archive); 191 archive_read_support_filter_all(archive); 192 sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]); 193 err = archive_read_open_filename(archive, path, 4096); 194 195 items[i*2 + 1] = "In Progress"; 196 archive_file = 0; 197 198 while ((err = archive_read_next_header(archive, &entry)) == 199 ARCHIVE_OK) { 200 last_progress = progress; 201 progress = (current_files*100)/total_files; 202 203 sprintf(status, "-%d", 204 (archive_file*100)/archive_files[i]); 205 items[i*2 + 1] = status; 206 207 if (progress > last_progress) 208 dialog_mixedgauge("Archive Extraction", 209 "Extracting distribution files...", 0, 0, 210 progress, nfiles, 211 __DECONST(char **, items)); 212 213 err = archive_read_extract(archive, entry, 214 ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | 215 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | 216 ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); 217 218 if (err != ARCHIVE_OK) 219 break; 220 221 archive_file++; 222 current_files++; 223 } 224 225 items[i*2 + 1] = "Done"; 226 227 if (err != ARCHIVE_EOF) { 228 snprintf(errormsg, sizeof(errormsg), 229 "Error while extracting %s: %s\n", items[i*2], 230 archive_error_string(archive)); 231 items[i*2 + 1] = "Failed"; 232 dialog_msgbox("Extract Error", errormsg, 0, 0, 233 TRUE); 234 return (err); 235 } 236 237 archive_read_free(archive); 238 } 239 240 return (0); 241 } 242