distextract.c revision 218915
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 218915 2011-02-21 14:28:31Z nwhitehorn $
27 */
28
29#include <stdio.h>
30#include <errno.h>
31#include <limits.h>
32#include <archive.h>
33#include <dialog.h>
34
35static int extract_files(int nfiles, const char **files);
36
37int
38main(void)
39{
40	char *diststring = strdup(getenv("DISTRIBUTIONS"));
41	const char **dists;
42	int i, retval, ndists = 0;
43	for (i = 0; diststring[i] != 0; i++)
44		if (isspace(diststring[i]) && !isspace(diststring[i+1]))
45			ndists++;
46	ndists++; /* Last one */
47
48	dists = calloc(ndists, sizeof(const char *));
49	if (dists == NULL) {
50		fprintf(stderr, "Out of memory!\n");
51		return (1);
52	}
53
54	for (i = 0; i < ndists; i++)
55		dists[i] = strsep(&diststring, " \t");
56
57	init_dialog(stdin, stdout);
58	dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
59	dlg_put_backtitle();
60
61	if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) {
62		char error[512];
63		sprintf(error, "Could could change to directory %s: %s\n",
64		    getenv("BSDINSTALL_DISTDIR"), strerror(errno));
65		dialog_msgbox("Error", error, 0, 0, TRUE);
66		end_dialog();
67		return (1);
68	}
69
70	retval = extract_files(ndists, dists);
71
72	end_dialog();
73
74	free(diststring);
75	free(dists);
76
77	return (retval);
78}
79
80static int
81extract_files(int nfiles, const char **files)
82{
83	const char *items[nfiles*2];
84	char path[PATH_MAX];
85	int archive_files[nfiles];
86	int total_files, current_files, archive_file;
87	struct archive *archive;
88	struct archive_entry *entry;
89	char errormsg[512];
90	char status[8];
91	int i, err, progress, last_progress;
92
93	err = 0;
94	progress = 0;
95
96	/* Make the transfer list for dialog */
97	for (i = 0; i < nfiles; i++) {
98		items[i*2] = strrchr(files[i], '/');
99		if (items[i*2] != NULL)
100			items[i*2]++;
101		else
102			items[i*2] = files[i];
103		items[i*2 + 1] = "Pending";
104	}
105
106	dialog_msgbox("",
107	    "Checking distribution archives.\nPlease wait...", 0, 0, FALSE);
108
109	/* Open all the archives */
110	total_files = 0;
111	for (i = 0; i < nfiles; i++) {
112		archive = archive_read_new();
113		archive_read_support_format_all(archive);
114		archive_read_support_compression_all(archive);
115		sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]);
116		err = archive_read_open_filename(archive, path, 4096);
117		if (err != ARCHIVE_OK) {
118			snprintf(errormsg, sizeof(errormsg),
119			    "Error while extracting %s: %s\n", items[i*2],
120			    archive_error_string(archive));
121			items[i*2 + 1] = "Failed";
122			dialog_msgbox("Extract Error", errormsg, 0, 0,
123			    TRUE);
124			return (err);
125		}
126		archive_files[i] = 0;
127		while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
128			archive_files[i]++;
129		total_files += archive_files[i];
130		archive_read_free(archive);
131	}
132
133	current_files = 0;
134
135	for (i = 0; i < nfiles; i++) {
136		archive = archive_read_new();
137		archive_read_support_format_all(archive);
138		archive_read_support_compression_all(archive);
139		sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]);
140		err = archive_read_open_filename(archive, path, 4096);
141
142		items[i*2 + 1] = "In Progress";
143		archive_file = 0;
144
145		while ((err = archive_read_next_header(archive, &entry)) ==
146		    ARCHIVE_OK) {
147			last_progress = progress;
148			progress = (current_files*100)/total_files;
149
150			sprintf(status, "-%d",
151			    (archive_file*100)/archive_files[i]);
152			items[i*2 + 1] = status;
153
154			if (progress > last_progress)
155				dialog_mixedgauge("Archive Extraction",
156				    "Extracting distribution files...", 0, 0,
157				    progress, nfiles,
158				    __DECONST(char **, items));
159
160			err = archive_read_extract(archive, entry,
161			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
162			    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
163			    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
164
165			if (err != ARCHIVE_OK)
166				break;
167
168			archive_file++;
169			current_files++;
170		}
171
172		items[i*2 + 1] = "Done";
173
174		if (err != ARCHIVE_EOF) {
175			snprintf(errormsg, sizeof(errormsg),
176			    "Error while extracting %s: %s\n", items[i*2],
177			    archive_error_string(archive));
178			items[i*2 + 1] = "Failed";
179			dialog_msgbox("Extract Error", errormsg, 0, 0,
180			    TRUE);
181			return (err);
182		}
183
184		archive_read_free(archive);
185	}
186
187	return (0);
188}
189