xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision bfd258f74e09bc2238f2b2248e863ca789a84d10)
12118f387SNathan Whitehorn /*-
22118f387SNathan Whitehorn  * Copyright (c) 2011 Nathan Whitehorn
32118f387SNathan Whitehorn  * All rights reserved.
42118f387SNathan Whitehorn  *
52118f387SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
62118f387SNathan Whitehorn  * modification, are permitted provided that the following conditions
72118f387SNathan Whitehorn  * are met:
82118f387SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
92118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
102118f387SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
112118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
122118f387SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
132118f387SNathan Whitehorn  *
142118f387SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152118f387SNathan Whitehorn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162118f387SNathan Whitehorn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172118f387SNathan Whitehorn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182118f387SNathan Whitehorn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192118f387SNathan Whitehorn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202118f387SNathan Whitehorn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212118f387SNathan Whitehorn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222118f387SNathan Whitehorn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232118f387SNathan Whitehorn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242118f387SNathan Whitehorn  * SUCH DAMAGE.
252118f387SNathan Whitehorn  *
262118f387SNathan Whitehorn  * $FreeBSD$
272118f387SNathan Whitehorn  */
282118f387SNathan Whitehorn 
2946cc2c02SNathan Whitehorn #include <sys/param.h>
302118f387SNathan Whitehorn #include <stdio.h>
312118f387SNathan Whitehorn #include <errno.h>
322118f387SNathan Whitehorn #include <limits.h>
332118f387SNathan Whitehorn #include <archive.h>
342118f387SNathan Whitehorn #include <dialog.h>
352118f387SNathan Whitehorn 
362118f387SNathan Whitehorn static int extract_files(int nfiles, const char **files);
372118f387SNathan Whitehorn 
382118f387SNathan Whitehorn int
392118f387SNathan Whitehorn main(void)
402118f387SNathan Whitehorn {
41*bfd258f7SNathan Whitehorn 	char *diststring;
422118f387SNathan Whitehorn 	const char **dists;
432118f387SNathan Whitehorn 	int i, retval, ndists = 0;
44*bfd258f7SNathan Whitehorn 
45*bfd258f7SNathan Whitehorn 	if (getenv("DISTRIBUTIONS") == NULL) {
46*bfd258f7SNathan Whitehorn 		fprintf(stderr, "DISTRIBUTIONS variable is not set\n");
47*bfd258f7SNathan Whitehorn 		return (1);
48*bfd258f7SNathan Whitehorn 	}
49*bfd258f7SNathan Whitehorn 
50*bfd258f7SNathan Whitehorn 	diststring = strdup(getenv("DISTRIBUTIONS"));
512118f387SNathan Whitehorn 	for (i = 0; diststring[i] != 0; i++)
522118f387SNathan Whitehorn 		if (isspace(diststring[i]) && !isspace(diststring[i+1]))
532118f387SNathan Whitehorn 			ndists++;
542118f387SNathan Whitehorn 	ndists++; /* Last one */
552118f387SNathan Whitehorn 
562118f387SNathan Whitehorn 	dists = calloc(ndists, sizeof(const char *));
5757bda1b6SNathan Whitehorn 	if (dists == NULL) {
5857bda1b6SNathan Whitehorn 		fprintf(stderr, "Out of memory!\n");
59c725e3efSKevin Lo 		free(diststring);
6057bda1b6SNathan Whitehorn 		return (1);
6157bda1b6SNathan Whitehorn 	}
6257bda1b6SNathan Whitehorn 
632118f387SNathan Whitehorn 	for (i = 0; i < ndists; i++)
642118f387SNathan Whitehorn 		dists[i] = strsep(&diststring, " \t");
652118f387SNathan Whitehorn 
6657bda1b6SNathan Whitehorn 	init_dialog(stdin, stdout);
6757bda1b6SNathan Whitehorn 	dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
6857bda1b6SNathan Whitehorn 	dlg_put_backtitle();
6957bda1b6SNathan Whitehorn 
7057bda1b6SNathan Whitehorn 	if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) {
7157bda1b6SNathan Whitehorn 		char error[512];
7257bda1b6SNathan Whitehorn 		sprintf(error, "Could could change to directory %s: %s\n",
7357bda1b6SNathan Whitehorn 		    getenv("BSDINSTALL_DISTDIR"), strerror(errno));
7457bda1b6SNathan Whitehorn 		dialog_msgbox("Error", error, 0, 0, TRUE);
7557bda1b6SNathan Whitehorn 		end_dialog();
7657bda1b6SNathan Whitehorn 		return (1);
7757bda1b6SNathan Whitehorn 	}
7857bda1b6SNathan Whitehorn 
792118f387SNathan Whitehorn 	retval = extract_files(ndists, dists);
802118f387SNathan Whitehorn 
8157bda1b6SNathan Whitehorn 	end_dialog();
8257bda1b6SNathan Whitehorn 
832118f387SNathan Whitehorn 	free(diststring);
842118f387SNathan Whitehorn 	free(dists);
852118f387SNathan Whitehorn 
862118f387SNathan Whitehorn 	return (retval);
872118f387SNathan Whitehorn }
882118f387SNathan Whitehorn 
892118f387SNathan Whitehorn static int
9046cc2c02SNathan Whitehorn count_files(const char *file)
9146cc2c02SNathan Whitehorn {
9246cc2c02SNathan Whitehorn 	struct archive *archive;
9346cc2c02SNathan Whitehorn 	struct archive_entry *entry;
9446cc2c02SNathan Whitehorn 	static FILE *manifest = NULL;
9546cc2c02SNathan Whitehorn 	char path[MAXPATHLEN];
9646cc2c02SNathan Whitehorn 	char errormsg[512];
9746cc2c02SNathan Whitehorn 	int file_count, err;
9846cc2c02SNathan Whitehorn 
9946cc2c02SNathan Whitehorn 	if (manifest == NULL) {
10046cc2c02SNathan Whitehorn 		sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR"));
10146cc2c02SNathan Whitehorn 		manifest = fopen(path, "r");
10246cc2c02SNathan Whitehorn 	}
10346cc2c02SNathan Whitehorn 
10446cc2c02SNathan Whitehorn 	if (manifest != NULL) {
10546cc2c02SNathan Whitehorn 		char line[512];
10646cc2c02SNathan Whitehorn 		char *tok1, *tok2;
107896a9484SNathan Whitehorn 
108896a9484SNathan Whitehorn 		rewind(manifest);
10946cc2c02SNathan Whitehorn 		while (fgets(line, sizeof(line), manifest) != NULL) {
11046cc2c02SNathan Whitehorn 			tok2 = line;
11146cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
11246cc2c02SNathan Whitehorn 			if (tok1 == NULL || strcmp(tok1, file) != 0)
11346cc2c02SNathan Whitehorn 				continue;
11446cc2c02SNathan Whitehorn 
11546cc2c02SNathan Whitehorn 			/*
11646cc2c02SNathan Whitehorn 			 * We're at the right manifest line. The file count is
11746cc2c02SNathan Whitehorn 			 * in the third element
11846cc2c02SNathan Whitehorn 			 */
11946cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
12046cc2c02SNathan Whitehorn 			tok1 = strsep(&tok2, "\t");
12146cc2c02SNathan Whitehorn 			if (tok1 != NULL)
12246cc2c02SNathan Whitehorn 				return atoi(tok1);
12346cc2c02SNathan Whitehorn 		}
12446cc2c02SNathan Whitehorn 	}
12546cc2c02SNathan Whitehorn 
12646cc2c02SNathan Whitehorn 	/* Either we didn't have a manifest, or this archive wasn't there */
12746cc2c02SNathan Whitehorn 	archive = archive_read_new();
12846cc2c02SNathan Whitehorn 	archive_read_support_format_all(archive);
12946cc2c02SNathan Whitehorn 	archive_read_support_compression_all(archive);
13046cc2c02SNathan Whitehorn 	sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file);
13146cc2c02SNathan Whitehorn 	err = archive_read_open_filename(archive, path, 4096);
13246cc2c02SNathan Whitehorn 	if (err != ARCHIVE_OK) {
13346cc2c02SNathan Whitehorn 		snprintf(errormsg, sizeof(errormsg),
13446cc2c02SNathan Whitehorn 		    "Error while extracting %s: %s\n", file,
13546cc2c02SNathan Whitehorn 		    archive_error_string(archive));
13646cc2c02SNathan Whitehorn 		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
13746cc2c02SNathan Whitehorn 		return (-1);
13846cc2c02SNathan Whitehorn 	}
13946cc2c02SNathan Whitehorn 
14046cc2c02SNathan Whitehorn 	file_count = 0;
14146cc2c02SNathan Whitehorn 	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
14246cc2c02SNathan Whitehorn 		file_count++;
14346cc2c02SNathan Whitehorn 	archive_read_free(archive);
14446cc2c02SNathan Whitehorn 
14546cc2c02SNathan Whitehorn 	return (file_count);
14646cc2c02SNathan Whitehorn }
14746cc2c02SNathan Whitehorn 
14846cc2c02SNathan Whitehorn static int
1492118f387SNathan Whitehorn extract_files(int nfiles, const char **files)
1502118f387SNathan Whitehorn {
1512118f387SNathan Whitehorn 	const char *items[nfiles*2];
1522118f387SNathan Whitehorn 	char path[PATH_MAX];
1532118f387SNathan Whitehorn 	int archive_files[nfiles];
1542118f387SNathan Whitehorn 	int total_files, current_files, archive_file;
1552118f387SNathan Whitehorn 	struct archive *archive;
1562118f387SNathan Whitehorn 	struct archive_entry *entry;
1572118f387SNathan Whitehorn 	char errormsg[512];
1582118f387SNathan Whitehorn 	char status[8];
1592118f387SNathan Whitehorn 	int i, err, progress, last_progress;
1602118f387SNathan Whitehorn 
1612118f387SNathan Whitehorn 	err = 0;
1622118f387SNathan Whitehorn 	progress = 0;
1632118f387SNathan Whitehorn 
1642118f387SNathan Whitehorn 	/* Make the transfer list for dialog */
1652118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
1662118f387SNathan Whitehorn 		items[i*2] = strrchr(files[i], '/');
1672118f387SNathan Whitehorn 		if (items[i*2] != NULL)
1682118f387SNathan Whitehorn 			items[i*2]++;
1692118f387SNathan Whitehorn 		else
1702118f387SNathan Whitehorn 			items[i*2] = files[i];
1712118f387SNathan Whitehorn 		items[i*2 + 1] = "Pending";
1722118f387SNathan Whitehorn 	}
1732118f387SNathan Whitehorn 
1742118f387SNathan Whitehorn 	dialog_msgbox("",
1752118f387SNathan Whitehorn 	    "Checking distribution archives.\nPlease wait...", 0, 0, FALSE);
1762118f387SNathan Whitehorn 
17746cc2c02SNathan Whitehorn 	/* Count all the files */
1782118f387SNathan Whitehorn 	total_files = 0;
1792118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
18046cc2c02SNathan Whitehorn 		archive_files[i] = count_files(files[i]);
18146cc2c02SNathan Whitehorn 		if (archive_files[i] < 0)
18246cc2c02SNathan Whitehorn 			return (-1);
1832118f387SNathan Whitehorn 		total_files += archive_files[i];
1842118f387SNathan Whitehorn 	}
1852118f387SNathan Whitehorn 
1862118f387SNathan Whitehorn 	current_files = 0;
1872118f387SNathan Whitehorn 
1882118f387SNathan Whitehorn 	for (i = 0; i < nfiles; i++) {
1892118f387SNathan Whitehorn 		archive = archive_read_new();
1902118f387SNathan Whitehorn 		archive_read_support_format_all(archive);
1912118f387SNathan Whitehorn 		archive_read_support_compression_all(archive);
1922118f387SNathan Whitehorn 		sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]);
1932118f387SNathan Whitehorn 		err = archive_read_open_filename(archive, path, 4096);
1942118f387SNathan Whitehorn 
1952118f387SNathan Whitehorn 		items[i*2 + 1] = "In Progress";
1962118f387SNathan Whitehorn 		archive_file = 0;
1972118f387SNathan Whitehorn 
1982118f387SNathan Whitehorn 		while ((err = archive_read_next_header(archive, &entry)) ==
1992118f387SNathan Whitehorn 		    ARCHIVE_OK) {
2002118f387SNathan Whitehorn 			last_progress = progress;
2012118f387SNathan Whitehorn 			progress = (current_files*100)/total_files;
2022118f387SNathan Whitehorn 
2032118f387SNathan Whitehorn 			sprintf(status, "-%d",
2042118f387SNathan Whitehorn 			    (archive_file*100)/archive_files[i]);
2052118f387SNathan Whitehorn 			items[i*2 + 1] = status;
2062118f387SNathan Whitehorn 
2072118f387SNathan Whitehorn 			if (progress > last_progress)
2082118f387SNathan Whitehorn 				dialog_mixedgauge("Archive Extraction",
2092118f387SNathan Whitehorn 				    "Extracting distribution files...", 0, 0,
2102118f387SNathan Whitehorn 				    progress, nfiles,
2112118f387SNathan Whitehorn 				    __DECONST(char **, items));
2122118f387SNathan Whitehorn 
2132118f387SNathan Whitehorn 			err = archive_read_extract(archive, entry,
2142118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
2152118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
2162118f387SNathan Whitehorn 			    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
2172118f387SNathan Whitehorn 
2182118f387SNathan Whitehorn 			if (err != ARCHIVE_OK)
2192118f387SNathan Whitehorn 				break;
2202118f387SNathan Whitehorn 
2212118f387SNathan Whitehorn 			archive_file++;
2222118f387SNathan Whitehorn 			current_files++;
2232118f387SNathan Whitehorn 		}
2242118f387SNathan Whitehorn 
2252118f387SNathan Whitehorn 		items[i*2 + 1] = "Done";
2262118f387SNathan Whitehorn 
2272118f387SNathan Whitehorn 		if (err != ARCHIVE_EOF) {
2282118f387SNathan Whitehorn 			snprintf(errormsg, sizeof(errormsg),
2292118f387SNathan Whitehorn 			    "Error while extracting %s: %s\n", items[i*2],
2302118f387SNathan Whitehorn 			    archive_error_string(archive));
2312118f387SNathan Whitehorn 			items[i*2 + 1] = "Failed";
2322118f387SNathan Whitehorn 			dialog_msgbox("Extract Error", errormsg, 0, 0,
2332118f387SNathan Whitehorn 			    TRUE);
23457bda1b6SNathan Whitehorn 			return (err);
2352118f387SNathan Whitehorn 		}
2362118f387SNathan Whitehorn 
2372118f387SNathan Whitehorn 		archive_read_free(archive);
2382118f387SNathan Whitehorn 	}
2392118f387SNathan Whitehorn 
24057bda1b6SNathan Whitehorn 	return (0);
2412118f387SNathan Whitehorn }
242