xref: /freebsd/usr.sbin/bsdinstall/distextract/distextract.c (revision a5837f2c265afb7d9ec64df6246f2194d61b8f69)
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>
3482ac9f2bSDevin Teske #include <archive.h>
3582ac9f2bSDevin Teske #include <ctype.h>
3682ac9f2bSDevin Teske #include <dialog.h>
374ff1fc3bSDevin Teske #include <dpv.h>
3882ac9f2bSDevin Teske #include <err.h>
392118f387SNathan Whitehorn #include <errno.h>
402118f387SNathan Whitehorn #include <limits.h>
4182ac9f2bSDevin Teske #include <stdio.h>
4282ac9f2bSDevin Teske #include <stdlib.h>
4382ac9f2bSDevin Teske #include <string.h>
4482ac9f2bSDevin Teske #include <unistd.h>
452118f387SNathan Whitehorn 
46bd1df636SDevin Teske /* Data to process */
47bd1df636SDevin Teske static char *distdir = NULL;
484ff1fc3bSDevin Teske static struct archive *archive = NULL;
494ff1fc3bSDevin Teske static struct dpv_file_node *dists = NULL;
50bd1df636SDevin Teske 
51bd1df636SDevin Teske /* Function prototypes */
524ff1fc3bSDevin Teske static void	sig_int(int sig);
5382ac9f2bSDevin Teske static int	count_files(const char *file);
544ff1fc3bSDevin Teske static int	extract_files(struct dpv_file_node *file, int out);
55bd1df636SDevin Teske 
56bd1df636SDevin Teske #if __FreeBSD_version <= 1000008 /* r232154: bump for libarchive update */
57bd1df636SDevin Teske #define archive_read_support_filter_all(x) \
58bd1df636SDevin Teske 	archive_read_support_compression_all(x)
59bd1df636SDevin Teske #endif
60bd1df636SDevin Teske 
61bd1df636SDevin Teske #define _errx(...) (end_dialog(), errx(__VA_ARGS__))
622118f387SNathan Whitehorn 
632118f387SNathan Whitehorn int
642118f387SNathan Whitehorn main(void)
652118f387SNathan Whitehorn {
66bd1df636SDevin Teske 	char *chrootdir;
67bd1df636SDevin Teske 	char *distributions;
6882ac9f2bSDevin Teske 	int retval;
694ff1fc3bSDevin Teske 	size_t config_size = sizeof(struct dpv_config);
704ff1fc3bSDevin Teske 	size_t file_node_size = sizeof(struct dpv_file_node);
71bd1df636SDevin Teske 	size_t span;
724ff1fc3bSDevin Teske 	struct dpv_config *config;
734ff1fc3bSDevin Teske 	struct dpv_file_node *dist = dists;
744ff1fc3bSDevin Teske 	static char backtitle[] = "FreeBSD Installer";
754ff1fc3bSDevin Teske 	static char title[] = "Archive Extraction";
764ff1fc3bSDevin Teske 	static char aprompt[] = "\n  Overall Progress:";
774ff1fc3bSDevin Teske 	static char pprompt[] = "Extracting distribution files...\n";
784ff1fc3bSDevin Teske 	struct sigaction act;
7982ac9f2bSDevin Teske 	char error[PATH_MAX + 512];
80bfd258f7SNathan Whitehorn 
81bd1df636SDevin Teske 	if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
8282ac9f2bSDevin Teske 		errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
83bd1df636SDevin Teske 	if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
84bd1df636SDevin Teske 		distdir = __DECONST(char *, "");
85bfd258f7SNathan Whitehorn 
86bd1df636SDevin Teske 	/* Initialize dialog(3) */
8757bda1b6SNathan Whitehorn 	init_dialog(stdin, stdout);
884ff1fc3bSDevin Teske 	dialog_vars.backtitle = backtitle;
8957bda1b6SNathan Whitehorn 	dlg_put_backtitle();
9057bda1b6SNathan Whitehorn 
91bd1df636SDevin Teske 	dialog_msgbox("",
92bd1df636SDevin Teske 	    "Checking distribution archives.\nPlease wait...", 4, 35, FALSE);
93bd1df636SDevin Teske 
94bd1df636SDevin Teske 	/*
954ff1fc3bSDevin Teske 	 * Parse $DISTRIBUTIONS into dpv(3) linked-list
96bd1df636SDevin Teske 	 */
97bd1df636SDevin Teske 	while (*distributions != '\0') {
98bd1df636SDevin Teske 		span = strcspn(distributions, "\t\n\v\f\r ");
99bd1df636SDevin Teske 		if (span < 1) { /* currently on whitespace */
100bd1df636SDevin Teske 			distributions++;
101bd1df636SDevin Teske 			continue;
102bd1df636SDevin Teske 		}
103bd1df636SDevin Teske 
104bd1df636SDevin Teske 		/* Allocate a new struct for the distribution */
105bd1df636SDevin Teske 		if (dist == NULL) {
106bd1df636SDevin Teske 			if ((dist = calloc(1, file_node_size)) == NULL)
107bd1df636SDevin Teske 				_errx(EXIT_FAILURE, "Out of memory!");
108bd1df636SDevin Teske 			dists = dist;
109bd1df636SDevin Teske 		} else {
110bd1df636SDevin Teske 			dist->next = calloc(1, file_node_size);
111bd1df636SDevin Teske 			if (dist->next == NULL)
112bd1df636SDevin Teske 				_errx(EXIT_FAILURE, "Out of memory!");
113bd1df636SDevin Teske 			dist = dist->next;
114bd1df636SDevin Teske 		}
115bd1df636SDevin Teske 
116bd1df636SDevin Teske 		/* Set path */
117bd1df636SDevin Teske 		if ((dist->path = malloc(span + 1)) == NULL)
118bd1df636SDevin Teske 			_errx(EXIT_FAILURE, "Out of memory!");
119bd1df636SDevin Teske 		snprintf(dist->path, span + 1, "%s", distributions);
120bd1df636SDevin Teske 		dist->path[span] = '\0';
121bd1df636SDevin Teske 
122bd1df636SDevin Teske 		/* Set display name */
123bd1df636SDevin Teske 		dist->name = strrchr(dist->path, '/');
124bd1df636SDevin Teske 		if (dist->name == NULL)
125bd1df636SDevin Teske 			dist->name = dist->path;
126bd1df636SDevin Teske 
127bd1df636SDevin Teske 		/* Set initial length in files (-1 == error) */
128bd1df636SDevin Teske 		dist->length = count_files(dist->path);
129bd1df636SDevin Teske 		if (dist->length < 0) {
130bd1df636SDevin Teske 			end_dialog();
131bd1df636SDevin Teske 			return (EXIT_FAILURE);
132bd1df636SDevin Teske 		}
133bd1df636SDevin Teske 
134bd1df636SDevin Teske 		distributions += span;
135bd1df636SDevin Teske 	}
136bd1df636SDevin Teske 
137bd1df636SDevin Teske 	/* Optionally chdir(2) into $BSDINSTALL_CHROOT */
138bd1df636SDevin Teske 	chrootdir = getenv("BSDINSTALL_CHROOT");
139bd1df636SDevin Teske 	if (chrootdir != NULL && chdir(chrootdir) != 0) {
14082ac9f2bSDevin Teske 		snprintf(error, sizeof(error),
141bd1df636SDevin Teske 		    "Could not change to directory %s: %s\n",
142bd1df636SDevin Teske 		    chrootdir, strerror(errno));
14357bda1b6SNathan Whitehorn 		dialog_msgbox("Error", error, 0, 0, TRUE);
14457bda1b6SNathan Whitehorn 		end_dialog();
14582ac9f2bSDevin Teske 		return (EXIT_FAILURE);
14657bda1b6SNathan Whitehorn 	}
14757bda1b6SNathan Whitehorn 
1484ff1fc3bSDevin Teske 	/* Set cleanup routine for Ctrl-C action */
1494ff1fc3bSDevin Teske 	act.sa_handler = sig_int;
1504ff1fc3bSDevin Teske 	sigaction(SIGINT, &act, 0);
1512118f387SNathan Whitehorn 
1524ff1fc3bSDevin Teske 	/*
1534ff1fc3bSDevin Teske 	 * Hand off to dpv(3)
1544ff1fc3bSDevin Teske 	 */
1554ff1fc3bSDevin Teske 	if ((config = calloc(1, config_size)) == NULL)
1564ff1fc3bSDevin Teske 		_errx(EXIT_FAILURE, "Out of memory!");
1574ff1fc3bSDevin Teske 	config->backtitle	= backtitle;
1584ff1fc3bSDevin Teske 	config->title		= title;
1594ff1fc3bSDevin Teske 	config->pprompt		= pprompt;
1604ff1fc3bSDevin Teske 	config->aprompt		= aprompt;
1614ff1fc3bSDevin Teske 	config->options		|= DPV_WIDE_MODE;
1624ff1fc3bSDevin Teske 	config->label_size	= -1;
1634ff1fc3bSDevin Teske 	config->action		= extract_files;
1644ff1fc3bSDevin Teske 	config->status_solo	=
1654ff1fc3bSDevin Teske 	    "%10lli files read @ %'9.1f files/sec.";
1664ff1fc3bSDevin Teske 	config->status_many	=
1674ff1fc3bSDevin Teske 	    "%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]";
16857bda1b6SNathan Whitehorn 	end_dialog();
1694ff1fc3bSDevin Teske 	retval = dpv(config, dists);
17057bda1b6SNathan Whitehorn 
1714ff1fc3bSDevin Teske 	dpv_free();
172bd1df636SDevin Teske 	while ((dist = dists) != NULL) {
173bd1df636SDevin Teske 		dists = dist->next;
174bd1df636SDevin Teske 		if (dist->path != NULL)
175bd1df636SDevin Teske 			free(dist->path);
176bd1df636SDevin Teske 		free(dist);
177bd1df636SDevin Teske 	}
1782118f387SNathan Whitehorn 
1792118f387SNathan Whitehorn 	return (retval);
1802118f387SNathan Whitehorn }
1812118f387SNathan Whitehorn 
1824ff1fc3bSDevin Teske static void
1834ff1fc3bSDevin Teske sig_int(int sig __unused)
1844ff1fc3bSDevin Teske {
1854ff1fc3bSDevin Teske 	dpv_interrupt = TRUE;
1864ff1fc3bSDevin Teske }
1874ff1fc3bSDevin Teske 
188bd1df636SDevin Teske /*
189bd1df636SDevin Teske  * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
190bd1df636SDevin Teske  * if it exists, otherwise uses archive(3) to read the archive file.
191bd1df636SDevin Teske  */
1922118f387SNathan Whitehorn static int
19346cc2c02SNathan Whitehorn count_files(const char *file)
19446cc2c02SNathan Whitehorn {
19582ac9f2bSDevin Teske 	static FILE *manifest = NULL;
196bd1df636SDevin Teske 	char *p;
19782ac9f2bSDevin Teske 	int file_count;
19882ac9f2bSDevin Teske 	int retval;
199bd1df636SDevin Teske 	size_t span;
20046cc2c02SNathan Whitehorn 	struct archive_entry *entry;
20182ac9f2bSDevin Teske 	char line[512];
20282ac9f2bSDevin Teske 	char path[PATH_MAX];
20382ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
20446cc2c02SNathan Whitehorn 
20546cc2c02SNathan Whitehorn 	if (manifest == NULL) {
206bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
20746cc2c02SNathan Whitehorn 		manifest = fopen(path, "r");
20846cc2c02SNathan Whitehorn 	}
20946cc2c02SNathan Whitehorn 
21046cc2c02SNathan Whitehorn 	if (manifest != NULL) {
211896a9484SNathan Whitehorn 		rewind(manifest);
21246cc2c02SNathan Whitehorn 		while (fgets(line, sizeof(line), manifest) != NULL) {
213bd1df636SDevin Teske 			p = &line[0];
214bd1df636SDevin Teske 			span = strcspn(p, "\t") ;
215bd1df636SDevin Teske 			if (span < 1 || strncmp(p, file, span) != 0)
21646cc2c02SNathan Whitehorn 				continue;
21746cc2c02SNathan Whitehorn 
21846cc2c02SNathan Whitehorn 			/*
21946cc2c02SNathan Whitehorn 			 * We're at the right manifest line. The file count is
22046cc2c02SNathan Whitehorn 			 * in the third element
22146cc2c02SNathan Whitehorn 			 */
222bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
223bd1df636SDevin Teske 			span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
224bd1df636SDevin Teske 			if (span > 0) {
225bd1df636SDevin Teske 				file_count = (int)strtol(p, (char **)NULL, 10);
226bd1df636SDevin Teske 				if (file_count == 0 && errno == EINVAL)
227bd1df636SDevin Teske 					continue;
228bd1df636SDevin Teske 				return (file_count);
229bd1df636SDevin Teske 			}
23046cc2c02SNathan Whitehorn 		}
23146cc2c02SNathan Whitehorn 	}
23246cc2c02SNathan Whitehorn 
233bd1df636SDevin Teske 	/*
234bd1df636SDevin Teske 	 * Either no manifest, or manifest didn't mention this archive.
235bd1df636SDevin Teske 	 * Use archive(3) to read the archive, counting files within.
236bd1df636SDevin Teske 	 */
237bd1df636SDevin Teske 	if ((archive = archive_read_new()) == NULL) {
238bd1df636SDevin Teske 		snprintf(errormsg, sizeof(errormsg),
239bd1df636SDevin Teske 		    "Error: %s\n", archive_error_string(NULL));
240bd1df636SDevin Teske 		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
241bd1df636SDevin Teske 		return (-1);
242bd1df636SDevin Teske 	}
24346cc2c02SNathan Whitehorn 	archive_read_support_format_all(archive);
244ebb8fc42SMartin Matuska 	archive_read_support_filter_all(archive);
245bd1df636SDevin Teske 	snprintf(path, sizeof(path), "%s/%s", distdir, file);
24682ac9f2bSDevin Teske 	retval = archive_read_open_filename(archive, path, 4096);
24782ac9f2bSDevin Teske 	if (retval != ARCHIVE_OK) {
24846cc2c02SNathan Whitehorn 		snprintf(errormsg, sizeof(errormsg),
24946cc2c02SNathan Whitehorn 		    "Error while extracting %s: %s\n", file,
25046cc2c02SNathan Whitehorn 		    archive_error_string(archive));
25146cc2c02SNathan Whitehorn 		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
2524ff1fc3bSDevin Teske 		archive = NULL;
25346cc2c02SNathan Whitehorn 		return (-1);
25446cc2c02SNathan Whitehorn 	}
25546cc2c02SNathan Whitehorn 
25646cc2c02SNathan Whitehorn 	file_count = 0;
25746cc2c02SNathan Whitehorn 	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
25846cc2c02SNathan Whitehorn 		file_count++;
25946cc2c02SNathan Whitehorn 	archive_read_free(archive);
2604ff1fc3bSDevin Teske 	archive = NULL;
26146cc2c02SNathan Whitehorn 
26246cc2c02SNathan Whitehorn 	return (file_count);
26346cc2c02SNathan Whitehorn }
26446cc2c02SNathan Whitehorn 
26546cc2c02SNathan Whitehorn static int
2664ff1fc3bSDevin Teske extract_files(struct dpv_file_node *file, int out __unused)
2672118f387SNathan Whitehorn {
26882ac9f2bSDevin Teske 	int retval;
2692118f387SNathan Whitehorn 	struct archive_entry *entry;
27082ac9f2bSDevin Teske 	char path[PATH_MAX];
27182ac9f2bSDevin Teske 	char errormsg[PATH_MAX + 512];
2722118f387SNathan Whitehorn 
2734ff1fc3bSDevin Teske 	/* Open the archive if necessary */
2744ff1fc3bSDevin Teske 	if (archive == NULL) {
275bd1df636SDevin Teske 		if ((archive = archive_read_new()) == NULL) {
276bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
277bd1df636SDevin Teske 			    "Error: %s\n", archive_error_string(NULL));
278bd1df636SDevin Teske 			dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
2794ff1fc3bSDevin Teske 			dpv_abort = 1;
2804ff1fc3bSDevin Teske 			return (-1);
2812118f387SNathan Whitehorn 		}
2822118f387SNathan Whitehorn 		archive_read_support_format_all(archive);
283ebb8fc42SMartin Matuska 		archive_read_support_filter_all(archive);
284bd1df636SDevin Teske 		snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
28582ac9f2bSDevin Teske 		retval = archive_read_open_filename(archive, path, 4096);
286bd1df636SDevin Teske 		if (retval != 0) {
287bd1df636SDevin Teske 			snprintf(errormsg, sizeof(errormsg),
288bd1df636SDevin Teske 			    "Error opening %s: %s\n", file->name,
289bd1df636SDevin Teske 			    archive_error_string(archive));
290bd1df636SDevin Teske 			dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
2914ff1fc3bSDevin Teske 			file->status = DPV_STATUS_FAILED;
2924ff1fc3bSDevin Teske 			dpv_abort = 1;
2934ff1fc3bSDevin Teske 			return (-1);
2944ff1fc3bSDevin Teske 		}
295bd1df636SDevin Teske 	}
2962118f387SNathan Whitehorn 
2974ff1fc3bSDevin Teske 	/* Read the next archive header */
2984ff1fc3bSDevin Teske 	retval = archive_read_next_header(archive, &entry);
2992118f387SNathan Whitehorn 
3004ff1fc3bSDevin Teske 	/* If that went well, perform the extraction */
3014ff1fc3bSDevin Teske 	if (retval == ARCHIVE_OK)
30282ac9f2bSDevin Teske 		retval = archive_read_extract(archive, entry,
3032118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
3042118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
3052118f387SNathan Whitehorn 		    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
3062118f387SNathan Whitehorn 
3074ff1fc3bSDevin Teske 	/* Test for either EOF or error */
3084ff1fc3bSDevin Teske 	if (retval == ARCHIVE_EOF) {
3092118f387SNathan Whitehorn 		archive_read_free(archive);
3104ff1fc3bSDevin Teske 		archive = NULL;
3114ff1fc3bSDevin Teske 		file->status = DPV_STATUS_DONE;
3124ff1fc3bSDevin Teske 		return (100);
313*a5837f2cSNathan Whitehorn 	} else if (retval != ARCHIVE_OK &&
314*a5837f2cSNathan Whitehorn 	    !(retval == ARCHIVE_WARN &&
315*a5837f2cSNathan Whitehorn 	    strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
316*a5837f2cSNathan Whitehorn 		/*
317*a5837f2cSNathan Whitehorn 		 * Print any warning/error messages except inability to set
318*a5837f2cSNathan Whitehorn 		 * ctime/mtime, which is not fatal, or even interesting,
319*a5837f2cSNathan Whitehorn 		 * for our purposes. Would be nice if this were a libarchive
320*a5837f2cSNathan Whitehorn 		 * option.
321*a5837f2cSNathan Whitehorn 		 */
3224ff1fc3bSDevin Teske 		snprintf(errormsg, sizeof(errormsg),
3234ff1fc3bSDevin Teske 		    "Error while extracting %s: %s\n", file->name,
3244ff1fc3bSDevin Teske 		    archive_error_string(archive));
3254ff1fc3bSDevin Teske 		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
3264ff1fc3bSDevin Teske 		file->status = DPV_STATUS_FAILED;
3274ff1fc3bSDevin Teske 		dpv_abort = 1;
3284ff1fc3bSDevin Teske 		return (-1);
3292118f387SNathan Whitehorn 	}
3302118f387SNathan Whitehorn 
3314ff1fc3bSDevin Teske 	dpv_overall_read++;
3324ff1fc3bSDevin Teske 	file->read++;
3334ff1fc3bSDevin Teske 
3344ff1fc3bSDevin Teske 	/* Calculate [overall] percentage of completion (if possible) */
3354ff1fc3bSDevin Teske 	if (file->length >= 0)
3364ff1fc3bSDevin Teske 		return (file->read * 100 / file->length);
3374ff1fc3bSDevin Teske 	else
3384ff1fc3bSDevin Teske 		return (-1);
3392118f387SNathan Whitehorn }
340