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 = strdup(getenv("DISTRIBUTIONS")); 42 const char **dists; 43 int i, retval, ndists = 0; 44 for (i = 0; diststring[i] != 0; i++) 45 if (isspace(diststring[i]) && !isspace(diststring[i+1])) 46 ndists++; 47 ndists++; /* Last one */ 48 49 dists = calloc(ndists, sizeof(const char *)); 50 if (dists == NULL) { 51 fprintf(stderr, "Out of memory!\n"); 52 return (1); 53 } 54 55 for (i = 0; i < ndists; i++) 56 dists[i] = strsep(&diststring, " \t"); 57 58 init_dialog(stdin, stdout); 59 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); 60 dlg_put_backtitle(); 61 62 if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) { 63 char error[512]; 64 sprintf(error, "Could could change to directory %s: %s\n", 65 getenv("BSDINSTALL_DISTDIR"), strerror(errno)); 66 dialog_msgbox("Error", error, 0, 0, TRUE); 67 end_dialog(); 68 return (1); 69 } 70 71 retval = extract_files(ndists, dists); 72 73 end_dialog(); 74 75 free(diststring); 76 free(dists); 77 78 return (retval); 79 } 80 81 static int 82 count_files(const char *file) 83 { 84 struct archive *archive; 85 struct archive_entry *entry; 86 static FILE *manifest = NULL; 87 char path[MAXPATHLEN]; 88 char errormsg[512]; 89 int file_count, err; 90 91 if (manifest == NULL) { 92 sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR")); 93 manifest = fopen(path, "r"); 94 } 95 96 if (manifest != NULL) { 97 char line[512]; 98 char *tok1, *tok2; 99 100 rewind(manifest); 101 while (fgets(line, sizeof(line), manifest) != NULL) { 102 tok2 = line; 103 tok1 = strsep(&tok2, "\t"); 104 if (tok1 == NULL || strcmp(tok1, file) != 0) 105 continue; 106 107 /* 108 * We're at the right manifest line. The file count is 109 * in the third element 110 */ 111 tok1 = strsep(&tok2, "\t"); 112 tok1 = strsep(&tok2, "\t"); 113 if (tok1 != NULL) 114 return atoi(tok1); 115 } 116 } 117 118 /* Either we didn't have a manifest, or this archive wasn't there */ 119 archive = archive_read_new(); 120 archive_read_support_format_all(archive); 121 archive_read_support_compression_all(archive); 122 sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file); 123 err = archive_read_open_filename(archive, path, 4096); 124 if (err != ARCHIVE_OK) { 125 snprintf(errormsg, sizeof(errormsg), 126 "Error while extracting %s: %s\n", file, 127 archive_error_string(archive)); 128 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); 129 return (-1); 130 } 131 132 file_count = 0; 133 while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) 134 file_count++; 135 archive_read_free(archive); 136 137 return (file_count); 138 } 139 140 static int 141 extract_files(int nfiles, const char **files) 142 { 143 const char *items[nfiles*2]; 144 char path[PATH_MAX]; 145 int archive_files[nfiles]; 146 int total_files, current_files, archive_file; 147 struct archive *archive; 148 struct archive_entry *entry; 149 char errormsg[512]; 150 char status[8]; 151 int i, err, progress, last_progress; 152 153 err = 0; 154 progress = 0; 155 156 /* Make the transfer list for dialog */ 157 for (i = 0; i < nfiles; i++) { 158 items[i*2] = strrchr(files[i], '/'); 159 if (items[i*2] != NULL) 160 items[i*2]++; 161 else 162 items[i*2] = files[i]; 163 items[i*2 + 1] = "Pending"; 164 } 165 166 dialog_msgbox("", 167 "Checking distribution archives.\nPlease wait...", 0, 0, FALSE); 168 169 /* Count all the files */ 170 total_files = 0; 171 for (i = 0; i < nfiles; i++) { 172 archive_files[i] = count_files(files[i]); 173 if (archive_files[i] < 0) 174 return (-1); 175 total_files += archive_files[i]; 176 } 177 178 current_files = 0; 179 180 for (i = 0; i < nfiles; i++) { 181 archive = archive_read_new(); 182 archive_read_support_format_all(archive); 183 archive_read_support_compression_all(archive); 184 sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]); 185 err = archive_read_open_filename(archive, path, 4096); 186 187 items[i*2 + 1] = "In Progress"; 188 archive_file = 0; 189 190 while ((err = archive_read_next_header(archive, &entry)) == 191 ARCHIVE_OK) { 192 last_progress = progress; 193 progress = (current_files*100)/total_files; 194 195 sprintf(status, "-%d", 196 (archive_file*100)/archive_files[i]); 197 items[i*2 + 1] = status; 198 199 if (progress > last_progress) 200 dialog_mixedgauge("Archive Extraction", 201 "Extracting distribution files...", 0, 0, 202 progress, nfiles, 203 __DECONST(char **, items)); 204 205 err = archive_read_extract(archive, entry, 206 ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | 207 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | 208 ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); 209 210 if (err != ARCHIVE_OK) 211 break; 212 213 archive_file++; 214 current_files++; 215 } 216 217 items[i*2 + 1] = "Done"; 218 219 if (err != ARCHIVE_EOF) { 220 snprintf(errormsg, sizeof(errormsg), 221 "Error while extracting %s: %s\n", items[i*2], 222 archive_error_string(archive)); 223 items[i*2 + 1] = "Failed"; 224 dialog_msgbox("Extract Error", errormsg, 0, 0, 225 TRUE); 226 return (err); 227 } 228 229 archive_read_free(archive); 230 } 231 232 return (0); 233 } 234