Tell the user more explicitly what port needs to be installed to get the
[dragonfly.git] / contrib / bsdtar / read.c
1 /*-
2  * Copyright (c) 2003-2004 Tim Kientzle
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  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "bsdtar_platform.h"
28 __FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.21 2004/10/17 23:57:10 kientzle Exp $");
29
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33
34 #include <archive.h>
35 #include <archive_entry.h>
36 #include <errno.h>
37 #include <grp.h>
38 #include <limits.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #include "bsdtar.h"
47
48 static void     cleanup_security(struct bsdtar *);
49 static void     list_item_verbose(struct bsdtar *, FILE *,
50                     struct archive_entry *);
51 static void     read_archive(struct bsdtar *bsdtar, char mode);
52 static int      security_problem(struct bsdtar *, struct archive_entry *);
53
54 void
55 tar_mode_t(struct bsdtar *bsdtar)
56 {
57         read_archive(bsdtar, 't');
58 }
59
60 void
61 tar_mode_x(struct bsdtar *bsdtar)
62 {
63         read_archive(bsdtar, 'x');
64 }
65
66 /*
67  * Handle 'x' and 't' modes.
68  */
69 static void
70 read_archive(struct bsdtar *bsdtar, char mode)
71 {
72         FILE                     *out;
73         struct archive           *a;
74         struct archive_entry     *entry;
75         int                       r;
76
77         while (*bsdtar->argv) {
78                 include(bsdtar, *bsdtar->argv);
79                 bsdtar->argv++;
80         }
81
82         if (bsdtar->names_from_file != NULL)
83                 include_from_file(bsdtar, bsdtar->names_from_file);
84
85         a = archive_read_new();
86         archive_read_support_compression_all(a);
87         archive_read_support_format_all(a);
88         if (archive_read_open_file(a, bsdtar->filename,
89             bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
90             DEFAULT_BYTES_PER_BLOCK))
91                 bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s",
92                     archive_error_string(a));
93
94         do_chdir(bsdtar);
95         for (;;) {
96                 /* Support --fast-read option */
97                 if (bsdtar->option_fast_read &&
98                     unmatched_inclusions(bsdtar) == 0)
99                         break;
100
101                 r = archive_read_next_header(a, &entry);
102                 if (r == ARCHIVE_EOF)
103                         break;
104                 if (r == ARCHIVE_WARN)
105                         bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
106                 if (r == ARCHIVE_FATAL) {
107                         bsdtar->return_value = 1;
108                         bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
109                         break;
110                 }
111                 if (r == ARCHIVE_RETRY) {
112                         /* Retryable error: try again */
113                         bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
114                         bsdtar_warnc(bsdtar, 0, "Retrying...");
115                         continue;
116                 }
117
118                 if (excluded(bsdtar, archive_entry_pathname(entry)))
119                         continue;
120
121                 if (mode == 't') {
122                         /* Perversely, gtar uses -O to mean "send to stderr"
123                          * when used with -t. */
124                         out = bsdtar->option_stdout ? stderr : stdout;
125
126                         if (bsdtar->verbose < 2)
127                                 safe_fprintf(out, "%s",
128                                     archive_entry_pathname(entry));
129                         else
130                                 list_item_verbose(bsdtar, out, entry);
131                         fflush(out);
132                         r = archive_read_data_skip(a);
133                         if (r == ARCHIVE_WARN) {
134                                 fprintf(out, "\n");
135                                 bsdtar_warnc(bsdtar, 0, "%s",
136                                     archive_error_string(a));
137                         }
138                         if (r == ARCHIVE_RETRY) {
139                                 fprintf(out, "\n");
140                                 bsdtar_warnc(bsdtar, 0, "%s",
141                                     archive_error_string(a));
142                         }
143                         if (r == ARCHIVE_FATAL) {
144                                 fprintf(out, "\n");
145                                 bsdtar_warnc(bsdtar, 0, "%s",
146                                     archive_error_string(a));
147                                 break;
148                         }
149                         fprintf(out, "\n");
150                 } else {
151                         if (bsdtar->option_interactive &&
152                             !yes("extract '%s'", archive_entry_pathname(entry)))
153                                 continue;
154
155                         if (security_problem(bsdtar, entry))
156                                 continue;
157
158                         /*
159                          * Format here is from SUSv2, including the
160                          * deferred '\n'.
161                          */
162                         if (bsdtar->verbose) {
163                                 safe_fprintf(stderr, "x %s",
164                                     archive_entry_pathname(entry));
165                                 fflush(stderr);
166                         }
167                         if (bsdtar->option_stdout) {
168                                 /* TODO: Catch/recover any errors here. */
169                                 archive_read_data_into_fd(a, 1);
170                         } else if (archive_read_extract(a, entry,
171                                        bsdtar->extract_flags)) {
172                                 if (!bsdtar->verbose)
173                                         safe_fprintf(stderr, "%s",
174                                             archive_entry_pathname(entry));
175                                 safe_fprintf(stderr, ": %s",
176                                     archive_error_string(a));
177                                 if (!bsdtar->verbose)
178                                         fprintf(stderr, "\n");
179                                 /*
180                                  * TODO: Decide how to handle
181                                  * extraction error... <sigh>
182                                  */
183                                 bsdtar->return_value = 1;
184                         }
185                         if (bsdtar->verbose)
186                                 fprintf(stderr, "\n");
187                 }
188         }
189
190         if (bsdtar->verbose > 2)
191                 fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
192                     archive_format_name(a), archive_compression_name(a));
193
194         archive_read_finish(a);
195         cleanup_security(bsdtar);
196 }
197
198
199 /*
200  * Display information about the current file.
201  *
202  * The format here roughly duplicates the output of 'ls -l'.
203  * This is based on SUSv2, where 'tar tv' is documented as
204  * listing additional information in an "unspecified format,"
205  * and 'pax -l' is documented as using the same format as 'ls -l'.
206  */
207 static void
208 list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
209 {
210         const struct stat       *st;
211         char                     tmp[100];
212         size_t                   w;
213         const char              *p;
214         const char              *fmt;
215         time_t                   tim;
216         static time_t            now;
217
218         st = archive_entry_stat(entry);
219
220         /*
221          * We avoid collecting the entire list in memory at once by
222          * listing things as we see them.  However, that also means we can't
223          * just pre-compute the field widths.  Instead, we start with guesses
224          * and just widen them as necessary.  These numbers are completely
225          * arbitrary.
226          */
227         if (!bsdtar->u_width) {
228                 bsdtar->u_width = 6;
229                 bsdtar->gs_width = 13;
230         }
231         if (!now)
232                 time(&now);
233         bsdtar_strmode(entry, tmp);
234         fprintf(out, "%s %d ", tmp, st->st_nlink);
235
236         /* Use uname if it's present, else uid. */
237         p = archive_entry_uname(entry);
238         if ((p == NULL) || (*p == '\0')) {
239                 sprintf(tmp, "%d ", st->st_uid);
240                 p = tmp;
241         }
242         w = strlen(p);
243         if (w > bsdtar->u_width)
244                 bsdtar->u_width = w;
245         fprintf(out, "%-*s ", (int)bsdtar->u_width, p);
246
247         /* Use gname if it's present, else gid. */
248         p = archive_entry_gname(entry);
249         if (p != NULL && p[0] != '\0') {
250                 fprintf(out, "%s", p);
251                 w = strlen(p);
252         } else {
253                 sprintf(tmp, "%d", st->st_gid);
254                 w = strlen(tmp);
255                 fprintf(out, "%s", tmp);
256         }
257
258         /*
259          * Print device number or file size, right-aligned so as to make
260          * total width of group and devnum/filesize fields be gs_width.
261          * If gs_width is too small, grow it.
262          */
263         if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
264                 sprintf(tmp, "%d,%u",
265                     major(st->st_rdev),
266                     (unsigned)minor(st->st_rdev)); /* ls(1) also casts here. */
267         } else {
268                 /*
269                  * Note the use of platform-dependent macros to format
270                  * the filesize here.  We need the format string and the
271                  * corresponding type for the cast.
272                  */
273                 sprintf(tmp, BSDTAR_FILESIZE_PRINTF,
274                     (BSDTAR_FILESIZE_TYPE)st->st_size);
275         }
276         if (w + strlen(tmp) >= bsdtar->gs_width)
277                 bsdtar->gs_width = w+strlen(tmp)+1;
278         fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);
279
280         /* Format the time using 'ls -l' conventions. */
281         tim = (time_t)st->st_mtime;
282         if (abs(tim - now) > (365/2)*86400)
283                 fmt = bsdtar->day_first ? "%e %b  %Y" : "%b %e  %Y";
284         else
285                 fmt = bsdtar->day_first ? "%e %b %R" : "%b %e %R";
286         strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
287         fprintf(out, " %s ", tmp);
288         safe_fprintf(out, "%s", archive_entry_pathname(entry));
289
290         /* Extra information for links. */
291         if (archive_entry_hardlink(entry)) /* Hard link */
292                 safe_fprintf(out, " link to %s",
293                     archive_entry_hardlink(entry));
294         else if (S_ISLNK(st->st_mode)) /* Symbolic link */
295                 safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
296 }
297
298 struct security {
299         char    *path;
300         size_t   path_size;
301 };
302
303 /*
304  * Check for a variety of security issues.  Fix what we can here,
305  * generate warnings as appropriate, return non-zero to prevent
306  * this entry from being extracted.
307  */
308 static int
309 security_problem(struct bsdtar *bsdtar, struct archive_entry *entry)
310 {
311         struct stat st;
312         const char *name, *pn;
313         char *p;
314         int r;
315
316         /* -P option forces us to just accept all pathnames. */
317         if (bsdtar->option_absolute_paths)
318                 return (0);
319
320         /* Strip leading '/'. */
321         name = archive_entry_pathname(entry);
322         if (name[0] == '/') {
323                 /* Generate a warning the first time this happens. */
324                 if (!bsdtar->warned_lead_slash) {
325                         bsdtar_warnc(bsdtar, 0,
326                             "Removing leading '/' from member names");
327                         bsdtar->warned_lead_slash = 1;
328                 }
329                 while (name[0] == '/')
330                         name++;
331                 archive_entry_set_pathname(entry, name);
332         }
333
334         /* Reject any archive entry with '..' as a path element. */
335         pn = name;
336         while (pn != NULL && pn[0] != '\0') {
337                 if (pn[0] == '.' && pn[1] == '.' &&
338                     (pn[2] == '\0' || pn[2] == '/')) {
339                         bsdtar_warnc(bsdtar, 0,
340                             "Skipping pathname containing ..");
341                         bsdtar->return_value = 1;
342                         return (1);
343                 }
344                 pn = strchr(pn, '/');
345                 if (pn != NULL)
346                         pn++;
347         }
348
349         /*
350          * Gaurd against symlink tricks.  Reject any archive entry whose
351          * destination would be altered by a symlink.
352          */
353         /* XXX TODO: Make this faster by comparing current path to
354          * prefix of last successful check to avoid duplicate lstat()
355          * calls. XXX */
356         pn = name;
357         if (bsdtar->security == NULL) {
358                 bsdtar->security = malloc(sizeof(*bsdtar->security));
359                 if (bsdtar->security == NULL)
360                         bsdtar_errc(bsdtar, 1, errno, "No Memory");
361                 bsdtar->security->path_size = MAXPATHLEN + 1;
362                 bsdtar->security->path = malloc(bsdtar->security->path_size);
363                 if (bsdtar->security->path == NULL)
364                         bsdtar_errc(bsdtar, 1, errno, "No Memory");
365         }
366         if (strlen(name) >= bsdtar->security->path_size) {
367                 free(bsdtar->security->path);
368                 while (strlen(name) >= bsdtar->security->path_size)
369                         bsdtar->security->path_size *= 2;
370                 bsdtar->security->path = malloc(bsdtar->security->path_size);
371                 if (bsdtar->security->path == NULL)
372                         bsdtar_errc(bsdtar, 1, errno, "No Memory");
373         }
374         p = bsdtar->security->path;
375         while (pn != NULL && pn[0] != '\0') {
376                 *p++ = *pn++;
377                 while (*pn != '\0' && *pn != '/')
378                         *p++ = *pn++;
379                 p[0] = '\0';
380                 r = lstat(bsdtar->security->path, &st);
381                 if (r != 0) {
382                         if (errno == ENOENT)
383                                 break;
384                 } else if (S_ISLNK(st.st_mode)) {
385                         if (pn[0] == '\0') {
386                                 /*
387                                  * Last element is symlink; remove it
388                                  * so we can overwrite it with the
389                                  * item being extracted.
390                                  */
391                                 if (!S_ISLNK(archive_entry_mode(entry))) {
392                                         /*
393                                          * Warn only if the symlink is being
394                                          * replaced with a non-symlink.
395                                          */
396                                         bsdtar_warnc(bsdtar, 0,
397                                             "Removing symlink %s",
398                                             bsdtar->security->path);
399                                 }
400                                 if (unlink(bsdtar->security->path))
401                                         bsdtar_errc(bsdtar, 1, errno,
402                                             "Unlink failed");
403                                 /* Symlink gone.  No more problem! */
404                                 return (0);
405                         } else if (bsdtar->option_unlink_first) {
406                                 /* User asked us to remove problems. */
407                                 if (unlink(bsdtar->security->path))
408                                         bsdtar_errc(bsdtar, 1, errno,
409                                             "Unlink failed");
410                         } else {
411                                 bsdtar_warnc(bsdtar, 0,
412                                     "Cannot extract %s through symlink %s",
413                                     name, bsdtar->security->path);
414                                 bsdtar->return_value = 1;
415                                 return (1);
416                         }
417                 }
418         }
419
420         return (0);
421 }
422
423 static void
424 cleanup_security(struct bsdtar *bsdtar)
425 {
426         if (bsdtar->security != NULL) {
427                 free(bsdtar->security->path);
428                 free(bsdtar->security);
429         }
430 }