/* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode) { FILE *out; struct archive *a; struct archive_entry *entry; const struct stat *st; int r; while (*bsdtar->argv) { include(bsdtar, *bsdtar->argv); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) include_from_file(bsdtar, bsdtar->names_from_file); a = archive_read_new(); if (bsdtar->compress_program != NULL) archive_read_support_compression_program(a, bsdtar->compress_program); else archive_read_support_compression_all(a); archive_read_support_format_all(a); if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK)) bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); for (;;) { /* Support --fast-read option */ if (bsdtar->option_fast_read && unmatched_inclusions(bsdtar) == 0) break; r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ bsdtar_warnc(bsdtar, 0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; /* * Exclude entries that are too old. */ st = archive_entry_stat(entry); if (bsdtar->newer_ctime_sec > 0) { if (st->st_ctime < bsdtar->newer_ctime_sec) continue; /* Too old, skip it. */ if (st->st_ctime == bsdtar->newer_ctime_sec && ARCHIVE_STAT_CTIME_NANOS(st) <= bsdtar->newer_ctime_nsec) continue; /* Too old, skip it. */ } if (bsdtar->newer_mtime_sec > 0) { if (st->st_mtime < bsdtar->newer_mtime_sec) continue; /* Too old, skip it. */ if (st->st_mtime == bsdtar->newer_mtime_sec && ARCHIVE_STAT_MTIME_NANOS(st) <= bsdtar->newer_mtime_nsec) continue; /* Too old, skip it. */ } /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (excluded(bsdtar, archive_entry_pathname(entry))) continue; /* Excluded by a pattern test. */ /* * Modify the pathname as requested by the user. We * do this for -t as well to give users a way to * preview the effects of their rewrites. We also do * this before extraction security checks (including * leading '/' removal). Note that some rewrite * failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; /* * Format here is from SUSv2, including the * deferred '\n'. */ if (bsdtar->verbose) { safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract(a, entry, bsdtar->extract_flags); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_compression_name(a)); archive_read_finish(a); }
int main(int argc, char *argv[]) { static char buff[16384]; struct cpio _cpio; /* Allocated on stack. */ struct cpio *cpio; int uid, gid; int opt; cpio = &_cpio; memset(cpio, 0, sizeof(*cpio)); cpio->buff = buff; cpio->buff_size = sizeof(buff); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure open() function will be used with a binary mode. */ /* on cygwin, we need something similar, but instead link against */ /* a special startup object, binmode.o */ _set_fmode(_O_BINARY); #endif /* Need cpio_progname before calling cpio_warnc. */ if (*argv == NULL) cpio_progname = "bsdcpio"; else { #if defined(_WIN32) && !defined(__CYGWIN__) cpio_progname = strrchr(*argv, '\\'); #else cpio_progname = strrchr(*argv, '/'); #endif if (cpio_progname != NULL) cpio_progname++; else cpio_progname = *argv; } cpio->uid_override = -1; cpio->gid_override = -1; cpio->argv = argv; cpio->argc = argc; cpio->line_separator = '\n'; cpio->mode = '\0'; cpio->verbose = 0; cpio->compress = '\0'; cpio->extract_flags = ARCHIVE_EXTRACT_NO_AUTODIR; cpio->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT; cpio->extract_flags |= ARCHIVE_EXTRACT_PERM; cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; cpio->extract_flags |= ARCHIVE_EXTRACT_ACL; #if defined(_WIN32) || defined(__CYGWIN__) if (bsdcpio_is_privileged()) #else if (geteuid() == 0) #endif cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; cpio->bytes_per_block = 512; cpio->filename = NULL; while ((opt = cpio_getopt(cpio)) != -1) { switch (opt) { case '0': /* GNU convention: --null, -0 */ cpio->line_separator = '\0'; break; case 'A': /* NetBSD/OpenBSD */ cpio->option_append = 1; break; case 'a': /* POSIX 1997 */ cpio->option_atime_restore = 1; break; case 'B': /* POSIX 1997 */ cpio->bytes_per_block = 5120; break; case 'C': /* NetBSD/OpenBSD */ cpio->bytes_per_block = atoi(cpio->optarg); if (cpio->bytes_per_block <= 0) cpio_errc(1, 0, "Invalid blocksize %s", cpio->optarg); break; case 'c': /* POSIX 1997 */ cpio->format = "odc"; break; case 'd': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR; break; case 'E': /* NetBSD/OpenBSD */ include_from_file(cpio, cpio->optarg); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ cpio->filename = cpio->optarg; break; case 'f': /* POSIX 1997 */ exclude(cpio, cpio->optarg); break; case 'H': /* GNU cpio (also --format) */ cpio->format = cpio->optarg; break; case 'h': long_help(); break; case 'I': /* NetBSD/OpenBSD */ cpio->filename = cpio->optarg; break; case 'i': /* POSIX 1997 */ if (cpio->mode != '\0') cpio_errc(1, 0, "Cannot use both -i and -%c", cpio->mode); cpio->mode = opt; break; case OPTION_INSECURE: cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; break; case 'L': /* GNU cpio */ cpio->option_follow_links = 1; break; case 'l': /* POSIX 1997 */ cpio->option_link = 1; break; case 'm': /* POSIX 1997 */ cpio->extract_flags |= ARCHIVE_EXTRACT_TIME; break; case 'n': /* GNU cpio */ cpio->option_numeric_uid_gid = 1; break; case OPTION_NO_PRESERVE_OWNER: /* GNU cpio */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_OWNER; break; case 'O': /* GNU cpio */ cpio->filename = cpio->optarg; break; case 'o': /* POSIX 1997 */ if (cpio->mode != '\0') cpio_errc(1, 0, "Cannot use both -o and -%c", cpio->mode); cpio->mode = opt; break; case 'p': /* POSIX 1997 */ if (cpio->mode != '\0') cpio_errc(1, 0, "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; break; case OPTION_QUIET: /* GNU cpio */ cpio->quiet = 1; break; case 'R': /* GNU cpio, also --owner */ if (owner_parse(cpio->optarg, &uid, &gid)) usage(); if (uid != -1) cpio->uid_override = uid; if (gid != -1) cpio->gid_override = gid; break; case 'r': /* POSIX 1997 */ cpio->option_rename = 1; break; case 't': /* POSIX 1997 */ cpio->option_list = 1; break; case 'u': /* POSIX 1997 */ cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER; break; case 'v': /* POSIX 1997 */ cpio->verbose++; break; case OPTION_VERSION: /* GNU convention */ version(); break; #if 0 /* * cpio_getopt() handles -W specially, so it's not * available here. */ case 'W': /* Obscure, but useful GNU convention. */ break; #endif case 'y': /* tar convention */ #if HAVE_LIBBZ2 cpio->compress = opt; #else cpio_warnc(0, "bzip2 compression not supported by " "this version of bsdcpio"); #endif break; case 'Z': /* tar convention */ cpio->compress = opt; break; case 'z': /* tar convention */ #if HAVE_LIBZ cpio->compress = opt; #else cpio_warnc(0, "gzip compression not supported by " "this version of bsdcpio"); #endif break; default: usage(); } } /* * Sanity-check args, error out on nonsensical combinations. */ /* -t implies -i if no mode was specified. */ if (cpio->option_list && cpio->mode == '\0') cpio->mode = 'i'; /* -t requires -i */ if (cpio->option_list && cpio->mode != 'i') cpio_errc(1, 0, "Option -t requires -i", cpio->mode); /* -n requires -it */ if (cpio->option_numeric_uid_gid && !cpio->option_list) cpio_errc(1, 0, "Option -n requires -it"); /* Can only specify format when writing */ if (cpio->format != NULL && cpio->mode != 'o') cpio_errc(1, 0, "Option --format requires -o"); /* -l requires -p */ if (cpio->option_link && cpio->mode != 'p') cpio_errc(1, 0, "Option -l requires -p"); /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { case 'o': /* TODO: Implement old binary format in libarchive, use that here. */ if (cpio->format == NULL) cpio->format = "odc"; /* Default format */ mode_out(cpio); break; case 'i': while (*cpio->argv != NULL) { include(cpio, *cpio->argv); --cpio->argc; ++cpio->argv; } if (cpio->option_list) mode_list(cpio); else mode_in(cpio); break; case 'p': if (*cpio->argv == NULL || **cpio->argv == '\0') cpio_errc(1, 0, "-p mode requires a target directory"); mode_pass(cpio, *cpio->argv); break; default: cpio_errc(1, 0, "Must specify at least one of -i, -o, or -p"); } free_cache(cpio->gname_cache); free_cache(cpio->uname_cache); return (0); }