xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
12118f387SNathan Whitehorn /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
42118f387SNathan Whitehorn  * Copyright (c) 2011 Nathan Whitehorn
582ac9f2bSDevin Teske  * Copyright (c) 2014 Devin Teske <dteske@FreeBSD.org>
62118f387SNathan Whitehorn  * All rights reserved.
72118f387SNathan Whitehorn  *
82118f387SNathan Whitehorn  * Redistribution and use in source and binary forms, with or without
92118f387SNathan Whitehorn  * modification, are permitted provided that the following conditions
102118f387SNathan Whitehorn  * are met:
112118f387SNathan Whitehorn  * 1. Redistributions of source code must retain the above copyright
122118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer.
132118f387SNathan Whitehorn  * 2. Redistributions in binary form must reproduce the above copyright
142118f387SNathan Whitehorn  *    notice, this list of conditions and the following disclaimer in the
152118f387SNathan Whitehorn  *    documentation and/or other materials provided with the distribution.
162118f387SNathan Whitehorn  *
172118f387SNathan Whitehorn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
182118f387SNathan Whitehorn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
192118f387SNathan Whitehorn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202118f387SNathan Whitehorn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
212118f387SNathan Whitehorn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
222118f387SNathan Whitehorn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
232118f387SNathan Whitehorn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
242118f387SNathan Whitehorn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
252118f387SNathan Whitehorn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
262118f387SNathan Whitehorn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
272118f387SNathan Whitehorn  * SUCH DAMAGE.
282118f387SNathan Whitehorn  */
292118f387SNathan Whitehorn 
3082ac9f2bSDevin Teske #include <sys/cdefs.h>
3182ac9f2bSDevin Teske __FBSDID("$FreeBSD$");
3282ac9f2bSDevin Teske 
3346cc2c02SNathan Whitehorn #include <sys/param.h>
34263660c0SAlfonso Siciliano 
3582ac9f2bSDevin Teske #include <archive.h>
3682ac9f2bSDevin Teske #include <ctype.h>
3718f20d5dSAlfonso Siciliano #include <bsddialog.h>
3818f20d5dSAlfonso Siciliano #include <bsddialog_progressview.h>
3982ac9f2bSDevin Teske #include <err.h>
402118f387SNathan Whitehorn #include <errno.h>
412118f387SNathan Whitehorn #include <limits.h>
4218f20d5dSAlfonso Siciliano #include <signal.h>
4382ac9f2bSDevin Teske #include <stdio.h>
4482ac9f2bSDevin Teske #include <stdlib.h>
4582ac9f2bSDevin Teske #include <string.h>
4682ac9f2bSDevin Teske #include <unistd.h>
472118f387SNathan Whitehorn 
48147585b4SBrad Davis #include "opt_osname.h"
49147585b4SBrad Davis 
50bd1df636SDevin Teske /* Data to process */
51f9549860SBaptiste Daroussin static const char *distdir = NULL;
524ff1fc3bSDevin Teske static struct archive *archive = NULL;
53bd1df636SDevin Teske 
54bd1df636SDevin Teske /* Function prototypes */
554ff1fc3bSDevin Teske static void	sig_int(int sig);
5682ac9f2bSDevin Teske static int	count_files(const char *file);
5718f20d5dSAlfonso Siciliano static int	extract_files(struct bsddialog_fileminibar *file);
58bd1df636SDevin Teske 
5918f20d5dSAlfonso Siciliano #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__))
602118f387SNathan Whitehorn 
612118f387SNathan Whitehorn int
622118f387SNathan Whitehorn main(void)
632118f387SNathan Whitehorn {
64bd1df636SDevin Teske 	char *chrootdir;
65bd1df636SDevin Teske 	char *distributions;
66dd8ca6b2SBaptiste Daroussin 	char *distribs, *distrib;
6782ac9f2bSDevin Teske 	int retval;
6818f20d5dSAlfonso Siciliano 	size_t minibar_size = sizeof(struct bsddialog_fileminibar);
6918f20d5dSAlfonso Siciliano 	unsigned int nminibars;
7018f20d5dSAlfonso Siciliano 	struct bsddialog_fileminibar *dists;
7118f20d5dSAlfonso Siciliano 	struct bsddialog_progviewconf pvconf;
7218f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
734ff1fc3bSDevin Teske 	struct sigaction act;
7482ac9f2bSDevin Teske 	char error[PATH_MAX + 512];
75bfd258f7SNathan Whitehorn 
76bd1df636SDevin Teske 	if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
7782ac9f2bSDevin Teske 		errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
78bd1df636SDevin Teske 	if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
79f9549860SBaptiste Daroussin 		distdir = "";
80dd8ca6b2SBaptiste Daroussin 	if ((distribs = strdup(distributions)) == NULL)
81dd8ca6b2SBaptiste Daroussin 		errx(EXIT_FAILURE, "memory error");
82bfd258f7SNathan Whitehorn 
8318f20d5dSAlfonso Siciliano 	if (bsddialog_init() == BSDDIALOG_ERROR)
84263660c0SAlfonso Siciliano 		errx(EXIT_FAILURE, "Error libbsdialog: %s",
85263660c0SAlfonso Siciliano 		    bsddialog_geterror());
8618f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
87147585b4SBrad Davis 	bsddialog_backtitle(&conf, OSNAME " Installer");
8818f20d5dSAlfonso Siciliano 	bsddialog_infobox(&conf,
89263660c0SAlfonso Siciliano 	    "Checking distribution archives.\nPlease wait...", 4, 35);
9057bda1b6SNathan Whitehorn 
9118f20d5dSAlfonso Siciliano 	/* Parse $DISTRIBUTIONS */
9218f20d5dSAlfonso Siciliano 	nminibars = 0;
9318f20d5dSAlfonso Siciliano 	dists = NULL;
94dd8ca6b2SBaptiste Daroussin 	while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) {
95dd8ca6b2SBaptiste Daroussin 		if (strlen(distrib) == 0)
96bd1df636SDevin Teske 			continue;
97bd1df636SDevin Teske 
98bd1df636SDevin Teske 		/* Allocate a new struct for the distribution */
9918f20d5dSAlfonso Siciliano 		dists = realloc(dists, (nminibars + 1) * minibar_size);
10018f20d5dSAlfonso Siciliano 		if (dists == NULL)
101bd1df636SDevin Teske 			_errx(EXIT_FAILURE, "Out of memory!");
102bd1df636SDevin Teske 
10318f20d5dSAlfonso Siciliano 		/* Set file path */
104dd8ca6b2SBaptiste Daroussin 		dists[nminibars].path = distrib;
105bd1df636SDevin Teske 
10618f20d5dSAlfonso Siciliano 		/* Set mini bar label */
10718f20d5dSAlfonso Siciliano 		dists[nminibars].label = strrchr(dists[nminibars].path, '/');
10818f20d5dSAlfonso Siciliano 		if (dists[nminibars].label == NULL)
10918f20d5dSAlfonso Siciliano 			dists[nminibars].label = dists[nminibars].path;
110bd1df636SDevin Teske 
111bd1df636SDevin Teske 		/* Set initial length in files (-1 == error) */
11218f20d5dSAlfonso Siciliano 		dists[nminibars].size = count_files(dists[nminibars].path);
11318f20d5dSAlfonso Siciliano 		if (dists[nminibars].size < 0) {
11418f20d5dSAlfonso Siciliano 			bsddialog_end();
115bd1df636SDevin Teske 			return (EXIT_FAILURE);
116bd1df636SDevin Teske 		}
117bd1df636SDevin Teske 
118263660c0SAlfonso Siciliano 		/* Set initial status and implicitly miniperc to pending */
119263660c0SAlfonso Siciliano 		dists[nminibars].status = BSDDIALOG_MG_PENDING;
12018f20d5dSAlfonso Siciliano 
12118f20d5dSAlfonso Siciliano 		/* Set initial read */
12218f20d5dSAlfonso Siciliano 		dists[nminibars].read = 0;
12318f20d5dSAlfonso Siciliano 
12418f20d5dSAlfonso Siciliano 		nminibars += 1;
125bd1df636SDevin Teske 	}
126bd1df636SDevin Teske 
127bd1df636SDevin Teske 	/* Optionally chdir(2) into $BSDINSTALL_CHROOT */
128bd1df636SDevin Teske 	chrootdir = getenv("BSDINSTALL_CHROOT");
129bd1df636SDevin Teske 	if (chrootdir != NULL && chdir(chrootdir) != 0) {
13082ac9f2bSDevin Teske 		snprintf(error, sizeof(error),
131bd1df636SDevin Teske 		    "Could not change to directory %s: %s\n",
132bd1df636SDevin Teske 		    chrootdir, strerror(errno));
133f9549860SBaptiste Daroussin 		conf.title = "Error";
13418f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, error, 0, 0);
13518f20d5dSAlfonso Siciliano 		bsddialog_end();
13682ac9f2bSDevin Teske 		return (EXIT_FAILURE);
13757bda1b6SNathan Whitehorn 	}
13857bda1b6SNathan Whitehorn 
1394ff1fc3bSDevin Teske 	/* Set cleanup routine for Ctrl-C action */
1404ff1fc3bSDevin Teske 	act.sa_handler = sig_int;
1414ff1fc3bSDevin Teske 	sigaction(SIGINT, &act, 0);
1422118f387SNathan Whitehorn 
143263660c0SAlfonso Siciliano 	conf.title = "Archive Extraction";
144263660c0SAlfonso Siciliano 	conf.auto_minwidth = 40;
14518f20d5dSAlfonso Siciliano 	pvconf.callback	= extract_files;
14618f20d5dSAlfonso Siciliano 	pvconf.refresh = 1;
147263660c0SAlfonso Siciliano 	pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec.";
14818f20d5dSAlfonso Siciliano 	bsddialog_total_progview = 0;
14918f20d5dSAlfonso Siciliano 	bsddialog_interruptprogview = bsddialog_abortprogview = false;
15018f20d5dSAlfonso Siciliano 	retval = bsddialog_progressview(&conf,
151263660c0SAlfonso Siciliano 	    "\nExtracting distribution files...\n", 0, 0,
152263660c0SAlfonso Siciliano 	    &pvconf, nminibars, dists);
153263660c0SAlfonso Siciliano 
154263660c0SAlfonso Siciliano 	if (retval == BSDDIALOG_ERROR) {
155263660c0SAlfonso Siciliano 		fprintf(stderr, "progressview error: %s\n",
156263660c0SAlfonso Siciliano 		    bsddialog_geterror());
157263660c0SAlfonso Siciliano 	}
15857bda1b6SNathan Whitehorn 
15918f20d5dSAlfonso Siciliano 	bsddialog_end();
16018f20d5dSAlfonso Siciliano 
161dd8ca6b2SBaptiste Daroussin 	free(distribs);
16218f20d5dSAlfonso Siciliano 	free(dists);
1632118f387SNathan Whitehorn 
1642118f387SNathan Whitehorn 	return (retval);
1652118f387SNathan Whitehorn }
1662118f387SNathan Whitehorn 
1674ff1fc3bSDevin Teske static void
1684ff1fc3bSDevin Teske sig_int(int sig __unused)
1694ff1fc3bSDevin Teske {
17018f20d5dSAlfonso Siciliano 	bsddialog_interruptprogview = true;
1714ff1fc3bSDevin Teske }
1724ff1fc3bSDevin Teske 
173bd1df636SDevin Teske /*
174bd1df636SDevin Teske  * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
175bd1df636SDevin Teske  * if it exists, otherwise uses archive(3) to read the archive file.
176bd1df636SDevin Teske  */
1772118f387SNathan Whitehorn static int
17846cc2c02SNathan Whitehorn count_files(const char *file)
17946cc2c02SNathan Whitehorn {
18082ac9f2bSDevin Teske 	static FILE *manifest = NULL;
181bd1df636SDevin Teske 	char *p;
18282ac9f2bSDevin Teske 	int file_count;
18382ac9f2bSDevin Teske 	int retval;
184bd1df636SDevin Teske 	size_t span;
18546cc2c02SNathan Whitehorn 	struct archive_entry *entry;
18682ac9f2bSDevin Teske 	char line[512];
18782ac9f2bSDevin Teske 	char path[PATH_MAX];
18882ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
18918f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
19046cc2c02SNathan Whitehorn 
19146cc2c02SNathan Whitehorn 	if (manifest == NULL) {
192bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
19346cc2c02SNathan Whitehorn 		manifest = fopen(path, "r");
19446cc2c02SNathan Whitehorn 	}
19546cc2c02SNathan Whitehorn 
19646cc2c02SNathan Whitehorn 	if (manifest != NULL) {
197896a9484SNathan Whitehorn 		rewind(manifest);
19846cc2c02SNathan Whitehorn 		while (fgets(line, sizeof(line), manifest) != NULL) {
199bd1df636SDevin Teske 			p = &line[0];
200bd1df636SDevin Teske 			span = strcspn(p, "\t") ;
201bd1df636SDevin Teske 			if (span < 1 || strncmp(p, file, span) != 0)
20246cc2c02SNathan Whitehorn 				continue;
20346cc2c02SNathan Whitehorn 
20446cc2c02SNathan Whitehorn 			/*
20546cc2c02SNathan Whitehorn 			 * We're at the right manifest line. The file count is
20646cc2c02SNathan Whitehorn 			 * in the third element
20746cc2c02SNathan Whitehorn 			 */
208bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
209bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
210bd1df636SDevin Teske 			if (span > 0) {
211bd1df636SDevin Teske 				file_count = (int)strtol(p, (char **)NULL, 10);
212bd1df636SDevin Teske 				if (file_count == 0 && errno == EINVAL)
213bd1df636SDevin Teske 					continue;
214bd1df636SDevin Teske 				return (file_count);
215bd1df636SDevin Teske 			}
21646cc2c02SNathan Whitehorn 		}
21746cc2c02SNathan Whitehorn 	}
21846cc2c02SNathan Whitehorn 
219bd1df636SDevin Teske 	/*
220bd1df636SDevin Teske 	 * Either no manifest, or manifest didn't mention this archive.
221bd1df636SDevin Teske 	 * Use archive(3) to read the archive, counting files within.
222bd1df636SDevin Teske 	 */
22318f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
224bd1df636SDevin Teske 	if ((archive = archive_read_new()) == NULL) {
225bd1df636SDevin Teske 		snprintf(errormsg, sizeof(errormsg),
226bd1df636SDevin Teske 		    "Error: %s\n", archive_error_string(NULL));
227263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
22818f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
229bd1df636SDevin Teske 		return (-1);
230bd1df636SDevin Teske 	}
23146cc2c02SNathan Whitehorn 	archive_read_support_format_all(archive);
232ebb8fc42SMartin Matuska 	archive_read_support_filter_all(archive);
233bd1df636SDevin Teske 	snprintf(path, sizeof(path), "%s/%s", distdir, file);
23482ac9f2bSDevin Teske 	retval = archive_read_open_filename(archive, path, 4096);
23582ac9f2bSDevin Teske 	if (retval != ARCHIVE_OK) {
23646cc2c02SNathan Whitehorn 		snprintf(errormsg, sizeof(errormsg),
23746cc2c02SNathan Whitehorn 		    "Error while extracting %s: %s\n", file,
23846cc2c02SNathan Whitehorn 		    archive_error_string(archive));
239263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
24018f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
2414ff1fc3bSDevin Teske 		archive = NULL;
24246cc2c02SNathan Whitehorn 		return (-1);
24346cc2c02SNathan Whitehorn 	}
24446cc2c02SNathan Whitehorn 
24546cc2c02SNathan Whitehorn 	file_count = 0;
24646cc2c02SNathan Whitehorn 	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
24746cc2c02SNathan Whitehorn 		file_count++;
24846cc2c02SNathan Whitehorn 	archive_read_free(archive);
2494ff1fc3bSDevin Teske 	archive = NULL;
25046cc2c02SNathan Whitehorn 
25146cc2c02SNathan Whitehorn 	return (file_count);
25246cc2c02SNathan Whitehorn }
25346cc2c02SNathan Whitehorn 
25446cc2c02SNathan Whitehorn static int
25518f20d5dSAlfonso Siciliano extract_files(struct bsddialog_fileminibar *file)
2562118f387SNathan Whitehorn {
25782ac9f2bSDevin Teske 	int retval;
2582118f387SNathan Whitehorn 	struct archive_entry *entry;
25982ac9f2bSDevin Teske 	char path[PATH_MAX];
26082ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
26118f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
26218f20d5dSAlfonso Siciliano 
26318f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
2642118f387SNathan Whitehorn 
2654ff1fc3bSDevin Teske 	/* Open the archive if necessary */
2664ff1fc3bSDevin Teske 	if (archive == NULL) {
267bd1df636SDevin Teske 		if ((archive = archive_read_new()) == NULL) {
268bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
269bd1df636SDevin Teske 			    "Error: %s\n", archive_error_string(NULL));
270263660c0SAlfonso Siciliano 			conf.title = "Extract Error";
27118f20d5dSAlfonso Siciliano 			bsddialog_msgbox(&conf, errormsg, 0, 0);
27218f20d5dSAlfonso Siciliano 			bsddialog_abortprogview = true;
2734ff1fc3bSDevin Teske 			return (-1);
2742118f387SNathan Whitehorn 		}
2752118f387SNathan Whitehorn 		archive_read_support_format_all(archive);
276ebb8fc42SMartin Matuska 		archive_read_support_filter_all(archive);
277bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
27882ac9f2bSDevin Teske 		retval = archive_read_open_filename(archive, path, 4096);
279bd1df636SDevin Teske 		if (retval != 0) {
280bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
28118f20d5dSAlfonso Siciliano 			    "Error opening %s: %s\n", file->label,
282bd1df636SDevin Teske 			    archive_error_string(archive));
283263660c0SAlfonso Siciliano 			conf.title = "Extract Error";
28418f20d5dSAlfonso Siciliano 			bsddialog_msgbox(&conf, errormsg, 0, 0);
285263660c0SAlfonso Siciliano 			file->status = BSDDIALOG_MG_FAILED;
28618f20d5dSAlfonso Siciliano 			bsddialog_abortprogview = true;
2874ff1fc3bSDevin Teske 			return (-1);
2884ff1fc3bSDevin Teske 		}
289bd1df636SDevin Teske 	}
2902118f387SNathan Whitehorn 
2914ff1fc3bSDevin Teske 	/* Read the next archive header */
2924ff1fc3bSDevin Teske 	retval = archive_read_next_header(archive, &entry);
2932118f387SNathan Whitehorn 
2944ff1fc3bSDevin Teske 	/* If that went well, perform the extraction */
2954ff1fc3bSDevin Teske 	if (retval == ARCHIVE_OK)
29682ac9f2bSDevin Teske 		retval = archive_read_extract(archive, entry,
2972118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
2982118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
2992118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
3002118f387SNathan Whitehorn 
3014ff1fc3bSDevin Teske 	/* Test for either EOF or error */
3024ff1fc3bSDevin Teske 	if (retval == ARCHIVE_EOF) {
3032118f387SNathan Whitehorn 		archive_read_free(archive);
3044ff1fc3bSDevin Teske 		archive = NULL;
305263660c0SAlfonso Siciliano 		file->status = BSDDIALOG_MG_DONE; /*Done*/;
3064ff1fc3bSDevin Teske 		return (100);
307a5837f2cSNathan Whitehorn 	} else if (retval != ARCHIVE_OK &&
308a5837f2cSNathan Whitehorn 	    !(retval == ARCHIVE_WARN &&
309a5837f2cSNathan Whitehorn 	    strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
310a5837f2cSNathan Whitehorn 		/*
311a5837f2cSNathan Whitehorn 		 * Print any warning/error messages except inability to set
312a5837f2cSNathan Whitehorn 		 * ctime/mtime, which is not fatal, or even interesting,
313a5837f2cSNathan Whitehorn 		 * for our purposes. Would be nice if this were a libarchive
314a5837f2cSNathan Whitehorn 		 * option.
315a5837f2cSNathan Whitehorn 		 */
3164ff1fc3bSDevin Teske 		snprintf(errormsg, sizeof(errormsg),
31718f20d5dSAlfonso Siciliano 		    "Error while extracting %s: %s\n", file->label,
3184ff1fc3bSDevin Teske 		    archive_error_string(archive));
319263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
32018f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
321263660c0SAlfonso Siciliano 		file->status = BSDDIALOG_MG_FAILED; /* Failed */
32218f20d5dSAlfonso Siciliano 		bsddialog_abortprogview = true;
3234ff1fc3bSDevin Teske 		return (-1);
3242118f387SNathan Whitehorn 	}
3252118f387SNathan Whitehorn 
32618f20d5dSAlfonso Siciliano 	bsddialog_total_progview++;
3274ff1fc3bSDevin Teske 	file->read++;
3284ff1fc3bSDevin Teske 
3294ff1fc3bSDevin Teske 	/* Calculate [overall] percentage of completion (if possible) */
33018f20d5dSAlfonso Siciliano 	if (file->size >= 0)
33118f20d5dSAlfonso Siciliano 		return (file->read * 100 / file->size);
3324ff1fc3bSDevin Teske 	else
3334ff1fc3bSDevin Teske 		return (-1);
3342118f387SNathan Whitehorn }
335