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