xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision 675be9115aae86ad6b3d877155d4fd7822892105)
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 		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 
82 static int
83 count_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 
141 static int
142 extract_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