xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision f95498601547f7aeb76426fb3282b51ceb59eb6f)
12118f387SNathan Whitehorn /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
48bd1df636SDevin Teske /* Data to process */
49*f9549860SBaptiste Daroussin static const char *distdir = NULL;
504ff1fc3bSDevin Teske static struct archive *archive = NULL;
51bd1df636SDevin Teske 
52bd1df636SDevin Teske /* Function prototypes */
534ff1fc3bSDevin Teske static void	sig_int(int sig);
5482ac9f2bSDevin Teske static int	count_files(const char *file);
5518f20d5dSAlfonso Siciliano static int	extract_files(struct bsddialog_fileminibar *file);
56bd1df636SDevin Teske 
5718f20d5dSAlfonso Siciliano #define _errx(...) (bsddialog_end(), errx(__VA_ARGS__))
582118f387SNathan Whitehorn 
592118f387SNathan Whitehorn int
602118f387SNathan Whitehorn main(void)
612118f387SNathan Whitehorn {
62bd1df636SDevin Teske 	char *chrootdir;
63bd1df636SDevin Teske 	char *distributions;
64dd8ca6b2SBaptiste Daroussin 	char *distribs, *distrib;
6582ac9f2bSDevin Teske 	int retval;
6618f20d5dSAlfonso Siciliano 	size_t minibar_size = sizeof(struct bsddialog_fileminibar);
6718f20d5dSAlfonso Siciliano 	unsigned int nminibars;
6818f20d5dSAlfonso Siciliano 	struct bsddialog_fileminibar *dists;
6918f20d5dSAlfonso Siciliano 	struct bsddialog_progviewconf pvconf;
7018f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
714ff1fc3bSDevin Teske 	struct sigaction act;
7282ac9f2bSDevin Teske 	char error[PATH_MAX + 512];
73bfd258f7SNathan Whitehorn 
74bd1df636SDevin Teske 	if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
7582ac9f2bSDevin Teske 		errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
76bd1df636SDevin Teske 	if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
77*f9549860SBaptiste Daroussin 		distdir = "";
78dd8ca6b2SBaptiste Daroussin 	if ((distribs = strdup(distributions)) == NULL)
79dd8ca6b2SBaptiste Daroussin 		errx(EXIT_FAILURE, "memory error");
80bfd258f7SNathan Whitehorn 
8118f20d5dSAlfonso Siciliano 	if (bsddialog_init() == BSDDIALOG_ERROR)
82263660c0SAlfonso Siciliano 		errx(EXIT_FAILURE, "Error libbsdialog: %s",
83263660c0SAlfonso Siciliano 		    bsddialog_geterror());
8418f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
85263660c0SAlfonso Siciliano 	bsddialog_backtitle(&conf, "FreeBSD Installer");
8618f20d5dSAlfonso Siciliano 	bsddialog_infobox(&conf,
87263660c0SAlfonso Siciliano 	    "Checking distribution archives.\nPlease wait...", 4, 35);
8857bda1b6SNathan Whitehorn 
8918f20d5dSAlfonso Siciliano 	/* Parse $DISTRIBUTIONS */
9018f20d5dSAlfonso Siciliano 	nminibars = 0;
9118f20d5dSAlfonso Siciliano 	dists = NULL;
92dd8ca6b2SBaptiste Daroussin 	while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) {
93dd8ca6b2SBaptiste Daroussin 		if (strlen(distrib) == 0)
94bd1df636SDevin Teske 			continue;
95bd1df636SDevin Teske 
96bd1df636SDevin Teske 		/* Allocate a new struct for the distribution */
9718f20d5dSAlfonso Siciliano 		dists = realloc(dists, (nminibars + 1) * minibar_size);
9818f20d5dSAlfonso Siciliano 		if (dists == NULL)
99bd1df636SDevin Teske 			_errx(EXIT_FAILURE, "Out of memory!");
100bd1df636SDevin Teske 
10118f20d5dSAlfonso Siciliano 		/* Set file path */
102dd8ca6b2SBaptiste Daroussin 		dists[nminibars].path = distrib;
103bd1df636SDevin Teske 
10418f20d5dSAlfonso Siciliano 		/* Set mini bar label */
10518f20d5dSAlfonso Siciliano 		dists[nminibars].label = strrchr(dists[nminibars].path, '/');
10618f20d5dSAlfonso Siciliano 		if (dists[nminibars].label == NULL)
10718f20d5dSAlfonso Siciliano 			dists[nminibars].label = dists[nminibars].path;
108bd1df636SDevin Teske 
109bd1df636SDevin Teske 		/* Set initial length in files (-1 == error) */
11018f20d5dSAlfonso Siciliano 		dists[nminibars].size = count_files(dists[nminibars].path);
11118f20d5dSAlfonso Siciliano 		if (dists[nminibars].size < 0) {
11218f20d5dSAlfonso Siciliano 			bsddialog_end();
113bd1df636SDevin Teske 			return (EXIT_FAILURE);
114bd1df636SDevin Teske 		}
115bd1df636SDevin Teske 
116263660c0SAlfonso Siciliano 		/* Set initial status and implicitly miniperc to pending */
117263660c0SAlfonso Siciliano 		dists[nminibars].status = BSDDIALOG_MG_PENDING;
11818f20d5dSAlfonso Siciliano 
11918f20d5dSAlfonso Siciliano 		/* Set initial read */
12018f20d5dSAlfonso Siciliano 		dists[nminibars].read = 0;
12118f20d5dSAlfonso Siciliano 
12218f20d5dSAlfonso Siciliano 		nminibars += 1;
123bd1df636SDevin Teske 	}
124bd1df636SDevin Teske 
125bd1df636SDevin Teske 	/* Optionally chdir(2) into $BSDINSTALL_CHROOT */
126bd1df636SDevin Teske 	chrootdir = getenv("BSDINSTALL_CHROOT");
127bd1df636SDevin Teske 	if (chrootdir != NULL && chdir(chrootdir) != 0) {
12882ac9f2bSDevin Teske 		snprintf(error, sizeof(error),
129bd1df636SDevin Teske 		    "Could not change to directory %s: %s\n",
130bd1df636SDevin Teske 		    chrootdir, strerror(errno));
131*f9549860SBaptiste Daroussin 		conf.title = "Error";
13218f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, error, 0, 0);
13318f20d5dSAlfonso Siciliano 		bsddialog_end();
13482ac9f2bSDevin Teske 		return (EXIT_FAILURE);
13557bda1b6SNathan Whitehorn 	}
13657bda1b6SNathan Whitehorn 
1374ff1fc3bSDevin Teske 	/* Set cleanup routine for Ctrl-C action */
1384ff1fc3bSDevin Teske 	act.sa_handler = sig_int;
1394ff1fc3bSDevin Teske 	sigaction(SIGINT, &act, 0);
1402118f387SNathan Whitehorn 
141263660c0SAlfonso Siciliano 	conf.title = "Archive Extraction";
142263660c0SAlfonso Siciliano 	conf.auto_minwidth = 40;
14318f20d5dSAlfonso Siciliano 	pvconf.callback	= extract_files;
14418f20d5dSAlfonso Siciliano 	pvconf.refresh = 1;
145263660c0SAlfonso Siciliano 	pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec.";
14618f20d5dSAlfonso Siciliano 	bsddialog_total_progview = 0;
14718f20d5dSAlfonso Siciliano 	bsddialog_interruptprogview = bsddialog_abortprogview = false;
14818f20d5dSAlfonso Siciliano 	retval = bsddialog_progressview(&conf,
149263660c0SAlfonso Siciliano 	    "\nExtracting distribution files...\n", 0, 0,
150263660c0SAlfonso Siciliano 	    &pvconf, nminibars, dists);
151263660c0SAlfonso Siciliano 
152263660c0SAlfonso Siciliano 	if (retval == BSDDIALOG_ERROR) {
153263660c0SAlfonso Siciliano 		fprintf(stderr, "progressview error: %s\n",
154263660c0SAlfonso Siciliano 		    bsddialog_geterror());
155263660c0SAlfonso Siciliano 	}
15657bda1b6SNathan Whitehorn 
15718f20d5dSAlfonso Siciliano 	bsddialog_end();
15818f20d5dSAlfonso Siciliano 
159dd8ca6b2SBaptiste Daroussin 	free(distribs);
16018f20d5dSAlfonso Siciliano 	free(dists);
1612118f387SNathan Whitehorn 
1622118f387SNathan Whitehorn 	return (retval);
1632118f387SNathan Whitehorn }
1642118f387SNathan Whitehorn 
1654ff1fc3bSDevin Teske static void
1664ff1fc3bSDevin Teske sig_int(int sig __unused)
1674ff1fc3bSDevin Teske {
16818f20d5dSAlfonso Siciliano 	bsddialog_interruptprogview = true;
1694ff1fc3bSDevin Teske }
1704ff1fc3bSDevin Teske 
171bd1df636SDevin Teske /*
172bd1df636SDevin Teske  * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
173bd1df636SDevin Teske  * if it exists, otherwise uses archive(3) to read the archive file.
174bd1df636SDevin Teske  */
1752118f387SNathan Whitehorn static int
17646cc2c02SNathan Whitehorn count_files(const char *file)
17746cc2c02SNathan Whitehorn {
17882ac9f2bSDevin Teske 	static FILE *manifest = NULL;
179bd1df636SDevin Teske 	char *p;
18082ac9f2bSDevin Teske 	int file_count;
18182ac9f2bSDevin Teske 	int retval;
182bd1df636SDevin Teske 	size_t span;
18346cc2c02SNathan Whitehorn 	struct archive_entry *entry;
18482ac9f2bSDevin Teske 	char line[512];
18582ac9f2bSDevin Teske 	char path[PATH_MAX];
18682ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
18718f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
18846cc2c02SNathan Whitehorn 
18946cc2c02SNathan Whitehorn 	if (manifest == NULL) {
190bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
19146cc2c02SNathan Whitehorn 		manifest = fopen(path, "r");
19246cc2c02SNathan Whitehorn 	}
19346cc2c02SNathan Whitehorn 
19446cc2c02SNathan Whitehorn 	if (manifest != NULL) {
195896a9484SNathan Whitehorn 		rewind(manifest);
19646cc2c02SNathan Whitehorn 		while (fgets(line, sizeof(line), manifest) != NULL) {
197bd1df636SDevin Teske 			p = &line[0];
198bd1df636SDevin Teske 			span = strcspn(p, "\t") ;
199bd1df636SDevin Teske 			if (span < 1 || strncmp(p, file, span) != 0)
20046cc2c02SNathan Whitehorn 				continue;
20146cc2c02SNathan Whitehorn 
20246cc2c02SNathan Whitehorn 			/*
20346cc2c02SNathan Whitehorn 			 * We're at the right manifest line. The file count is
20446cc2c02SNathan Whitehorn 			 * in the third element
20546cc2c02SNathan Whitehorn 			 */
206bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
207bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
208bd1df636SDevin Teske 			if (span > 0) {
209bd1df636SDevin Teske 				file_count = (int)strtol(p, (char **)NULL, 10);
210bd1df636SDevin Teske 				if (file_count == 0 && errno == EINVAL)
211bd1df636SDevin Teske 					continue;
212bd1df636SDevin Teske 				return (file_count);
213bd1df636SDevin Teske 			}
21446cc2c02SNathan Whitehorn 		}
21546cc2c02SNathan Whitehorn 	}
21646cc2c02SNathan Whitehorn 
217bd1df636SDevin Teske 	/*
218bd1df636SDevin Teske 	 * Either no manifest, or manifest didn't mention this archive.
219bd1df636SDevin Teske 	 * Use archive(3) to read the archive, counting files within.
220bd1df636SDevin Teske 	 */
22118f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
222bd1df636SDevin Teske 	if ((archive = archive_read_new()) == NULL) {
223bd1df636SDevin Teske 		snprintf(errormsg, sizeof(errormsg),
224bd1df636SDevin Teske 		    "Error: %s\n", archive_error_string(NULL));
225263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
22618f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
227bd1df636SDevin Teske 		return (-1);
228bd1df636SDevin Teske 	}
22946cc2c02SNathan Whitehorn 	archive_read_support_format_all(archive);
230ebb8fc42SMartin Matuska 	archive_read_support_filter_all(archive);
231bd1df636SDevin Teske 	snprintf(path, sizeof(path), "%s/%s", distdir, file);
23282ac9f2bSDevin Teske 	retval = archive_read_open_filename(archive, path, 4096);
23382ac9f2bSDevin Teske 	if (retval != ARCHIVE_OK) {
23446cc2c02SNathan Whitehorn 		snprintf(errormsg, sizeof(errormsg),
23546cc2c02SNathan Whitehorn 		    "Error while extracting %s: %s\n", file,
23646cc2c02SNathan Whitehorn 		    archive_error_string(archive));
237263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
23818f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
2394ff1fc3bSDevin Teske 		archive = NULL;
24046cc2c02SNathan Whitehorn 		return (-1);
24146cc2c02SNathan Whitehorn 	}
24246cc2c02SNathan Whitehorn 
24346cc2c02SNathan Whitehorn 	file_count = 0;
24446cc2c02SNathan Whitehorn 	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
24546cc2c02SNathan Whitehorn 		file_count++;
24646cc2c02SNathan Whitehorn 	archive_read_free(archive);
2474ff1fc3bSDevin Teske 	archive = NULL;
24846cc2c02SNathan Whitehorn 
24946cc2c02SNathan Whitehorn 	return (file_count);
25046cc2c02SNathan Whitehorn }
25146cc2c02SNathan Whitehorn 
25246cc2c02SNathan Whitehorn static int
25318f20d5dSAlfonso Siciliano extract_files(struct bsddialog_fileminibar *file)
2542118f387SNathan Whitehorn {
25582ac9f2bSDevin Teske 	int retval;
2562118f387SNathan Whitehorn 	struct archive_entry *entry;
25782ac9f2bSDevin Teske 	char path[PATH_MAX];
25882ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
25918f20d5dSAlfonso Siciliano 	struct bsddialog_conf conf;
26018f20d5dSAlfonso Siciliano 
26118f20d5dSAlfonso Siciliano 	bsddialog_initconf(&conf);
2622118f387SNathan Whitehorn 
2634ff1fc3bSDevin Teske 	/* Open the archive if necessary */
2644ff1fc3bSDevin Teske 	if (archive == NULL) {
265bd1df636SDevin Teske 		if ((archive = archive_read_new()) == NULL) {
266bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
267bd1df636SDevin Teske 			    "Error: %s\n", archive_error_string(NULL));
268263660c0SAlfonso Siciliano 			conf.title = "Extract Error";
26918f20d5dSAlfonso Siciliano 			bsddialog_msgbox(&conf, errormsg, 0, 0);
27018f20d5dSAlfonso Siciliano 			bsddialog_abortprogview = true;
2714ff1fc3bSDevin Teske 			return (-1);
2722118f387SNathan Whitehorn 		}
2732118f387SNathan Whitehorn 		archive_read_support_format_all(archive);
274ebb8fc42SMartin Matuska 		archive_read_support_filter_all(archive);
275bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
27682ac9f2bSDevin Teske 		retval = archive_read_open_filename(archive, path, 4096);
277bd1df636SDevin Teske 		if (retval != 0) {
278bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
27918f20d5dSAlfonso Siciliano 			    "Error opening %s: %s\n", file->label,
280bd1df636SDevin Teske 			    archive_error_string(archive));
281263660c0SAlfonso Siciliano 			conf.title = "Extract Error";
28218f20d5dSAlfonso Siciliano 			bsddialog_msgbox(&conf, errormsg, 0, 0);
283263660c0SAlfonso Siciliano 			file->status = BSDDIALOG_MG_FAILED;
28418f20d5dSAlfonso Siciliano 			bsddialog_abortprogview = true;
2854ff1fc3bSDevin Teske 			return (-1);
2864ff1fc3bSDevin Teske 		}
287bd1df636SDevin Teske 	}
2882118f387SNathan Whitehorn 
2894ff1fc3bSDevin Teske 	/* Read the next archive header */
2904ff1fc3bSDevin Teske 	retval = archive_read_next_header(archive, &entry);
2912118f387SNathan Whitehorn 
2924ff1fc3bSDevin Teske 	/* If that went well, perform the extraction */
2934ff1fc3bSDevin Teske 	if (retval == ARCHIVE_OK)
29482ac9f2bSDevin Teske 		retval = archive_read_extract(archive, entry,
2952118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
2962118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
2972118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
2982118f387SNathan Whitehorn 
2994ff1fc3bSDevin Teske 	/* Test for either EOF or error */
3004ff1fc3bSDevin Teske 	if (retval == ARCHIVE_EOF) {
3012118f387SNathan Whitehorn 		archive_read_free(archive);
3024ff1fc3bSDevin Teske 		archive = NULL;
303263660c0SAlfonso Siciliano 		file->status = BSDDIALOG_MG_DONE; /*Done*/;
3044ff1fc3bSDevin Teske 		return (100);
305a5837f2cSNathan Whitehorn 	} else if (retval != ARCHIVE_OK &&
306a5837f2cSNathan Whitehorn 	    !(retval == ARCHIVE_WARN &&
307a5837f2cSNathan Whitehorn 	    strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
308a5837f2cSNathan Whitehorn 		/*
309a5837f2cSNathan Whitehorn 		 * Print any warning/error messages except inability to set
310a5837f2cSNathan Whitehorn 		 * ctime/mtime, which is not fatal, or even interesting,
311a5837f2cSNathan Whitehorn 		 * for our purposes. Would be nice if this were a libarchive
312a5837f2cSNathan Whitehorn 		 * option.
313a5837f2cSNathan Whitehorn 		 */
3144ff1fc3bSDevin Teske 		snprintf(errormsg, sizeof(errormsg),
31518f20d5dSAlfonso Siciliano 		    "Error while extracting %s: %s\n", file->label,
3164ff1fc3bSDevin Teske 		    archive_error_string(archive));
317263660c0SAlfonso Siciliano 		conf.title = "Extract Error";
31818f20d5dSAlfonso Siciliano 		bsddialog_msgbox(&conf, errormsg, 0, 0);
319263660c0SAlfonso Siciliano 		file->status = BSDDIALOG_MG_FAILED; /* Failed */
32018f20d5dSAlfonso Siciliano 		bsddialog_abortprogview = true;
3214ff1fc3bSDevin Teske 		return (-1);
3222118f387SNathan Whitehorn 	}
3232118f387SNathan Whitehorn 
32418f20d5dSAlfonso Siciliano 	bsddialog_total_progview++;
3254ff1fc3bSDevin Teske 	file->read++;
3264ff1fc3bSDevin Teske 
3274ff1fc3bSDevin Teske 	/* Calculate [overall] percentage of completion (if possible) */
32818f20d5dSAlfonso Siciliano 	if (file->size >= 0)
32918f20d5dSAlfonso Siciliano 		return (file->read * 100 / file->size);
3304ff1fc3bSDevin Teske 	else
3314ff1fc3bSDevin Teske 		return (-1);
3322118f387SNathan Whitehorn }
333