xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision 82ac9f2bf78b62e8e164c613d3142e78a0028fe8)
12118f387SNathan Whitehorn /*-
22118f387SNathan Whitehorn  * Copyright (c) 2011 Nathan Whitehorn
3*82ac9f2bSDevin Teske  * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org>
42118f387SNathan Whitehorn  * All rights reserved.
52118f387SNathan Whitehorn  *
62118f387SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
72118f387SNathan Whitehorn  * modification, are permitted provided that the following conditions
82118f387SNathan Whitehorn  * are met:
92118f387SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
102118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
112118f387SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
122118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
132118f387SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
142118f387SNathan Whitehorn  *
152118f387SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162118f387SNathan Whitehorn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172118f387SNathan Whitehorn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182118f387SNathan Whitehorn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192118f387SNathan Whitehorn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202118f387SNathan Whitehorn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212118f387SNathan Whitehorn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222118f387SNathan Whitehorn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232118f387SNathan Whitehorn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242118f387SNathan Whitehorn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252118f387SNathan Whitehorn  * SUCH DAMAGE.
262118f387SNathan Whitehorn  */
272118f387SNathan Whitehorn 
28*82ac9f2bSDevin Teske #include <sys/cdefs.h>
29*82ac9f2bSDevin Teske __FBSDID("$FreeBSD$");
30*82ac9f2bSDevin Teske 
3146cc2c02SNathan Whitehorn #include <sys/param.h>
32*82ac9f2bSDevin Teske #include <archive.h>
33*82ac9f2bSDevin Teske #include <ctype.h>
34*82ac9f2bSDevin Teske #include <dialog.h>
35*82ac9f2bSDevin Teske #include <err.h>
362118f387SNathan Whitehorn #include <errno.h>
372118f387SNathan Whitehorn #include <limits.h>
38*82ac9f2bSDevin Teske #include <stdio.h>
39*82ac9f2bSDevin Teske #include <stdlib.h>
40*82ac9f2bSDevin Teske #include <string.h>
41*82ac9f2bSDevin Teske #include <unistd.h>
422118f387SNathan Whitehorn 
43*82ac9f2bSDevin Teske static int	count_files(const char *file);
442118f387SNathan Whitehorn static int	extract_files(int nfiles, const char **files);
452118f387SNathan Whitehorn 
462118f387SNathan Whitehorn int
472118f387SNathan Whitehorn main(void)
482118f387SNathan Whitehorn {
492118f387SNathan Whitehorn 	const char **dists;
50*82ac9f2bSDevin Teske 	char *diststring;
51*82ac9f2bSDevin Teske 	int i;
52*82ac9f2bSDevin Teske 	int ndists = 0;
53*82ac9f2bSDevin Teske 	int retval;
54*82ac9f2bSDevin Teske 	char error[PATH_MAX + 512];
55bfd258f7SNathan Whitehorn 
56*82ac9f2bSDevin Teske 	if (getenv("DISTRIBUTIONS") == NULL)
57*82ac9f2bSDevin Teske 		errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
58bfd258f7SNathan Whitehorn 
59bfd258f7SNathan Whitehorn 	diststring = strdup(getenv("DISTRIBUTIONS"));
602118f387SNathan Whitehorn 	for (i = 0; diststring[i] != 0; i++)
612118f387SNathan Whitehorn 		if (isspace(diststring[i]) && !isspace(diststring[i+1]))
622118f387SNathan Whitehorn 			ndists++;
632118f387SNathan Whitehorn 	ndists++; /* Last one */
642118f387SNathan Whitehorn 
652118f387SNathan Whitehorn 	dists = calloc(ndists, sizeof(const char *));
6657bda1b6SNathan Whitehorn 	if (dists == NULL) {
67c725e3efSKevin Lo 		free(diststring);
68*82ac9f2bSDevin Teske 		errx(EXIT_FAILURE, "Out of memory!");
6957bda1b6SNathan Whitehorn 	}
7057bda1b6SNathan Whitehorn 
712118f387SNathan Whitehorn 	for (i = 0; i < ndists; i++)
722118f387SNathan Whitehorn 		dists[i] = strsep(&diststring, " \t");
732118f387SNathan Whitehorn 
7457bda1b6SNathan Whitehorn 	init_dialog(stdin, stdout);
7557bda1b6SNathan Whitehorn 	dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
7657bda1b6SNathan Whitehorn 	dlg_put_backtitle();
7757bda1b6SNathan Whitehorn 
7857bda1b6SNathan Whitehorn 	if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) {
79*82ac9f2bSDevin Teske 		snprintf(error, sizeof(error),
80*82ac9f2bSDevin Teske 		    "Could could change to directory %s: %s\n",
8157bda1b6SNathan Whitehorn 		    getenv("BSDINSTALL_DISTDIR"), strerror(errno));
8257bda1b6SNathan Whitehorn 		dialog_msgbox("Error", error, 0, 0, TRUE);
8357bda1b6SNathan Whitehorn 		end_dialog();
84*82ac9f2bSDevin Teske 		return (EXIT_FAILURE);
8557bda1b6SNathan Whitehorn 	}
8657bda1b6SNathan Whitehorn 
872118f387SNathan Whitehorn 	retval = extract_files(ndists, dists);
882118f387SNathan Whitehorn 
8957bda1b6SNathan Whitehorn 	end_dialog();
9057bda1b6SNathan Whitehorn 
912118f387SNathan Whitehorn 	free(diststring);
922118f387SNathan Whitehorn 	free(dists);
932118f387SNathan Whitehorn 
942118f387SNathan Whitehorn 	return (retval);
952118f387SNathan Whitehorn }
962118f387SNathan Whitehorn 
972118f387SNathan Whitehorn static int
9846cc2c02SNathan Whitehorn count_files(const char *file)
9946cc2c02SNathan Whitehorn {
100*82ac9f2bSDevin Teske 	static FILE *manifest = NULL;
101*82ac9f2bSDevin Teske 	char *tok1;
102*82ac9f2bSDevin Teske 	char *tok2;
103*82ac9f2bSDevin Teske 	int file_count;
104*82ac9f2bSDevin Teske 	int retval;
10546cc2c02SNathan Whitehorn 	struct archive *archive;
10646cc2c02SNathan Whitehorn 	struct archive_entry *entry;
107*82ac9f2bSDevin Teske 	char line[512];
108*82ac9f2bSDevin Teske 	char path[PATH_MAX];
109*82ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
11046cc2c02SNathan Whitehorn 
11146cc2c02SNathan Whitehorn 	if (manifest == NULL) {
112*82ac9f2bSDevin Teske 		snprintf(path, sizeof(path), "%s/MANIFEST",
113*82ac9f2bSDevin Teske 		    getenv("BSDINSTALL_DISTDIR"));
11446cc2c02SNathan Whitehorn 		manifest = fopen(path, "r");
11546cc2c02SNathan Whitehorn 	}
11646cc2c02SNathan Whitehorn 
11746cc2c02SNathan Whitehorn 	if (manifest != NULL) {
118896a9484SNathan Whitehorn 		rewind(manifest);
11946cc2c02SNathan Whitehorn 		while (fgets(line, sizeof(line), manifest) != NULL) {
12046cc2c02SNathan Whitehorn 			tok2 = line;
12146cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
12246cc2c02SNathan Whitehorn 			if (tok1 == NULL || strcmp(tok1, file) != 0)
12346cc2c02SNathan Whitehorn 				continue;
12446cc2c02SNathan Whitehorn 
12546cc2c02SNathan Whitehorn 			/*
12646cc2c02SNathan Whitehorn 			 * We're at the right manifest line. The file count is
12746cc2c02SNathan Whitehorn 			 * in the third element
12846cc2c02SNathan Whitehorn 			 */
12946cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
13046cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
13146cc2c02SNathan Whitehorn 			if (tok1 != NULL)
13246cc2c02SNathan Whitehorn 				return atoi(tok1);
13346cc2c02SNathan Whitehorn 		}
13446cc2c02SNathan Whitehorn 	}
13546cc2c02SNathan Whitehorn 
13646cc2c02SNathan Whitehorn 	/* Either we didn't have a manifest, or this archive wasn't there */
13746cc2c02SNathan Whitehorn 	archive = archive_read_new();
13846cc2c02SNathan Whitehorn 	archive_read_support_format_all(archive);
139ebb8fc42SMartin Matuska 	archive_read_support_filter_all(archive);
140*82ac9f2bSDevin Teske 	snprintf(path, sizeof(path), "%s/%s", getenv("BSDINSTALL_DISTDIR"),
141*82ac9f2bSDevin Teske 	    file);
142*82ac9f2bSDevin Teske 	retval = archive_read_open_filename(archive, path, 4096);
143*82ac9f2bSDevin Teske 	if (retval != ARCHIVE_OK) {
14446cc2c02SNathan Whitehorn 		snprintf(errormsg, sizeof(errormsg),
14546cc2c02SNathan Whitehorn 		    "Error while extracting %s: %s\n", file,
14646cc2c02SNathan Whitehorn 		    archive_error_string(archive));
14746cc2c02SNathan Whitehorn 		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
14846cc2c02SNathan Whitehorn 		return (-1);
14946cc2c02SNathan Whitehorn 	}
15046cc2c02SNathan Whitehorn 
15146cc2c02SNathan Whitehorn 	file_count = 0;
15246cc2c02SNathan Whitehorn 	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
15346cc2c02SNathan Whitehorn 		file_count++;
15446cc2c02SNathan Whitehorn 	archive_read_free(archive);
15546cc2c02SNathan Whitehorn 
15646cc2c02SNathan Whitehorn 	return (file_count);
15746cc2c02SNathan Whitehorn }
15846cc2c02SNathan Whitehorn 
15946cc2c02SNathan Whitehorn static int
1602118f387SNathan Whitehorn extract_files(int nfiles, const char **files)
1612118f387SNathan Whitehorn {
162*82ac9f2bSDevin Teske 	int archive_file;
1632118f387SNathan Whitehorn 	int archive_files[nfiles];
164*82ac9f2bSDevin Teske 	int current_files = 0;
165*82ac9f2bSDevin Teske 	int i;
166*82ac9f2bSDevin Teske 	int last_progress;
167*82ac9f2bSDevin Teske 	int progress = 0;
168*82ac9f2bSDevin Teske 	int retval;
169*82ac9f2bSDevin Teske 	int total_files = 0;
1702118f387SNathan Whitehorn 	struct archive *archive;
1712118f387SNathan Whitehorn 	struct archive_entry *entry;
1722118f387SNathan Whitehorn 	char status[8];
173*82ac9f2bSDevin Teske 	char path[PATH_MAX];
174*82ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
175*82ac9f2bSDevin Teske 	const char *items[nfiles*2];
1762118f387SNathan Whitehorn 
1772118f387SNathan Whitehorn 	/* Make the transfer list for dialog */
1782118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
1792118f387SNathan Whitehorn 		items[i*2] = strrchr(files[i], '/');
1802118f387SNathan Whitehorn 		if (items[i*2] != NULL)
1812118f387SNathan Whitehorn 			items[i*2]++;
1822118f387SNathan Whitehorn 		else
1832118f387SNathan Whitehorn 			items[i*2] = files[i];
1842118f387SNathan Whitehorn 		items[i*2 + 1] = "Pending";
1852118f387SNathan Whitehorn 	}
1862118f387SNathan Whitehorn 
1872118f387SNathan Whitehorn 	dialog_msgbox("",
1882118f387SNathan Whitehorn 	    "Checking distribution archives.\nPlease wait...", 0, 0, FALSE);
1892118f387SNathan Whitehorn 
19046cc2c02SNathan Whitehorn 	/* Count all the files */
1912118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
19246cc2c02SNathan Whitehorn 		archive_files[i] = count_files(files[i]);
19346cc2c02SNathan Whitehorn 		if (archive_files[i] < 0)
19446cc2c02SNathan Whitehorn 			return (-1);
1952118f387SNathan Whitehorn 		total_files += archive_files[i];
1962118f387SNathan Whitehorn 	}
1972118f387SNathan Whitehorn 
1982118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
1992118f387SNathan Whitehorn 		archive = archive_read_new();
2002118f387SNathan Whitehorn 		archive_read_support_format_all(archive);
201ebb8fc42SMartin Matuska 		archive_read_support_filter_all(archive);
202*82ac9f2bSDevin Teske 		snprintf(path, sizeof(path), "%s/%s",
203*82ac9f2bSDevin Teske 		    getenv("BSDINSTALL_DISTDIR"), files[i]);
204*82ac9f2bSDevin Teske 		retval = archive_read_open_filename(archive, path, 4096);
2052118f387SNathan Whitehorn 
2062118f387SNathan Whitehorn 		items[i*2 + 1] = "In Progress";
2072118f387SNathan Whitehorn 		archive_file = 0;
2082118f387SNathan Whitehorn 
209*82ac9f2bSDevin Teske 		while ((retval = archive_read_next_header(archive, &entry)) ==
2102118f387SNathan Whitehorn 		    ARCHIVE_OK) {
2112118f387SNathan Whitehorn 			last_progress = progress;
2122118f387SNathan Whitehorn 			progress = (current_files*100)/total_files;
2132118f387SNathan Whitehorn 
214*82ac9f2bSDevin Teske 			snprintf(status, sizeof(status), "-%d",
2152118f387SNathan Whitehorn 			    (archive_file*100)/archive_files[i]);
2162118f387SNathan Whitehorn 			items[i*2 + 1] = status;
2172118f387SNathan Whitehorn 
2182118f387SNathan Whitehorn 			if (progress > last_progress)
2192118f387SNathan Whitehorn 				dialog_mixedgauge("Archive Extraction",
2202118f387SNathan Whitehorn 				    "Extracting distribution files...", 0, 0,
2212118f387SNathan Whitehorn 				    progress, nfiles,
2222118f387SNathan Whitehorn 				    __DECONST(char **, items));
2232118f387SNathan Whitehorn 
224*82ac9f2bSDevin Teske 			retval = archive_read_extract(archive, entry,
2252118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
2262118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
2272118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
2282118f387SNathan Whitehorn 
229*82ac9f2bSDevin Teske 			if (retval != ARCHIVE_OK)
2302118f387SNathan Whitehorn 				break;
2312118f387SNathan Whitehorn 
2322118f387SNathan Whitehorn 			archive_file++;
2332118f387SNathan Whitehorn 			current_files++;
2342118f387SNathan Whitehorn 		}
2352118f387SNathan Whitehorn 
2362118f387SNathan Whitehorn 		items[i*2 + 1] = "Done";
2372118f387SNathan Whitehorn 
238*82ac9f2bSDevin Teske 		if (retval != ARCHIVE_EOF) {
2392118f387SNathan Whitehorn 			snprintf(errormsg, sizeof(errormsg),
2402118f387SNathan Whitehorn 			    "Error while extracting %s: %s\n", items[i*2],
2412118f387SNathan Whitehorn 			    archive_error_string(archive));
2422118f387SNathan Whitehorn 			items[i*2 + 1] = "Failed";
2432118f387SNathan Whitehorn 			dialog_msgbox("Extract Error", errormsg, 0, 0,
2442118f387SNathan Whitehorn 			    TRUE);
245*82ac9f2bSDevin Teske 			return (retval);
2462118f387SNathan Whitehorn 		}
2472118f387SNathan Whitehorn 
2482118f387SNathan Whitehorn 		archive_read_free(archive);
2492118f387SNathan Whitehorn 	}
2502118f387SNathan Whitehorn 
251*82ac9f2bSDevin Teske 	return (EXIT_SUCCESS);
2522118f387SNathan Whitehorn }
253