1218799Snwhitehorn/*-
2218799Snwhitehorn * Copyright (c) 2011 Nathan Whitehorn
3218799Snwhitehorn * All rights reserved.
4218799Snwhitehorn *
5218799Snwhitehorn * Redistribution and use in source and binary forms, with or without
6218799Snwhitehorn * modification, are permitted provided that the following conditions
7218799Snwhitehorn * are met:
8218799Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9218799Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10218799Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11218799Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12218799Snwhitehorn *    documentation and/or other materials provided with the distribution.
13218799Snwhitehorn *
14218799Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15218799Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16218799Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17218799Snwhitehorn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18218799Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19218799Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20218799Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21218799Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22218799Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23218799Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24218799Snwhitehorn * SUCH DAMAGE.
25218799Snwhitehorn *
26218799Snwhitehorn * $FreeBSD$
27218799Snwhitehorn */
28218799Snwhitehorn
29219617Snwhitehorn#include <sys/param.h>
30218799Snwhitehorn#include <stdio.h>
31218799Snwhitehorn#include <errno.h>
32218799Snwhitehorn#include <limits.h>
33218799Snwhitehorn#include <archive.h>
34218799Snwhitehorn#include <dialog.h>
35218799Snwhitehorn
36218799Snwhitehornstatic int extract_files(int nfiles, const char **files);
37218799Snwhitehorn
38218799Snwhitehornint
39218799Snwhitehornmain(void)
40218799Snwhitehorn{
41232433Snwhitehorn	char *diststring;
42218799Snwhitehorn	const char **dists;
43218799Snwhitehorn	int i, retval, ndists = 0;
44232433Snwhitehorn
45232433Snwhitehorn	if (getenv("DISTRIBUTIONS") == NULL) {
46232433Snwhitehorn		fprintf(stderr, "DISTRIBUTIONS variable is not set\n");
47232433Snwhitehorn		return (1);
48232433Snwhitehorn	}
49232433Snwhitehorn
50232433Snwhitehorn	diststring = strdup(getenv("DISTRIBUTIONS"));
51218799Snwhitehorn	for (i = 0; diststring[i] != 0; i++)
52218799Snwhitehorn		if (isspace(diststring[i]) && !isspace(diststring[i+1]))
53218799Snwhitehorn			ndists++;
54218799Snwhitehorn	ndists++; /* Last one */
55218799Snwhitehorn
56218799Snwhitehorn	dists = calloc(ndists, sizeof(const char *));
57218915Snwhitehorn	if (dists == NULL) {
58218915Snwhitehorn		fprintf(stderr, "Out of memory!\n");
59229688Skevlo		free(diststring);
60218915Snwhitehorn		return (1);
61218915Snwhitehorn	}
62218915Snwhitehorn
63218799Snwhitehorn	for (i = 0; i < ndists; i++)
64218799Snwhitehorn		dists[i] = strsep(&diststring, " \t");
65218799Snwhitehorn
66218915Snwhitehorn	init_dialog(stdin, stdout);
67218915Snwhitehorn	dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
68218915Snwhitehorn	dlg_put_backtitle();
69218915Snwhitehorn
70218915Snwhitehorn	if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) {
71218915Snwhitehorn		char error[512];
72218915Snwhitehorn		sprintf(error, "Could could change to directory %s: %s\n",
73218915Snwhitehorn		    getenv("BSDINSTALL_DISTDIR"), strerror(errno));
74218915Snwhitehorn		dialog_msgbox("Error", error, 0, 0, TRUE);
75218915Snwhitehorn		end_dialog();
76218915Snwhitehorn		return (1);
77218915Snwhitehorn	}
78218915Snwhitehorn
79218799Snwhitehorn	retval = extract_files(ndists, dists);
80218799Snwhitehorn
81218915Snwhitehorn	end_dialog();
82218915Snwhitehorn
83218799Snwhitehorn	free(diststring);
84218799Snwhitehorn	free(dists);
85218799Snwhitehorn
86218799Snwhitehorn	return (retval);
87218799Snwhitehorn}
88218799Snwhitehorn
89218799Snwhitehornstatic int
90219617Snwhitehorncount_files(const char *file)
91219617Snwhitehorn{
92219617Snwhitehorn	struct archive *archive;
93219617Snwhitehorn	struct archive_entry *entry;
94219617Snwhitehorn	static FILE *manifest = NULL;
95219617Snwhitehorn	char path[MAXPATHLEN];
96219617Snwhitehorn	char errormsg[512];
97219617Snwhitehorn	int file_count, err;
98219617Snwhitehorn
99219617Snwhitehorn	if (manifest == NULL) {
100219617Snwhitehorn		sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR"));
101219617Snwhitehorn		manifest = fopen(path, "r");
102219617Snwhitehorn	}
103219617Snwhitehorn
104219617Snwhitehorn	if (manifest != NULL) {
105219617Snwhitehorn		char line[512];
106219617Snwhitehorn		char *tok1, *tok2;
107219618Snwhitehorn
108219618Snwhitehorn		rewind(manifest);
109219617Snwhitehorn		while (fgets(line, sizeof(line), manifest) != NULL) {
110219617Snwhitehorn			tok2 = line;
111219617Snwhitehorn			tok1 = strsep(&tok2, "\t");
112219617Snwhitehorn			if (tok1 == NULL || strcmp(tok1, file) != 0)
113219617Snwhitehorn				continue;
114219617Snwhitehorn
115219617Snwhitehorn			/*
116219617Snwhitehorn			 * We're at the right manifest line. The file count is
117219617Snwhitehorn			 * in the third element
118219617Snwhitehorn			 */
119219617Snwhitehorn			tok1 = strsep(&tok2, "\t");
120219617Snwhitehorn			tok1 = strsep(&tok2, "\t");
121219617Snwhitehorn			if (tok1 != NULL)
122219617Snwhitehorn				return atoi(tok1);
123219617Snwhitehorn		}
124219617Snwhitehorn	}
125219617Snwhitehorn
126219617Snwhitehorn	/* Either we didn't have a manifest, or this archive wasn't there */
127219617Snwhitehorn	archive = archive_read_new();
128219617Snwhitehorn	archive_read_support_format_all(archive);
129219617Snwhitehorn	archive_read_support_compression_all(archive);
130219617Snwhitehorn	sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file);
131219617Snwhitehorn	err = archive_read_open_filename(archive, path, 4096);
132219617Snwhitehorn	if (err != ARCHIVE_OK) {
133219617Snwhitehorn		snprintf(errormsg, sizeof(errormsg),
134219617Snwhitehorn		    "Error while extracting %s: %s\n", file,
135219617Snwhitehorn		    archive_error_string(archive));
136219617Snwhitehorn		dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
137219617Snwhitehorn		return (-1);
138219617Snwhitehorn	}
139219617Snwhitehorn
140219617Snwhitehorn	file_count = 0;
141219617Snwhitehorn	while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
142219617Snwhitehorn		file_count++;
143219617Snwhitehorn	archive_read_free(archive);
144219617Snwhitehorn
145219617Snwhitehorn	return (file_count);
146219617Snwhitehorn}
147219617Snwhitehorn
148219617Snwhitehornstatic int
149218799Snwhitehornextract_files(int nfiles, const char **files)
150218799Snwhitehorn{
151218799Snwhitehorn	const char *items[nfiles*2];
152218799Snwhitehorn	char path[PATH_MAX];
153218799Snwhitehorn	int archive_files[nfiles];
154218799Snwhitehorn	int total_files, current_files, archive_file;
155218799Snwhitehorn	struct archive *archive;
156218799Snwhitehorn	struct archive_entry *entry;
157218799Snwhitehorn	char errormsg[512];
158218799Snwhitehorn	char status[8];
159218799Snwhitehorn	int i, err, progress, last_progress;
160218799Snwhitehorn
161218799Snwhitehorn	err = 0;
162218799Snwhitehorn	progress = 0;
163218799Snwhitehorn
164218799Snwhitehorn	/* Make the transfer list for dialog */
165218799Snwhitehorn	for (i = 0; i < nfiles; i++) {
166218799Snwhitehorn		items[i*2] = strrchr(files[i], '/');
167218799Snwhitehorn		if (items[i*2] != NULL)
168218799Snwhitehorn			items[i*2]++;
169218799Snwhitehorn		else
170218799Snwhitehorn			items[i*2] = files[i];
171218799Snwhitehorn		items[i*2 + 1] = "Pending";
172218799Snwhitehorn	}
173218799Snwhitehorn
174218799Snwhitehorn	dialog_msgbox("",
175218799Snwhitehorn	    "Checking distribution archives.\nPlease wait...", 0, 0, FALSE);
176218799Snwhitehorn
177219617Snwhitehorn	/* Count all the files */
178218799Snwhitehorn	total_files = 0;
179218799Snwhitehorn	for (i = 0; i < nfiles; i++) {
180219617Snwhitehorn		archive_files[i] = count_files(files[i]);
181219617Snwhitehorn		if (archive_files[i] < 0)
182219617Snwhitehorn			return (-1);
183218799Snwhitehorn		total_files += archive_files[i];
184218799Snwhitehorn	}
185218799Snwhitehorn
186218799Snwhitehorn	current_files = 0;
187218799Snwhitehorn
188218799Snwhitehorn	for (i = 0; i < nfiles; i++) {
189218799Snwhitehorn		archive = archive_read_new();
190218799Snwhitehorn		archive_read_support_format_all(archive);
191218799Snwhitehorn		archive_read_support_compression_all(archive);
192218799Snwhitehorn		sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]);
193218799Snwhitehorn		err = archive_read_open_filename(archive, path, 4096);
194218799Snwhitehorn
195218799Snwhitehorn		items[i*2 + 1] = "In Progress";
196218799Snwhitehorn		archive_file = 0;
197218799Snwhitehorn
198218799Snwhitehorn		while ((err = archive_read_next_header(archive, &entry)) ==
199218799Snwhitehorn		    ARCHIVE_OK) {
200218799Snwhitehorn			last_progress = progress;
201218799Snwhitehorn			progress = (current_files*100)/total_files;
202218799Snwhitehorn
203218799Snwhitehorn			sprintf(status, "-%d",
204218799Snwhitehorn			    (archive_file*100)/archive_files[i]);
205218799Snwhitehorn			items[i*2 + 1] = status;
206218799Snwhitehorn
207218799Snwhitehorn			if (progress > last_progress)
208218799Snwhitehorn				dialog_mixedgauge("Archive Extraction",
209218799Snwhitehorn				    "Extracting distribution files...", 0, 0,
210218799Snwhitehorn				    progress, nfiles,
211218799Snwhitehorn				    __DECONST(char **, items));
212218799Snwhitehorn
213218799Snwhitehorn			err = archive_read_extract(archive, entry,
214218799Snwhitehorn			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
215218799Snwhitehorn			    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
216218799Snwhitehorn			    ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
217218799Snwhitehorn
218218799Snwhitehorn			if (err != ARCHIVE_OK)
219218799Snwhitehorn				break;
220218799Snwhitehorn
221218799Snwhitehorn			archive_file++;
222218799Snwhitehorn			current_files++;
223218799Snwhitehorn		}
224218799Snwhitehorn
225218799Snwhitehorn		items[i*2 + 1] = "Done";
226218799Snwhitehorn
227218799Snwhitehorn		if (err != ARCHIVE_EOF) {
228218799Snwhitehorn			snprintf(errormsg, sizeof(errormsg),
229218799Snwhitehorn			    "Error while extracting %s: %s\n", items[i*2],
230218799Snwhitehorn			    archive_error_string(archive));
231218799Snwhitehorn			items[i*2 + 1] = "Failed";
232218799Snwhitehorn			dialog_msgbox("Extract Error", errormsg, 0, 0,
233218799Snwhitehorn			    TRUE);
234218915Snwhitehorn			return (err);
235218799Snwhitehorn		}
236218799Snwhitehorn
237218799Snwhitehorn		archive_read_free(archive);
238218799Snwhitehorn	}
239218799Snwhitehorn
240218915Snwhitehorn	return (0);
241218799Snwhitehorn}
242