Index: vendor/libarchive/dist/cpio/cpio.c =================================================================== --- vendor/libarchive/dist/cpio/cpio.c (revision 299894) +++ vendor/libarchive/dist/cpio/cpio.c (revision 299895) @@ -1,1470 +1,1471 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cpio_platform.h" __FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.15 2008/12/06 07:30:40 kientzle Exp $"); #include #include #include #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_SIGNAL_H #include #endif #ifdef HAVE_STDARG_H #include #endif #ifdef HAVE_STDINT_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_TIME_H #include #endif #include "cpio.h" #include "err.h" #include "line_reader.h" #include "passphrase.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; size_t size; struct { id_t id; char *name; } cache[name_cache_size]; }; 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 *); static void free_cache(struct name_cache *cache); static void list_item_verbose(struct cpio *, struct archive_entry *); static void long_help(void); static const char *lookup_gname(struct cpio *, gid_t gid); static int lookup_gname_helper(struct cpio *, const char **name, id_t gid); static const char *lookup_uname(struct cpio *, uid_t uid); static int lookup_uname_helper(struct cpio *, const char **name, id_t uid); static void mode_in(struct cpio *); static void mode_list(struct cpio *); static void mode_out(struct cpio *); static void mode_pass(struct cpio *, const char *); static const char *remove_leading_slash(const char *); static int restore_time(struct cpio *, struct archive_entry *, const char *, int fd); static void usage(void); static void version(void); static const char * passphrase_callback(struct archive *, void *); static void passphrase_free(char *); int 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; cpio = &_cpio; memset(cpio, 0, sizeof(*cpio)); cpio->buff = buff; cpio->buff_size = sizeof(buff); #if defined(HAVE_SIGACTION) && defined(SIGPIPE) { /* Ignore SIGPIPE signals. */ struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); } #endif /* Set lafe_progname before calling lafe_warnc. */ lafe_setprogname(*argv, "bsdcpio"); #if HAVE_SETLOCALE if (setlocale(LC_ALL, "") == NULL) lafe_warnc(0, "Failed to set default locale"); #endif cpio->uid_override = -1; cpio->gid_override = -1; cpio->argv = argv; cpio->argc = argc; 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_SECURE_NOABSOLUTEPATHS; cpio->extract_flags |= ARCHIVE_EXTRACT_PERM; cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; cpio->extract_flags |= ARCHIVE_EXTRACT_ACL; #if !defined(_WIN32) && !defined(__CYGWIN__) if (geteuid() == 0) cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; #endif cpio->bytes_per_block = 512; cpio->filename = NULL; cpio->matching = archive_match_new(); if (cpio->matching == NULL) lafe_errc(1, 0, "Out of memory"); while ((opt = cpio_getopt(cpio)) != -1) { switch (opt) { case '0': /* GNU convention: --null, -0 */ cpio->option_null = 1; 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 OPTION_B64ENCODE: cpio->add_filter = opt; break; case 'C': /* NetBSD/OpenBSD */ cpio->bytes_per_block = atoi(cpio->argument); if (cpio->bytes_per_block <= 0) lafe_errc(1, 0, "Invalid blocksize %s", cpio->argument); 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 */ if (archive_match_include_pattern_from_file( cpio->matching, cpio->argument, cpio->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ cpio->filename = cpio->argument; break; case 'f': /* POSIX 1997 */ if (archive_match_exclude_pattern(cpio->matching, cpio->argument) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); break; case OPTION_GRZIP: cpio->compress = opt; break; case 'H': /* GNU cpio (also --format) */ cpio->format = cpio->argument; break; case 'h': long_help(); break; case 'I': /* NetBSD/OpenBSD */ cpio->filename = cpio->argument; break; case 'i': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_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; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case 'L': /* GNU cpio */ cpio->option_follow_links = 1; break; case 'l': /* POSIX 1997 */ cpio->option_link = 1; break; case OPTION_LRZIP: case OPTION_LZ4: case OPTION_LZMA: /* GNU tar, others */ case OPTION_LZOP: /* GNU tar, others */ cpio->compress = opt; 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->argument; break; case 'o': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -o and -%c", cpio->mode); cpio->mode = opt; break; case 'p': /* POSIX 1997 */ if (cpio->mode != '\0') lafe_errc(1, 0, "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; + cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS; break; case OPTION_PASSPHRASE: cpio->passphrase = cpio->argument; 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 */ /* TODO: owner_parse should return uname/gname * also; use that to set [ug]name_override. */ errmsg = owner_parse(cpio->argument, &uid, &gid); if (errmsg) { lafe_warnc(-1, "%s", errmsg); usage(); } if (uid != -1) { cpio->uid_override = uid; cpio->uname_override = NULL; } if (gid != -1) { cpio->gid_override = gid; cpio->gname_override = NULL; } 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 OPTION_UUENCODE: cpio->add_filter = opt; break; case 'v': /* POSIX 1997 */ cpio->verbose++; break; case 'V': /* GNU cpio */ cpio->dot++; 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 */ cpio->compress = opt; break; case 'Z': /* tar convention */ cpio->compress = opt; break; case 'z': /* tar convention */ cpio->compress = opt; 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') lafe_errc(1, 0, "Option -t requires -i"); /* -n requires -it */ if (cpio->option_numeric_uid_gid && !cpio->option_list) lafe_errc(1, 0, "Option -n requires -it"); /* Can only specify format when writing */ if (cpio->format != NULL && cpio->mode != 'o') lafe_errc(1, 0, "Option --format requires -o"); /* -l requires -p */ if (cpio->option_link && cpio->mode != 'p') lafe_errc(1, 0, "Option -l requires -p"); /* -v overrides -V */ if (cpio->dot && cpio->verbose) cpio->dot = 0; /* 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) { if (archive_match_include_pattern(cpio->matching, *cpio->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error : %s", archive_error_string(cpio->matching)); --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') lafe_errc(1, 0, "-p mode requires a target directory"); mode_pass(cpio, *cpio->argv); break; default: lafe_errc(1, 0, "Must specify at least one of -i, -o, or -p"); } archive_match_free(cpio->matching); free_cache(cpio->gname_cache); free_cache(cpio->uname_cache); free(cpio->destdir); passphrase_free(cpio->ppbuff); return (cpio->return_value); } static void usage(void) { const char *p; p = lafe_getprogname(); fprintf(stderr, "Brief Usage:\n"); fprintf(stderr, " List: %s -it < archive\n", p); fprintf(stderr, " Extract: %s -i < archive\n", p); fprintf(stderr, " Create: %s -o < filenames > archive\n", p); fprintf(stderr, " Help: %s --help\n", p); exit(1); } 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" "Create: %p -o [options] < [list of files] > [archive]\n" " -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"; /* * Note that the word 'bsdcpio' will always appear in the first line * of output. * * In particular, /bin/sh scripts that need to test for the presence * of bsdcpio can use the following template: * * if (cpio --help 2>&1 | grep bsdcpio >/dev/null 2>&1 ) then \ * echo bsdcpio; else echo not bsdcpio; fi */ static void long_help(void) { const char *prog; const char *p; prog = lafe_getprogname(); fflush(stderr); p = (strcmp(prog,"bsdcpio") != 0) ? "(bsdcpio)" : ""; printf("%s%s: manipulate archive files\n", prog, p); for (p = long_help_msg; *p != '\0'; p++) { if (*p == '%') { if (p[1] == 'p') { fputs(prog, stdout); p++; } else putchar('%'); } else putchar(*p); } version(); } static void version(void) { fprintf(stdout,"bsdcpio %s -- %s\n", BSDCPIO_VERSION_STRING, archive_version_details()); exit(0); } static void mode_out(struct cpio *cpio) { struct archive_entry *entry, *spare; struct lafe_line_reader *lr; const char *p; int r; if (cpio->option_append) lafe_errc(1, 0, "Append mode not yet supported."); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_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) lafe_errc(1, 0, "Failed to allocate archive object"); switch (cpio->compress) { case OPTION_GRZIP: r = archive_write_add_filter_grzip(cpio->archive); break; case 'J': r = archive_write_add_filter_xz(cpio->archive); break; case OPTION_LRZIP: r = archive_write_add_filter_lrzip(cpio->archive); break; case OPTION_LZ4: r = archive_write_add_filter_lz4(cpio->archive); break; case OPTION_LZMA: r = archive_write_add_filter_lzma(cpio->archive); break; case OPTION_LZOP: r = archive_write_add_filter_lzop(cpio->archive); break; case 'j': case 'y': r = archive_write_add_filter_bzip2(cpio->archive); break; case 'z': r = archive_write_add_filter_gzip(cpio->archive); break; case 'Z': r = archive_write_add_filter_compress(cpio->archive); break; default: r = archive_write_add_filter_none(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested compression not available"); switch (cpio->add_filter) { case 0: r = ARCHIVE_OK; break; case OPTION_B64ENCODE: r = archive_write_add_filter_b64encode(cpio->archive); break; case OPTION_UUENCODE: r = archive_write_add_filter_uuencode(cpio->archive); break; } if (r < ARCHIVE_WARN) lafe_errc(1, 0, "Requested filter not available"); r = archive_write_set_format_by_name(cpio->archive, cpio->format); if (r != ARCHIVE_OK) lafe_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)); if (cpio->passphrase != NULL) r = archive_write_set_passphrase(cpio->archive, cpio->passphrase); else r = archive_write_set_passphrase_callback(cpio->archive, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); /* * The main loop: Copy each file into the output archive. */ r = archive_write_open_filename(cpio->archive, cpio->filename); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); lr = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_line_reader_free(lr); /* * The hardlink detection may have queued up a couple of entries * that can now be flushed. */ entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); while (entry != NULL) { entry_to_archive(cpio, entry); archive_entry_free(entry); entry = NULL; archive_entry_linkify(cpio->linkresolver, &entry, &spare); } r = archive_write_close(cpio->archive); if (cpio->dot) fprintf(stderr, "\n"); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); } static const char * remove_leading_slash(const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; } do { rp = p; /* Remove leading drive letter from archives created * on Windows. */ if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; } /* Remove leading "/../", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" * for next pass. */ } else p += 1; /* Remove "/". */ } } while (rp != p); return (p); } /* * This is used by both out mode (to copy objects from disk into * an archive) and pass mode (to copy objects from disk to * an archive_write_disk "archive"). */ static int file_to_archive(struct cpio *cpio, const char *srcpath) { const char *destpath; struct archive_entry *entry, *spare; size_t len; int r; /* * Create an archive_entry describing the source file. * */ entry = archive_entry_new(); if (entry == NULL) lafe_errc(1, 0, "Couldn't allocate entry"); archive_entry_copy_sourcepath(entry, srcpath); r = archive_read_disk_entry_from_file(cpio->archive_read_disk, entry, -1, NULL); if (r < ARCHIVE_FAILED) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive_read_disk)); if (r < ARCHIVE_OK) lafe_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) { archive_entry_set_uid(entry, cpio->uid_override); archive_entry_set_uname(entry, cpio->uname_override); } if (cpio->gid_override >= 0) { archive_entry_set_gid(entry, cpio->gid_override); archive_entry_set_gname(entry, cpio->gname_override); } /* * Generate a destination path for this entry. * "destination path" is the name to which it will be copied in * pass mode or the name that will go into the archive in * output mode. */ destpath = srcpath; if (cpio->destdir) { len = strlen(cpio->destdir) + strlen(srcpath) + 8; if (len >= cpio->pass_destpath_alloc) { while (len >= cpio->pass_destpath_alloc) { cpio->pass_destpath_alloc += 512; cpio->pass_destpath_alloc *= 2; } free(cpio->pass_destpath); cpio->pass_destpath = malloc(cpio->pass_destpath_alloc); if (cpio->pass_destpath == NULL) lafe_errc(1, ENOMEM, "Can't allocate path buffer"); } strcpy(cpio->pass_destpath, cpio->destdir); strcat(cpio->pass_destpath, remove_leading_slash(srcpath)); destpath = cpio->pass_destpath; } if (cpio->option_rename) destpath = cpio_rename(destpath); if (destpath == NULL) return (0); archive_entry_copy_pathname(entry, destpath); /* * If we're trying to preserve hardlinks, match them here. */ spare = NULL; if (cpio->linkresolver != NULL && 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); } } return (r); } static int entry_to_archive(struct cpio *cpio, struct archive_entry *entry) { const char *destpath = archive_entry_pathname(entry); const char *srcpath = archive_entry_sourcepath(entry); int fd = -1; ssize_t bytes_read; int r; /* 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 * regular files. Also note: if a link operation fails * because of cross-device restrictions, we'll fall back * to copy mode for that entry. * * TODO: Test other cpio implementations to see if they * hard-link anything other than regular files here. */ if (cpio->option_link && archive_entry_filetype(entry) == AE_IFREG) { struct archive_entry *t; /* Save the original entry in case we need it later. */ t = archive_entry_clone(entry); if (t == NULL) lafe_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. */ archive_entry_set_hardlink(t, srcpath); /* This is a straight link that carries no data. */ archive_entry_set_size(t, 0); r = archive_write_header(cpio->archive, t); archive_entry_free(t); if (r != ARCHIVE_OK) lafe_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. */ lafe_warnc(0, "Copying file instead"); } else #endif return (0); } /* * Make sure we can open the file (if necessary) before * trying to write the header. */ if (archive_entry_filetype(entry) == AE_IFREG) { if (archive_entry_size(entry) > 0) { fd = open(srcpath, O_RDONLY | O_BINARY); if (fd < 0) { lafe_warnc(errno, "%s: could not open file", srcpath); goto cleanup; } } } else { archive_entry_set_size(entry, 0); } r = archive_write_header(cpio->archive, entry); if (r != ARCHIVE_OK) lafe_warnc(archive_errno(cpio->archive), "%s: %s", srcpath, archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) exit(1); if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) { bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); while (bytes_read > 0) { ssize_t bytes_write; bytes_write = archive_write_data(cpio->archive, cpio->buff, bytes_read); if (bytes_write < 0) lafe_errc(1, archive_errno(cpio->archive), "%s", archive_error_string(cpio->archive)); if (bytes_write < bytes_read) { lafe_warnc(0, "Truncated write; file may have " "grown while being archived."); } bytes_read = read(fd, cpio->buff, (unsigned)cpio->buff_size); } } fd = restore_time(cpio, entry, srcpath, fd); cleanup: if (cpio->verbose) fprintf(stderr,"\n"); if (fd >= 0) close(fd); return (0); } static int restore_time(struct cpio *cpio, struct archive_entry *entry, const char *name, int fd) { #ifndef HAVE_UTIMES static int warned = 0; (void)cpio; /* UNUSED */ (void)entry; /* UNUSED */ (void)name; /* UNUSED */ if (!warned) lafe_warnc(0, "Can't restore access times on this platform"); warned = 1; return (fd); #else #if defined(_WIN32) && !defined(__CYGWIN__) struct __timeval times[2]; #else struct timeval times[2]; #endif if (!cpio->option_atime_restore) return (fd); times[1].tv_sec = archive_entry_mtime(entry); times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000; times[0].tv_sec = archive_entry_atime(entry); times[0].tv_usec = archive_entry_atime_nsec(entry) / 1000; #if defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (fd >= 0 && futimes(fd, times) == 0) return (fd); #endif /* * Some platform cannot restore access times if the file descriptor * is still opened. */ if (fd >= 0) { close(fd); fd = -1; } #ifdef HAVE_LUTIMES if (lutimes(name, times) != 0) #else if ((AE_IFLNK != archive_entry_filetype(entry)) && utimes(name, times) != 0) #endif lafe_warnc(errno, "Can't update time for %s", name); #endif return (fd); } static void mode_in(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; struct archive *ext; const char *destpath; int r; ext = archive_write_disk_new(); if (ext == NULL) lafe_errc(1, 0, "Couldn't allocate restore object"); r = archive_write_disk_set_options(ext, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_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) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->option_rename) { destpath = cpio_rename(archive_entry_pathname(entry)); archive_entry_set_pathname(entry, destpath); } else destpath = archive_entry_pathname(entry); if (destpath == NULL) continue; if (cpio->verbose) fprintf(stderr, "%s\n", destpath); if (cpio->dot) fprintf(stderr, "."); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) archive_entry_set_gid(entry, cpio->gid_override); r = archive_write_header(ext, entry); if (r != ARCHIVE_OK) { fprintf(stderr, "%s: %s\n", archive_entry_pathname(entry), archive_error_string(ext)); } else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) { 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) lafe_errc(1, 0, "%s", archive_error_string(a)); r = archive_write_close(ext); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(ext)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); archive_write_free(ext); exit(cpio->return_value); } /* * Exits if there's a fatal error. Returns ARCHIVE_OK * if everything is kosher. */ static int extract_data(struct archive *ar, struct archive *aw) { int r; size_t size; const void *block; int64_t offset; for (;;) { r = archive_read_data_block(ar, &block, &size, &offset); if (r == ARCHIVE_EOF) return (ARCHIVE_OK); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(ar), "%s", archive_error_string(ar)); exit(1); } r = (int)archive_write_data_block(aw, block, size, offset); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(aw), "%s", archive_error_string(aw)); return (r); } } } static void mode_list(struct cpio *cpio) { struct archive *a; struct archive_entry *entry; int r; a = archive_read_new(); if (a == NULL) lafe_errc(1, 0, "Couldn't allocate archive object"); archive_read_support_filter_all(a); archive_read_support_format_all(a); if (cpio->passphrase != NULL) r = archive_read_add_passphrase(a, cpio->passphrase); else r = archive_read_set_passphrase_callback(a, cpio, &passphrase_callback); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, cpio->filename, cpio->bytes_per_block)) lafe_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) { lafe_errc(1, archive_errno(a), "%s", archive_error_string(a)); } if (archive_match_path_excluded(cpio->matching, entry)) continue; if (cpio->verbose) list_item_verbose(cpio, entry); else fprintf(stdout, "%s\n", archive_entry_pathname(entry)); } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(a)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(a, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_read_free(a); exit(0); } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ static void list_item_verbose(struct cpio *cpio, struct archive_entry *entry) { char size[32]; char date[32]; char uids[16], gids[16]; const char *uname, *gname; FILE *out = stdout; const char *fmt; time_t mtime; static time_t now; if (!now) time(&now); if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); uname = uids; strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ uname = archive_entry_uname(entry); if (uname == NULL) uname = lookup_uname(cpio, (uid_t)archive_entry_uid(entry)); /* Use gname if it's present, else lookup name from gid. */ gname = archive_entry_gname(entry); if (gname == NULL) gname = lookup_gname(cpio, (uid_t)archive_entry_gid(entry)); } /* Print device number or file size. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { snprintf(size, sizeof(size), "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(size, cpio_i64toa(archive_entry_size(entry))); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ 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 (mtime - now > 365*86400/2 || mtime - now < -365*86400/2) 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(&mtime)); fprintf(out, "%s%3d %-8s %-8s %8s %12s %s", archive_entry_strmode(entry), archive_entry_nlink(entry), uname, gname, size, date, archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ fprintf(out, " -> %s", archive_entry_symlink(entry)); fprintf(out, "\n"); } static void mode_pass(struct cpio *cpio, const char *destdir) { struct lafe_line_reader *lr; const char *p; int r; /* Ensure target dir has a trailing '/' to simplify path surgery. */ cpio->destdir = malloc(strlen(destdir) + 8); strcpy(cpio->destdir, destdir); if (destdir[strlen(destdir) - 1] != '/') strcat(cpio->destdir, "/"); cpio->archive = archive_write_disk_new(); if (cpio->archive == NULL) lafe_errc(1, 0, "Failed to allocate archive object"); r = archive_write_disk_set_options(cpio->archive, cpio->extract_flags); if (r != ARCHIVE_OK) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); cpio->linkresolver = archive_entry_linkresolver_new(); archive_write_disk_set_standard_lookup(cpio->archive); cpio->archive_read_disk = archive_read_disk_new(); if (cpio->archive_read_disk == NULL) lafe_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 = lafe_line_reader("-", cpio->option_null); while ((p = lafe_line_reader_next(lr)) != NULL) file_to_archive(cpio, p); lafe_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) lafe_errc(1, 0, "%s", archive_error_string(cpio->archive)); if (!cpio->quiet) { int64_t blocks = (archive_filter_bytes(cpio->archive, 0) + 511) / 512; fprintf(stderr, "%lu %s\n", (unsigned long)blocks, blocks == 1 ? "block" : "blocks"); } archive_write_free(cpio->archive); } /* * Prompt for a new name for this entry. Returns a pointer to the * new name or NULL if the entry should not be copied. This * implements the semantics defined in POSIX.1-1996, which specifies * that an input of '.' means the name should be unchanged. GNU cpio * treats '.' as a literal new name. */ static const char * cpio_rename(const char *name) { static char buff[1024]; FILE *t; char *p, *ret; #if defined(_WIN32) && !defined(__CYGWIN__) FILE *to; t = fopen("CONIN$", "r"); if (t == NULL) return (name); to = fopen("CONOUT$", "w"); if (to == NULL) { fclose(t); return (name); } fprintf(to, "%s (Enter/./(new name))? ", name); fclose(to); #else t = fopen("/dev/tty", "r+"); if (t == NULL) return (name); fprintf(t, "%s (Enter/./(new name))? ", name); fflush(t); #endif p = fgets(buff, sizeof(buff), t); fclose(t); if (p == NULL) /* End-of-file is a blank line. */ return (NULL); while (*p == ' ' || *p == '\t') ++p; if (*p == '\n' || *p == '\0') /* Empty line. */ return (NULL); if (*p == '.' && p[1] == '\n') /* Single period preserves original name. */ return (name); ret = p; /* Trim the final newline. */ while (*p != '\0' && *p != '\n') ++p; /* Overwrite the final \n with a null character. */ *p = '\0'; return (ret); } static void free_cache(struct name_cache *cache) { size_t i; if (cache != NULL) { for (i = 0; i < cache->size; i++) free(cache->cache[i].name); free(cache); } } /* * Lookup uname/gname from uid/gid, return NULL if no match. */ static const char * lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable, int (*lookup_fn)(struct cpio *, const char **, id_t), id_t id) { char asnum[16]; struct name_cache *cache; const char *name; int slot; if (*name_cache_variable == NULL) { *name_cache_variable = malloc(sizeof(struct name_cache)); if (*name_cache_variable == NULL) lafe_errc(1, ENOMEM, "No more memory"); memset(*name_cache_variable, 0, sizeof(struct name_cache)); (*name_cache_variable)->size = name_cache_size; } cache = *name_cache_variable; cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { cache->hits++; return (cache->cache[slot].name); } free(cache->cache[slot].name); cache->cache[slot].name = NULL; } if (lookup_fn(cpio, &name, id) == 0) { if (name == NULL || name[0] == '\0') { /* If lookup failed, format it as a number. */ snprintf(asnum, sizeof(asnum), "%u", (unsigned)id); name = asnum; } cache->cache[slot].name = strdup(name); if (cache->cache[slot].name != NULL) { cache->cache[slot].id = id; return (cache->cache[slot].name); } /* * Conveniently, NULL marks an empty slot, so * if the strdup() fails, we've just failed to * cache it. No recovery necessary. */ } return (NULL); } static const char * lookup_uname(struct cpio *cpio, uid_t uid) { return (lookup_name(cpio, &cpio->uname_cache, &lookup_uname_helper, (id_t)uid)); } static int lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) { struct passwd *pwent; (void)cpio; /* UNUSED */ errno = 0; pwent = getpwuid((uid_t)id); if (pwent == NULL) { *name = NULL; if (errno != 0 && errno != ENOENT) lafe_warnc(errno, "getpwuid(%s) failed", cpio_i64toa((int64_t)id)); return (errno); } *name = pwent->pw_name; return (0); } static const char * lookup_gname(struct cpio *cpio, gid_t gid) { return (lookup_name(cpio, &cpio->gname_cache, &lookup_gname_helper, (id_t)gid)); } static int lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) { struct group *grent; (void)cpio; /* UNUSED */ errno = 0; grent = getgrgid((gid_t)id); if (grent == NULL) { *name = NULL; if (errno != 0) lafe_warnc(errno, "getgrgid(%s) failed", cpio_i64toa((int64_t)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; } #define PPBUFF_SIZE 1024 static const char * passphrase_callback(struct archive *a, void *_client_data) { struct cpio *cpio = (struct cpio *)_client_data; (void)a; /* UNUSED */ if (cpio->ppbuff == NULL) { cpio->ppbuff = malloc(PPBUFF_SIZE); if (cpio->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", cpio->ppbuff, PPBUFF_SIZE); } static void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } Index: vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c =================================================================== --- vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c (revision 299894) +++ vendor/libarchive/dist/libarchive/archive_read_support_format_cpio.c (revision 299895) @@ -1,1074 +1,1079 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #define bin_magic_offset 0 #define bin_magic_size 2 #define bin_dev_offset 2 #define bin_dev_size 2 #define bin_ino_offset 4 #define bin_ino_size 2 #define bin_mode_offset 6 #define bin_mode_size 2 #define bin_uid_offset 8 #define bin_uid_size 2 #define bin_gid_offset 10 #define bin_gid_size 2 #define bin_nlink_offset 12 #define bin_nlink_size 2 #define bin_rdev_offset 14 #define bin_rdev_size 2 #define bin_mtime_offset 16 #define bin_mtime_size 4 #define bin_namesize_offset 20 #define bin_namesize_size 2 #define bin_filesize_offset 22 #define bin_filesize_size 4 #define bin_header_size 26 #define odc_magic_offset 0 #define odc_magic_size 6 #define odc_dev_offset 6 #define odc_dev_size 6 #define odc_ino_offset 12 #define odc_ino_size 6 #define odc_mode_offset 18 #define odc_mode_size 6 #define odc_uid_offset 24 #define odc_uid_size 6 #define odc_gid_offset 30 #define odc_gid_size 6 #define odc_nlink_offset 36 #define odc_nlink_size 6 #define odc_rdev_offset 42 #define odc_rdev_size 6 #define odc_mtime_offset 48 #define odc_mtime_size 11 #define odc_namesize_offset 59 #define odc_namesize_size 6 #define odc_filesize_offset 65 #define odc_filesize_size 11 #define odc_header_size 76 #define newc_magic_offset 0 #define newc_magic_size 6 #define newc_ino_offset 6 #define newc_ino_size 8 #define newc_mode_offset 14 #define newc_mode_size 8 #define newc_uid_offset 22 #define newc_uid_size 8 #define newc_gid_offset 30 #define newc_gid_size 8 #define newc_nlink_offset 38 #define newc_nlink_size 8 #define newc_mtime_offset 46 #define newc_mtime_size 8 #define newc_filesize_offset 54 #define newc_filesize_size 8 #define newc_devmajor_offset 62 #define newc_devmajor_size 8 #define newc_devminor_offset 70 #define newc_devminor_size 8 #define newc_rdevmajor_offset 78 #define newc_rdevmajor_size 8 #define newc_rdevminor_offset 86 #define newc_rdevminor_size 8 #define newc_namesize_offset 94 #define newc_namesize_size 8 #define newc_checksum_offset 102 #define newc_checksum_size 8 #define newc_header_size 110 /* * An afio large ASCII header, which they named itself. * afio utility uses this header, if a file size is larger than 2G bytes * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than * 0x7fffffff, which we cannot record to odc header because of its limit. * If not, uses odc header. */ #define afiol_magic_offset 0 #define afiol_magic_size 6 #define afiol_dev_offset 6 #define afiol_dev_size 8 /* hex */ #define afiol_ino_offset 14 #define afiol_ino_size 16 /* hex */ #define afiol_ino_m_offset 30 /* 'm' */ #define afiol_mode_offset 31 #define afiol_mode_size 6 /* oct */ #define afiol_uid_offset 37 #define afiol_uid_size 8 /* hex */ #define afiol_gid_offset 45 #define afiol_gid_size 8 /* hex */ #define afiol_nlink_offset 53 #define afiol_nlink_size 8 /* hex */ #define afiol_rdev_offset 61 #define afiol_rdev_size 8 /* hex */ #define afiol_mtime_offset 69 #define afiol_mtime_size 16 /* hex */ #define afiol_mtime_n_offset 85 /* 'n' */ #define afiol_namesize_offset 86 #define afiol_namesize_size 4 /* hex */ #define afiol_flag_offset 90 #define afiol_flag_size 4 /* hex */ #define afiol_xsize_offset 94 #define afiol_xsize_size 4 /* hex */ #define afiol_xsize_s_offset 98 /* 's' */ #define afiol_filesize_offset 99 #define afiol_filesize_size 16 /* hex */ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 struct links_entry { struct links_entry *next; struct links_entry *previous; int links; dev_t dev; int64_t ino; char *name; }; #define CPIO_MAGIC 0x13141516 struct cpio { int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; int64_t entry_bytes_remaining; int64_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *, int); static int archive_read_format_cpio_options(struct archive_read *, const char *, const char *); static int archive_read_format_cpio_cleanup(struct archive_read *); static int archive_read_format_cpio_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); static int64_t be4(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_bin_le(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int64_t le4(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); int archive_read_support_format_cpio(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cpio *cpio; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cpio"); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, "cpio", archive_read_format_cpio_bid, archive_read_format_cpio_options, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, archive_read_format_cpio_skip, NULL, archive_read_format_cpio_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cpio); return (ARCHIVE_OK); } static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { const unsigned char *p; struct cpio *cpio; int bid; (void)best_bid; /* UNUSED */ cpio = (struct cpio *)(a->format->data); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); bid = 0; if (memcmp(p, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070727", 6) == 0) { /* afio large ASCII cpio archive */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that almost hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (p[0] * 256 + p[1] == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ } else if (p[0] + p[1] * 256 == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; /* Is more verification possible here? */ } else return (ARCHIVE_WARN); return (bid); } static int archive_read_format_cpio_options(struct archive_read *a, const char *key, const char *val) { struct cpio *cpio; int ret = ARCHIVE_FAILED; cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filnames as libarchive 2.x */ cpio->init_default_conversion = (val != NULL)?1:0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cpio: hdrcharset option needs a character-set name"); else { cpio->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; int r; cpio = (struct cpio *)(a->format->data); sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, (const char *)h, namelength, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname can't be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } cpio->entry_offset = 0; __archive_read_consume(a, namelength + name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { + if (cpio->entry_bytes_remaining > 1024 * 1024) { + archive_set_error(&a->archive, ENOMEM, + "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); + return (ARCHIVE_FATAL); + } h = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_symlink_l(entry, (const char *)h, (size_t)cpio->entry_bytes_remaining, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname can't be converted from %s to " "current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } __archive_read_consume(a, cpio->entry_bytes_remaining); cpio->entry_bytes_remaining = 0; } /* XXX TODO: If the full mode is 0160200, then this is a Solaris * ACL description for the following entry. Read this body * and parse it as a Solaris-style ACL, then read the next * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } return (r); } static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct cpio *cpio; cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_unconsumed) { __archive_read_consume(a, cpio->entry_bytes_unconsumed); cpio->entry_bytes_unconsumed = 0; } if (cpio->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) bytes_read = (ssize_t)cpio->entry_bytes_remaining; *size = bytes_read; cpio->entry_bytes_unconsumed = bytes_read; *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { if (cpio->entry_padding != __archive_read_consume(a, cpio->entry_padding)) { return (ARCHIVE_FATAL); } cpio->entry_padding = 0; *buff = NULL; *size = 0; *offset = cpio->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_cpio_skip(struct archive_read *a) { struct cpio *cpio = (struct cpio *)(a->format->data); int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding + cpio->entry_bytes_unconsumed; if (to_skip != __archive_read_consume(a, to_skip)) { return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = 0; cpio->entry_padding = 0; cpio->entry_bytes_unconsumed = 0; return (ARCHIVE_OK); } /* * Skip forward to the next cpio newc header by searching for the * 07070[12] string. This should be generalized and merged with * find_odc_header below. */ static int is_hex(const char *p, size_t len) { while (len-- > 0) { if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) ++p; else return (0); } return (1); } static int find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, newc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("07070", p, 5) == 0 && (p[5] == '1' || p[5] == '2') && is_hex(p, newc_header_size)) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like a newc header. */ while (p + newc_header_size <= q) { switch (p[5]) { case '1': case '2': if (memcmp("07070", p, 5) == 0 && is_hex(p, newc_header_size)) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_newc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; int r; r = find_newc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, newc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out hex fields. */ header = (const char *)h; if (memcmp(header + newc_magic_offset, "070701", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; } else { /* TODO: Abort here? */ } archive_entry_set_devmajor(entry, (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size)); archive_entry_set_devminor(entry, (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size)); archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol16(header + newc_mode_offset, newc_mode_size)); archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size)); archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size)); archive_entry_set_rdevmajor(entry, (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size)); archive_entry_set_rdevminor(entry, (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size)); archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0); *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size); /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol16(header + newc_filesize_offset, newc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); /* Pad file contents to a multiple of 4. */ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; __archive_read_consume(a, newc_header_size); return (r); } /* * Skip forward to the next cpio odc header by searching for the * 070707 string. This is a hand-optimized search that could * probably be easily generalized to handle all character-based * cpio variants. */ static int is_octal(const char *p, size_t len) { while (len-- > 0) { if (*p < '0' || *p > '7') return (0); ++p; } return (1); } static int is_afio_large(const char *h, size_t len) { if (len < afiol_header_size) return (0); if (h[afiol_ino_m_offset] != 'm' || h[afiol_mtime_n_offset] != 'n' || h[afiol_xsize_s_offset] != 's' || h[afiol_filesize_c_offset] != ':') return (0); if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset)) return (0); if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset)) return (0); if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset)) return (0); if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size)) return (0); return (1); } static int find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, odc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) return (ARCHIVE_OK); if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; return (ARCHIVE_OK); } /* * Scan ahead until we find something that looks * like an odc header. */ while (p + odc_header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) || (memcmp("070727", p, 6) == 0 && is_afio_large(p, q - p))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (p[4] == '2') a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_odc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; int r; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; /* Find the start of the next header. */ r = find_odc_header(a); if (r < ARCHIVE_WARN) return (r); if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) { int r2 = (header_afiol(a, cpio, entry, namelength, name_pad)); if (r2 == ARCHIVE_OK) return (r); else return (r2); } /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, odc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol8(header + odc_dev_offset, odc_dev_size)); archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + odc_mode_offset, odc_mode_size)); archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size)); archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size)); archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0); *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size); *name_pad = 0; /* No padding of filename. */ /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol8(header + odc_filesize_offset, odc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, odc_header_size); return (r); } /* * NOTE: if a filename suffix is ".z", it is the file gziped by afio. * it would be nice that we can show uncompressed file size and we can * uncompressed file contents automatically, unfortunately we have nothing * to get a uncompressed file size while reading each header. it means * we also cannot uncompressed file contens under the our framework. */ static int header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; a->archive.archive_format_name = "afio large ASCII"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, afiol_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size)); archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0); *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size); *name_pad = 0; /* No padding of filename. */ cpio->entry_bytes_remaining = atol16(header + afiol_filesize_offset, afiol_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, afiol_header_size); return (ARCHIVE_OK); } static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int header_bin_be(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; if (cpio->links_head->name) free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } free(cpio); (a->format->data) = NULL; return (ARCHIVE_OK); } static int64_t le4(const unsigned char *p) { return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); } static int64_t be4(const unsigned char *p) { return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t atol8(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else return (l); p++; l <<= 3; l |= digit; } return (l); } static int64_t atol16(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10; else if (*p >= '0' && *p <= '9') digit = *p - '0'; else return (l); p++; l <<= 4; l |= digit; } return (l); } static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; int64_t ino; if (archive_entry_nlink(entry) <= 1) return (ARCHIVE_OK); dev = archive_entry_dev(entry); ino = archive_entry_ino64(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return (ARCHIVE_OK); } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; le->name = strdup(archive_entry_pathname(entry)); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); }