]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bsdinstall/distextract/distextract.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / bsdinstall / distextract / distextract.c
1 /*-
2  * Copyright (c) 2011 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/param.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <archive.h>
34 #include <dialog.h>
35
36 static int extract_files(int nfiles, const char **files);
37
38 int
39 main(void)
40 {
41         char *diststring;
42         const char **dists;
43         int i, retval, ndists = 0;
44
45         if (getenv("DISTRIBUTIONS") == NULL) {
46                 fprintf(stderr, "DISTRIBUTIONS variable is not set\n");
47                 return (1);
48         }
49
50         diststring = strdup(getenv("DISTRIBUTIONS"));
51         for (i = 0; diststring[i] != 0; i++)
52                 if (isspace(diststring[i]) && !isspace(diststring[i+1]))
53                         ndists++;
54         ndists++; /* Last one */
55
56         dists = calloc(ndists, sizeof(const char *));
57         if (dists == NULL) {
58                 fprintf(stderr, "Out of memory!\n");
59                 free(diststring);
60                 return (1);
61         }
62
63         for (i = 0; i < ndists; i++)
64                 dists[i] = strsep(&diststring, " \t");
65
66         init_dialog(stdin, stdout);
67         dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
68         dlg_put_backtitle();
69
70         if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) {
71                 char error[512];
72                 sprintf(error, "Could could change to directory %s: %s\n",
73                     getenv("BSDINSTALL_DISTDIR"), strerror(errno));
74                 dialog_msgbox("Error", error, 0, 0, TRUE);
75                 end_dialog();
76                 return (1);
77         }
78
79         retval = extract_files(ndists, dists);
80
81         end_dialog();
82
83         free(diststring);
84         free(dists);
85
86         return (retval);
87 }
88
89 static int
90 count_files(const char *file)
91 {
92         struct archive *archive;
93         struct archive_entry *entry;
94         static FILE *manifest = NULL;
95         char path[MAXPATHLEN];
96         char errormsg[512];
97         int file_count, err;
98
99         if (manifest == NULL) {
100                 sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR"));
101                 manifest = fopen(path, "r");
102         }
103
104         if (manifest != NULL) {
105                 char line[512];
106                 char *tok1, *tok2;
107
108                 rewind(manifest);
109                 while (fgets(line, sizeof(line), manifest) != NULL) {
110                         tok2 = line;
111                         tok1 = strsep(&tok2, "\t");
112                         if (tok1 == NULL || strcmp(tok1, file) != 0)
113                                 continue;
114
115                         /*
116                          * We're at the right manifest line. The file count is
117                          * in the third element
118                          */
119                         tok1 = strsep(&tok2, "\t");
120                         tok1 = strsep(&tok2, "\t");
121                         if (tok1 != NULL)
122                                 return atoi(tok1);
123                 }
124         }
125
126         /* Either we didn't have a manifest, or this archive wasn't there */
127         archive = archive_read_new();
128         archive_read_support_format_all(archive);
129         archive_read_support_filter_all(archive);
130         sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file);
131         err = archive_read_open_filename(archive, path, 4096);
132         if (err != ARCHIVE_OK) {
133                 snprintf(errormsg, sizeof(errormsg),
134                     "Error while extracting %s: %s\n", file,
135                     archive_error_string(archive));
136                 dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE);
137                 return (-1);
138         }
139
140         file_count = 0;
141         while (archive_read_next_header(archive, &entry) == ARCHIVE_OK)
142                 file_count++;
143         archive_read_free(archive);
144
145         return (file_count);
146 }
147
148 static int
149 extract_files(int nfiles, const char **files)
150 {
151         const char *items[nfiles*2];
152         char path[PATH_MAX];
153         int archive_files[nfiles];
154         int total_files, current_files, archive_file;
155         struct archive *archive;
156         struct archive_entry *entry;
157         char errormsg[512];
158         char status[8];
159         int i, err, progress, last_progress;
160
161         err = 0;
162         progress = 0;
163         
164         /* Make the transfer list for dialog */
165         for (i = 0; i < nfiles; i++) {
166                 items[i*2] = strrchr(files[i], '/');
167                 if (items[i*2] != NULL)
168                         items[i*2]++;
169                 else
170                         items[i*2] = files[i];
171                 items[i*2 + 1] = "Pending";
172         }
173
174         dialog_msgbox("",
175             "Checking distribution archives.\nPlease wait...", 0, 0, FALSE);
176
177         /* Count all the files */
178         total_files = 0;
179         for (i = 0; i < nfiles; i++) {
180                 archive_files[i] = count_files(files[i]);
181                 if (archive_files[i] < 0)
182                         return (-1);
183                 total_files += archive_files[i];
184         }
185
186         current_files = 0;
187
188         for (i = 0; i < nfiles; i++) {
189                 archive = archive_read_new();
190                 archive_read_support_format_all(archive);
191                 archive_read_support_filter_all(archive);
192                 sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]);
193                 err = archive_read_open_filename(archive, path, 4096);
194
195                 items[i*2 + 1] = "In Progress";
196                 archive_file = 0;
197
198                 while ((err = archive_read_next_header(archive, &entry)) ==
199                     ARCHIVE_OK) {
200                         last_progress = progress;
201                         progress = (current_files*100)/total_files; 
202
203                         sprintf(status, "-%d",
204                             (archive_file*100)/archive_files[i]);
205                         items[i*2 + 1] = status;
206
207                         if (progress > last_progress)
208                                 dialog_mixedgauge("Archive Extraction",
209                                     "Extracting distribution files...", 0, 0,
210                                     progress, nfiles,
211                                     __DECONST(char **, items));
212
213                         err = archive_read_extract(archive, entry,
214                             ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER |
215                             ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL |
216                             ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS);
217
218                         if (err != ARCHIVE_OK)
219                                 break;
220
221                         archive_file++;
222                         current_files++;
223                 }
224
225                 items[i*2 + 1] = "Done";
226
227                 if (err != ARCHIVE_EOF) {
228                         snprintf(errormsg, sizeof(errormsg),
229                             "Error while extracting %s: %s\n", items[i*2],
230                             archive_error_string(archive));
231                         items[i*2 + 1] = "Failed";
232                         dialog_msgbox("Extract Error", errormsg, 0, 0,
233                             TRUE);
234                         return (err);
235                 }
236
237                 archive_read_free(archive);
238         }
239
240         return (0);
241 }