2 * Copyright (c) 2011 Nathan Whitehorn
3 * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
45 static char *distdir = NULL;
46 static struct archive *archive = NULL;
47 static struct dpv_file_node *dists = NULL;
49 /* Function prototypes */
50 static void sig_int(int sig);
51 static int count_files(const char *file);
52 static int extract_files(struct dpv_file_node *file, int out);
54 #if __FreeBSD_version <= 1000008 /* r232154: bump for libarchive update */
55 #define archive_read_support_filter_all(x) \
56 archive_read_support_compression_all(x)
59 #define _errx(...) (end_dialog(), errx(__VA_ARGS__))
67 size_t config_size = sizeof(struct dpv_config);
68 size_t file_node_size = sizeof(struct dpv_file_node);
70 struct dpv_config *config;
71 struct dpv_file_node *dist = dists;
72 static char backtitle[] = "FreeBSD Installer";
73 static char title[] = "Archive Extraction";
74 static char aprompt[] = "\n Overall Progress:";
75 static char pprompt[] = "Extracting distribution files...\n";
77 char error[PATH_MAX + 512];
79 if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
80 errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
81 if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
82 distdir = __DECONST(char *, "");
84 /* Initialize dialog(3) */
85 init_dialog(stdin, stdout);
86 dialog_vars.backtitle = backtitle;
90 "Checking distribution archives.\nPlease wait...", 4, 35, FALSE);
93 * Parse $DISTRIBUTIONS into dpv(3) linked-list
95 while (*distributions != '\0') {
96 span = strcspn(distributions, "\t\n\v\f\r ");
97 if (span < 1) { /* currently on whitespace */
102 /* Allocate a new struct for the distribution */
104 if ((dist = calloc(1, file_node_size)) == NULL)
105 _errx(EXIT_FAILURE, "Out of memory!");
108 dist->next = calloc(1, file_node_size);
109 if (dist->next == NULL)
110 _errx(EXIT_FAILURE, "Out of memory!");
115 if ((dist->path = malloc(span + 1)) == NULL)
116 _errx(EXIT_FAILURE, "Out of memory!");
117 snprintf(dist->path, span + 1, "%s", distributions);
118 dist->path[span] = '\0';
120 /* Set display name */
121 dist->name = strrchr(dist->path, '/');
122 if (dist->name == NULL)
123 dist->name = dist->path;
125 /* Set initial length in files (-1 == error) */
126 dist->length = count_files(dist->path);
127 if (dist->length < 0) {
129 return (EXIT_FAILURE);
132 distributions += span;
135 /* Optionally chdir(2) into $BSDINSTALL_CHROOT */
136 chrootdir = getenv("BSDINSTALL_CHROOT");
137 if (chrootdir != NULL && chdir(chrootdir) != 0) {
138 snprintf(error, sizeof(error),
139 "Could not change to directory %s: %s\n",
140 chrootdir, strerror(errno));
141 dialog_msgbox("Error", error, 0, 0, TRUE);
143 return (EXIT_FAILURE);
146 /* Set cleanup routine for Ctrl-C action */
147 act.sa_handler = sig_int;
148 sigaction(SIGINT, &act, 0);
153 if ((config = calloc(1, config_size)) == NULL)
154 _errx(EXIT_FAILURE, "Out of memory!");
155 config->backtitle = backtitle;
156 config->title = title;
157 config->pprompt = pprompt;
158 config->aprompt = aprompt;
159 config->options |= DPV_WIDE_MODE;
160 config->label_size = -1;
161 config->action = extract_files;
162 config->status_solo =
163 "%10lli files read @ %'9.1f files/sec.";
164 config->status_many =
165 "%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]";
167 retval = dpv(config, dists);
170 while ((dist = dists) != NULL) {
172 if (dist->path != NULL)
181 sig_int(int sig __unused)
183 dpv_interrupt = TRUE;
187 * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
188 * if it exists, otherwise uses archive(3) to read the archive file.
191 count_files(const char *file)
193 static FILE *manifest = NULL;
198 struct archive_entry *entry;
201 char errormsg[PATH_MAX + 512];
203 if (manifest == NULL) {
204 snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
205 manifest = fopen(path, "r");
208 if (manifest != NULL) {
210 while (fgets(line, sizeof(line), manifest) != NULL) {
212 span = strcspn(p, "\t") ;
213 if (span < 1 || strncmp(p, file, span) != 0)
217 * We're at the right manifest line. The file count is
218 * in the third element
220 span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
221 span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
223 file_count = (int)strtol(p, (char **)NULL, 10);
224 if (file_count == 0 && errno == EINVAL)
232 * Either no manifest, or manifest didn't mention this archive.
233 * Use archive(3) to read the archive, counting files within.
235 if ((archive = archive_read_new()) == NULL) {
236 snprintf(errormsg, sizeof(errormsg),
237 "Error: %s\n", archive_error_string(NULL));
238 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
241 archive_read_support_format_all(archive);
242 archive_read_support_filter_all(archive);
243 snprintf(path, sizeof(path), "%s/%s", distdir, file);
244 retval = archive_read_open_filename(archive, path, 4096);
245 if (retval != ARCHIVE_OK) {
246 snprintf(errormsg, sizeof(errormsg),
247 "Error while extracting %s: %s\n", file,
248 archive_error_string(archive));
249 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
255 while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
257 archive_read_free(archive);
264 extract_files(struct dpv_file_node *file, int out __unused)
267 struct archive_entry *entry;
269 char errormsg[PATH_MAX + 512];
271 /* Open the archive if necessary */
272 if (archive == NULL) {
273 if ((archive = archive_read_new()) == NULL) {
274 snprintf(errormsg, sizeof(errormsg),
275 "Error: %s\n", archive_error_string(NULL));
276 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
280 archive_read_support_format_all(archive);
281 archive_read_support_filter_all(archive);
282 snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
283 retval = archive_read_open_filename(archive, path, 4096);
285 snprintf(errormsg, sizeof(errormsg),
286 "Error opening %s: %s\n", file->name,
287 archive_error_string(archive));
288 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
289 file->status = DPV_STATUS_FAILED;
295 /* Read the next archive header */
296 retval = archive_read_next_header(archive, &entry);
298 /* If that went well, perform the extraction */
299 if (retval == ARCHIVE_OK)
300 retval = archive_read_extract(archive, entry,
301 ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
302 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
303 ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
305 /* Test for either EOF or error */
306 if (retval == ARCHIVE_EOF) {
307 archive_read_free(archive);
309 file->status = DPV_STATUS_DONE;
311 } else if (retval != ARCHIVE_OK) {
312 snprintf(errormsg, sizeof(errormsg),
313 "Error while extracting %s: %s\n", file->name,
314 archive_error_string(archive));
315 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
316 file->status = DPV_STATUS_FAILED;
324 /* Calculate [overall] percentage of completion (if possible) */
325 if (file->length >= 0)
326 return (file->read * 100 / file->length);