distextract.c revision 219618
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 219618 2011-03-13 18:26:16Z nwhitehorn $
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		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
81static int
82count_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
140static int
141extract_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