Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bsdinstall/distextract/distextract.c
105518 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2011 Nathan Whitehorn
5
* Copyright (c) 2014 Devin Teske <[email protected]>
6
* All rights reserved.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
#include <sys/param.h>
31
32
#include <archive.h>
33
#include <ctype.h>
34
#include <bsddialog.h>
35
#include <bsddialog_progressview.h>
36
#include <err.h>
37
#include <errno.h>
38
#include <limits.h>
39
#include <signal.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <unistd.h>
44
45
#include "opt_osname.h"
46
47
/* Data to process */
48
static const char *distdir = NULL;
49
static struct archive *archive = NULL;
50
51
/* Function prototypes */
52
static void sig_int(int sig);
53
static int count_files(const char *file);
54
static int extract_files(struct bsddialog_fileminibar *file);
55
56
#define _errx(...) (bsddialog_end(), errx(__VA_ARGS__))
57
58
int
59
main(void)
60
{
61
char *chrootdir;
62
char *distributions;
63
char *distribs, *distrib;
64
int retval;
65
size_t minibar_size = sizeof(struct bsddialog_fileminibar);
66
unsigned int nminibars;
67
struct bsddialog_fileminibar *dists;
68
struct bsddialog_progviewconf pvconf;
69
struct bsddialog_conf conf;
70
struct sigaction act;
71
char error[PATH_MAX + 512];
72
73
if ((distributions = getenv("DISTRIBUTIONS")) == NULL)
74
errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set");
75
if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL)
76
distdir = "";
77
if ((distribs = strdup(distributions)) == NULL)
78
errx(EXIT_FAILURE, "memory error");
79
80
if (bsddialog_init() == BSDDIALOG_ERROR)
81
errx(EXIT_FAILURE, "Error libbsdialog: %s",
82
bsddialog_geterror());
83
bsddialog_initconf(&conf);
84
bsddialog_backtitle(&conf, OSNAME " Installer");
85
bsddialog_infobox(&conf,
86
"Checking distribution archives.\nPlease wait...", 4, 35);
87
88
/* Parse $DISTRIBUTIONS */
89
nminibars = 0;
90
dists = NULL;
91
while ((distrib = strsep(&distribs, "\t\n\v\f\r ")) != NULL) {
92
if (strlen(distrib) == 0)
93
continue;
94
95
/* Allocate a new struct for the distribution */
96
dists = realloc(dists, (nminibars + 1) * minibar_size);
97
if (dists == NULL)
98
_errx(EXIT_FAILURE, "Out of memory!");
99
100
/* Set file path */
101
dists[nminibars].path = distrib;
102
103
/* Set mini bar label */
104
dists[nminibars].label = strrchr(dists[nminibars].path, '/');
105
if (dists[nminibars].label == NULL)
106
dists[nminibars].label = dists[nminibars].path;
107
108
/* Set initial length in files (-1 == error) */
109
dists[nminibars].size = count_files(dists[nminibars].path);
110
if (dists[nminibars].size < 0) {
111
bsddialog_end();
112
return (EXIT_FAILURE);
113
}
114
115
/* Set initial status and implicitly miniperc to pending */
116
dists[nminibars].status = BSDDIALOG_MG_PENDING;
117
118
/* Set initial read */
119
dists[nminibars].read = 0;
120
121
nminibars += 1;
122
}
123
124
/* Optionally chdir(2) into $BSDINSTALL_CHROOT */
125
chrootdir = getenv("BSDINSTALL_CHROOT");
126
if (chrootdir != NULL && chdir(chrootdir) != 0) {
127
snprintf(error, sizeof(error),
128
"Could not change to directory %s: %s\n",
129
chrootdir, strerror(errno));
130
conf.title = "Error";
131
bsddialog_msgbox(&conf, error, 0, 0);
132
bsddialog_end();
133
return (EXIT_FAILURE);
134
}
135
136
/* Set cleanup routine for Ctrl-C action */
137
act.sa_handler = sig_int;
138
sigaction(SIGINT, &act, 0);
139
140
conf.title = "Archive Extraction";
141
conf.auto_minwidth = 40;
142
pvconf.callback = extract_files;
143
pvconf.refresh = 1;
144
pvconf.fmtbottomstr = "%10lli files read @ %'9.1f files/sec.";
145
bsddialog_total_progview = 0;
146
bsddialog_interruptprogview = bsddialog_abortprogview = false;
147
retval = bsddialog_progressview(&conf,
148
"\nExtracting distribution files...\n", 0, 0,
149
&pvconf, nminibars, dists);
150
151
if (retval == BSDDIALOG_ERROR) {
152
fprintf(stderr, "progressview error: %s\n",
153
bsddialog_geterror());
154
}
155
156
bsddialog_end();
157
158
free(distribs);
159
free(dists);
160
161
return (retval);
162
}
163
164
static void
165
sig_int(int sig __unused)
166
{
167
bsddialog_interruptprogview = true;
168
}
169
170
/*
171
* Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST
172
* if it exists, otherwise uses archive(3) to read the archive file.
173
*/
174
static int
175
count_files(const char *file)
176
{
177
static FILE *manifest = NULL;
178
char *p;
179
int file_count;
180
int retval;
181
size_t span;
182
struct archive_entry *entry;
183
char line[512];
184
char path[PATH_MAX];
185
char errormsg[PATH_MAX + 512];
186
struct bsddialog_conf conf;
187
188
if (manifest == NULL) {
189
snprintf(path, sizeof(path), "%s/MANIFEST", distdir);
190
manifest = fopen(path, "r");
191
}
192
193
if (manifest != NULL) {
194
rewind(manifest);
195
while (fgets(line, sizeof(line), manifest) != NULL) {
196
p = &line[0];
197
span = strcspn(p, "\t") ;
198
if (span < 1 || strncmp(p, file, span) != 0)
199
continue;
200
201
/*
202
* We're at the right manifest line. The file count is
203
* in the third element
204
*/
205
span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
206
span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t");
207
if (span > 0) {
208
file_count = (int)strtol(p, (char **)NULL, 10);
209
if (file_count == 0 && errno == EINVAL)
210
continue;
211
return (file_count);
212
}
213
}
214
}
215
216
/*
217
* Either no manifest, or manifest didn't mention this archive.
218
* Use archive(3) to read the archive, counting files within.
219
*/
220
bsddialog_initconf(&conf);
221
if ((archive = archive_read_new()) == NULL) {
222
snprintf(errormsg, sizeof(errormsg),
223
"Error: %s\n", archive_error_string(NULL));
224
conf.title = "Extract Error";
225
bsddialog_msgbox(&conf, errormsg, 0, 0);
226
return (-1);
227
}
228
archive_read_support_format_all(archive);
229
archive_read_support_filter_all(archive);
230
snprintf(path, sizeof(path), "%s/%s", distdir, file);
231
retval = archive_read_open_filename(archive, path, 4096);
232
if (retval != ARCHIVE_OK) {
233
snprintf(errormsg, sizeof(errormsg),
234
"Error while extracting %s: %s\n", file,
235
archive_error_string(archive));
236
conf.title = "Extract Error";
237
bsddialog_msgbox(&conf, errormsg, 0, 0);
238
archive = NULL;
239
return (-1);
240
}
241
242
file_count = 0;
243
while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
244
file_count++;
245
archive_read_free(archive);
246
archive = NULL;
247
248
return (file_count);
249
}
250
251
static int
252
extract_files(struct bsddialog_fileminibar *file)
253
{
254
int retval;
255
struct archive_entry *entry;
256
char path[PATH_MAX];
257
char errormsg[PATH_MAX + 512];
258
struct bsddialog_conf conf;
259
260
bsddialog_initconf(&conf);
261
262
/* Open the archive if necessary */
263
if (archive == NULL) {
264
if ((archive = archive_read_new()) == NULL) {
265
snprintf(errormsg, sizeof(errormsg),
266
"Error: %s\n", archive_error_string(NULL));
267
conf.title = "Extract Error";
268
bsddialog_msgbox(&conf, errormsg, 0, 0);
269
bsddialog_abortprogview = true;
270
return (-1);
271
}
272
archive_read_support_format_all(archive);
273
archive_read_support_filter_all(archive);
274
snprintf(path, sizeof(path), "%s/%s", distdir, file->path);
275
retval = archive_read_open_filename(archive, path, 4096);
276
if (retval != 0) {
277
snprintf(errormsg, sizeof(errormsg),
278
"Error opening %s: %s\n", file->label,
279
archive_error_string(archive));
280
conf.title = "Extract Error";
281
bsddialog_msgbox(&conf, errormsg, 0, 0);
282
file->status = BSDDIALOG_MG_FAILED;
283
bsddialog_abortprogview = true;
284
return (-1);
285
}
286
}
287
288
/* Read the next archive header */
289
retval = archive_read_next_header(archive, &entry);
290
291
/* If that went well, perform the extraction */
292
if (retval == ARCHIVE_OK)
293
retval = archive_read_extract(archive, entry,
294
ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
295
ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
296
ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
297
298
/* Test for either EOF or error */
299
if (retval == ARCHIVE_EOF) {
300
archive_read_free(archive);
301
archive = NULL;
302
file->status = BSDDIALOG_MG_DONE; /*Done*/;
303
return (100);
304
} else if (retval != ARCHIVE_OK &&
305
!(retval == ARCHIVE_WARN &&
306
strcmp(archive_error_string(archive), "Can't restore time") == 0)) {
307
/*
308
* Print any warning/error messages except inability to set
309
* ctime/mtime, which is not fatal, or even interesting,
310
* for our purposes. Would be nice if this were a libarchive
311
* option.
312
*/
313
snprintf(errormsg, sizeof(errormsg),
314
"Error while extracting %s: %s\n", file->label,
315
archive_error_string(archive));
316
conf.title = "Extract Error";
317
bsddialog_msgbox(&conf, errormsg, 0, 0);
318
file->status = BSDDIALOG_MG_FAILED; /* Failed */
319
bsddialog_abortprogview = true;
320
return (-1);
321
}
322
323
bsddialog_total_progview++;
324
file->read++;
325
326
/* Calculate [overall] percentage of completion (if possible) */
327
if (file->size >= 0)
328
return (file->read * 100 / file->size);
329
else
330
return (-1);
331
}
332
333