xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision 5dcd9c10612684d1c823670cbb5b4715028784e7)
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 <stdio.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <archive.h>
33 #include <dialog.h>
34 
35 static int extract_files(int nfiles, const char **files);
36 
37 int
38 main(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 
80 static int
81 extract_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