diff options
author | kientzle <kientzle@FreeBSD.org> | 2010-05-08 16:47:33 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2010-05-08 16:47:33 +0000 |
commit | 94ac1169df0fe3609aa671582574d421e89b7ff3 (patch) | |
tree | 551fb87f2d73c01d8a568f60595409846d1450d2 /usr.bin/cpio/cpio.c | |
parent | 4ae7167075e6eae3f41a1efdb35bb448a52bddb0 (diff) | |
download | FreeBSD-src-94ac1169df0fe3609aa671582574d421e89b7ff3.zip FreeBSD-src-94ac1169df0fe3609aa671582574d421e89b7ff3.tar.gz |
bsdcpio 2.8.3
Diffstat (limited to 'usr.bin/cpio/cpio.c')
-rw-r--r-- | usr.bin/cpio/cpio.c | 576 |
1 files changed, 224 insertions, 352 deletions
diff --git a/usr.bin/cpio/cpio.c b/usr.bin/cpio/cpio.c index 53195aa..9b76fe8 100644 --- a/usr.bin/cpio/cpio.c +++ b/usr.bin/cpio/cpio.c @@ -56,6 +56,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STDARG_H #include <stdarg.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -74,11 +77,17 @@ __FBSDID("$FreeBSD$"); #endif #include "cpio.h" +#include "err.h" +#include "line_reader.h" #include "matching.h" /* Fixed size of uname/gname caches. */ #define name_cache_size 101 +#ifndef O_BINARY +#define O_BINARY 0 +#endif + struct name_cache { int probes; int hits; @@ -89,7 +98,8 @@ struct name_cache { } cache[name_cache_size]; }; -static int copy_data(struct archive *, struct archive *); +static int extract_data(struct archive *, struct archive *); +const char * cpio_i64toa(int64_t); static const char *cpio_rename(const char *name); static int entry_to_archive(struct cpio *, struct archive_entry *); static int file_to_archive(struct cpio *, const char *); @@ -117,6 +127,7 @@ main(int argc, char *argv[]) static char buff[16384]; struct cpio _cpio; /* Allocated on stack. */ struct cpio *cpio; + const char *errmsg; int uid, gid; int opt; @@ -124,33 +135,26 @@ main(int argc, char *argv[]) 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. */ + /* Need progname before calling warnc. */ if (*argv == NULL) - cpio_progname = "bsdcpio"; + progname = "bsdcpio"; else { #if defined(_WIN32) && !defined(__CYGWIN__) - cpio_progname = strrchr(*argv, '\\'); + progname = strrchr(*argv, '\\'); #else - cpio_progname = strrchr(*argv, '/'); + progname = strrchr(*argv, '/'); #endif - if (cpio_progname != NULL) - cpio_progname++; + if (progname != NULL) + progname++; else - cpio_progname = *argv; + 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'; @@ -161,19 +165,17 @@ main(int argc, char *argv[]) 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 !defined(_WIN32) && !defined(__CYGWIN__) if (geteuid() == 0) -#endif cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; +#endif 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'; + cpio->option_null = 1; break; case 'A': /* NetBSD/OpenBSD */ cpio->option_append = 1; @@ -187,7 +189,7 @@ main(int argc, char *argv[]) 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); + errc(1, 0, "Invalid blocksize %s", cpio->optarg); break; case 'c': /* POSIX 1997 */ cpio->format = "odc"; @@ -196,13 +198,14 @@ main(int argc, char *argv[]) cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR; break; case 'E': /* NetBSD/OpenBSD */ - include_from_file(cpio, cpio->optarg); + include_from_file(&cpio->matching, + cpio->optarg, cpio->option_null); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ cpio->filename = cpio->optarg; break; case 'f': /* POSIX 1997 */ - exclude(cpio, cpio->optarg); + exclude(&cpio->matching, cpio->optarg); break; case 'H': /* GNU cpio (also --format) */ cpio->format = cpio->optarg; @@ -215,10 +218,16 @@ main(int argc, char *argv[]) break; case 'i': /* POSIX 1997 */ if (cpio->mode != '\0') - cpio_errc(1, 0, + errc(1, 0, "Cannot use both -i and -%c", cpio->mode); cpio->mode = opt; break; + case 'J': /* GNU tar, others */ + cpio->compress = opt; + break; + case 'j': /* GNU tar, others */ + cpio->compress = opt; + break; case OPTION_INSECURE: cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_SYMLINKS; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; @@ -229,6 +238,9 @@ main(int argc, char *argv[]) case 'l': /* POSIX 1997 */ cpio->option_link = 1; break; + case OPTION_LZMA: /* GNU tar, others */ + cpio->compress = opt; + break; case 'm': /* POSIX 1997 */ cpio->extract_flags |= ARCHIVE_EXTRACT_TIME; break; @@ -243,23 +255,29 @@ main(int argc, char *argv[]) break; case 'o': /* POSIX 1997 */ if (cpio->mode != '\0') - cpio_errc(1, 0, + 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, + errc(1, 0, "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; break; + case OPTION_PRESERVE_OWNER: + cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; + break; case OPTION_QUIET: /* GNU cpio */ cpio->quiet = 1; break; case 'R': /* GNU cpio, also --owner */ - if (owner_parse(cpio->optarg, &uid, &gid)) + errmsg = owner_parse(cpio->optarg, &uid, &gid); + if (errmsg) { + warnc(-1, "%s", errmsg); usage(); + } if (uid != -1) cpio->uid_override = uid; if (gid != -1) @@ -278,9 +296,6 @@ main(int argc, char *argv[]) case 'v': /* POSIX 1997 */ cpio->verbose++; break; - case 'V': /* GNU cpio */ - cpio->dot++; - break; case OPTION_VERSION: /* GNU convention */ version(); break; @@ -293,23 +308,13 @@ main(int argc, char *argv[]) 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(); @@ -324,19 +329,16 @@ main(int argc, char *argv[]) cpio->mode = 'i'; /* -t requires -i */ if (cpio->option_list && cpio->mode != 'i') - cpio_errc(1, 0, "Option -t requires -i", cpio->mode); + errc(1, 0, "Option -t requires -i"); /* -n requires -it */ if (cpio->option_numeric_uid_gid && !cpio->option_list) - cpio_errc(1, 0, "Option -n requires -it"); + 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"); + 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"); - /* -v overrides -V */ - if (cpio->dot && cpio->verbose) - cpio->dot = 0; + errc(1, 0, "Option -l requires -p"); /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { @@ -350,7 +352,7 @@ main(int argc, char *argv[]) break; case 'i': while (*cpio->argv != NULL) { - include(cpio, *cpio->argv); + include(&cpio->matching, *cpio->argv); --cpio->argc; ++cpio->argv; } @@ -361,26 +363,26 @@ main(int argc, char *argv[]) break; case 'p': if (*cpio->argv == NULL || **cpio->argv == '\0') - cpio_errc(1, 0, + errc(1, 0, "-p mode requires a target directory"); mode_pass(cpio, *cpio->argv); break; default: - cpio_errc(1, 0, + 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); + return (cpio->return_value); } -void +static void usage(void) { const char *p; - p = cpio_progname; + p = progname; fprintf(stderr, "Brief Usage:\n"); fprintf(stderr, " List: %s -it < archive\n", p); @@ -394,14 +396,9 @@ static const char *long_help_msg = "First option must be a mode specifier:\n" " -i Input -o Output -p Pass\n" "Common Options:\n" - " -v Verbose filenames -V one dot per file\n" + " -v Verbose\n" "Create: %p -o [options] < [list of files] > [archive]\n" -#ifdef HAVE_BZLIB_H - " -y Compress archive with bzip2\n" -#endif -#ifdef HAVE_ZLIB_H - " -z Compress archive with gzip\n" -#endif + " -J,-y,-z,--lzma Compress archive with xz/bzip2/gzip/lzma\n" " --format {odc|newc|ustar} Select archive format\n" "List: %p -it < [archive]\n" "Extract: %p -i [options] < [archive]\n"; @@ -423,7 +420,7 @@ long_help(void) const char *prog; const char *p; - prog = cpio_progname; + prog = progname; fflush(stderr); @@ -455,19 +452,33 @@ version(void) static void mode_out(struct cpio *cpio) { - unsigned long blocks; struct archive_entry *entry, *spare; struct line_reader *lr; const char *p; int r; if (cpio->option_append) - cpio_errc(1, 0, "Append mode not yet supported."); + errc(1, 0, "Append mode not yet supported."); + + cpio->archive_read_disk = archive_read_disk_new(); + if (cpio->archive_read_disk == NULL) + errc(1, 0, "Failed to allocate archive object"); + if (cpio->option_follow_links) + archive_read_disk_set_symlink_logical(cpio->archive_read_disk); + else + archive_read_disk_set_symlink_physical(cpio->archive_read_disk); + archive_read_disk_set_standard_lookup(cpio->archive_read_disk); + cpio->archive = archive_write_new(); if (cpio->archive == NULL) - cpio_errc(1, 0, "Failed to allocate archive object"); + errc(1, 0, "Failed to allocate archive object"); switch (cpio->compress) { -#ifndef SMALLER + case 'J': + r = archive_write_set_compression_xz(cpio->archive); + break; + case OPTION_LZMA: + r = archive_write_set_compression_lzma(cpio->archive); + break; case 'j': case 'y': r = archive_write_set_compression_bzip2(cpio->archive); break; @@ -477,41 +488,30 @@ mode_out(struct cpio *cpio) case 'Z': r = archive_write_set_compression_compress(cpio->archive); break; -#endif - case '\0': + default: r = archive_write_set_compression_none(cpio->archive); break; - default: - cpio_errc(1, 0, "Unrecognized compression option"); } - if (r != ARCHIVE_OK) - cpio_errc(1, 0, "Unsupported compression format"); -#ifdef SMALLER - if (strcmp(cpio->format, "cpio")) - r = archive_write_set_format_cpio(cpio->archive); - else if (strcmp(cpio->format, "odc")) - r = archive_write_set_format_cpio(cpio->archive); - else if (strcmp(cpio->format, "newc")) - r = archive_write_set_format_cpio(cpio->archive); - else if (strcmp(cpio->format, "ustar")) - r = archive_write_set_format_cpio(cpio->archive); -#else + if (r < ARCHIVE_WARN) + errc(1, 0, "Requested compression not available"); r = archive_write_set_format_by_name(cpio->archive, cpio->format); -#endif if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(cpio->archive)); + errc(1, 0, "%s", archive_error_string(cpio->archive)); archive_write_set_bytes_per_block(cpio->archive, cpio->bytes_per_block); cpio->linkresolver = archive_entry_linkresolver_new(); archive_entry_linkresolver_set_strategy(cpio->linkresolver, archive_format(cpio->archive)); + /* + * The main loop: Copy each file into the output archive. + */ r = archive_write_open_file(cpio->archive, cpio->filename); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(cpio->archive)); - lr = process_lines_init("-", cpio->line_separator); - while ((p = process_lines_next(lr)) != NULL) + errc(1, 0, "%s", archive_error_string(cpio->archive)); + lr = line_reader("-", cpio->option_null); + while ((p = line_reader_next(lr)) != NULL) file_to_archive(cpio, p); - process_lines_free(lr); + line_reader_free(lr); /* * The hardlink detection may have queued up a couple of entries @@ -527,15 +527,14 @@ mode_out(struct cpio *cpio) } r = archive_write_close(cpio->archive); - if (cpio->dot) - fprintf(stderr, "\n"); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(cpio->archive)); + errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { - blocks = (archive_position_uncompressed(cpio->archive) + 511) - / 512; - fprintf(stderr, "%lu %s\n", blocks, + int64_t blocks = + (archive_position_uncompressed(cpio->archive) + 511) + / 512; + fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_finish(cpio->archive); @@ -549,57 +548,37 @@ mode_out(struct cpio *cpio) static int file_to_archive(struct cpio *cpio, const char *srcpath) { - struct stat st; const char *destpath; struct archive_entry *entry, *spare; size_t len; const char *p; -#if !defined(_WIN32) || defined(__CYGWIN__) - int lnklen; -#endif int r; /* * Create an archive_entry describing the source file. * - * XXX TODO: rework to use archive_read_disk_entry_from_file() */ entry = archive_entry_new(); if (entry == NULL) - cpio_errc(1, 0, "Couldn't allocate entry"); + errc(1, 0, "Couldn't allocate entry"); archive_entry_copy_sourcepath(entry, srcpath); - - /* Get stat information. */ - if (cpio->option_follow_links) - r = stat(srcpath, &st); - else - r = lstat(srcpath, &st); - if (r != 0) { - cpio_warnc(errno, "Couldn't stat \"%s\"", srcpath); - archive_entry_free(entry); - return (0); + r = archive_read_disk_entry_from_file(cpio->archive_read_disk, + entry, -1, NULL); + if (r < ARCHIVE_FAILED) + errc(1, 0, "%s", + archive_error_string(cpio->archive_read_disk)); + if (r < ARCHIVE_OK) + warnc(0, "%s", + archive_error_string(cpio->archive_read_disk)); + if (r <= ARCHIVE_FAILED) { + cpio->return_value = 1; + return (r); } if (cpio->uid_override >= 0) - st.st_uid = cpio->uid_override; + archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) - st.st_gid = cpio->gid_override; - archive_entry_copy_stat(entry, &st); - -#if !defined(_WIN32) || defined(__CYGWIN__) - /* If its a symlink, pull the target. */ - if (S_ISLNK(st.st_mode)) { - lnklen = readlink(srcpath, cpio->buff, cpio->buff_size); - if (lnklen < 0) { - cpio_warnc(errno, - "%s: Couldn't read symbolic link", srcpath); - archive_entry_free(entry); - return (0); - } - cpio->buff[lnklen] = 0; - archive_entry_set_symlink(entry, cpio->buff); - } -#endif + archive_entry_set_gid(entry, cpio->gid_override); /* * Generate a destination path for this entry. @@ -618,7 +597,7 @@ file_to_archive(struct cpio *cpio, const char *srcpath) free(cpio->pass_destpath); cpio->pass_destpath = malloc(cpio->pass_destpath_alloc); if (cpio->pass_destpath == NULL) - cpio_errc(1, ENOMEM, + errc(1, ENOMEM, "Can't allocate path buffer"); } strcpy(cpio->pass_destpath, cpio->destdir); @@ -639,18 +618,18 @@ file_to_archive(struct cpio *cpio, const char *srcpath) */ spare = NULL; if (cpio->linkresolver != NULL - && !S_ISDIR(st.st_mode)) { + && archive_entry_filetype(entry) != AE_IFDIR) { archive_entry_linkify(cpio->linkresolver, &entry, &spare); } if (entry != NULL) { r = entry_to_archive(cpio, entry); archive_entry_free(entry); - } - if (spare != NULL) { - if (r == 0) - r = entry_to_archive(cpio, spare); - archive_entry_free(spare); + if (spare != NULL) { + if (r == 0) + r = entry_to_archive(cpio, spare); + archive_entry_free(spare); + } } return (r); } @@ -667,8 +646,6 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) /* Print out the destination name to the user. */ if (cpio->verbose) fprintf(stderr,"%s", destpath); - if (cpio->dot) - fprintf(stderr, "."); /* * Option_link only makes sense in pass mode and for @@ -686,7 +663,7 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) /* Save the original entry in case we need it later. */ t = archive_entry_clone(entry); if (t == NULL) - cpio_errc(1, ENOMEM, "Can't create link"); + errc(1, ENOMEM, "Can't create link"); /* Note: link(2) doesn't create parent directories, * so we use archive_write_header() instead as a * convenience. */ @@ -696,15 +673,15 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) r = archive_write_header(cpio->archive, t); archive_entry_free(t); if (r != ARCHIVE_OK) - cpio_warnc(archive_errno(cpio->archive), - archive_error_string(cpio->archive)); + warnc(archive_errno(cpio->archive), + "%s", archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); #ifdef EXDEV if (r != ARCHIVE_OK && archive_errno(cpio->archive) == EXDEV) { /* Cross-device link: Just fall through and use * the original entry to copy the file over. */ - cpio_warnc(0, "Copying file instead"); + warnc(0, "Copying file instead"); } else #endif return (0); @@ -716,9 +693,9 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) */ if (archive_entry_filetype(entry) == AE_IFREG) { if (archive_entry_size(entry) > 0) { - fd = open(srcpath, O_RDONLY); + fd = open(srcpath, O_RDONLY | O_BINARY); if (fd < 0) { - cpio_warnc(errno, + warnc(errno, "%s: could not open file", srcpath); goto cleanup; } @@ -730,7 +707,7 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) r = archive_write_header(cpio->archive, entry); if (r != ARCHIVE_OK) - cpio_warnc(archive_errno(cpio->archive), + warnc(archive_errno(cpio->archive), "%s: %s", srcpath, archive_error_string(cpio->archive)); @@ -744,10 +721,10 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) r = archive_write_data(cpio->archive, cpio->buff, bytes_read); if (r < 0) - cpio_errc(1, archive_errno(cpio->archive), - archive_error_string(cpio->archive)); + errc(1, archive_errno(cpio->archive), + "%s", archive_error_string(cpio->archive)); if (r < bytes_read) { - cpio_warnc(0, + warnc(0, "Truncated write; file may have grown while being archived."); } bytes_read = read(fd, cpio->buff, cpio->buff_size); @@ -776,7 +753,7 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, (void)name; /* UNUSED */ if (!warned) - cpio_warnc(0, "Can't restore access times on this platform"); + warnc(0, "Can't restore access times on this platform"); warned = 1; return (fd); #else @@ -795,7 +772,7 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, times[0].tv_sec = archive_entry_atime(entry); times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000; -#ifdef HAVE_FUTIMES +#if defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (fd >= 0 && futimes(fd, times) == 0) return (fd); #endif @@ -811,9 +788,10 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, #ifdef HAVE_LUTIMES if (lutimes(name, times) != 0) #else - if (!S_ISLNK(archive_entry_mode(entry)) && utimes(name, times) != 0) + if ((AE_IFLNK != archive_entry_filetype(entry)) + && utimes(name, times) != 0) #endif - cpio_warnc(errno, "Can't update time for %s", name); + warnc(errno, "Can't update time for %s", name); #endif return (fd); } @@ -826,38 +804,32 @@ mode_in(struct cpio *cpio) struct archive_entry *entry; struct archive *ext; const char *destpath; - unsigned long blocks; int r; ext = archive_write_disk_new(); if (ext == NULL) - cpio_errc(1, 0, "Couldn't allocate restore object"); + errc(1, 0, "Couldn't allocate restore object"); r = archive_write_disk_set_options(ext, cpio->extract_flags); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(ext)); + errc(1, 0, "%s", archive_error_string(ext)); a = archive_read_new(); if (a == NULL) - cpio_errc(1, 0, "Couldn't allocate archive object"); -#ifdef SMALLER - archive_read_support_format_cpio(a); - archive_read_support_format_tar(a); -#else + errc(1, 0, "Couldn't allocate archive object"); archive_read_support_compression_all(a); archive_read_support_format_all(a); -#endif if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block)) - cpio_errc(1, archive_errno(a), - archive_error_string(a)); + errc(1, archive_errno(a), + "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { - cpio_errc(1, archive_errno(a), - archive_error_string(a)); + errc(1, archive_errno(a), + "%s", archive_error_string(a)); } - if (excluded(cpio, archive_entry_pathname(entry))) + if (excluded(cpio->matching, archive_entry_pathname(entry))) continue; if (cpio->option_rename) { destpath = cpio_rename(archive_entry_pathname(entry)); @@ -867,9 +839,7 @@ mode_in(struct cpio *cpio) if (destpath == NULL) continue; if (cpio->verbose) - fprintf(stderr, "%s\n", destpath); - if (cpio->dot) - fprintf(stderr, "."); + fprintf(stdout, "%s\n", destpath); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) @@ -880,30 +850,34 @@ mode_in(struct cpio *cpio) archive_entry_pathname(entry), archive_error_string(ext)); } else if (archive_entry_size(entry) > 0) { - r = copy_data(a, ext); + r = extract_data(a, ext); + if (r != ARCHIVE_OK) + cpio->return_value = 1; } } r = archive_read_close(a); - if (cpio->dot) - fprintf(stderr, "\n"); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(a)); + errc(1, 0, "%s", archive_error_string(a)); r = archive_write_close(ext); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(ext)); + errc(1, 0, "%s", archive_error_string(ext)); if (!cpio->quiet) { - blocks = (archive_position_uncompressed(a) + 511) + int64_t blocks = (archive_position_uncompressed(a) + 511) / 512; - fprintf(stderr, "%lu %s\n", blocks, + fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_finish(a); archive_write_finish(ext); - exit(0); + exit(cpio->return_value); } +/* + * Exits if there's a fatal error. Returns ARCHIVE_OK + * if everything is kosher. + */ static int -copy_data(struct archive *ar, struct archive *aw) +extract_data(struct archive *ar, struct archive *aw) { int r; size_t size; @@ -915,14 +889,14 @@ copy_data(struct archive *ar, struct archive *aw) if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r != ARCHIVE_OK) { - cpio_warnc(archive_errno(ar), + warnc(archive_errno(ar), "%s", archive_error_string(ar)); - return (r); + exit(1); } r = archive_write_data_block(aw, block, size, offset); if (r != ARCHIVE_OK) { - cpio_warnc(archive_errno(aw), - archive_error_string(aw)); + warnc(archive_errno(aw), + "%s", archive_error_string(aw)); return (r); } } @@ -933,32 +907,26 @@ mode_list(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; - unsigned long blocks; int r; a = archive_read_new(); if (a == NULL) - cpio_errc(1, 0, "Couldn't allocate archive object"); -#ifdef SMALLER - archive_read_support_format_cpio(a); - archive_read_support_format_tar(a); -#else + errc(1, 0, "Couldn't allocate archive object"); archive_read_support_compression_all(a); archive_read_support_format_all(a); -#endif if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block)) - cpio_errc(1, archive_errno(a), - archive_error_string(a)); + errc(1, archive_errno(a), + "%s", archive_error_string(a)); for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { - cpio_errc(1, archive_errno(a), - archive_error_string(a)); + errc(1, archive_errno(a), + "%s", archive_error_string(a)); } - if (excluded(cpio, archive_entry_pathname(entry))) + if (excluded(cpio->matching, archive_entry_pathname(entry))) continue; if (cpio->verbose) list_item_verbose(cpio, entry); @@ -967,11 +935,11 @@ mode_list(struct cpio *cpio) } r = archive_read_close(a); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(a)); + errc(1, 0, "%s", archive_error_string(a)); if (!cpio->quiet) { - blocks = (archive_position_uncompressed(a) + 511) + int64_t blocks = (archive_position_uncompressed(a) + 511) / 512; - fprintf(stderr, "%lu %s\n", blocks, + fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_finish(a); @@ -994,23 +962,18 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; - const struct stat *st; const char *fmt; - time_t tim; + time_t mtime; static time_t now; - st = archive_entry_stat(entry); - if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ - snprintf(uids, sizeof(uids), "%jd", - (intmax_t)archive_entry_uid(entry)); + strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; - snprintf(gids, sizeof(gids), "%jd", - (intmax_t)archive_entry_gid(entry)); + strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ @@ -1024,30 +987,31 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) } /* Print device number or file size. */ - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + if (archive_entry_filetype(entry) == AE_IFCHR + || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", - (unsigned long)major(st->st_rdev), - (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ + (unsigned long)archive_entry_rdevmajor(entry), + (unsigned long)archive_entry_rdevminor(entry)); } else { - snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF, - (CPIO_FILESIZE_TYPE)st->st_size); + strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ - tim = (time_t)st->st_mtime; + mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ - if (abs(tim - now) > (365/2)*86400) + if (mtime - now > 365*86400/2 + || mtime - now < -365*86400/2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else - if (abs(tim - now) > (365/2)*86400) + if (abs(mtime - now) > (365/2)*86400) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; #endif - strftime(date, sizeof(date), fmt, localtime(&tim)); + strftime(date, sizeof(date), fmt, localtime(&mtime)); fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), @@ -1066,7 +1030,6 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) static void mode_pass(struct cpio *cpio, const char *destdir) { - unsigned long blocks; struct line_reader *lr; const char *p; int r; @@ -1079,28 +1042,37 @@ mode_pass(struct cpio *cpio, const char *destdir) cpio->archive = archive_write_disk_new(); if (cpio->archive == NULL) - cpio_errc(1, 0, "Failed to allocate archive object"); + errc(1, 0, "Failed to allocate archive object"); r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(cpio->archive)); + errc(1, 0, "%s", archive_error_string(cpio->archive)); cpio->linkresolver = archive_entry_linkresolver_new(); archive_write_disk_set_standard_lookup(cpio->archive); - lr = process_lines_init("-", cpio->line_separator); - while ((p = process_lines_next(lr)) != NULL) + + cpio->archive_read_disk = archive_read_disk_new(); + if (cpio->archive_read_disk == NULL) + errc(1, 0, "Failed to allocate archive object"); + if (cpio->option_follow_links) + archive_read_disk_set_symlink_logical(cpio->archive_read_disk); + else + archive_read_disk_set_symlink_physical(cpio->archive_read_disk); + archive_read_disk_set_standard_lookup(cpio->archive_read_disk); + + lr = line_reader("-", cpio->option_null); + while ((p = line_reader_next(lr)) != NULL) file_to_archive(cpio, p); - process_lines_free(lr); + line_reader_free(lr); archive_entry_linkresolver_free(cpio->linkresolver); r = archive_write_close(cpio->archive); - if (cpio->dot) - fprintf(stderr, "\n"); if (r != ARCHIVE_OK) - cpio_errc(1, 0, archive_error_string(cpio->archive)); + errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { - blocks = (archive_position_uncompressed(cpio->archive) + 511) - / 512; - fprintf(stderr, "%lu %s\n", blocks, + int64_t blocks = + (archive_position_uncompressed(cpio->archive) + 511) + / 512; + fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } @@ -1150,130 +1122,6 @@ cpio_rename(const char *name) return (ret); } - -/* - * Read lines from file and do something with each one. If option_null - * is set, lines are terminated with zero bytes; otherwise, they're - * terminated with newlines. - * - * This uses a self-sizing buffer to handle arbitrarily-long lines. - */ -struct line_reader { - FILE *f; - char *buff, *buff_end, *line_start, *line_end, *p; - char *pathname; - size_t buff_length; - int separator; - int ret; -}; - -struct line_reader * -process_lines_init(const char *pathname, char separator) -{ - struct line_reader *lr; - - lr = calloc(1, sizeof(*lr)); - if (lr == NULL) - cpio_errc(1, ENOMEM, "Can't open %s", pathname); - - lr->separator = separator; - lr->pathname = strdup(pathname); - - if (strcmp(pathname, "-") == 0) - lr->f = stdin; - else - lr->f = fopen(pathname, "r"); - if (lr->f == NULL) - cpio_errc(1, errno, "Couldn't open %s", pathname); - lr->buff_length = 8192; - lr->buff = malloc(lr->buff_length); - if (lr->buff == NULL) - cpio_errc(1, ENOMEM, "Can't read %s", pathname); - lr->line_start = lr->line_end = lr->buff_end = lr->buff; - - return (lr); -} - -const char * -process_lines_next(struct line_reader *lr) -{ - size_t bytes_wanted, bytes_read, new_buff_size; - char *line_start, *p; - - for (;;) { - /* If there's a line in the buffer, return it immediately. */ - while (lr->line_end < lr->buff_end) { - if (*lr->line_end == lr->separator) { - *lr->line_end = '\0'; - line_start = lr->line_start; - lr->line_start = lr->line_end + 1; - lr->line_end = lr->line_start; - return (line_start); - } else - lr->line_end++; - } - - /* If we're at end-of-file, process the final data. */ - if (lr->f == NULL) { - /* If there's more text, return one last line. */ - if (lr->line_end > lr->line_start) { - *lr->line_end = '\0'; - line_start = lr->line_start; - lr->line_start = lr->line_end + 1; - lr->line_end = lr->line_start; - return (line_start); - } - /* Otherwise, we're done. */ - return (NULL); - } - - /* Buffer only has part of a line. */ - if (lr->line_start > lr->buff) { - /* Move a leftover fractional line to the beginning. */ - memmove(lr->buff, lr->line_start, - lr->buff_end - lr->line_start); - lr->buff_end -= lr->line_start - lr->buff; - lr->line_end -= lr->line_start - lr->buff; - lr->line_start = lr->buff; - } else { - /* Line is too big; enlarge the buffer. */ - new_buff_size = lr->buff_length * 2; - if (new_buff_size <= lr->buff_length) - cpio_errc(1, ENOMEM, - "Line too long in %s", lr->pathname); - lr->buff_length = new_buff_size; - p = realloc(lr->buff, new_buff_size); - if (p == NULL) - cpio_errc(1, ENOMEM, - "Line too long in %s", lr->pathname); - lr->buff_end = p + (lr->buff_end - lr->buff); - lr->line_end = p + (lr->line_end - lr->buff); - lr->line_start = lr->buff = p; - } - - /* Get some more data into the buffer. */ - bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; - bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); - lr->buff_end += bytes_read; - - if (ferror(lr->f)) - cpio_errc(1, errno, "Can't read %s", lr->pathname); - if (feof(lr->f)) { - if (lr->f != stdin) - fclose(lr->f); - lr->f = NULL; - } - } -} - -void -process_lines_free(struct line_reader *lr) -{ - free(lr->buff); - free(lr->pathname); - free(lr); -} - static void free_cache(struct name_cache *cache) { @@ -1302,7 +1150,7 @@ lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable, if (*name_cache_variable == NULL) { *name_cache_variable = malloc(sizeof(struct name_cache)); if (*name_cache_variable == NULL) - cpio_errc(1, ENOMEM, "No more memory"); + errc(1, ENOMEM, "No more memory"); memset(*name_cache_variable, 0, sizeof(struct name_cache)); (*name_cache_variable)->size = name_cache_size; } @@ -1358,8 +1206,8 @@ lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) pwent = getpwuid((uid_t)id); if (pwent == NULL) { *name = NULL; - if (errno != 0) - cpio_warnc(errno, "getpwuid(%d) failed", id); + if (errno != 0 && errno != ENOENT) + warnc(errno, "getpwuid(%d) failed", id); return (errno); } @@ -1386,10 +1234,34 @@ lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) if (grent == NULL) { *name = NULL; if (errno != 0) - cpio_warnc(errno, "getgrgid(%d) failed", id); + warnc(errno, "getgrgid(%d) failed", id); return (errno); } *name = grent->gr_name; return (0); } + +/* + * It would be nice to just use printf() for formatting large numbers, + * but the compatibility problems are a big headache. Hence the + * following simple utility function. + */ +const char * +cpio_i64toa(int64_t n0) +{ + // 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice. + // We also need 1 byte for '-' and 1 for '\0'. + static char buff[22]; + int64_t n = n0 < 0 ? -n0 : n0; + char *p = buff + sizeof(buff); + + *--p = '\0'; + do { + *--p = '0' + (int)(n % 10); + n /= 10; + } while (n > 0); + if (n0 < 0) + *--p = '-'; + return p; +} |