1/*-
2 * Copyright (c) 2003-2007 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 * 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(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "bsdtar_platform.h"
27__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $");
28
29#ifdef HAVE_SYS_TYPES_H
30#include <sys/types.h>
31#endif
32#ifdef HAVE_SYS_PARAM_H
33#include <sys/param.h>
34#endif
35#ifdef HAVE_SYS_STAT_H
36#include <sys/stat.h>
37#endif
38
39#ifdef HAVE_ERRNO_H
40#include <errno.h>
41#endif
42#ifdef HAVE_GRP_H
43#include <grp.h>
44#endif
45#ifdef HAVE_LIMITS_H
46#include <limits.h>
47#endif
48#ifdef HAVE_PWD_H
49#include <pwd.h>
50#endif
51#ifdef HAVE_STDINT_H
52#include <stdint.h>
53#endif
54#include <stdio.h>
55#ifdef HAVE_STDLIB_H
56#include <stdlib.h>
57#endif
58#ifdef HAVE_STRING_H
59#include <string.h>
60#endif
61#ifdef HAVE_TIME_H
62#include <time.h>
63#endif
64#ifdef HAVE_UNISTD_H
65#include <unistd.h>
66#endif
67#include <sys/queue.h>
68#include <copyfile.h>
69#include <fcntl.h>
70#include <libgen.h>
71
72#include <TargetConditionals.h>
73#if TARGET_OS_MAC && !TARGET_OS_IPHONE
74#define HAVE_QUARANTINE 1
75#endif /* TARGET_OS_MAC */
76
77#ifdef HAVE_QUARANTINE
78#include <quarantine.h>
79#endif /* HAVE_QUARANTINE */
80
81#include "bsdtar.h"
82#include "err.h"
83
84struct progress_data {
85	struct bsdtar *bsdtar;
86	struct archive *archive;
87	struct archive_entry *entry;
88};
89
90struct copyfile_list_entry_t {
91	char *src;
92	char *dst;
93	char *tmp;
94	LIST_ENTRY(copyfile_list_entry_t) link;
95};
96
97static void	list_item_verbose(struct bsdtar *, FILE *,
98		    struct archive_entry *);
99static void	read_archive(struct bsdtar *bsdtar, char mode);
100
101void
102tar_mode_t(struct bsdtar *bsdtar)
103{
104	read_archive(bsdtar, 't');
105	if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
106		bsdtar->return_value = 1;
107}
108
109void
110tar_mode_x(struct bsdtar *bsdtar)
111{
112	read_archive(bsdtar, 'x');
113
114	if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
115		bsdtar->return_value = 1;
116}
117
118static void
119progress_func(void *cookie)
120{
121	struct progress_data *progress_data = cookie;
122	struct bsdtar *bsdtar = progress_data->bsdtar;
123	struct archive *a = progress_data->archive;
124	struct archive_entry *entry = progress_data->entry;
125	uint64_t comp, uncomp;
126
127	if (!need_report())
128		return;
129
130	if (bsdtar->verbose)
131		fprintf(stderr, "\n");
132	if (a != NULL) {
133		comp = archive_position_compressed(a);
134		uncomp = archive_position_uncompressed(a);
135		fprintf(stderr,
136		    "In: %s bytes, compression %d%%;",
137		    tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp));
138		fprintf(stderr, "  Out: %d files, %s bytes\n",
139		    archive_file_count(a), tar_i64toa(uncomp));
140	}
141	if (entry != NULL) {
142		safe_fprintf(stderr, "Current: %s",
143		    archive_entry_pathname(entry));
144		fprintf(stderr, " (%s bytes)\n",
145		    tar_i64toa(archive_entry_size(entry)));
146	}
147}
148
149#ifdef HAVE_QUARANTINE
150void
151_qtnapply(struct bsdtar *bsdtar, qtn_file_t qf, char *path)
152{
153	int stat_ok;
154	struct stat sb;
155	int qstatus;
156
157	if (qf == NULL)
158		return;
159
160	stat_ok = (stat(path, &sb) == 0);
161
162	if (stat_ok) chmod(path, sb.st_mode | S_IWUSR);
163	qstatus = qtn_file_apply_to_path(qf, path);
164	if (stat_ok) chmod(path, sb.st_mode);
165
166	if (qstatus)
167		lafe_warnc(0, "qtn_file_apply_to_path(%s): %s", path, qtn_error(qstatus));
168}
169#endif /* HAVE_QUARANTINE */
170
171/*
172 * Handle 'x' and 't' modes.
173 */
174static void
175read_archive(struct bsdtar *bsdtar, char mode)
176{
177	struct progress_data	progress_data;
178	FILE			 *out;
179	struct archive		 *a;
180	struct archive_entry	 *entry;
181	const struct stat	 *st;
182	int			  r;
183#ifdef HAVE_QUARANTINE
184	qtn_file_t		  qf = NULL;
185#endif /* HAVE_QUARANTINE */
186	LIST_HEAD(copyfile_list_t, copyfile_list_entry_t) copyfile_list;
187	struct copyfile_list_entry_t *cle;
188
189	LIST_INIT(&copyfile_list);
190
191	while (*bsdtar->argv) {
192		lafe_include(&bsdtar->matching, *bsdtar->argv);
193		bsdtar->argv++;
194	}
195
196	if (bsdtar->names_from_file != NULL)
197		lafe_include_from_file(&bsdtar->matching,
198		    bsdtar->names_from_file, bsdtar->option_null);
199
200	a = archive_read_new();
201	if (bsdtar->compress_program != NULL)
202		archive_read_support_compression_program(a, bsdtar->compress_program);
203	else
204		archive_read_support_compression_all(a);
205	archive_read_support_format_all(a);
206	if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
207		lafe_errc(1, 0, "%s", archive_error_string(a));
208	if (archive_read_open_file(a, bsdtar->filename,
209	    bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
210	    DEFAULT_BYTES_PER_BLOCK))
211		lafe_errc(1, 0, "Error opening archive: %s",
212		    archive_error_string(a));
213
214	do_chdir(bsdtar);
215
216	if (mode == 'x') {
217		/* Set an extract callback so that we can handle SIGINFO. */
218		progress_data.bsdtar = bsdtar;
219		progress_data.archive = a;
220		archive_read_extract_set_progress_callback(a, progress_func,
221		    &progress_data);
222	}
223
224	if (mode == 'x' && bsdtar->option_chroot) {
225#if HAVE_CHROOT
226		if (chroot(".") != 0)
227			lafe_errc(1, errno, "Can't chroot to \".\"");
228#else
229		lafe_errc(1, 0,
230		    "chroot isn't supported on this platform");
231#endif
232	}
233
234#ifdef HAVE_QUARANTINE
235	if (mode == 'x' && bsdtar->filename != NULL && !bsdtar->option_stdout) {
236		if ((qf = qtn_file_alloc()) != NULL) {
237			int qstatus = qtn_file_init_with_path(qf, bsdtar->filename);
238			if (qstatus != 0) {
239				qtn_file_free(qf);
240				qf = NULL;
241			}
242		}
243	}
244#endif /* HAVE_QUARANTINE */
245
246	for (;;) {
247		/* Support --fast-read option */
248		if (bsdtar->option_fast_read &&
249		    lafe_unmatched_inclusions(bsdtar->matching) == 0)
250			break;
251
252		r = archive_read_next_header(a, &entry);
253		progress_data.entry = entry;
254		if (r == ARCHIVE_EOF)
255			break;
256		if (r < ARCHIVE_OK)
257			lafe_warnc(0, "%s", archive_error_string(a));
258		if (r <= ARCHIVE_WARN)
259			bsdtar->return_value = 1;
260		if (r == ARCHIVE_RETRY) {
261			/* Retryable error: try again */
262			lafe_warnc(0, "Retrying...");
263			continue;
264		}
265		if (r == ARCHIVE_FATAL)
266			break;
267
268		if (bsdtar->option_numeric_owner) {
269			archive_entry_set_uname(entry, NULL);
270			archive_entry_set_gname(entry, NULL);
271		}
272
273		/*
274		 * Exclude entries that are too old.
275		 */
276		st = archive_entry_stat(entry);
277		if (bsdtar->newer_ctime_sec > 0) {
278			if (st->st_ctime < bsdtar->newer_ctime_sec)
279				continue; /* Too old, skip it. */
280			if (st->st_ctime == bsdtar->newer_ctime_sec
281			    && ARCHIVE_STAT_CTIME_NANOS(st)
282			    <= bsdtar->newer_ctime_nsec)
283				continue; /* Too old, skip it. */
284		}
285		if (bsdtar->newer_mtime_sec > 0) {
286			if (st->st_mtime < bsdtar->newer_mtime_sec)
287				continue; /* Too old, skip it. */
288			if (st->st_mtime == bsdtar->newer_mtime_sec
289			    && ARCHIVE_STAT_MTIME_NANOS(st)
290			    <= bsdtar->newer_mtime_nsec)
291				continue; /* Too old, skip it. */
292		}
293
294		/*
295		 * Note that pattern exclusions are checked before
296		 * pathname rewrites are handled.  This gives more
297		 * control over exclusions, since rewrites always lose
298		 * information.  (For example, consider a rewrite
299		 * s/foo[0-9]/foo/.  If we check exclusions after the
300		 * rewrite, there would be no way to exclude foo1/bar
301		 * while allowing foo2/bar.)
302		 */
303		if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry)))
304			continue; /* Excluded by a pattern test. */
305
306		if (mode == 't') {
307			/* Perversely, gtar uses -O to mean "send to stderr"
308			 * when used with -t. */
309			out = bsdtar->option_stdout ? stderr : stdout;
310
311			/*
312			 * TODO: Provide some reasonable way to
313			 * preview rewrites.  gtar always displays
314			 * the unedited path in -t output, which means
315			 * you cannot easily preview rewrites.
316			 */
317			if (bsdtar->verbose < 2)
318				safe_fprintf(out, "%s",
319				    archive_entry_pathname(entry));
320			else
321				list_item_verbose(bsdtar, out, entry);
322			fflush(out);
323			r = archive_read_data_skip(a);
324			if (r == ARCHIVE_WARN) {
325				fprintf(out, "\n");
326				lafe_warnc(0, "%s",
327				    archive_error_string(a));
328			}
329			if (r == ARCHIVE_RETRY) {
330				fprintf(out, "\n");
331				lafe_warnc(0, "%s",
332				    archive_error_string(a));
333			}
334			if (r == ARCHIVE_FATAL) {
335				fprintf(out, "\n");
336				lafe_warnc(0, "%s",
337				    archive_error_string(a));
338				bsdtar->return_value = 1;
339				break;
340			}
341			fprintf(out, "\n");
342		} else {
343			/* Note: some rewrite failures prevent extraction. */
344			if (edit_pathname(bsdtar, entry))
345				continue; /* Excluded by a rewrite failure. */
346
347			if (bsdtar->option_interactive &&
348			    !yes("extract '%s'", archive_entry_pathname(entry)))
349				continue;
350
351			/*
352			 * Format here is from SUSv2, including the
353			 * deferred '\n'.
354			 */
355			if (bsdtar->verbose) {
356				safe_fprintf(stderr, "x %s",
357				    archive_entry_pathname(entry));
358				fflush(stderr);
359			}
360
361			// TODO siginfo_printinfo(bsdtar, 0);
362
363			if (bsdtar->option_stdout)
364				r = archive_read_data_into_fd(a, 1);
365			else {
366				/* do this even if disable_copyfile is set, because it can get blown away by its associated real file */
367				if (strncmp(basename((char *)archive_entry_pathname(entry)), "._", 2) == 0) {
368					cle = calloc(1, sizeof(struct copyfile_list_entry_t));
369					cle->src = strdup(archive_entry_pathname(entry));
370					asprintf(&cle->tmp, "%s.XXXXXX", cle->src);
371					mktemp(cle->tmp);
372					asprintf(&cle->dst, "%s/%s", dirname(cle->src), basename(cle->src) + 2);
373					LIST_INSERT_HEAD(&copyfile_list, cle, link);
374					archive_entry_set_pathname(entry, cle->tmp);
375				}
376				r = archive_read_extract(a, entry,
377				    bsdtar->extract_flags);
378#ifdef HAVE_QUARANTINE
379				if (r == ARCHIVE_OK) {
380					_qtnapply(bsdtar, qf, (char *)archive_entry_pathname(entry));
381				}
382#endif /* HAVE_QUARANTINE */
383			}
384			if (r != ARCHIVE_OK) {
385				if (!bsdtar->verbose)
386					safe_fprintf(stderr, "%s",
387					    archive_entry_pathname(entry));
388				safe_fprintf(stderr, ": %s",
389				    archive_error_string(a));
390				if (!bsdtar->verbose)
391					fprintf(stderr, "\n");
392				bsdtar->return_value = 1;
393			}
394			if (bsdtar->verbose)
395				fprintf(stderr, "\n");
396			if (r == ARCHIVE_FATAL)
397				break;
398		}
399	}
400
401
402	r = archive_read_close(a);
403	if (r != ARCHIVE_OK)
404		lafe_warnc(0, "%s", archive_error_string(a));
405	if (r <= ARCHIVE_WARN)
406		bsdtar->return_value = 1;
407
408	LIST_FOREACH(cle, &copyfile_list, link) {
409		if (!bsdtar->disable_copyfile && copyfile(cle->tmp, cle->dst, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) == 0) {
410			unlink(cle->tmp);
411#ifdef HAVE_QUARANTINE
412			_qtnapply(bsdtar, qf, cle->dst);
413#endif /* HAVE_QUARANTINE */
414		} else {
415			if (!bsdtar->disable_copyfile)
416				lafe_warnc(errno, "copyfile unpack (%s) failed", cle->dst);
417			rename(cle->tmp, cle->src);
418#ifdef HAVE_QUARANTINE
419			_qtnapply(bsdtar, qf, cle->src);
420#endif /* HAVE_QUARANTINE */
421		}
422	}
423
424	if (bsdtar->verbose > 2)
425		fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
426		    archive_format_name(a), archive_compression_name(a));
427
428	archive_read_finish(a);
429
430#ifdef HAVE_QUARANTINE
431	if (qf != NULL) {
432		qtn_file_free(qf);
433		qf = NULL;
434	}
435#endif /* HAVE_QUARANTINE */
436}
437
438
439/*
440 * Display information about the current file.
441 *
442 * The format here roughly duplicates the output of 'ls -l'.
443 * This is based on SUSv2, where 'tar tv' is documented as
444 * listing additional information in an "unspecified format,"
445 * and 'pax -l' is documented as using the same format as 'ls -l'.
446 */
447static void
448list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
449{
450	char			 tmp[100];
451	size_t			 w;
452	const char		*p;
453	const char		*fmt;
454	time_t			 tim;
455	static time_t		 now;
456
457	/*
458	 * We avoid collecting the entire list in memory at once by
459	 * listing things as we see them.  However, that also means we can't
460	 * just pre-compute the field widths.  Instead, we start with guesses
461	 * and just widen them as necessary.  These numbers are completely
462	 * arbitrary.
463	 */
464	if (!bsdtar->u_width) {
465		bsdtar->u_width = 6;
466		bsdtar->gs_width = 13;
467	}
468	if (!now)
469		time(&now);
470	fprintf(out, "%s %d ",
471	    archive_entry_strmode(entry),
472	    archive_entry_nlink(entry));
473
474	/* Use uname if it's present, else uid. */
475	p = archive_entry_uname(entry);
476	if ((p == NULL) || (*p == '\0')) {
477		sprintf(tmp, "%lu ",
478		    (unsigned long)archive_entry_uid(entry));
479		p = tmp;
480	}
481	w = strlen(p);
482	if (w > bsdtar->u_width)
483		bsdtar->u_width = w;
484	fprintf(out, "%-*s ", (int)bsdtar->u_width, p);
485
486	/* Use gname if it's present, else gid. */
487	p = archive_entry_gname(entry);
488	if (p != NULL && p[0] != '\0') {
489		fprintf(out, "%s", p);
490		w = strlen(p);
491	} else {
492		sprintf(tmp, "%lu",
493		    (unsigned long)archive_entry_gid(entry));
494		w = strlen(tmp);
495		fprintf(out, "%s", tmp);
496	}
497
498	/*
499	 * Print device number or file size, right-aligned so as to make
500	 * total width of group and devnum/filesize fields be gs_width.
501	 * If gs_width is too small, grow it.
502	 */
503	if (archive_entry_filetype(entry) == AE_IFCHR
504	    || archive_entry_filetype(entry) == AE_IFBLK) {
505		sprintf(tmp, "%lu,%lu",
506		    (unsigned long)archive_entry_rdevmajor(entry),
507		    (unsigned long)archive_entry_rdevminor(entry));
508	} else {
509		strcpy(tmp, tar_i64toa(archive_entry_size(entry)));
510	}
511	if (w + strlen(tmp) >= bsdtar->gs_width)
512		bsdtar->gs_width = w+strlen(tmp)+1;
513	fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);
514
515	/* Format the time using 'ls -l' conventions. */
516	tim = archive_entry_mtime(entry);
517#define HALF_YEAR (time_t)365 * 86400 / 2
518#if defined(_WIN32) && !defined(__CYGWIN__)
519#define DAY_FMT  "%d"  /* Windows' strftime function does not support %e format. */
520#else
521#define DAY_FMT  "%e"  /* Day number without leading zeros */
522#endif
523	if (tim < now - HALF_YEAR || tim > now + HALF_YEAR)
524		fmt = bsdtar->day_first ? DAY_FMT " %b  %Y" : "%b " DAY_FMT "  %Y";
525	else
526		fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M";
527	strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
528	fprintf(out, " %s ", tmp);
529	safe_fprintf(out, "%s", archive_entry_pathname(entry));
530
531	/* Extra information for links. */
532	if (archive_entry_hardlink(entry)) /* Hard link */
533		safe_fprintf(out, " link to %s",
534		    archive_entry_hardlink(entry));
535	else if (archive_entry_symlink(entry)) /* Symbolic link */
536		safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
537}
538