From 8029ab021872414134d341d74c6d4f5e15dbd404 Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Wed, 24 Jun 2009 11:29:09 -1000 Subject: [PATCH] Upgrade to libarchive-2.7.0. --- contrib/libarchive/NEWS | 76 +- contrib/libarchive/README | 22 +- contrib/libarchive/README.DELETED | 24 +- contrib/libarchive/build/version | 1 + contrib/libarchive/cpio/bsdcpio.1 | 9 + contrib/libarchive/cpio/cmdline.c | 286 +++-- contrib/libarchive/cpio/cpio.c | 414 ++++++- contrib/libarchive/cpio/cpio.h | 15 +- contrib/libarchive/cpio/cpio_platform.h | 20 +- contrib/libarchive/cpio/pathmatch.c | 65 +- contrib/libarchive/cpio/pathmatch.h | 5 + contrib/libarchive/libarchive/COPYING | 36 + contrib/libarchive/{ => libarchive}/README | 74 +- contrib/libarchive/libarchive/archive.h | 214 +++- contrib/libarchive/libarchive/archive.h.in | 542 -------- .../libarchive/archive_check_magic.c | 12 +- .../libarchive/libarchive/archive_endian.h | 20 +- contrib/libarchive/libarchive/archive_entry.c | 170 ++- contrib/libarchive/libarchive/archive_entry.h | 51 +- .../libarchive/archive_entry_copy_stat.c | 20 +- .../libarchive/archive_entry_link_resolver.c | 6 +- .../libarchive/archive_entry_private.h | 15 +- .../libarchive/archive_entry_stat.c | 20 +- .../libarchive/libarchive/archive_platform.h | 15 +- .../libarchive/libarchive/archive_private.h | 23 +- contrib/libarchive/libarchive/archive_read.3 | 62 + contrib/libarchive/libarchive/archive_read.c | 778 +++++++++--- .../libarchive/libarchive/archive_read_disk.3 | 308 +++++ .../libarchive/libarchive/archive_read_disk.c | 198 +++ .../archive_read_disk_entry_from_file.c | 548 ++++++++ .../archive_read_disk_private.h} | 33 +- .../archive_read_disk_set_standard_lookup.c | 270 ++++ .../libarchive/archive_read_open_fd.c | 59 +- .../libarchive/archive_read_open_file.c | 42 +- .../libarchive/archive_read_open_filename.c | 167 +-- .../libarchive/archive_read_private.h | 151 ++- .../archive_read_support_compression_all.c | 21 +- .../archive_read_support_compression_bzip2.c | 480 +++---- ...rchive_read_support_compression_compress.c | 257 ++-- .../archive_read_support_compression_gzip.c | 695 +++++------ .../archive_read_support_compression_none.c | 340 +---- ...archive_read_support_compression_program.c | 516 +++++--- .../archive_read_support_compression_xz.c | 642 ++++++++++ .../archive_read_support_format_ar.c | 75 +- .../archive_read_support_format_cpio.c | 84 +- .../archive_read_support_format_empty.c | 10 +- .../archive_read_support_format_iso9660.c | 785 +++++++++--- .../archive_read_support_format_mtree.c | 86 +- .../archive_read_support_format_tar.c | 114 +- .../archive_read_support_format_zip.c | 215 ++-- .../libarchive/libarchive/archive_string.c | 330 +++-- .../libarchive/libarchive/archive_string.h | 19 +- .../libarchive/archive_string_sprintf.c | 36 +- contrib/libarchive/libarchive/archive_util.c | 216 +++- .../libarchive/libarchive/archive_virtual.c | 18 +- contrib/libarchive/libarchive/archive_write.3 | 82 +- contrib/libarchive/libarchive/archive_write.c | 113 +- .../libarchive/archive_write_disk.3 | 6 +- .../libarchive/archive_write_disk.c | 658 +++++++--- .../archive_write_disk_set_standard_lookup.c | 60 +- .../libarchive/archive_write_open_filename.c | 47 +- .../libarchive/archive_write_private.h | 5 + .../archive_write_set_compression_bzip2.c | 207 ++-- .../archive_write_set_compression_gzip.c | 254 ++-- .../archive_write_set_compression_program.c | 4 +- .../archive_write_set_compression_xz.c | 439 +++++++ .../libarchive/archive_write_set_format.c | 3 +- .../libarchive/archive_write_set_format_ar.c | 6 +- .../archive_write_set_format_by_name.c | 5 +- .../archive_write_set_format_cpio.c | 1 + .../archive_write_set_format_cpio_newc.c | 1 + .../archive_write_set_format_mtree.c | 1101 +++++++++++++++++ .../libarchive/archive_write_set_format_pax.c | 31 +- .../archive_write_set_format_shar.c | 448 ++++--- .../archive_write_set_format_ustar.c | 1 + contrib/libarchive/libarchive/filter_fork.c | 16 +- contrib/libarchive/tar/COPYING | 62 + contrib/libarchive/tar/bsdtar.1 | 138 ++- contrib/libarchive/tar/bsdtar.c | 418 ++----- contrib/libarchive/tar/bsdtar.h | 41 +- contrib/libarchive/tar/bsdtar_platform.h | 33 +- contrib/libarchive/tar/cmdline.c | 380 ++++++ contrib/libarchive/tar/getdate.c | 1050 ++++++++++++++++ contrib/libarchive/tar/getdate.y | 811 ------------ contrib/libarchive/tar/matching.c | 48 +- contrib/libarchive/tar/read.c | 40 +- contrib/libarchive/tar/siginfo.c | 4 + contrib/libarchive/tar/subst.c | 2 +- contrib/libarchive/tar/tree.c | 76 +- contrib/libarchive/tar/tree.h | 36 +- contrib/libarchive/tar/util.c | 309 +++-- contrib/libarchive/tar/write.c | 833 ++++--------- contrib/libarchive/version | 1 - 93 files changed, 11665 insertions(+), 5815 deletions(-) create mode 100644 contrib/libarchive/build/version create mode 100644 contrib/libarchive/libarchive/COPYING copy contrib/libarchive/{ => libarchive}/README (56%) delete mode 100644 contrib/libarchive/libarchive/archive.h.in create mode 100644 contrib/libarchive/libarchive/archive_read_disk.3 create mode 100644 contrib/libarchive/libarchive/archive_read_disk.c create mode 100644 contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c copy contrib/libarchive/{cpio/pathmatch.h => libarchive/archive_read_disk_private.h} (59%) create mode 100644 contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c create mode 100644 contrib/libarchive/libarchive/archive_read_support_compression_xz.c create mode 100644 contrib/libarchive/libarchive/archive_write_set_compression_xz.c create mode 100644 contrib/libarchive/libarchive/archive_write_set_format_mtree.c create mode 100644 contrib/libarchive/tar/COPYING create mode 100644 contrib/libarchive/tar/cmdline.c create mode 100644 contrib/libarchive/tar/getdate.c delete mode 100644 contrib/libarchive/tar/getdate.y delete mode 100644 contrib/libarchive/version diff --git a/contrib/libarchive/NEWS b/contrib/libarchive/NEWS index 36005fd1e9..de617c3335 100644 --- a/contrib/libarchive/NEWS +++ b/contrib/libarchive/NEWS @@ -1,4 +1,78 @@ +Apr 16, 2009: libarchive 2.7.0 released + +Apr 10, 2009: libarchive 2.6.992a released +Apr 09, 2009: Fix SIGPIPE issue building with MSVC. +Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test + +Apr 08, 2009: libarchive 2.6.991a released +Apr 07, 2009: Additional tests added to bsdcpio_test + +Apr 01, 2009: libarchive 2.6.990a released +Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for + decompression if the library is built without suitable + libraries. The setup functions return ARCHIVE_WARN + in this case so clients can adapt if necessary. +Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety. +Mar 24, 2009: Add archive_read_next_header2(), which is up to 25% + more efficient for some clients; from Brian Harring. +Mar 22, 2009: PDF versions of manpages are now included in the distribution. +Mar, 2009: Major work to improve Cygwin build by Charles Wilson. +Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA. +Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA. + All tests now pass. +Feb 25, 2009: Fix Debian Bug #516577 +Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C. +Jan/Feb, 2009: Mtree work by Michihiro. +Feb, 2009: Joliet support by Andreas Henriksson. +Jan/Feb, 2009: New options framework by Michihiro. +Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke. +Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format. +Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs, + extended attributes, etc so that bsdtar and bsdcpio don't require + such system-specific knowledge. +Jan 03, 2009: Read filter system extensively refactored. In particular, + read filter pipelines are now built out automatically and individual + filters should be much easier to implement. Documentation on the + Googlecode Wiki explains how to implement new filters. +Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA. + +Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce + server to Google Code. This should make it easier for more + people to participate in libarchive development. + +Dec 28, 2008: libarchive 2.6.0 released +Dec 25, 2008: libarchive 2.5.905a released +Dec 10, 2008: libarchive 2.5.904a released +Dec 04, 2008: libarchive 2.5.903a released +Nov 09, 2008: libarchive 2.5.902a released +Nov 08, 2008: libarchive 2.5.901a released +Nov 08, 2008: Start of pre-release testing for libarchive 2.6 + +Nov 07, 2008: Read filter refactor: The decompression routines just + consume and produce arbitrarily-sized blocks. The reblocking + from read_support_compression_none() has been pulled into the + read core. Also, the decompression bid now makes multiple + passes and stacks read filters. +Oct 21, 2008: bsdcpio: New command-line parser. +Oct 19, 2008: Internal read_ahead change: short reads are now an error +Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(), + gives consistent option parsing on all platforms. +Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive +Sep 17, 2008: Pedro Giffuni: birthtime support +Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have + some concerns about the auto-detection (LZMA file format + doesn't support auto-detection well), so this is not yet + enabled under archive_read_support_compression_all(). For + now, you must call archive_read_support_compression_lzma() if + you want LZMA read support. +Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files +Jul 26, 2008: archive_entry now tracks which values have not been set. + This helps zip extraction (file size is often "unknown") and + time restores (tar usually doesn't know atime). +Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer +Jul 25, 2008: Joerg Sonnenberger: mtree write support + Jul 02, 2008: libarchive 2.5.5 released Jul 02, 2008: libarchive 2.5.5b released @@ -323,7 +397,7 @@ Feb 27, 2007: Make the GID restore checks more robust by checking Feb 26, 2007: libarchive 2.0b15 released Feb 26, 2007: Don't lose symlinks when extracting from ISOs. - Thanks to Diego "Flameeyes" Pettenò for telling me about the + Thanks to Diego "Flameeyes" Pettenò for telling me about the broken testcase on Gentoo that (finally!) led me to the cause of this long-standing bug. diff --git a/contrib/libarchive/README b/contrib/libarchive/README index 50a9d628f3..07fb359e83 100644 --- a/contrib/libarchive/README +++ b/contrib/libarchive/README @@ -1,5 +1,10 @@ README for libarchive bundle. +Questions? Issues? + * http://libarchive.googlecode.com/ is the home for ongoing + libarchive development, including issue tracker, additional + documentation, and links to the libarchive mailing lists. + This distribution bundle includes the following components: * libarchive: a library for reading and writing streaming archives @@ -20,6 +25,7 @@ The top-level directory contains the following information files: * INSTALL - installation instructions * README - this file * configure - configuration script, see INSTALL for details. + * CMakeLists.txt - input for "cmake" build tool, see INSTALL The following files in the top-level directory are used by the 'configure' script: @@ -28,7 +34,6 @@ The following files in the top-level directory are used by the - used to build this distribution, only needed by maintainers * Makefile.in, config.h.in - templates used by configure script - * config.aux/* - auxiliary scripts used by build system Guide to Documentation installed by this system: * bsdtar.1 explains the use of the bsdtar program @@ -52,6 +57,7 @@ Currently, the library automatically detects and reads the following: * gzip compression * bzip2 compression * compress/LZW compression + * lzma and xz compression * GNU tar format (including GNU long filenames, long link names, and sparse files) * Solaris 9 extended tar format (including ACLs) @@ -61,7 +67,7 @@ Currently, the library automatically detects and reads the following: * POSIX octet-oriented cpio * SVR4 ASCII cpio * Binary cpio (big-endian or little-endian) - * ISO9660 CD-ROM images (with optional Rockridge extensions) + * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions) * ZIP archives (with uncompressed or "deflate" compressed entries) * GNU and BSD 'ar' archives * 'mtree' format @@ -70,6 +76,7 @@ The library can write: * gzip compression * bzip2 compression * compress/LZW compression + * lzma and xz compression * POSIX ustar * POSIX pax interchange format * "restricted" pax format, which will create ustar archives except for @@ -78,6 +85,7 @@ The library can write: * SVR4 "newc" cpio * shar archives * GNU and BSD 'ar' archives + * 'mtree' format Notes about the library architecture: @@ -103,7 +111,7 @@ Notes about the library architecture: environments where that matters. * On read, the library accepts whatever blocks you hand it. - Your read callback is free to pass the library a byte at a time[1] + Your read callback is free to pass the library a byte at a time or mmap the entire archive and give it to the library at once. On write, the library always produces correctly-blocked output. @@ -125,11 +133,3 @@ Notes about the library architecture: * Note: "pax interchange format" is really an extended tar format, despite what the name says. - -[1] Gzip and compress formats are identical in the first byte. -For that reason, the first block must be at least two bytes if -you have both of these formats enabled at read time. This is -currently the only restriction on block size. (This restriction -could be lifted by buffering the initial blocks prior to the -compression tasting step, but it doesn't really seem worth the -effort.) diff --git a/contrib/libarchive/README.DELETED b/contrib/libarchive/README.DELETED index fed87a8c0c..409636eeaf 100644 --- a/contrib/libarchive/README.DELETED +++ b/contrib/libarchive/README.DELETED @@ -1,20 +1,38 @@ +CMakeLists.txt INSTALL Makefile.am Makefile.in +PROJECTS aclocal.m4 -config.aux/ +build/autoconf/ +build/autogen.sh +build/cmake/ +build/release.sh +build/windows/ config.h.in configure configure.ac contrib/ +cpio/CMakeLists.txt +cpio/config_freebsd.h +cpio/cpio_cygwin.c +cpio/cpio_cygwin.h +cpio/cpio_windows.c +cpio/cpio_windows.h cpio/test/ doc/ examples/ +libarchive/CMakeLists.txt libarchive/archive_windows.c libarchive/archive_windows.h libarchive/config_freebsd.h libarchive/config_windows.h +libarchive/filter_fork_windows.c libarchive/test/ -tar/getdate.c +tar/CMakeLists.txt +tar/bsdtar_cygwin.c +tar/bsdtar_cygwin.h +tar/bsdtar_windows.c +tar/bsdtar_windows.h +tar/config_freebsd.h tar/test/ -windows/ diff --git a/contrib/libarchive/build/version b/contrib/libarchive/build/version new file mode 100644 index 0000000000..3a01fdf5d7 --- /dev/null +++ b/contrib/libarchive/build/version @@ -0,0 +1 @@ +2007000 diff --git a/contrib/libarchive/cpio/bsdcpio.1 b/contrib/libarchive/cpio/bsdcpio.1 index d4a1857852..81b34626bb 100644 --- a/contrib/libarchive/cpio/bsdcpio.1 +++ b/contrib/libarchive/cpio/bsdcpio.1 @@ -167,6 +167,15 @@ instead of copying. (i and p modes) Set file modification time on created files to match those in the source. +.It Fl n +(i mode, only with +.Fl t ) +Display numeric uid and gid. +By default, +.Nm +displays the user and group names when they are provided in the +archive, or looks up the user and group names in the system +password database. .It Fl O Ar file Write archive to .Ar file . diff --git a/contrib/libarchive/cpio/cmdline.c b/contrib/libarchive/cpio/cmdline.c index a602e39c17..f0033e9839 100644 --- a/contrib/libarchive/cpio/cmdline.c +++ b/contrib/libarchive/cpio/cmdline.c @@ -26,23 +26,11 @@ #include "cpio_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.3 2008/06/21 02:20:20 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.5 2008/12/06 07:30:40 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_GETOPT_LONG -#include -#else -struct option { - const char *name; - int has_arg; - int *flag; - int val; -}; -#define no_argument 0 -#define required_argument 1 -#endif #ifdef HAVE_GRP_H #include #endif @@ -60,118 +48,216 @@ struct option { #include "cpio.h" /* - * - * Option parsing routines for bsdcpio. - * - */ - - -static const char *cpio_opts = "0AaBC:F:O:cdE:f:H:hijLlmopR:rtuvW:yZz"; - -/* - * On systems that lack getopt_long, long options can be specified - * using -W longopt and -W longopt=value, e.g. "-W version" is the - * same as "--version" and "-W format=ustar" is the same as "--format - * ustar". This does not rely the GNU getopt() "W;" extension, so - * should work correctly on any system with a POSIX-compliant - * getopt(). + * Short options for cpio. Please keep this sorted. */ +static const char *short_options = "0AaBC:F:O:cdE:f:H:hijLlmnopR:rtuvW:yZz"; /* - * If you add anything, be very careful to keep this list properly - * sorted, as the -W logic below relies on it. + * Long options for cpio. Please keep this sorted. */ -static const struct option cpio_longopts[] = { - { "create", no_argument, NULL, 'o' }, - { "extract", no_argument, NULL, 'i' }, - { "file", required_argument, NULL, 'F' }, - { "format", required_argument, NULL, 'H' }, - { "help", no_argument, NULL, 'h' }, - { "insecure", no_argument, NULL, OPTION_INSECURE }, - { "link", no_argument, NULL, 'l' }, - { "list", no_argument, NULL, 't' }, - { "make-directories", no_argument, NULL, 'd' }, - { "null", no_argument, NULL, '0' }, - { "owner", required_argument, NULL, 'R' }, - { "pass-through", no_argument, NULL, 'p' }, - { "preserve-modification-time", no_argument, NULL, 'm' }, - { "quiet", no_argument, NULL, OPTION_QUIET }, - { "unconditional", no_argument, NULL, 'u' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, OPTION_VERSION }, - { NULL, 0, NULL, 0 } +static const struct option { + const char *name; + int required; /* 1 if this option requires an argument */ + int equivalent; /* Equivalent short option. */ +} cpio_longopts[] = { + { "create", 0, 'o' }, + { "extract", 0, 'i' }, + { "file", 1, 'F' }, + { "format", 1, 'H' }, + { "help", 0, 'h' }, + { "insecure", 0, OPTION_INSECURE }, + { "link", 0, 'l' }, + { "list", 0, 't' }, + { "make-directories", 0, 'd' }, + { "no-preserve-owner", 0, OPTION_NO_PRESERVE_OWNER }, + { "null", 0, '0' }, + { "numeric-uid-gid", 0, 'n' }, + { "owner", 1, 'R' }, + { "pass-through", 0, 'p' }, + { "preserve-modification-time", 0, 'm' }, + { "quiet", 0, OPTION_QUIET }, + { "unconditional", 0, 'u' }, + { "verbose", 0, 'v' }, + { "version", 0, OPTION_VERSION }, + { NULL, 0, 0 } }; /* - * Parse command-line options using system-provided getopt() or getopt_long(). - * If option is -W, then parse argument as a long option. + * I used to try to select platform-provided getopt() or + * getopt_long(), but that caused a lot of headaches. In particular, + * I couldn't consistently use long options in the test harness + * because not all platforms have getopt_long(). That in turn led to + * overuse of the -W hack in the test harness, which made it rough to + * run the test harness against GNU cpio. (I periodically run the + * test harness here against GNU cpio as a sanity-check. Yes, + * I've found a couple of bugs in GNU cpio that way.) */ int cpio_getopt(struct cpio *cpio) { - char *p, *q; - const struct option *option, *option2; - int opt; - int option_index; - size_t option_length; + enum { state_start = 0, state_next_word, state_short, state_long }; + static int state = state_start; + static char *opt_word; - option_index = -1; + const struct option *popt, *match = NULL, *match2 = NULL; + const char *p, *long_prefix = "--"; + size_t optlength; + int opt = '?'; + int required = 0; -#ifdef HAVE_GETOPT_LONG - opt = getopt_long(cpio->argc, cpio->argv, cpio_opts, - cpio_longopts, &option_index); -#else - opt = getopt(cpio->argc, cpio->argv, cpio_opts); -#endif + cpio->optarg = NULL; - /* Support long options through -W longopt=value */ - if (opt == 'W') { - p = optarg; - q = strchr(optarg, '='); - if (q != NULL) { - option_length = (size_t)(q - p); - optarg = q + 1; + /* First time through, initialize everything. */ + if (state == state_start) { + /* Skip program name. */ + ++cpio->argv; + --cpio->argc; + state = state_next_word; + } + + /* + * We're ready to look at the next word in argv. + */ + if (state == state_next_word) { + /* No more arguments, so no more options. */ + if (cpio->argv[0] == NULL) + return (-1); + /* Doesn't start with '-', so no more options. */ + if (cpio->argv[0][0] != '-') + return (-1); + /* "--" marks end of options; consume it and return. */ + if (strcmp(cpio->argv[0], "--") == 0) { + ++cpio->argv; + --cpio->argc; + return (-1); + } + /* Get next word for parsing. */ + opt_word = *cpio->argv++; + --cpio->argc; + if (opt_word[1] == '-') { + /* Set up long option parser. */ + state = state_long; + opt_word += 2; /* Skip leading '--' */ } else { - option_length = strlen(p); - optarg = NULL; + /* Set up short option parser. */ + state = state_short; + ++opt_word; /* Skip leading '-' */ } - option = cpio_longopts; - while (option->name != NULL && - (strlen(option->name) < option_length || - strncmp(p, option->name, option_length) != 0 )) { - option++; + } + + /* + * We're parsing a group of POSIX-style single-character options. + */ + if (state == state_short) { + /* Peel next option off of a group of short options. */ + opt = *opt_word++; + if (opt == '\0') { + /* End of this group; recurse to get next option. */ + state = state_next_word; + return cpio_getopt(cpio); } - if (option->name != NULL) { - option2 = option; - opt = option->val; + /* Does this option take an argument? */ + p = strchr(short_options, opt); + if (p == NULL) + return ('?'); + if (p[1] == ':') + required = 1; - /* If the first match was exact, we're done. */ - if (strncmp(p, option->name, strlen(option->name)) == 0) { - while (option->name != NULL) - option++; + /* If it takes an argument, parse that. */ + if (required) { + /* If arg is run-in, opt_word already points to it. */ + if (opt_word[0] == '\0') { + /* Otherwise, pick up the next word. */ + opt_word = *cpio->argv; + if (opt_word == NULL) { + cpio_warnc(0, + "Option -%c requires an argument", + opt); + return ('?'); + } + ++cpio->argv; + --cpio->argc; + } + if (opt == 'W') { + state = state_long; + long_prefix = "-W "; /* For clearer errors. */ } else { - /* Check if there's another match. */ - option++; - while (option->name != NULL && - (strlen(option->name) < option_length || - strncmp(p, option->name, option_length) != 0)) { - option++; + state = state_next_word; + cpio->optarg = opt_word; + } + } + } + + /* We're reading a long option, including -W long=arg convention. */ + if (state == state_long) { + /* After this long option, we'll be starting a new word. */ + state = state_next_word; + + /* Option name ends at '=' if there is one. */ + p = strchr(opt_word, '='); + if (p != NULL) { + optlength = (size_t)(p - opt_word); + cpio->optarg = (char *)(uintptr_t)(p + 1); + } else { + optlength = strlen(opt_word); + } + + /* Search the table for an unambiguous match. */ + for (popt = cpio_longopts; popt->name != NULL; popt++) { + /* Short-circuit if first chars don't match. */ + if (popt->name[0] != opt_word[0]) + continue; + /* If option is a prefix of name in table, record it.*/ + if (strncmp(opt_word, popt->name, optlength) == 0) { + match2 = match; /* Record up to two matches. */ + match = popt; + /* If it's an exact match, we're done. */ + if (strlen(popt->name) == optlength) { + match2 = NULL; /* Forget the others. */ + break; } } - if (option->name != NULL) - cpio_errc(1, 0, - "Ambiguous option %s " - "(matches both %s and %s)", - p, option2->name, option->name); + } - if (option2->has_arg == required_argument - && optarg == NULL) - cpio_errc(1, 0, - "Option \"%s\" requires argument", p); + /* Fail if there wasn't a unique match. */ + if (match == NULL) { + cpio_warnc(0, + "Option %s%s is not supported", + long_prefix, opt_word); + return ('?'); + } + if (match2 != NULL) { + cpio_warnc(0, + "Ambiguous option %s%s (matches --%s and --%s)", + long_prefix, opt_word, match->name, match2->name); + return ('?'); + } + + /* We've found a unique match; does it need an argument? */ + if (match->required) { + /* Argument required: get next word if necessary. */ + if (cpio->optarg == NULL) { + cpio->optarg = *cpio->argv; + if (cpio->optarg == NULL) { + cpio_warnc(0, + "Option %s%s requires an argument", + long_prefix, match->name); + return ('?'); + } + ++cpio->argv; + --cpio->argc; + } } else { - opt = '?'; + /* Argument forbidden: fail if there is one. */ + if (cpio->optarg != NULL) { + cpio_warnc(0, + "Option %s%s does not allow an argument", + long_prefix, match->name); + return ('?'); + } } + return (match->equivalent); } return (opt); diff --git a/contrib/libarchive/cpio/cpio.c b/contrib/libarchive/cpio/cpio.c index 76a91cf715..c62ed1166d 100644 --- a/contrib/libarchive/cpio/cpio.c +++ b/contrib/libarchive/cpio/cpio.c @@ -26,21 +26,33 @@ #include "cpio_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.4 2008/06/24 15:18:40 kientzle Exp $"); +__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_PWD_H +#include +#endif #ifdef HAVE_STDARG_H #include #endif @@ -54,20 +66,47 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.4 2008/06/24 15:18:40 kientzle E #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif #include "cpio.h" #include "matching.h" +/* Fixed size of uname/gname caches. */ +#define name_cache_size 101 + +struct name_cache { + int probes; + int hits; + size_t size; + struct { + id_t id; + char *name; + } cache[name_cache_size]; +}; + static int copy_data(struct archive *, struct archive *); 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 void restore_time(struct cpio *, struct archive_entry *, +static int restore_time(struct cpio *, struct archive_entry *, const char *, int fd); static void usage(void); static void version(void); @@ -85,12 +124,22 @@ 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. */ if (*argv == NULL) cpio_progname = "bsdcpio"; else { +#if defined(_WIN32) && !defined(__CYGWIN__) + cpio_progname = strrchr(*argv, '\\'); +#else cpio_progname = strrchr(*argv, '/'); +#endif if (cpio_progname != NULL) cpio_progname++; else @@ -105,13 +154,19 @@ main(int argc, char *argv[]) cpio->mode = '\0'; cpio->verbose = 0; cpio->compress = '\0'; - /* TODO: Implement old binary format in libarchive, use that here. */ - cpio->format = "odc"; /* Default format */ 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; - /* TODO: If run by root, set owner as well. */ + cpio->extract_flags |= ARCHIVE_EXTRACT_PERM; + cpio->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; + cpio->extract_flags |= ARCHIVE_EXTRACT_ACL; +#if defined(_WIN32) || defined(__CYGWIN__) + if (bsdcpio_is_privileged()) +#else + if (geteuid() == 0) +#endif + cpio->extract_flags |= ARCHIVE_EXTRACT_OWNER; cpio->bytes_per_block = 512; cpio->filename = NULL; @@ -130,9 +185,9 @@ main(int argc, char *argv[]) cpio->bytes_per_block = 5120; break; case 'C': /* NetBSD/OpenBSD */ - cpio->bytes_per_block = atoi(optarg); + cpio->bytes_per_block = atoi(cpio->optarg); if (cpio->bytes_per_block <= 0) - cpio_errc(1, 0, "Invalid blocksize %s", optarg); + cpio_errc(1, 0, "Invalid blocksize %s", cpio->optarg); break; case 'c': /* POSIX 1997 */ cpio->format = "odc"; @@ -141,24 +196,27 @@ main(int argc, char *argv[]) cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR; break; case 'E': /* NetBSD/OpenBSD */ - include_from_file(cpio, optarg); + include_from_file(cpio, cpio->optarg); break; case 'F': /* NetBSD/OpenBSD/GNU cpio */ - cpio->filename = optarg; + cpio->filename = cpio->optarg; break; case 'f': /* POSIX 1997 */ - exclude(cpio, optarg); + exclude(cpio, cpio->optarg); break; case 'H': /* GNU cpio (also --format) */ - cpio->format = optarg; + cpio->format = cpio->optarg; break; case 'h': long_help(); break; case 'I': /* NetBSD/OpenBSD */ - cpio->filename = optarg; + cpio->filename = cpio->optarg; break; case 'i': /* POSIX 1997 */ + if (cpio->mode != '\0') + cpio_errc(1, 0, + "Cannot use both -i and -%c", cpio->mode); cpio->mode = opt; break; case OPTION_INSECURE: @@ -174,13 +232,25 @@ main(int argc, char *argv[]) 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 = optarg; + cpio->filename = cpio->optarg; break; case 'o': /* POSIX 1997 */ + if (cpio->mode != '\0') + cpio_errc(1, 0, + "Cannot use both -o and -%c", cpio->mode); cpio->mode = opt; break; case 'p': /* POSIX 1997 */ + if (cpio->mode != '\0') + cpio_errc(1, 0, + "Cannot use both -p and -%c", cpio->mode); cpio->mode = opt; cpio->extract_flags &= ~ARCHIVE_EXTRACT_SECURE_NODOTDOT; break; @@ -188,7 +258,7 @@ main(int argc, char *argv[]) cpio->quiet = 1; break; case 'R': /* GNU cpio, also --owner */ - if (owner_parse(optarg, &uid, &gid)) + if (owner_parse(cpio->optarg, &uid, &gid)) usage(); if (uid != -1) cpio->uid_override = uid; @@ -220,26 +290,56 @@ 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(); } } - /* TODO: Sanity-check args, error out on nonsensical combinations. */ - - cpio->argc -= optind; - cpio->argv += optind; + /* + * Sanity-check args, error out on nonsensical combinations. + */ + /* -t implies -i if no mode was specified. */ + if (cpio->option_list && cpio->mode == '\0') + cpio->mode = 'i'; + /* -t requires -i */ + if (cpio->option_list && cpio->mode != 'i') + cpio_errc(1, 0, "Option -t requires -i", cpio->mode); + /* -n requires -it */ + if (cpio->option_numeric_uid_gid && !cpio->option_list) + cpio_errc(1, 0, "Option -n requires -it"); + /* Can only specify format when writing */ + if (cpio->format != NULL && cpio->mode != 'o') + cpio_errc(1, 0, "Option --format requires -o"); + /* -l requires -p */ + if (cpio->option_link && cpio->mode != 'p') + cpio_errc(1, 0, "Option -l requires -p"); + /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { case 'o': + /* TODO: Implement old binary format in libarchive, + use that here. */ + if (cpio->format == NULL) + cpio->format = "odc"; /* Default format */ + mode_out(cpio); break; case 'i': @@ -264,6 +364,8 @@ main(int argc, char *argv[]) "Must specify at least one of -i, -o, or -p"); } + free_cache(cpio->gname_cache); + free_cache(cpio->uname_cache); return (0); } @@ -278,11 +380,7 @@ usage(void) 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); -#ifdef HAVE_GETOPT_LONG fprintf(stderr, " Help: %s --help\n", p); -#else - fprintf(stderr, " Help: %s -h\n", p); -#endif exit(1); } @@ -292,7 +390,12 @@ static const char *long_help_msg = "Common Options:\n" " -v Verbose\n" "Create: %p -o [options] < [list of files] > [archive]\n" - " -z, -y Compress archive with gzip/bzip2\n" +#ifdef HAVE_BZLIB_H + " -y Compress archive with bzip2\n" +#endif +#ifdef HAVE_ZLIB_H + " -z Compress archive with gzip\n" +#endif " --format {odc|newc|ustar} Select archive format\n" "List: %p -it < [archive]\n" "Extract: %p -i [options] < [archive]\n"; @@ -358,12 +461,16 @@ mode_out(struct cpio *cpio) if (cpio->archive == NULL) cpio_errc(1, 0, "Failed to allocate archive object"); switch (cpio->compress) { +#ifdef HAVE_BZLIB_H case 'j': case 'y': archive_write_set_compression_bzip2(cpio->archive); break; +#endif +#ifdef HAVE_ZLIB_H case 'z': archive_write_set_compression_gzip(cpio->archive); break; +#endif case 'Z': archive_write_set_compression_compress(cpio->archive); break; @@ -426,11 +533,15 @@ file_to_archive(struct cpio *cpio, const char *srcpath) 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) @@ -454,6 +565,7 @@ file_to_archive(struct cpio *cpio, const char *srcpath) st.st_gid = cpio->uid_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); @@ -466,6 +578,7 @@ file_to_archive(struct cpio *cpio, const char *srcpath) cpio->buff[lnklen] = 0; archive_entry_set_symlink(entry, cpio->buff); } +#endif /* * Generate a destination path for this entry. @@ -596,7 +709,7 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) if (r != ARCHIVE_OK) cpio_warnc(archive_errno(cpio->archive), "%s: %s", - destpath, + srcpath, archive_error_string(cpio->archive)); if (r == ARCHIVE_FATAL) @@ -618,7 +731,7 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) } } - restore_time(cpio, entry, srcpath, fd); + fd = restore_time(cpio, entry, srcpath, fd); cleanup: if (cpio->verbose) @@ -628,7 +741,7 @@ cleanup: return (0); } -static void +static int restore_time(struct cpio *cpio, struct archive_entry *entry, const char *name, int fd) { @@ -638,17 +751,20 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, (void)cpio; /* UNUSED */ (void)entry; /* UNUSED */ (void)name; /* UNUSED */ - (void)fd; /* UNUSED */ if (!warned) cpio_warnc(0, "Can't restore access times on this platform"); warned = 1; - return; + return (fd); +#else +#if defined(_WIN32) && !defined(__CYGWIN__) + struct __timeval times[2]; #else struct timeval times[2]; +#endif if (!cpio->option_atime_restore) - return; + return (fd); times[1].tv_sec = archive_entry_mtime(entry); times[1].tv_usec = archive_entry_mtime_nsec(entry) / 1000; @@ -658,8 +774,16 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, #ifdef HAVE_FUTIMES if (fd >= 0 && futimes(fd, times) == 0) - return; + 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) @@ -668,6 +792,7 @@ restore_time(struct cpio *cpio, struct archive_entry *entry, #endif cpio_warnc(errno, "Can't update time for %s", name); #endif + return (fd); } @@ -798,18 +923,9 @@ mode_list(struct cpio *cpio) } if (excluded(cpio, archive_entry_pathname(entry))) continue; - if (cpio->verbose) { - /* TODO: uname/gname lookups */ - /* TODO: Clean this up. */ - fprintf(stdout, - "%s%3d %8s%8s " CPIO_FILESIZE_PRINTF " %s\n", - archive_entry_strmode(entry), - archive_entry_nlink(entry), - archive_entry_uname(entry), - archive_entry_gname(entry), - (CPIO_FILESIZE_TYPE)archive_entry_size(entry), - archive_entry_pathname(entry)); - } else + if (cpio->verbose) + list_item_verbose(cpio, entry); + else fprintf(stdout, "%s\n", archive_entry_pathname(entry)); } r = archive_read_close(a); @@ -825,9 +941,95 @@ mode_list(struct cpio *cpio) 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 struct stat *st; + const char *fmt; + time_t tim; + 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)); + uname = uids; + snprintf(gids, sizeof(gids), "%jd", + (intmax_t)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, 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, archive_entry_gid(entry)); + } + + /* Print device number or file size. */ + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + snprintf(size, sizeof(size), "%lu,%lu", + (unsigned long)major(st->st_rdev), + (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ + } else { + snprintf(size, sizeof(size), CPIO_FILESIZE_PRINTF, + (CPIO_FILESIZE_TYPE)st->st_size); + } + + /* Format the time using 'ls -l' conventions. */ + tim = (time_t)st->st_mtime; +#if defined(_WIN32) && !defined(__CYGWIN__) + /* Windows' strftime function does not support %e format. */ + if (abs(tim - now) > (365/2)*86400) + 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) + 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)); + + 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) { + unsigned long blocks; struct line_reader *lr; const char *p; int r; @@ -855,6 +1057,14 @@ mode_pass(struct cpio *cpio, const char *destdir) r = archive_write_close(cpio->archive); if (r != ARCHIVE_OK) cpio_errc(1, 0, archive_error_string(cpio->archive)); + + if (!cpio->quiet) { + blocks = (archive_position_uncompressed(cpio->archive) + 511) + / 512; + fprintf(stderr, "%lu %s\n", blocks, + blocks == 1 ? "block" : "blocks"); + } + archive_write_finish(cpio->archive); } @@ -1024,3 +1234,123 @@ process_lines_free(struct line_reader *lr) free(lr->pathname); free(lr); } + +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) + cpio_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) + cpio_warnc(errno, "getpwuid(%d) failed", 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) + cpio_warnc(errno, "getgrgid(%d) failed", id); + return (errno); + } + + *name = grent->gr_name; + return (0); +} diff --git a/contrib/libarchive/cpio/cpio.h b/contrib/libarchive/cpio/cpio.h index 3afd952030..79b222331f 100644 --- a/contrib/libarchive/cpio/cpio.h +++ b/contrib/libarchive/cpio/cpio.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/cpio/cpio.h,v 1.2 2008/06/21 02:20:20 kientzle Exp $ + * $FreeBSD: src/usr.bin/cpio/cpio.h,v 1.7 2008/12/06 07:30:40 kientzle Exp $ */ #ifndef CPIO_H_INCLUDED @@ -42,8 +42,11 @@ * functions. */ struct cpio { + /* Option parsing */ + const char *optarg; + /* Options */ - char *filename; + const char *filename; char mode; /* -i -o -p */ char compress; /* -j, -y, or -z */ const char *format; /* -H format */ @@ -59,12 +62,14 @@ struct cpio { int option_follow_links; /* -L */ int option_link; /* -l */ int option_list; /* -t */ + int option_numeric_uid_gid; /* -n */ int option_rename; /* -r */ char *destdir; size_t pass_destpath_alloc; char *pass_destpath; int uid_override; int gid_override; + int day_first; /* true if locale prefers day/mon */ /* If >= 0, then close this when done. */ int fd; @@ -76,6 +81,9 @@ struct cpio { int return_value; /* Value returned by main() */ struct archive_entry_linkresolver *linkresolver; + struct name_cache *uname_cache; + struct name_cache *gname_cache; + /* Work data. */ struct matching *matching; char *buff; @@ -85,7 +93,7 @@ struct cpio { /* Name of this program; used in error reporting, initialized in main(). */ const char *cpio_progname; -void cpio_errc(int _eval, int _code, const char *fmt, ...); +void cpio_errc(int _eval, int _code, const char *fmt, ...) __LA_DEAD; void cpio_warnc(int _code, const char *fmt, ...); int owner_parse(const char *, int *, int *); @@ -94,6 +102,7 @@ int owner_parse(const char *, int *, int *); /* Fake short equivalents for long options that otherwise lack them. */ enum { OPTION_INSECURE = 1, + OPTION_NO_PRESERVE_OWNER, OPTION_QUIET, OPTION_VERSION }; diff --git a/contrib/libarchive/cpio/cpio_platform.h b/contrib/libarchive/cpio/cpio_platform.h index 3145198298..8ade73130e 100644 --- a/contrib/libarchive/cpio/cpio_platform.h +++ b/contrib/libarchive/cpio/cpio_platform.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/usr.bin/cpio/cpio_platform.h,v 1.2 2008/12/06 07:15:42 kientzle Exp $ */ /* @@ -39,7 +39,7 @@ #include PLATFORM_CONFIG_H #elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ -#include "../config.h" +#include "config.h" #else /* Warn if cpio hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no built-in configuration in cpio_platform.h. @@ -48,7 +48,7 @@ /* No non-FreeBSD platform will have __FBSDID, so just define it here. */ #ifdef __FreeBSD__ #include /* For __FBSDID */ -#else +#elif !defined(__FBSDID) /* Just leaving this macro replacement empty leads to a dangling semicolon. */ #define __FBSDID(a) struct _undefined_hack #endif @@ -81,4 +81,18 @@ #endif #endif +/* How to mark functions that don't return. */ +#if defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_DEAD __attribute__((__noreturn__)) +#else +#define __LA_DEAD +#endif + +#if defined(__CYGWIN__) +#include "cpio_cygwin.h" +#elif defined(_WIN32) /* && !__CYGWIN__ */ +#include "cpio_windows.h" +#endif + #endif /* !CPIO_PLATFORM_H_INCLUDED */ diff --git a/contrib/libarchive/cpio/pathmatch.c b/contrib/libarchive/cpio/pathmatch.c index d33bd34389..40fa836041 100644 --- a/contrib/libarchive/cpio/pathmatch.c +++ b/contrib/libarchive/cpio/pathmatch.c @@ -101,11 +101,10 @@ pm_list(const char *start, const char *end, const char c, int flags) */ static const char * pm_slashskip(const char *s) { - while (*s == '.' || *s == '/') { - if (s[0] != '/' && s[1] != '/') - break; + while ((*s == '/') + || (s[0] == '.' && s[1] == '/') + || (s[0] == '.' && s[1] == '\0')) ++s; - } return (s); } @@ -130,8 +129,6 @@ pm(const char *p, const char *s, int flags) return (1); /* "dir" == "dir/" == "dir/." */ s = pm_slashskip(s); - if (s[0] == '.' && s[1] == '\0') - return (1); } return (*s == '\0'); break; @@ -176,19 +173,6 @@ pm(const char *p, const char *s, int flags) if (*p != *s) return (0); break; - default: - if (*p == *s) - break; - if ((*s == '\0') && (*p == '/')) { - p = pm_slashskip(p); - if (*p == '\0') - return (1); - if (p[0] == '.' && p[1] == '\0') - return (1); - return (0); - } - return (0); - break; case '\\': /* Trailing '\\' matches itself. */ if (p[1] == '\0') { @@ -200,19 +184,34 @@ pm(const char *p, const char *s, int flags) return (0); } break; - } - /* - * TODO: pattern of "\/\.\/" should not match plain "/", - * it should only match explicit "/./". - */ - if (*p == '/') + case '/': + if (*s != '/' && *s != '\0') + return (0); + /* Note: pattern "/\./" won't match "/"; + * pm_slashskip() correctly stops at backslash. */ p = pm_slashskip(p); - else - ++p; - if (*s == '/') s = pm_slashskip(s); - else - ++s; + if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)) + return (1); + --p; /* Counteract the increment below. */ + --s; + break; + case '$': + /* '$' is special only at end of pattern and only + * if PATHMATCH_NO_ANCHOR_END is specified. */ + if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){ + /* "dir" == "dir/" == "dir/." */ + return (*pm_slashskip(s) == '\0'); + } + /* Otherwise, '$' is not special. */ + /* FALL THROUGH */ + default: + if (*p != *s) + return (0); + break; + } + ++p; + ++s; } } @@ -236,9 +235,9 @@ pathmatch(const char *p, const char *s, int flags) /* If start is unanchored, try to match start of each path element. */ if (flags & PATHMATCH_NO_ANCHOR_START) { - for ( ; p != NULL; p = strchr(p, '/')) { - if (*p == '/') - p++; + for ( ; s != NULL; s = strchr(s, '/')) { + if (*s == '/') + s++; if (pm(p, s, flags)) return (1); } diff --git a/contrib/libarchive/cpio/pathmatch.h b/contrib/libarchive/cpio/pathmatch.h index 990fa1fa1e..fd2c2575cc 100644 --- a/contrib/libarchive/cpio/pathmatch.h +++ b/contrib/libarchive/cpio/pathmatch.h @@ -29,9 +29,14 @@ #ifndef PATHMATCH_H #define PATHMATCH_H +/* Don't anchor at beginning unless the pattern starts with "^" */ #define PATHMATCH_NO_ANCHOR_START 1 +/* Don't anchor at end unless the pattern ends with "$" */ #define PATHMATCH_NO_ANCHOR_END 2 +/* Note that "^" and "$" are not special unless you set the corresponding + * flag above. */ + int pathmatch(const char *p, const char *s, int flags); #endif diff --git a/contrib/libarchive/libarchive/COPYING b/contrib/libarchive/libarchive/COPYING new file mode 100644 index 0000000000..90ef9549fe --- /dev/null +++ b/contrib/libarchive/libarchive/COPYING @@ -0,0 +1,36 @@ +All of the C source code, header files, and documentation in this +package are covered by the following: + +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. +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. + +=========================================================================== + +Shell scripts, makefiles, and certain other files may be covered by +other licenses. In particular, some distributions of this library +contain Makefiles and/or shell scripts that are generated +automatically by GNU autoconf and GNU automake. Those generated files +are controlled by the relevant licenses. + +$FreeBSD: src/lib/libarchive/COPYING,v 1.3 2007/01/09 08:05:54 kientzle Exp $ + diff --git a/contrib/libarchive/README b/contrib/libarchive/libarchive/README similarity index 56% copy from contrib/libarchive/README copy to contrib/libarchive/libarchive/README index 50a9d628f3..df62c2307e 100644 --- a/contrib/libarchive/README +++ b/contrib/libarchive/libarchive/README @@ -1,52 +1,21 @@ -README for libarchive bundle. +$FreeBSD: src/lib/libarchive/README,v 1.5 2007/03/03 07:37:35 kientzle Exp $ -This distribution bundle includes the following components: +libarchive: a library for reading and writing streaming archives - * libarchive: a library for reading and writing streaming archives - * tar: the 'bsdtar' program is a full-featured 'tar' - replacement built on libarchive - * cpio: the 'bsdcpio' program is a different interface to - essentially the same functionality - * examples: Some small example programs that you may find useful. - * examples/minitar: a compact sample demonstrating use of libarchive. - I use this for testing link pollution; it should produce a very - small executable file on most systems. - * contrib: Various items sent to me by third parties; - please contact the authors with any questions. +This is all under a BSD license. Use, enjoy, but don't blame me if it breaks! -The top-level directory contains the following information files: - * NEWS - highlights of recent changes - * COPYING - what you can do with this - * INSTALL - installation instructions - * README - this file - * configure - configuration script, see INSTALL for details. - -The following files in the top-level directory are used by the -'configure' script: - - * Makefile.am, aclocal.m4, configure.ac - - used to build this distribution, only needed by maintainers - * Makefile.in, config.h.in - - templates used by configure script - * config.aux/* - auxiliary scripts used by build system - -Guide to Documentation installed by this system: - * bsdtar.1 explains the use of the bsdtar program - * bsdcpio.1 explains the use of the bsdcpio program +Documentation: * libarchive.3 gives an overview of the library as a whole * archive_read.3, archive_write.3, and archive_write_disk.3 provide detailed calling sequences for the read and write APIs * archive_entry.3 details the "struct archive_entry" utility class - * archive_internals.3 provides some insight into libarchive's - internal structure and operation. * libarchive-formats.5 documents the file formats supported by the library - * cpio.5, mtree.5, and tar.5 provide detailed information about a - variety of different archive formats, including hard-to-find details - about modern cpio and tar variants. + * tar.5 provides some detailed information about a variety of different + "tar" formats. You should also read the copious comments in "archive.h" and the source -code for the sample "bsdtar" program for more details. Please let me know -about any errors or omissions you find. +code for the sample "bsdtar" and "minitar" programs for more details. +Please let me know about any errors or omissions you find. Currently, the library automatically detects and reads the following: * gzip compression @@ -63,24 +32,18 @@ Currently, the library automatically detects and reads the following: * Binary cpio (big-endian or little-endian) * ISO9660 CD-ROM images (with optional Rockridge extensions) * ZIP archives (with uncompressed or "deflate" compressed entries) - * GNU and BSD 'ar' archives - * 'mtree' format The library can write: * gzip compression * bzip2 compression - * compress/LZW compression * POSIX ustar * POSIX pax interchange format * "restricted" pax format, which will create ustar archives except for entries that require pax extensions (for long filenames, ACLs, etc). * POSIX octet-oriented cpio - * SVR4 "newc" cpio * shar archives - * GNU and BSD 'ar' archives - -Notes about the library architecture: +Notes: * This is a heavily stream-oriented system. There is no direct support for in-place modification or random access and no intention of ever adding such support. Adding such support would require @@ -103,9 +66,10 @@ Notes about the library architecture: environments where that matters. * On read, the library accepts whatever blocks you hand it. - Your read callback is free to pass the library a byte at a time[1] + Your read callback is free to pass the library a byte at a time or mmap the entire archive and give it to the library at once. - On write, the library always produces correctly-blocked output. + On write, the library always produces correctly-blocked + output. * The object-style approach allows you to have multiple archive streams open at once. bsdtar uses this in its "@archive" extension. @@ -120,16 +84,10 @@ Notes about the library architecture: a block of data in memory and add it to a tar archive without first writing a temporary file. You can also read an entry from an archive and write the data directly to a socket. If you want - to read/write entries to disk, there are convenience functions to - make this especially easy. + to read/write entries to disk, the archive_write_disk interface + treats a directory as if it were an archive so you can copy + from archive->disk using the same code you use for archive->archive + transfers. * Note: "pax interchange format" is really an extended tar format, despite what the name says. - -[1] Gzip and compress formats are identical in the first byte. -For that reason, the first block must be at least two bytes if -you have both of these formats enabled at read time. This is -currently the only restriction on block size. (This restriction -could be lifted by buffering the initial blocks prior to the -compression tasting step, but it doesn't really seem worth the -effort.) diff --git a/contrib/libarchive/libarchive/archive.h b/contrib/libarchive/libarchive/archive.h index 3f9f6e1cf6..0b3f8e1b41 100644 --- a/contrib/libarchive/libarchive/archive.h +++ b/contrib/libarchive/libarchive/archive.h @@ -36,21 +36,28 @@ * platform macros. */ +#include #include /* Linux requires this for off_t */ -#if !defined(__WATCOMC__) && !defined(_MSC_VER) -/* Header unavailable on Watcom C or MS Visual C++. */ +#if !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) +/* Header unavailable on Watcom C or MS Visual C++ or SFU. */ #include /* int64_t, etc. */ #endif #include /* For FILE * */ /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ -#ifdef _WIN32 -#define __LA_SSIZE_T long +#if defined(_WIN32) && !defined(__CYGWIN__) +#define __LA_INT64_T __int64 +# if defined(_WIN64) +# define __LA_SSIZE_T __int64 +# else +# define __LA_SSIZE_T long +# endif #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #else #include /* ssize_t, uid_t, and gid_t */ +#define __LA_INT64_T int64_t #define __LA_SSIZE_T ssize_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t @@ -61,7 +68,7 @@ * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ -#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern @@ -80,7 +87,6 @@ # define __LA_DECL #endif - #ifdef __cplusplus extern "C" { #endif @@ -112,13 +118,13 @@ extern "C" { * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * #endif */ -#define ARCHIVE_VERSION_NUMBER 2005005 +#define ARCHIVE_VERSION_NUMBER 2007000 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_STRING "libarchive 2.5.5" +#define ARCHIVE_VERSION_STRING "libarchive 2.7.0" __LA_DECL const char * archive_version_string(void); #if ARCHIVE_VERSION_NUMBER < 3000000 @@ -183,20 +189,37 @@ struct archive_entry; */ /* Returns pointer and size of next block of data from archive. */ -typedef __LA_SSIZE_T archive_read_callback(struct archive *, void *_client_data, - const void **_buffer); +typedef __LA_SSIZE_T archive_read_callback(struct archive *, + void *_client_data, const void **_buffer); + /* Skips at most request bytes from archive and returns the skipped amount */ #if ARCHIVE_VERSION_NUMBER < 2000000 -typedef __LA_SSIZE_T archive_skip_callback(struct archive *, void *_client_data, - size_t request); +/* Libarchive 1.0 used ssize_t for the return, which is only 32 bits + * on most 32-bit platforms; not large enough. */ +typedef __LA_SSIZE_T archive_skip_callback(struct archive *, + void *_client_data, size_t request); +#elif ARCHIVE_VERSION_NUMBER < 3000000 +/* Libarchive 2.0 used off_t here, but that is a bad idea on Linux and a + * few other platforms where off_t varies with build settings. */ +typedef off_t archive_skip_callback(struct archive *, + void *_client_data, off_t request); #else -typedef off_t archive_skip_callback(struct archive *, void *_client_data, - off_t request); +/* Libarchive 3.0 uses int64_t here, which is actually guaranteed to be + * 64 bits on every platform. */ +typedef __LA_INT64_T archive_skip_callback(struct archive *, + void *_client_data, __LA_INT64_T request); #endif + /* Returns size actually written, zero on EOF, -1 on error. */ -typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data, - const void *_buffer, size_t _length); +typedef __LA_SSIZE_T archive_write_callback(struct archive *, + void *_client_data, + const void *_buffer, size_t _length); + +#if ARCHIVE_VERSION_NUMBER < 3000000 +/* Open callback is actually never needed; remove it in libarchive 3.0. */ typedef int archive_open_callback(struct archive *, void *_client_data); +#endif + typedef int archive_close_callback(struct archive *, void *_client_data); /* @@ -207,6 +230,8 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_COMPRESSION_BZIP2 2 #define ARCHIVE_COMPRESSION_COMPRESS 3 #define ARCHIVE_COMPRESSION_PROGRAM 4 +#define ARCHIVE_COMPRESSION_LZMA 5 +#define ARCHIVE_COMPRESSION_XZ 6 /* * Codes returned by archive_format. @@ -247,8 +272,6 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) #define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) #define ARCHIVE_FORMAT_MTREE 0x80000 -#define ARCHIVE_FORMAT_MTREE_V1 (ARCHIVE_FORMAT_MTREE | 1) -#define ARCHIVE_FORMAT_MTREE_V2 (ARCHIVE_FORMAT_MTREE | 2) /*- * Basic outline for reading an archive: @@ -275,9 +298,15 @@ __LA_DECL int archive_read_support_compression_all(struct archive *); __LA_DECL int archive_read_support_compression_bzip2(struct archive *); __LA_DECL int archive_read_support_compression_compress(struct archive *); __LA_DECL int archive_read_support_compression_gzip(struct archive *); +__LA_DECL int archive_read_support_compression_lzma(struct archive *); __LA_DECL int archive_read_support_compression_none(struct archive *); __LA_DECL int archive_read_support_compression_program(struct archive *, const char *command); +__LA_DECL int archive_read_support_compression_program_signature + (struct archive *, const char *, + const void * /* match */, size_t); + +__LA_DECL int archive_read_support_compression_xz(struct archive *); __LA_DECL int archive_read_support_format_all(struct archive *); __LA_DECL int archive_read_support_format_ar(struct archive *); @@ -326,22 +355,34 @@ __LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); __LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); +/* Parses and returns next entry header using the archive_entry passed in */ +__LA_DECL int archive_read_next_header2(struct archive *, + struct archive_entry *); + /* * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ -__LA_DECL int64_t archive_read_header_position(struct archive *); +__LA_DECL __LA_INT64_T archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ -__LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t); +__LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, + void *, size_t); + /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will * be strictly increasing and that returned blocks will not overlap. */ +#if ARCHIVE_VERSION_NUMBER < 3000000 +__LA_DECL int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, off_t *offset); +#else __LA_DECL int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, off_t *offset); + const void **buff, size_t *size, + __LA_INT64_T *offset); +#endif /*- * Some convenience functions that are built on archive_read_data: @@ -350,10 +391,23 @@ __LA_DECL int archive_read_data_block(struct archive *a, * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); -__LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer, - __LA_SSIZE_T len); +__LA_DECL int archive_read_data_into_buffer(struct archive *, + void *buffer, __LA_SSIZE_T len); __LA_DECL int archive_read_data_into_fd(struct archive *, int fd); +/* + * Set read options. + */ +/* Apply option string to the format only. */ +__LA_DECL int archive_read_set_format_options(struct archive *_a, + const char *s); +/* Apply option string to the filter only. */ +__LA_DECL int archive_read_set_filter_options(struct archive *_a, + const char *s); +/* Apply option string to both the format and the filter. */ +__LA_DECL int archive_read_set_options(struct archive *_a, + const char *s); + /*- * Convenience function to recreate the current entry (whose header * has just been read) on disk. @@ -414,12 +468,11 @@ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, __LA_DECL int archive_read_close(struct archive *); /* Release all resources and destroy the object. */ /* Note that archive_read_finish will call archive_read_close for you. */ -#if ARCHIVE_VERSION_NUMBER >= 2000000 -__LA_DECL int archive_read_finish(struct archive *); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +#if ARCHIVE_VERSION_NUMBER < 2000000 /* Erroneously declared to return void in libarchive 1.x */ __LA_DECL void archive_read_finish(struct archive *); +#else +__LA_DECL int archive_read_finish(struct archive *); #endif /*- @@ -453,9 +506,11 @@ __LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t); __LA_DECL int archive_write_set_compression_bzip2(struct archive *); __LA_DECL int archive_write_set_compression_compress(struct archive *); __LA_DECL int archive_write_set_compression_gzip(struct archive *); +__LA_DECL int archive_write_set_compression_lzma(struct archive *); __LA_DECL int archive_write_set_compression_none(struct archive *); __LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd); +__LA_DECL int archive_write_set_compression_xz(struct archive *); /* A convenience function to set the format based on the code or name. */ __LA_DECL int archive_write_set_format(struct archive *, int format_code); __LA_DECL int archive_write_set_format_by_name(struct archive *, @@ -465,6 +520,7 @@ __LA_DECL int archive_write_set_format_ar_bsd(struct archive *); __LA_DECL int archive_write_set_format_ar_svr4(struct archive *); __LA_DECL int archive_write_set_format_cpio(struct archive *); __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); +__LA_DECL int archive_write_set_format_mtree(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); @@ -490,29 +546,59 @@ __LA_DECL int archive_write_open_memory(struct archive *, */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); -#if ARCHIVE_VERSION_NUMBER >= 2000000 -__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, const void *, size_t); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +#if ARCHIVE_VERSION_NUMBER < 2000000 /* This was erroneously declared to return "int" in libarchive 1.x. */ -__LA_DECL int archive_write_data(struct archive *, const void *, size_t); +__LA_DECL int archive_write_data(struct archive *, + const void *, size_t); +#else +/* Libarchive 2.0 and later return ssize_t here. */ +__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, + const void *, size_t); +#endif + +#if ARCHIVE_VERSION_NUMBER < 3000000 +/* Libarchive 1.x and 2.x use off_t for the argument, but that's not + * stable on Linux. */ +__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, + const void *, size_t, off_t); +#else +/* Libarchive 3.0 uses explicit int64_t to ensure consistent 64-bit support. */ +__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, + const void *, size_t, __LA_INT64_T); #endif -__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, off_t); __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); -#if ARCHIVE_VERSION_NUMBER >= 2000000 -__LA_DECL int archive_write_finish(struct archive *); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ +#if ARCHIVE_VERSION_NUMBER < 2000000 /* Return value was incorrect in libarchive 1.x. */ __LA_DECL void archive_write_finish(struct archive *); +#else +/* Libarchive 2.x and later returns an error if this fails. */ +/* It can fail if the archive wasn't already closed, in which case + * archive_write_finish() will implicitly call archive_write_close(). */ +__LA_DECL int archive_write_finish(struct archive *); #endif +/* + * Set write options. + */ +/* Apply option string to the format only. */ +__LA_DECL int archive_write_set_format_options(struct archive *_a, + const char *s); +/* Apply option string to the compressor only. */ +__LA_DECL int archive_write_set_compressor_options(struct archive *_a, + const char *s); +/* Apply option string to both the format and the compressor. */ +__LA_DECL int archive_write_set_options(struct archive *_a, + const char *s); + + /*- + * ARCHIVE_WRITE_DISK API + * * To create objects on disk: * 1) Ask archive_write_disk_new for a new archive_write_disk object. - * 2) Set any global properties. In particular, you should set - * the compression and format to use. + * 2) Set any global properties. In particular, you probably + * want to set the options. * 3) For each entry: * - construct an appropriate struct archive_entry structure * - archive_write_header to create the file/dir/etc on disk @@ -526,7 +612,8 @@ __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, dev_t, ino_t); -/* Set flags to control how the next item gets created. */ +/* Set flags to control how the next item gets created. + * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* @@ -561,14 +648,48 @@ __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, __LA_UID_T (*)(void *, const char *, __LA_UID_T), void (* /* cleanup */)(void *)); +/* + * ARCHIVE_READ_DISK API + * + * This is still evolving and somewhat experimental. + */ +__LA_DECL struct archive *archive_read_disk_new(void); +/* The names for symlink modes here correspond to an old BSD + * command-line argument convention: -L, -P, -H */ +/* Follow all symlinks. */ +__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *); +/* Follow no symlinks. */ +__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *); +/* Follow symlink initially, then not. */ +__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *); +/* TODO: Handle Linux stat32/stat64 ugliness. */ +__LA_DECL int archive_read_disk_entry_from_file(struct archive *, + struct archive_entry *, int /* fd */, const struct stat *); +/* Look up gname for gid or uname for uid. */ +/* Default implementations are very, very stupid. */ +__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T); +__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T); +/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the + * results for performance. */ +__LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); +/* You can install your own lookups if you like. */ +__LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, + void * /* private_data */, + const char *(* /* lookup_fn */)(void *, __LA_GID_T), + void (* /* cleanup_fn */)(void *)); +__LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, + void * /* private_data */, + const char *(* /* lookup_fn */)(void *, __LA_UID_T), + void (* /* cleanup_fn */)(void *)); + /* * Accessor functions to read/set various information in * the struct archive object: */ /* Bytes written after compression or read before decompression. */ -__LA_DECL int64_t archive_position_compressed(struct archive *); +__LA_DECL __LA_INT64_T archive_position_compressed(struct archive *); /* Bytes written to compressor or read from decompressor. */ -__LA_DECL int64_t archive_position_uncompressed(struct archive *); +__LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *); __LA_DECL const char *archive_compression_name(struct archive *); __LA_DECL int archive_compression(struct archive *); @@ -586,7 +707,14 @@ __LA_DECL void archive_copy_error(struct archive *dest, } #endif -/* This is meaningless outside of this header. */ +/* These are meaningless outside of this header. */ #undef __LA_DECL +#undef __LA_GID_T +#undef __LA_UID_T + +/* These need to remain defined because they're used in the + * callback type definitions. XXX Fix this. This is ugly. XXX */ +/* #undef __LA_INT64_T */ +/* #undef __LA_SSIZE_T */ #endif /* !ARCHIVE_H_INCLUDED */ diff --git a/contrib/libarchive/libarchive/archive.h.in b/contrib/libarchive/libarchive/archive.h.in deleted file mode 100644 index d76c85e51b..0000000000 --- a/contrib/libarchive/libarchive/archive.h.in +++ /dev/null @@ -1,542 +0,0 @@ -/*- - * 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. - * 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. - * - * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.47 2007/12/30 04:58:21 kientzle Exp $ - */ - -#ifndef ARCHIVE_H_INCLUDED -#define ARCHIVE_H_INCLUDED - -/* - * This header file corresponds to: - * Library version @ARCHIVE_VERSION@ - * Shared library version @SHLIB_MAJOR@ - */ - -#include /* Linux requires this for off_t */ -@ARCHIVE_H_INCLUDE_INTTYPES_H@ -#include /* For FILE * */ -#ifndef _WIN32 -#include /* For ssize_t and size_t */ -#else -typedef long ssize_t; -typedef unsigned int uid_t; -typedef unsigned int gid_t; -typedef unsigned short mode_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Each of the version identifiers comes as a macro and a function. - * The macro identifies the installed header; the function identifies - * the library version (which may not be the same if you're using a - * dynamically-linked version of the library). - */ - -/* - * Textual name/version of the library, useful for version displays. - */ -#define ARCHIVE_LIBRARY_VERSION "libarchive @LIBARCHIVE_VERSION_STRING@" -const char * archive_version(void); - -/* - * The "version stamp" is a single integer that makes it easy to check - * the exact version: for version a.b.c, the version stamp is - * printf("%d%03d%03d",a,b,c). For example, version 2.12.108 has - * version stamp 2012108. - * - * This was introduced with libarchive 1.9.0 in the libarchive 1.x family - * and libarchive 2.2.4 in the libarchive 2.x family. The following - * may be useful if you really want to do feature detection for earlier - * libarchive versions (which defined API_VERSION and API_FEATURE): - * - * #ifndef ARCHIVE_VERSION_STAMP - * #define ARCHIVE_VERSION_STAMP \ - * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) - * #endif - */ -#define ARCHIVE_VERSION_STAMP @LIBARCHIVE_VERSION@ -int archive_version_stamp(void); - -/* - * Major version number: If ARCHIVE_API_VERSION != - * archive_api_version(), then the library you were linked with is - * using an incompatible API to the one you were compiled with. This - * is almost certainly a fatal problem. - * This is deprecated and will be removed; use ARCHIVE_VERSION_STAMP - * instead. - */ -#define ARCHIVE_API_VERSION (ARCHIVE_VERSION_STAMP / 1000000) -int archive_api_version(void); - -/* - * Minor version number. This is deprecated and will be removed. - * Use ARCHIVE_VERSION_STAMP to adapt to libarchive API variations. - */ -#define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_STAMP / 1000) % 1000) -int archive_api_feature(void); - - -#define ARCHIVE_BYTES_PER_RECORD 512 -#define ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240 - -/* Declare our basic types. */ -struct archive; -struct archive_entry; - -/* - * Error codes: Use archive_errno() and archive_error_string() - * to retrieve details. Unless specified otherwise, all functions - * that return 'int' use these codes. - */ -#define ARCHIVE_EOF 1 /* Found end of archive. */ -#define ARCHIVE_OK 0 /* Operation was successful. */ -#define ARCHIVE_RETRY (-10) /* Retry might succeed. */ -#define ARCHIVE_WARN (-20) /* Partial success. */ -/* For example, if write_header "fails", then you can't push data. */ -#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */ -#define ARCHIVE_FATAL (-30) /* No more operations are possible. */ - -/* - * As far as possible, archive_errno returns standard platform errno codes. - * Of course, the details vary by platform, so the actual definitions - * here are stored in "archive_platform.h". The symbols are listed here - * for reference; as a rule, clients should not need to know the exact - * platform-dependent error code. - */ -/* Unrecognized or invalid file format. */ -/* #define ARCHIVE_ERRNO_FILE_FORMAT */ -/* Illegal usage of the library. */ -/* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */ -/* Unknown or unclassified error. */ -/* #define ARCHIVE_ERRNO_MISC */ - -/* - * Callbacks are invoked to automatically read/skip/write/open/close the - * archive. You can provide your own for complex tasks (like breaking - * archives across multiple tapes) or use standard ones built into the - * library. - */ - -/* Returns pointer and size of next block of data from archive. */ -typedef ssize_t archive_read_callback(struct archive *, void *_client_data, - const void **_buffer); -/* Skips at most request bytes from archive and returns the skipped amount */ -#if ARCHIVE_API_VERSION < 2 -typedef ssize_t archive_skip_callback(struct archive *, void *_client_data, - size_t request); -#else -typedef off_t archive_skip_callback(struct archive *, void *_client_data, - off_t request); -#endif -/* Returns size actually written, zero on EOF, -1 on error. */ -typedef ssize_t archive_write_callback(struct archive *, void *_client_data, - const void *_buffer, size_t _length); -typedef int archive_open_callback(struct archive *, void *_client_data); -typedef int archive_close_callback(struct archive *, void *_client_data); - -/* - * Codes for archive_compression. - */ -#define ARCHIVE_COMPRESSION_NONE 0 -#define ARCHIVE_COMPRESSION_GZIP 1 -#define ARCHIVE_COMPRESSION_BZIP2 2 -#define ARCHIVE_COMPRESSION_COMPRESS 3 -#define ARCHIVE_COMPRESSION_PROGRAM 4 - -/* - * Codes returned by archive_format. - * - * Top 16 bits identifies the format family (e.g., "tar"); lower - * 16 bits indicate the variant. This is updated by read_next_header. - * Note that the lower 16 bits will often vary from entry to entry. - * In some cases, this variation occurs as libarchive learns more about - * the archive (for example, later entries might utilize extensions that - * weren't necessary earlier in the archive; in this case, libarchive - * will change the format code to indicate the extended format that - * was used). In other cases, it's because different tools have - * modified the archive and so different parts of the archive - * actually have slightly different formts. (Both tar and cpio store - * format codes in each entry, so it is quite possible for each - * entry to be in a different format.) - */ -#define ARCHIVE_FORMAT_BASE_MASK 0xff0000 -#define ARCHIVE_FORMAT_CPIO 0x10000 -#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1) -#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2) -#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3) -#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4) -#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5) -#define ARCHIVE_FORMAT_SHAR 0x20000 -#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1) -#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2) -#define ARCHIVE_FORMAT_TAR 0x30000 -#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1) -#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2) -#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3) -#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4) -#define ARCHIVE_FORMAT_ISO9660 0x40000 -#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1) -#define ARCHIVE_FORMAT_ZIP 0x50000 -#define ARCHIVE_FORMAT_EMPTY 0x60000 -#define ARCHIVE_FORMAT_AR 0x70000 -#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1) -#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2) -#define ARCHIVE_FORMAT_MTREE 0x80000 -#define ARCHIVE_FORMAT_MTREE_V1 (ARCHIVE_FORMAT_MTREE | 1) -#define ARCHIVE_FORMAT_MTREE_V2 (ARCHIVE_FORMAT_MTREE | 2) - -/*- - * Basic outline for reading an archive: - * 1) Ask archive_read_new for an archive reader object. - * 2) Update any global properties as appropriate. - * In particular, you'll certainly want to call appropriate - * archive_read_support_XXX functions. - * 3) Call archive_read_open_XXX to open the archive - * 4) Repeatedly call archive_read_next_header to get information about - * successive archive entries. Call archive_read_data to extract - * data for entries of interest. - * 5) Call archive_read_finish to end processing. - */ -struct archive *archive_read_new(void); - -/* - * The archive_read_support_XXX calls enable auto-detect for this - * archive handle. They also link in the necessary support code. - * For example, if you don't want bzlib linked in, don't invoke - * support_compression_bzip2(). The "all" functions provide the - * obvious shorthand. - */ -int archive_read_support_compression_all(struct archive *); -int archive_read_support_compression_bzip2(struct archive *); -int archive_read_support_compression_compress(struct archive *); -int archive_read_support_compression_gzip(struct archive *); -int archive_read_support_compression_none(struct archive *); -int archive_read_support_compression_program(struct archive *, - const char *command); - -int archive_read_support_format_all(struct archive *); -int archive_read_support_format_ar(struct archive *); -int archive_read_support_format_cpio(struct archive *); -int archive_read_support_format_empty(struct archive *); -int archive_read_support_format_gnutar(struct archive *); -int archive_read_support_format_iso9660(struct archive *); -int archive_read_support_format_mtree(struct archive *); -int archive_read_support_format_tar(struct archive *); -int archive_read_support_format_zip(struct archive *); - - -/* Open the archive using callbacks for archive I/O. */ -int archive_read_open(struct archive *, void *_client_data, - archive_open_callback *, archive_read_callback *, - archive_close_callback *); -int archive_read_open2(struct archive *, void *_client_data, - archive_open_callback *, archive_read_callback *, - archive_skip_callback *, archive_close_callback *); - -/* - * A variety of shortcuts that invoke archive_read_open() with - * canned callbacks suitable for common situations. The ones that - * accept a block size handle tape blocking correctly. - */ -/* Use this if you know the filename. Note: NULL indicates stdin. */ -int archive_read_open_filename(struct archive *, - const char *_filename, size_t _block_size); -/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ -int archive_read_open_file(struct archive *, - const char *_filename, size_t _block_size); -/* Read an archive that's stored in memory. */ -int archive_read_open_memory(struct archive *, - void * buff, size_t size); -/* A more involved version that is only used for internal testing. */ -int archive_read_open_memory2(struct archive *a, void *buff, - size_t size, size_t read_size); -/* Read an archive that's already open, using the file descriptor. */ -int archive_read_open_fd(struct archive *, int _fd, - size_t _block_size); -/* Read an archive that's already open, using a FILE *. */ -/* Note: DO NOT use this with tape drives. */ -int archive_read_open_FILE(struct archive *, FILE *_file); - -/* Parses and returns next entry header. */ -int archive_read_next_header(struct archive *, - struct archive_entry **); - -/* - * Retrieve the byte offset in UNCOMPRESSED data where last-read - * header started. - */ -int64_t archive_read_header_position(struct archive *); - -/* Read data from the body of an entry. Similar to read(2). */ -ssize_t archive_read_data(struct archive *, void *, size_t); -/* - * A zero-copy version of archive_read_data that also exposes the file offset - * of each returned block. Note that the client has no way to specify - * the desired size of the block. The API does guarantee that offsets will - * be strictly increasing and that returned blocks will not overlap. - */ -int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, off_t *offset); - -/*- - * Some convenience functions that are built on archive_read_data: - * 'skip': skips entire entry - * 'into_buffer': writes data into memory buffer that you provide - * 'into_fd': writes data to specified filedes - */ -int archive_read_data_skip(struct archive *); -int archive_read_data_into_buffer(struct archive *, void *buffer, - ssize_t len); -int archive_read_data_into_fd(struct archive *, int fd); - -/*- - * Convenience function to recreate the current entry (whose header - * has just been read) on disk. - * - * This does quite a bit more than just copy data to disk. It also: - * - Creates intermediate directories as required. - * - Manages directory permissions: non-writable directories will - * be initially created with write permission enabled; when the - * archive is closed, dir permissions are edited to the values specified - * in the archive. - * - Checks hardlinks: hardlinks will not be extracted unless the - * linked-to file was also extracted within the same session. (TODO) - */ - -/* The "flags" argument selects optional behavior, 'OR' the flags you want. */ - -/* Default: Do not try to set owner/group. */ -#define ARCHIVE_EXTRACT_OWNER (1) -/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ -#define ARCHIVE_EXTRACT_PERM (2) -/* Default: Do not restore mtime/atime. */ -#define ARCHIVE_EXTRACT_TIME (4) -/* Default: Replace existing files. */ -#define ARCHIVE_EXTRACT_NO_OVERWRITE (8) -/* Default: Try create first, unlink only if create fails with EEXIST. */ -#define ARCHIVE_EXTRACT_UNLINK (16) -/* Default: Do not restore ACLs. */ -#define ARCHIVE_EXTRACT_ACL (32) -/* Default: Do not restore fflags. */ -#define ARCHIVE_EXTRACT_FFLAGS (64) -/* Default: Do not restore xattrs. */ -#define ARCHIVE_EXTRACT_XATTR (128) -/* Default: Do not try to guard against extracts redirected by symlinks. */ -/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */ -#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (256) -/* Default: Do not reject entries with '..' as path elements. */ -#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (512) -/* Default: Create parent directories as needed. */ -#define ARCHIVE_EXTRACT_NO_AUTODIR (1024) -/* Default: Overwrite files, even if one on disk is newer. */ -#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (2048) - -int archive_read_extract(struct archive *, struct archive_entry *, - int flags); -void archive_read_extract_set_progress_callback(struct archive *, - void (*_progress_func)(void *), void *_user_data); - -/* Record the dev/ino of a file that will not be written. This is - * generally set to the dev/ino of the archive being read. */ -void archive_read_extract_set_skip_file(struct archive *, - dev_t, ino_t); - -/* Close the file and release most resources. */ -int archive_read_close(struct archive *); -/* Release all resources and destroy the object. */ -/* Note that archive_read_finish will call archive_read_close for you. */ -#if ARCHIVE_API_VERSION > 1 -int archive_read_finish(struct archive *); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ -/* Erroneously declared to return void in libarchive 1.x */ -void archive_read_finish(struct archive *); -#endif - -/*- - * To create an archive: - * 1) Ask archive_write_new for a archive writer object. - * 2) Set any global properties. In particular, you should set - * the compression and format to use. - * 3) Call archive_write_open to open the file (most people - * will use archive_write_open_file or archive_write_open_fd, - * which provide convenient canned I/O callbacks for you). - * 4) For each entry: - * - construct an appropriate struct archive_entry structure - * - archive_write_header to write the header - * - archive_write_data to write the entry data - * 5) archive_write_close to close the output - * 6) archive_write_finish to cleanup the writer and release resources - */ -struct archive *archive_write_new(void); -int archive_write_set_bytes_per_block(struct archive *, - int bytes_per_block); -int archive_write_get_bytes_per_block(struct archive *); -/* XXX This is badly misnamed; suggestions appreciated. XXX */ -int archive_write_set_bytes_in_last_block(struct archive *, - int bytes_in_last_block); -int archive_write_get_bytes_in_last_block(struct archive *); - -/* The dev/ino of a file that won't be archived. This is used - * to avoid recursively adding an archive to itself. */ -int archive_write_set_skip_file(struct archive *, dev_t, ino_t); - -int archive_write_set_compression_bzip2(struct archive *); -int archive_write_set_compression_gzip(struct archive *); -int archive_write_set_compression_none(struct archive *); -int archive_write_set_compression_program(struct archive *, - const char *cmd); -/* A convenience function to set the format based on the code or name. */ -int archive_write_set_format(struct archive *, int format_code); -int archive_write_set_format_by_name(struct archive *, - const char *name); -/* To minimize link pollution, use one or more of the following. */ -int archive_write_set_format_ar_bsd(struct archive *); -int archive_write_set_format_ar_svr4(struct archive *); -int archive_write_set_format_cpio(struct archive *); -int archive_write_set_format_cpio_newc(struct archive *); -/* TODO: int archive_write_set_format_old_tar(struct archive *); */ -int archive_write_set_format_pax(struct archive *); -int archive_write_set_format_pax_restricted(struct archive *); -int archive_write_set_format_shar(struct archive *); -int archive_write_set_format_shar_dump(struct archive *); -int archive_write_set_format_ustar(struct archive *); -int archive_write_open(struct archive *, void *, - archive_open_callback *, archive_write_callback *, - archive_close_callback *); -int archive_write_open_fd(struct archive *, int _fd); -int archive_write_open_filename(struct archive *, const char *_file); -/* A deprecated synonym for archive_write_open_filename() */ -int archive_write_open_file(struct archive *, const char *_file); -int archive_write_open_FILE(struct archive *, FILE *); -/* _buffSize is the size of the buffer, _used refers to a variable that - * will be updated after each write into the buffer. */ -int archive_write_open_memory(struct archive *, - void *_buffer, size_t _buffSize, size_t *_used); - -/* - * Note that the library will truncate writes beyond the size provided - * to archive_write_header or pad if the provided data is short. - */ -int archive_write_header(struct archive *, - struct archive_entry *); -#if ARCHIVE_API_VERSION > 1 -ssize_t archive_write_data(struct archive *, const void *, size_t); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ -/* This was erroneously declared to return "int" in libarchive 1.x. */ -int archive_write_data(struct archive *, const void *, size_t); -#endif -ssize_t archive_write_data_block(struct archive *, const void *, size_t, off_t); -int archive_write_finish_entry(struct archive *); -int archive_write_close(struct archive *); -#if ARCHIVE_API_VERSION > 1 -int archive_write_finish(struct archive *); -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ -/* Return value was incorrect in libarchive 1.x. */ -void archive_write_finish(struct archive *); -#endif - -/*- - * To create objects on disk: - * 1) Ask archive_write_disk_new for a new archive_write_disk object. - * 2) Set any global properties. In particular, you should set - * the compression and format to use. - * 3) For each entry: - * - construct an appropriate struct archive_entry structure - * - archive_write_header to create the file/dir/etc on disk - * - archive_write_data to write the entry data - * 4) archive_write_finish to cleanup the writer and release resources - * - * In particular, you can use this in conjunction with archive_read() - * to pull entries out of an archive and create them on disk. - */ -struct archive *archive_write_disk_new(void); -/* This file will not be overwritten. */ -int archive_write_disk_set_skip_file(struct archive *, - dev_t, ino_t); -/* Set flags to control how the next item gets created. */ -int archive_write_disk_set_options(struct archive *, - int flags); -/* - * The lookup functions are given uname/uid (or gname/gid) pairs and - * return a uid (gid) suitable for this system. These are used for - * restoring ownership and for setting ACLs. The default functions - * are naive, they just return the uid/gid. These are small, so reasonable - * for applications that don't need to preserve ownership; they - * are probably also appropriate for applications that are doing - * same-system backup and restore. - */ -/* - * The "standard" lookup functions use common system calls to lookup - * the uname/gname, falling back to the uid/gid if the names can't be - * found. They cache lookups and are reasonably fast, but can be very - * large, so they are not used unless you ask for them. In - * particular, these match the specifications of POSIX "pax" and old - * POSIX "tar". - */ -int archive_write_disk_set_standard_lookup(struct archive *); -/* - * If neither the default (naive) nor the standard (big) functions suit - * your needs, you can write your own and register them. Be sure to - * include a cleanup function if you have allocated private data. - */ -int archive_write_disk_set_group_lookup(struct archive *, - void *private_data, - gid_t (*loookup)(void *, const char *gname, gid_t gid), - void (*cleanup)(void *)); -int archive_write_disk_set_user_lookup(struct archive *, - void *private_data, - uid_t (*)(void *, const char *uname, uid_t uid), - void (*cleanup)(void *)); - -/* - * Accessor functions to read/set various information in - * the struct archive object: - */ -/* Bytes written after compression or read before decompression. */ -int64_t archive_position_compressed(struct archive *); -/* Bytes written to compressor or read from decompressor. */ -int64_t archive_position_uncompressed(struct archive *); - -const char *archive_compression_name(struct archive *); -int archive_compression(struct archive *); -int archive_errno(struct archive *); -const char *archive_error_string(struct archive *); -const char *archive_format_name(struct archive *); -int archive_format(struct archive *); -void archive_clear_error(struct archive *); -void archive_set_error(struct archive *, int _err, const char *fmt, ...); -void archive_copy_error(struct archive *dest, struct archive *src); - -#ifdef __cplusplus -} -#endif - -#endif /* !ARCHIVE_H_INCLUDED */ diff --git a/contrib/libarchive/libarchive/archive_check_magic.c b/contrib/libarchive/libarchive/archive_check_magic.c index 715486dcfe..7a19087f14 100644 --- a/contrib/libarchive/libarchive/archive_check_magic.c +++ b/contrib/libarchive/libarchive/archive_check_magic.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.8 2007/04/02 00:15:45 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.9 2008/12/06 05:52:01 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -40,18 +40,26 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.8 2007/04/02 00 #ifdef HAVE_UNISTD_H #include #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#endif #include "archive_private.h" static void errmsg(const char *m) { - write(STDERR_FILENO, m, strlen(m)); + write(2, m, strlen(m)); } static void diediedie(void) { +#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) + /* Cause a breakpoint exception */ + DebugBreak(); +#endif *(char *)0 = 1; /* Deliberately segfault and force a coredump. */ _exit(1); /* If that didn't work, just exit with an error. */ } diff --git a/contrib/libarchive/libarchive/archive_endian.h b/contrib/libarchive/libarchive/archive_endian.h index 550be16402..3c62fbf530 100644 --- a/contrib/libarchive/libarchive/archive_endian.h +++ b/contrib/libarchive/libarchive/archive_endian.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_endian.h,v 1.3 2008/05/26 17:00:22 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_endian.h,v 1.4 2008/12/06 06:12:24 kientzle Exp $ * * Borrowed from FreeBSD's */ @@ -35,14 +35,16 @@ #define ARCHIVE_ENDIAN_H_INCLUDED -/* Watcom C++ doesn't support 'inline' in C code. (For any version?) */ -#if defined( __WATCOMC__ ) - #define inline -#endif - -/* Visual C++ 6.0 doesn't support 'inline' in C code. (Does VC7? VC8?) */ -#if defined(_MSC_VER) - #define inline +/* + * Disabling inline keyword for compilers known to choke on it: + * - Watcom C++ in C code. (For any version?) + * - SGI MIPSpro + * - Microsoft Visual C++ 6.0 (supposedly newer versions too) + */ +#if defined(__WATCOMC__) || defined(__sgi) +#define inline +#elif defined(_MSC_VER) +#define inline __inline #endif /* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ diff --git a/contrib/libarchive/libarchive/archive_entry.c b/contrib/libarchive/libarchive/archive_entry.c index 69921c309f..ffbd92f6e2 100644 --- a/contrib/libarchive/libarchive/archive_entry.c +++ b/contrib/libarchive/libarchive/archive_entry.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.52 2008/05/26 17:00:22 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.55 2008/12/23 05:01:43 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -39,18 +39,22 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.52 2008/05/26 17:00:2 #include #endif #endif -#ifdef HAVE_EXT2FS_EXT2_FS_H -#include /* for Linux file flags */ -#endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif +/* + * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. + * As the include guards don't agree, the order of include is important. + */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif +#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) +#include /* for Linux file flags */ +#endif #include #include #ifdef HAVE_STDLIB_H @@ -79,7 +83,7 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.52 2008/05/26 17:00:2 #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) -#elif defined mkdev || defined _WIN32 || defined __WIN32__ +#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else @@ -228,8 +232,8 @@ aes_get_wcs(struct aes *aes) if (w == NULL) __archive_errx(1, "No memory for aes_get_wcs()"); r = mbstowcs(w, aes->aes_mbs.s, wcs_length); - w[wcs_length] = 0; if (r > 0) { + w[r] = 0; aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs = w); } @@ -239,7 +243,8 @@ aes_get_wcs(struct aes *aes) if (aes->aes_set & AES_SET_UTF8) { /* Try converting UTF8 to WCS. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); - aes->aes_set |= AES_SET_WCS; + if (aes->aes_wcs != NULL) + aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs); } return (NULL); @@ -365,6 +370,7 @@ archive_entry_clear(struct archive_entry *entry) aes_clean(&entry->ae_gname); aes_clean(&entry->ae_hardlink); aes_clean(&entry->ae_pathname); + aes_clean(&entry->ae_sourcepath); aes_clean(&entry->ae_symlink); aes_clean(&entry->ae_uname); archive_entry_acl_clear(entry); @@ -394,9 +400,9 @@ archive_entry_clone(struct archive_entry *entry) aes_copy(&entry2->ae_gname, &entry->ae_gname); aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); aes_copy(&entry2->ae_pathname, &entry->ae_pathname); + aes_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); aes_copy(&entry2->ae_symlink, &entry->ae_symlink); - entry2->ae_hardlinkset = entry->ae_hardlinkset; - entry2->ae_symlinkset = entry->ae_symlinkset; + entry2->ae_set = entry->ae_set; aes_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ @@ -455,12 +461,42 @@ archive_entry_atime_nsec(struct archive_entry *entry) return (entry->ae_stat.aest_atime_nsec); } +int +archive_entry_atime_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_ATIME); +} + +time_t +archive_entry_birthtime(struct archive_entry *entry) +{ + return (entry->ae_stat.aest_birthtime); +} + +long +archive_entry_birthtime_nsec(struct archive_entry *entry) +{ + return (entry->ae_stat.aest_birthtime_nsec); +} + +int +archive_entry_birthtime_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_BIRTHTIME); +} + time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } +int +archive_entry_ctime_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_CTIME); +} + long archive_entry_ctime_nsec(struct archive_entry *entry) { @@ -562,17 +598,17 @@ archive_entry_gname_w(struct archive_entry *entry) const char * archive_entry_hardlink(struct archive_entry *entry) { - if (!entry->ae_hardlinkset) - return (NULL); - return (aes_get_mbs(&entry->ae_hardlink)); + if (entry->ae_set & AE_SET_HARDLINK) + return (aes_get_mbs(&entry->ae_hardlink)); + return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { - if (!entry->ae_hardlinkset) - return (NULL); - return (aes_get_wcs(&entry->ae_hardlink)); + if (entry->ae_set & AE_SET_HARDLINK) + return (aes_get_wcs(&entry->ae_hardlink)); + return (NULL); } ino_t @@ -599,6 +635,12 @@ archive_entry_mtime_nsec(struct archive_entry *entry) return (entry->ae_stat.aest_mtime_nsec); } +int +archive_entry_mtime_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_MTIME); +} + unsigned int archive_entry_nlink(struct archive_entry *entry) { @@ -651,6 +693,12 @@ archive_entry_size(struct archive_entry *entry) return (entry->ae_stat.aest_size); } +int +archive_entry_size_is_set(struct archive_entry *entry) +{ + return (entry->ae_set & AE_SET_SIZE); +} + const char * archive_entry_sourcepath(struct archive_entry *entry) { @@ -660,17 +708,17 @@ archive_entry_sourcepath(struct archive_entry *entry) const char * archive_entry_symlink(struct archive_entry *entry) { - if (!entry->ae_symlinkset) - return (NULL); - return (aes_get_mbs(&entry->ae_symlink)); + if (entry->ae_set & AE_SET_SYMLINK) + return (aes_get_mbs(&entry->ae_symlink)); + return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { - if (!entry->ae_symlinkset) - return (NULL); - return (aes_get_wcs(&entry->ae_symlink)); + if (entry->ae_set & AE_SET_SYMLINK) + return (aes_get_wcs(&entry->ae_symlink)); + return (NULL); } uid_t @@ -773,7 +821,9 @@ archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { aes_set_mbs(&entry->ae_hardlink, target); if (target != NULL) - entry->ae_hardlinkset = 1; + entry->ae_set |= AE_SET_HARDLINK; + else + entry->ae_set &= ~AE_SET_HARDLINK; } void @@ -781,7 +831,9 @@ archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { aes_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) - entry->ae_hardlinkset = 1; + entry->ae_set |= AE_SET_HARDLINK; + else + entry->ae_set &= ~AE_SET_HARDLINK; } void @@ -789,25 +841,59 @@ archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target { aes_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) - entry->ae_hardlinkset = 1; + entry->ae_set |= AE_SET_HARDLINK; + else + entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; + entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } +void +archive_entry_unset_atime(struct archive_entry *entry) +{ + archive_entry_set_atime(entry, 0, 0); + entry->ae_set &= ~AE_SET_ATIME; +} + +void +archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns) +{ + entry->stat_valid = 0; + entry->ae_set |= AE_SET_BIRTHTIME; + entry->ae_stat.aest_birthtime = m; + entry->ae_stat.aest_birthtime_nsec = ns; +} + +void +archive_entry_unset_birthtime(struct archive_entry *entry) +{ + archive_entry_set_birthtime(entry, 0, 0); + entry->ae_set &= ~AE_SET_BIRTHTIME; +} + void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; + entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } +void +archive_entry_unset_ctime(struct archive_entry *entry) +{ + archive_entry_set_ctime(entry, 0, 0); + entry->ae_set &= ~AE_SET_CTIME; +} + void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { @@ -836,7 +922,7 @@ archive_entry_set_devminor(struct archive_entry *entry, dev_t m) void archive_entry_set_link(struct archive_entry *entry, const char *target) { - if (entry->ae_symlinkset) + if (entry->ae_set & AE_SET_SYMLINK) aes_set_mbs(&entry->ae_symlink, target); else aes_set_mbs(&entry->ae_hardlink, target); @@ -846,7 +932,7 @@ archive_entry_set_link(struct archive_entry *entry, const char *target) void archive_entry_copy_link(struct archive_entry *entry, const char *target) { - if (entry->ae_symlinkset) + if (entry->ae_set & AE_SET_SYMLINK) aes_copy_mbs(&entry->ae_symlink, target); else aes_copy_mbs(&entry->ae_hardlink, target); @@ -856,7 +942,7 @@ archive_entry_copy_link(struct archive_entry *entry, const char *target) void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { - if (entry->ae_symlinkset) + if (entry->ae_set & AE_SET_SYMLINK) aes_copy_wcs(&entry->ae_symlink, target); else aes_copy_wcs(&entry->ae_hardlink, target); @@ -865,7 +951,7 @@ archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { - if (entry->ae_symlinkset) + if (entry->ae_set & AE_SET_SYMLINK) return (aes_update_utf8(&entry->ae_symlink, target)); else return (aes_update_utf8(&entry->ae_hardlink, target)); @@ -882,10 +968,18 @@ void archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; + entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = m; entry->ae_stat.aest_mtime_nsec = ns; } +void +archive_entry_unset_mtime(struct archive_entry *entry) +{ + archive_entry_set_mtime(entry, 0, 0); + entry->ae_set &= ~AE_SET_MTIME; +} + void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { @@ -954,6 +1048,14 @@ archive_entry_set_size(struct archive_entry *entry, int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; + entry->ae_set |= AE_SET_SIZE; +} + +void +archive_entry_unset_size(struct archive_entry *entry) +{ + archive_entry_set_size(entry, 0); + entry->ae_set &= ~AE_SET_SIZE; } void @@ -967,7 +1069,9 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { aes_set_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) - entry->ae_symlinkset = 1; + entry->ae_set |= AE_SET_SYMLINK; + else + entry->ae_set &= ~AE_SET_SYMLINK; } void @@ -975,7 +1079,9 @@ archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { aes_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) - entry->ae_symlinkset = 1; + entry->ae_set |= AE_SET_SYMLINK; + else + entry->ae_set &= ~AE_SET_SYMLINK; } void @@ -983,7 +1089,9 @@ archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linknam { aes_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) - entry->ae_symlinkset = 1; + entry->ae_set |= AE_SET_SYMLINK; + else + entry->ae_set &= ~AE_SET_SYMLINK; } void diff --git a/contrib/libarchive/libarchive/archive_entry.h b/contrib/libarchive/libarchive/archive_entry.h index ee96c56a79..52fcc4a39f 100644 --- a/contrib/libarchive/libarchive/archive_entry.h +++ b/contrib/libarchive/libarchive/archive_entry.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.27 2008/05/26 17:00:22 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.31 2008/12/06 06:18:46 kientzle Exp $ */ #ifndef ARCHIVE_ENTRY_H_INCLUDED @@ -42,13 +42,15 @@ /* Get appropriate definitions of standard POSIX-style types. */ /* These should match the types used in 'struct stat' */ -#ifdef _WIN32 +#if defined(_WIN32) && !defined(__CYGWIN__) +#define __LA_INT64_T __int64 #define __LA_UID_T unsigned int #define __LA_GID_T unsigned int #define __LA_DEV_T unsigned int #define __LA_MODE_T unsigned short #else #include +#define __LA_INT64_T int64_t #define __LA_UID_T uid_t #define __LA_GID_T gid_t #define __LA_DEV_T dev_t @@ -69,7 +71,7 @@ * .lib. The default here assumes you're building a DLL. Only * libarchive source should ever define __LIBARCHIVE_BUILD. */ -#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC) # ifdef __LIBARCHIVE_BUILD # ifdef __GNUC__ # define __LA_DECL __attribute__((dllexport)) extern @@ -152,12 +154,32 @@ __LA_DECL struct archive_entry *archive_entry_new(void); /* * Retrieve fields from an archive_entry. + * + * There are a number of implicit conversions among these fields. For + * example, if a regular string field is set and you read the _w wide + * character field, the entry will implicitly convert narrow-to-wide + * using the current locale. Similarly, dev values are automatically + * updated when you write devmajor or devminor and vice versa. + * + * In addition, fields can be "set" or "unset." Unset string fields + * return NULL, non-string fields have _is_set() functions to test + * whether they've been set. You can "unset" a string field by + * assigning NULL; non-string fields have _unset() functions to + * unset them. + * + * Note: There is one ambiguity in the above; string fields will + * also return NULL when implicit character set conversions fail. + * This is usually what you want. */ - __LA_DECL time_t archive_entry_atime(struct archive_entry *); __LA_DECL long archive_entry_atime_nsec(struct archive_entry *); +__LA_DECL int archive_entry_atime_is_set(struct archive_entry *); +__LA_DECL time_t archive_entry_birthtime(struct archive_entry *); +__LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *); +__LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *); __LA_DECL time_t archive_entry_ctime(struct archive_entry *); __LA_DECL long archive_entry_ctime_nsec(struct archive_entry *); +__LA_DECL int archive_entry_ctime_is_set(struct archive_entry *); __LA_DECL dev_t archive_entry_dev(struct archive_entry *); __LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_devminor(struct archive_entry *); @@ -175,6 +197,7 @@ __LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); __LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); +__LA_DECL int archive_entry_mtime_is_set(struct archive_entry *); __LA_DECL unsigned int archive_entry_nlink(struct archive_entry *); __LA_DECL const char *archive_entry_pathname(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); @@ -182,7 +205,8 @@ __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); __LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); __LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); -__LA_DECL int64_t archive_entry_size(struct archive_entry *); +__LA_DECL __LA_INT64_T archive_entry_size(struct archive_entry *); +__LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); @@ -195,10 +219,18 @@ __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); * * Note that string 'set' functions do not copy the string, only the pointer. * In contrast, 'copy' functions do copy the object pointed to. + * + * Note: As of libarchive 2.4, 'set' functions do copy the string and + * are therefore exact synonyms for the 'copy' versions. The 'copy' + * names will be retired in libarchive 3.0. */ __LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_unset_atime(struct archive_entry *); +__LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_unset_birthtime(struct archive_entry *); __LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_unset_ctime(struct archive_entry *); __LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t); @@ -226,6 +258,7 @@ __LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t * __LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_unset_mtime(struct archive_entry *); __LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int); __LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); @@ -235,7 +268,8 @@ __LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); __LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); __LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t); -__LA_DECL void archive_entry_set_size(struct archive_entry *, int64_t); +__LA_DECL void archive_entry_set_size(struct archive_entry *, __LA_INT64_T); +__LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); @@ -257,6 +291,7 @@ __LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char __LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); + /* * ACL routines. This used to simply store and return text-format ACL * strings, but that proved insufficient for a number of reasons: @@ -406,7 +441,7 @@ __LA_DECL int archive_entry_xattr_next(struct archive_entry *, * Note that archive_entry_size() is reset to zero if the file * body should not be written to the archive. Pay attention! */ -__LA_DECL struct archive_entry_linkresolver; +struct archive_entry_linkresolver; /* * There are three different strategies for marking hardlinks. diff --git a/contrib/libarchive/libarchive/archive_entry_copy_stat.c b/contrib/libarchive/libarchive/archive_entry_copy_stat.c index 514db02743..3f624fc8af 100644 --- a/contrib/libarchive/libarchive/archive_entry_copy_stat.c +++ b/contrib/libarchive/libarchive/archive_entry_copy_stat.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_copy_stat.c,v 1.1 2007/05/29 01:00:18 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_copy_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -43,10 +43,28 @@ archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec); archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); +#elif HAVE_STRUCT_STAT_ST_MTIME_N + archive_entry_set_atime(entry, st->st_atime, st->st_atime_n); + archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n); + archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); +#elif HAVE_STRUCT_STAT_ST_UMTIME + archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000); + archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000); + archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000); +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC + archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000); + archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000); + archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000); #else archive_entry_set_atime(entry, st->st_atime, 0); archive_entry_set_ctime(entry, st->st_ctime, 0); archive_entry_set_mtime(entry, st->st_mtime, 0); +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + archive_entry_set_birthtime(entry, st->st_birthtime, 0); +#endif +#endif +#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC + archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec); #endif archive_entry_set_dev(entry, st->st_dev); archive_entry_set_gid(entry, st->st_gid); diff --git a/contrib/libarchive/libarchive/archive_entry_link_resolver.c b/contrib/libarchive/libarchive/archive_entry_link_resolver.c index 4deee260d4..9300fe311f 100644 --- a/contrib/libarchive/libarchive/archive_entry_link_resolver.c +++ b/contrib/libarchive/libarchive/archive_entry_link_resolver.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_link_resolver.c,v 1.3 2008/06/15 04:31:43 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_link_resolver.c,v 1.4 2008/09/05 06:15:25 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -189,7 +189,7 @@ archive_entry_linkify(struct archive_entry_linkresolver *res, case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR: le = find_entry(res, *e); if (le != NULL) { - archive_entry_set_size(*e, 0); + archive_entry_unset_size(*e); archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); } else @@ -217,7 +217,7 @@ archive_entry_linkify(struct archive_entry_linkresolver *res, *e = le->entry; le->entry = t; /* Make the old entry into a hardlink. */ - archive_entry_set_size(*e, 0); + archive_entry_unset_size(*e); archive_entry_copy_hardlink(*e, archive_entry_pathname(le->canonical)); /* If we ran out of links, return the diff --git a/contrib/libarchive/libarchive/archive_entry_private.h b/contrib/libarchive/libarchive/archive_entry_private.h index 0289290b19..a4317decbf 100644 --- a/contrib/libarchive/libarchive/archive_entry_private.h +++ b/contrib/libarchive/libarchive/archive_entry_private.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.4 2008/05/26 17:00:22 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.6 2008/09/30 03:53:03 kientzle Exp $ */ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED @@ -112,6 +112,8 @@ struct archive_entry { uint32_t aest_ctime_nsec; int64_t aest_mtime; uint32_t aest_mtime_nsec; + int64_t aest_birthtime; + uint32_t aest_birthtime_nsec; gid_t aest_gid; ino_t aest_ino; mode_t aest_mode; @@ -136,6 +138,15 @@ struct archive_entry { dev_t aest_rdevminor; } ae_stat; + int ae_set; /* bitmap of fields that are currently set */ +#define AE_SET_HARDLINK 1 +#define AE_SET_SYMLINK 2 +#define AE_SET_ATIME 4 +#define AE_SET_CTIME 8 +#define AE_SET_MTIME 16 +#define AE_SET_BIRTHTIME 32 +#define AE_SET_SIZE 64 + /* * Use aes here so that we get transparent mbs<->wcs conversions. */ @@ -147,8 +158,6 @@ struct archive_entry { struct aes ae_pathname; /* Name of entry */ struct aes ae_symlink; /* symlink contents */ struct aes ae_uname; /* Name of owner */ - unsigned char ae_hardlinkset; - unsigned char ae_symlinkset; /* Not used within libarchive; useful for some clients. */ struct aes ae_sourcepath; /* Path this entry is sourced from. */ diff --git a/contrib/libarchive/libarchive/archive_entry_stat.c b/contrib/libarchive/libarchive/archive_entry_stat.c index 6ef5b37287..315d5cf615 100644 --- a/contrib/libarchive/libarchive/archive_entry_stat.c +++ b/contrib/libarchive/libarchive/archive_entry_stat.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_stat.c,v 1.1 2007/05/29 01:00:18 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_stat.c,v 1.2 2008/09/30 03:53:03 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -64,6 +64,9 @@ archive_entry_stat(struct archive_entry *entry) * the appropriate conversions get invoked. */ st->st_atime = archive_entry_atime(entry); +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + st->st_birthtime = archive_entry_birthtime(entry); +#endif st->st_ctime = archive_entry_ctime(entry); st->st_mtime = archive_entry_mtime(entry); st->st_dev = archive_entry_dev(entry); @@ -87,6 +90,21 @@ archive_entry_stat(struct archive_entry *entry) st->st_atim.tv_nsec = archive_entry_atime_nsec(entry); st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry); st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry); +#elif HAVE_STRUCT_STAT_ST_MTIME_N + st->st_atime_n = archive_entry_atime_nsec(entry); + st->st_ctime_n = archive_entry_ctime_nsec(entry); + st->st_mtime_n = archive_entry_mtime_nsec(entry); +#elif HAVE_STRUCT_STAT_ST_UMTIME + st->st_uatime = archive_entry_atime_nsec(entry) / 1000; + st->st_uctime = archive_entry_ctime_nsec(entry) / 1000; + st->st_umtime = archive_entry_mtime_nsec(entry) / 1000; +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC + st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000; + st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000; + st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000; +#endif +#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC + st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry); #endif /* diff --git a/contrib/libarchive/libarchive/archive_platform.h b/contrib/libarchive/libarchive/archive_platform.h index 99dfacc50f..763c296579 100644 --- a/contrib/libarchive/libarchive/archive_platform.h +++ b/contrib/libarchive/libarchive/archive_platform.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.30 2008/05/26 17:00:22 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.32 2008/12/06 05:53:05 kientzle Exp $ */ /* @@ -39,15 +39,12 @@ /* archive.h and archive_entry.h require this. */ #define __LIBARCHIVE_BUILD 1 -#ifdef _WIN32 -#include "config_windows.h" -#include "archive_windows.h" -#elif defined(PLATFORM_CONFIG_H) +#if defined(PLATFORM_CONFIG_H) /* Use hand-built config.h in environments that need it. */ #include PLATFORM_CONFIG_H #elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ -#include "../config.h" +#include "config.h" #else /* Warn if the library hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no pre-built configuration in archive_platform.h. @@ -70,7 +67,8 @@ /* Try to get standard C99-style integer type definitions. */ #if HAVE_INTTYPES_H #include -#elif HAVE_STDINT_H +#endif +#if HAVE_STDINT_H #include #endif @@ -78,6 +76,9 @@ #if !HAVE_DECL_SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif +#if !HAVE_DECL_SSIZE_MAX +#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1)) +#endif #if !HAVE_DECL_UINT32_MAX #define UINT32_MAX (~(uint32_t)0) #endif diff --git a/contrib/libarchive/libarchive/archive_private.h b/contrib/libarchive/libarchive/archive_private.h index 9ca5893d80..a25dd22746 100644 --- a/contrib/libarchive/libarchive/archive_private.h +++ b/contrib/libarchive/libarchive/archive_private.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.29 2007/04/02 00:15:45 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.32 2008/12/06 06:23:37 kientzle Exp $ */ #ifndef ARCHIVE_PRIVATE_H_INCLUDED @@ -31,22 +31,30 @@ #include "archive.h" #include "archive_string.h" +#if defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_DEAD __attribute__((__noreturn__)) +#else +#define __LA_DEAD +#endif + #define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU) #define ARCHIVE_READ_MAGIC (0xdeb0c5U) -#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U) +#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U) +#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U) #define ARCHIVE_STATE_ANY 0xFFFFU #define ARCHIVE_STATE_NEW 1U #define ARCHIVE_STATE_HEADER 2U #define ARCHIVE_STATE_DATA 4U -#define ARCHIVE_STATE_DATA_END 8U +#define ARCHIVE_STATE_DATA_END 8U #define ARCHIVE_STATE_EOF 0x10U #define ARCHIVE_STATE_CLOSED 0x20U #define ARCHIVE_STATE_FATAL 0x8000U struct archive_vtable { - int (*archive_write_close)(struct archive *); - int (*archive_write_finish)(struct archive *); + int (*archive_close)(struct archive *); + int (*archive_finish)(struct archive *); int (*archive_write_header)(struct archive *, struct archive_entry *); int (*archive_write_finish_entry)(struct archive *); @@ -92,7 +100,10 @@ struct archive { void __archive_check_magic(struct archive *, unsigned int magic, unsigned int state, const char *func); -void __archive_errx(int retvalue, const char *msg); +void __archive_errx(int retvalue, const char *msg) __LA_DEAD; + +int __archive_parse_options(const char *p, const char *fn, + int keysize, char *key, int valsize, char *val); #define err_combine(a,b) ((a) < (b) ? (a) : (b)) diff --git a/contrib/libarchive/libarchive/archive_read.3 b/contrib/libarchive/libarchive/archive_read.3 index 13063d917c..d6d2c74c85 100644 --- a/contrib/libarchive/libarchive/archive_read.3 +++ b/contrib/libarchive/libarchive/archive_read.3 @@ -29,6 +29,9 @@ .Os .Sh NAME .Nm archive_read_new , +.Nm archive_read_set_filter_options , +.Nm archive_read_set_format_options , +.Nm archive_read_set_options , .Nm archive_read_support_compression_all , .Nm archive_read_support_compression_bzip2 , .Nm archive_read_support_compression_compress , @@ -48,6 +51,7 @@ .Nm archive_read_open_filename , .Nm archive_read_open_memory , .Nm archive_read_next_header , +.Nm archive_read_next_header2 , .Nm archive_read_data , .Nm archive_read_data_block , .Nm archive_read_data_skip , @@ -93,6 +97,12 @@ .Ft int .Fn archive_read_support_format_zip "struct archive *" .Ft int +.Fn archive_read_set_filter_options "struct archive *" "const char *" +.Ft int +.Fn archive_read_set_format_options "struct archive *" "const char *" +.Ft int +.Fn archive_read_set_options "struct archive *" "const char *" +.Ft int .Fo archive_read_open .Fa "struct archive *" .Fa "void *client_data" @@ -123,6 +133,8 @@ .Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size" .Ft int .Fn archive_read_next_header "struct archive *" "struct archive_entry **" +.Ft int +.Fn archive_read_next_header2 "struct archive *" "struct archive_entry *" .Ft ssize_t .Fn archive_read_data "struct archive *" "void *buff" "size_t len" .Ft int @@ -214,6 +226,48 @@ For convenience, .Fn archive_read_support_format_all enables support for all available formats. Only empty archives are supported by default. +.It Xo +.Fn archive_read_set_filter_options , +.Fn archive_read_set_format_options , +.Fn archive_read_set_options +.Xc +Specifies options that will be passed to currently-registered +filters (including decompression filters) and/or format readers. +The argument is a comma-separated list of individual options. +Individual options have one of the following forms: +.Bl -tag -compact -width indent +.It Ar option=value +The option/value pair will be provided to every module. +Modules that do not accept an option with this name will ignore it. +.It Ar option +The option will be provided to every module with a value of +.Dq 1 . +.It Ar !option +The option will be provided to every module with a NULL value. +.It Ar module:option=value , Ar module:option , Ar module:!option +As above, but the corresponding option and value will be provided +only to modules whose name matches +.Ar module . +.El +The return value will be +.Cm ARCHIVE_OK +if any module accepts the option, or +.Cm ARCHIVE_WARN +if no module accepted the option, or +.Cm ARCHIVE_FATAL +if there was a fatal error while attempting to process the option. +.Pp +The currently supported options are: +.Bl -tag -compact -width indent +.It Format iso9660 +.Bl -tag -compact -width indent +.It Cm joliet +Support Joliet extensions. +Defaults to enabled, use +.Cm !joliet +to disable. +.El +.El .It Fn archive_read_open The same as .Fn archive_read_open2 , @@ -266,6 +320,14 @@ memory containing the archive data. Read the header for the next entry and return a pointer to a .Tn struct archive_entry . +This is a convenience wrapper around +.Fn archive_read_next_header2 +that uses an internal +.Tn struct archive_entry +object. +.It Fn archive_read_next_header2 +Read the header for the next entry and populate the provided +.Tn struct archive_entry . .It Fn archive_read_data Read data associated with the header just read. Internally, this is a convenience function that calls diff --git a/contrib/libarchive/libarchive/archive_read.c b/contrib/libarchive/libarchive/archive_read.c index 327969f418..d4233c9ad5 100644 --- a/contrib/libarchive/libarchive/archive_read.c +++ b/contrib/libarchive/libarchive/archive_read.c @@ -32,7 +32,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.38 2008/03/12 04:58:32 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.39 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -53,9 +53,26 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.38 2008/03/12 04:58:32 #include "archive_private.h" #include "archive_read_private.h" -static void choose_decompressor(struct archive_read *, const void*, size_t); +#define minimum(a, b) (a < b ? a : b) + +static int build_stream(struct archive_read *); static int choose_format(struct archive_read *); -static off_t dummy_skip(struct archive_read *, off_t); +static struct archive_vtable *archive_read_vtable(void); +static int _archive_read_close(struct archive *); +static int _archive_read_finish(struct archive *); + +static struct archive_vtable * +archive_read_vtable(void) +{ + static struct archive_vtable av; + static int inited = 0; + + if (!inited) { + av.archive_finish = _archive_read_finish; + av.archive_close = _archive_read_close; + } + return (&av); +} /* * Allocate, initialize and return a struct archive object. @@ -73,9 +90,7 @@ archive_read_new(void) a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new(); - - /* We always support uncompressed archives. */ - archive_read_support_compression_none(&a->archive); + a->archive.vtable = archive_read_vtable(); return (&a->archive); } @@ -93,6 +108,111 @@ archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i) a->skip_file_ino = i; } +/* + * Set read options for the format. + */ +int +archive_read_set_format_options(struct archive *_a, const char *s) +{ + struct archive_read *a; + struct archive_format_descriptor *format; + char key[64], val[64]; + size_t i; + int len, r; + + if (s == NULL || *s == '\0') + return (ARCHIVE_OK); + a = (struct archive_read *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_set_format_options"); + len = 0; + for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) { + format = &a->formats[i]; + if (format == NULL || format->options == NULL || + format->name == NULL) + /* This format does not support option. */ + continue; + + while ((len = __archive_parse_options(s, format->name, + sizeof(key), key, sizeof(val), val)) > 0) { + if (val[0] == '\0') + r = format->options(a, key, NULL); + else + r = format->options(a, key, val); + if (r == ARCHIVE_FATAL) + return (r); + s += len; + } + } + if (len < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Illegal format options."); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +/* + * Set read options for the filter. + */ +int +archive_read_set_filter_options(struct archive *_a, const char *s) +{ + struct archive_read *a; + struct archive_read_filter *filter; + struct archive_read_filter_bidder *bidder; + char key[64], val[64]; + int len, r; + + if (s == NULL || *s == '\0') + return (ARCHIVE_OK); + a = (struct archive_read *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_set_filter_options"); + filter = a->filter; + len = 0; + for (filter = a->filter; filter != NULL; filter = filter->upstream) { + bidder = filter->bidder; + if (bidder == NULL) + continue; + if (bidder->options == NULL) + /* This bidder does not support option */ + continue; + while ((len = __archive_parse_options(s, filter->name, + sizeof(key), key, sizeof(val), val)) > 0) { + if (val[0] == '\0') + r = bidder->options(bidder, key, NULL); + else + r = bidder->options(bidder, key, val); + if (r == ARCHIVE_FATAL) + return (r); + s += len; + } + } + if (len < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Illegal format options."); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +/* + * Set read options for the format and the filter. + */ +int +archive_read_set_options(struct archive *_a, const char *s) +{ + int r; + + r = archive_read_set_format_options(_a, s); + if (r != ARCHIVE_OK) + return (r); + r = archive_read_set_filter_options(_a, s); + if (r != ARCHIVE_OK) + return (r); + return (ARCHIVE_OK); +} /* * Open the archive @@ -108,6 +228,41 @@ archive_read_open(struct archive *a, void *client_data, client_reader, NULL, client_closer); } +static ssize_t +client_read_proxy(struct archive_read_filter *self, const void **buff) +{ + ssize_t r; + r = (self->archive->client.reader)(&self->archive->archive, + self->data, buff); + self->archive->archive.raw_position += r; + return (r); +} + +static int64_t +client_skip_proxy(struct archive_read_filter *self, int64_t request) +{ + int64_t r; + if (self->archive->client.skipper == NULL) + return (0); + r = (self->archive->client.skipper)(&self->archive->archive, + self->data, request); + self->archive->archive.raw_position += r; + return (r); +} + +static int +client_close_proxy(struct archive_read_filter *self) +{ + int r = ARCHIVE_OK; + + if (self->archive->client.closer != NULL) + r = (self->archive->client.closer)((struct archive *)self->archive, + self->data); + self->data = NULL; + return (r); +} + + int archive_read_open2(struct archive *_a, void *client_data, archive_open_callback *client_opener, @@ -116,28 +271,16 @@ archive_read_open2(struct archive *_a, void *client_data, archive_close_callback *client_closer) { struct archive_read *a = (struct archive_read *)_a; - const void *buffer; - ssize_t bytes_read; + struct archive_read_filter *filter; int e; - __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); + __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_open"); if (client_reader == NULL) __archive_errx(1, "No reader function provided to archive_read_open"); - /* - * Set these NULL initially. If the open or initial read fails, - * we'll leave them NULL to indicate that the file is invalid. - * (In particular, this helps ensure that the closer doesn't - * get called more than once.) - */ - a->client_opener = NULL; - a->client_reader = NULL; - a->client_skipper = NULL; - a->client_closer = NULL; - a->client_data = NULL; - /* Open data source. */ if (client_opener != NULL) { e =(client_opener)(&a->archive, client_data); @@ -149,147 +292,99 @@ archive_read_open2(struct archive *_a, void *client_data, } } - /* Read first block now for compress format detection. */ - bytes_read = (client_reader)(&a->archive, client_data, &buffer); - - if (bytes_read < 0) { - /* If the first read fails, close before returning error. */ - if (client_closer) - (client_closer)(&a->archive, client_data); - /* client_reader should have already set error information. */ - return (ARCHIVE_FATAL); - } - - /* Now that the client callbacks have worked, remember them. */ - a->client_opener = client_opener; /* Do we need to remember this? */ - a->client_reader = client_reader; - a->client_skipper = client_skipper; - a->client_closer = client_closer; - a->client_data = client_data; + /* Save the client functions and mock up the initial source. */ + a->client.reader = client_reader; + a->client.skipper = client_skipper; + a->client.closer = client_closer; - /* Select a decompression routine. */ - choose_decompressor(a, buffer, (size_t)bytes_read); - if (a->decompressor == NULL) + filter = calloc(1, sizeof(*filter)); + if (filter == NULL) return (ARCHIVE_FATAL); - - /* Initialize decompression routine with the first block of data. */ - e = (a->decompressor->init)(a, buffer, (size_t)bytes_read); - + filter->bidder = NULL; + filter->upstream = NULL; + filter->archive = a; + filter->data = client_data; + filter->read = client_read_proxy; + filter->skip = client_skip_proxy; + filter->close = client_close_proxy; + filter->name = "none"; + filter->code = ARCHIVE_COMPRESSION_NONE; + a->filter = filter; + + /* Build out the input pipeline. */ + e = build_stream(a); if (e == ARCHIVE_OK) a->archive.state = ARCHIVE_STATE_HEADER; - /* - * If the decompressor didn't register a skip function, provide a - * dummy compression-layer skip function. - */ - if (a->decompressor->skip == NULL) - a->decompressor->skip = dummy_skip; - return (e); } /* - * Allow each registered decompression routine to bid on whether it - * wants to handle this stream. Return index of winning bidder. + * Allow each registered stream transform to bid on whether + * it wants to handle this stream. Repeat until we've finished + * building the pipeline. */ -static void -choose_decompressor(struct archive_read *a, - const void *buffer, size_t bytes_read) +static int +build_stream(struct archive_read *a) { - int decompression_slots, i, bid, best_bid; - struct decompressor_t *decompressor, *best_decompressor; + int number_bidders, i, bid, best_bid; + struct archive_read_filter_bidder *bidder, *best_bidder; + struct archive_read_filter *filter; + int r; - decompression_slots = sizeof(a->decompressors) / - sizeof(a->decompressors[0]); + for (;;) { + number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); - best_bid = 0; - a->decompressor = NULL; - best_decompressor = NULL; + best_bid = 0; + best_bidder = NULL; - decompressor = a->decompressors; - for (i = 0; i < decompression_slots; i++) { - if (decompressor->bid) { - bid = (decompressor->bid)(buffer, bytes_read); - if (bid > best_bid || best_decompressor == NULL) { - best_bid = bid; - best_decompressor = decompressor; + bidder = a->bidders; + for (i = 0; i < number_bidders; i++, bidder++) { + if (bidder->bid != NULL) { + bid = (bidder->bid)(bidder, a->filter); + if (bid > best_bid) { + best_bid = bid; + best_bidder = bidder; + } } } - decompressor ++; - } - - /* - * There were no bidders; this is a serious programmer error - * and demands a quick and definitive abort. - */ - if (best_decompressor == NULL) - __archive_errx(1, "No decompressors were registered; you " - "must call at least one " - "archive_read_support_compression_XXX function in order " - "to successfully read an archive."); - /* - * There were bidders, but no non-zero bids; this means we can't - * support this stream. - */ - if (best_bid < 1) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unrecognized archive format"); - return; - } - - /* Record the best decompressor for this stream. */ - a->decompressor = best_decompressor; -} - -/* - * Dummy skip function, for use if the compression layer doesn't provide - * one: This code just reads data and discards it. - */ -static off_t -dummy_skip(struct archive_read * a, off_t request) -{ - const void * dummy_buffer; - ssize_t bytes_read; - off_t bytes_skipped; + /* If no bidder, we're done. */ + if (best_bidder == NULL) { + a->archive.compression_name = a->filter->name; + a->archive.compression_code = a->filter->code; + return (ARCHIVE_OK); + } - for (bytes_skipped = 0; request > 0;) { - bytes_read = (a->decompressor->read_ahead)(a, &dummy_buffer, 1); - if (bytes_read < 0) - return (bytes_read); - if (bytes_read == 0) { - /* Premature EOF. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated input file (need to skip %jd bytes)", - (intmax_t)request); + filter + = (struct archive_read_filter *)calloc(1, sizeof(*filter)); + if (filter == NULL) return (ARCHIVE_FATAL); + filter->bidder = best_bidder; + filter->archive = a; + filter->upstream = a->filter; + r = (best_bidder->init)(filter); + if (r != ARCHIVE_OK) { + free(filter); + return (r); } - if (bytes_read > request) - bytes_read = (ssize_t)request; - (a->decompressor->consume)(a, (size_t)bytes_read); - request -= bytes_read; - bytes_skipped += bytes_read; + a->filter = filter; } - - return (bytes_skipped); } /* * Read header of next entry. */ int -archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read *a = (struct archive_read *)_a; - struct archive_entry *entry; int slot, ret; __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header"); - *entryp = NULL; - entry = a->entry; archive_entry_clear(entry); archive_clear_error(&a->archive); @@ -347,12 +442,22 @@ archive_read_next_header(struct archive *_a, struct archive_entry **entryp) break; } - *entryp = entry; a->read_data_output_offset = 0; a->read_data_remaining = 0; return (ret); } +int +archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +{ + int ret; + struct archive_read *a = (struct archive_read *)_a; + *entryp = NULL; + ret = archive_read_next_header2(_a, a->entry); + *entryp = a->entry; + return ret; +} + /* * Allow each registered format to bid on whether it wants to handle * the next entry. Return index of winning bidder. @@ -581,8 +686,8 @@ archive_read_data_block(struct archive *_a, * Don't assume we actually read anything or performed any non-trivial * initialization. */ -int -archive_read_close(struct archive *_a) +static int +_archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; @@ -590,29 +695,37 @@ archive_read_close(struct archive *_a) __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close"); + archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; + /* Call cleanup functions registered by optional components. */ if (a->cleanup_archive_extract != NULL) r = (a->cleanup_archive_extract)(a); /* TODO: Clean up the formatters. */ - /* Clean up the decompressors. */ - n = sizeof(a->decompressors)/sizeof(a->decompressors[0]); - for (i = 0; i < n; i++) { - if (a->decompressors[i].finish != NULL) { - r1 = (a->decompressors[i].finish)(a); + /* Clean up the filter pipeline. */ + while (a->filter != NULL) { + struct archive_read_filter *t = a->filter->upstream; + if (a->filter->close != NULL) { + r1 = (a->filter->close)(a->filter); if (r1 < r) r = r1; } + free(a->filter->buffer); + free(a->filter); + a->filter = t; } - /* Close the client stream. */ - if (a->client_closer != NULL) { - r1 = ((a->client_closer)(&a->archive, a->client_data)); - if (r1 < r) - r = r1; + /* Release the bidder objects. */ + n = sizeof(a->bidders)/sizeof(a->bidders[0]); + for (i = 0; i < n; i++) { + if (a->bidders[i].free != NULL) { + r1 = (a->bidders[i].free)(&a->bidders[i]); + if (r1 < r) + r = r1; + } } return (r); @@ -621,13 +734,8 @@ archive_read_close(struct archive *_a) /* * Release memory and other resources. */ -#if ARCHIVE_API_VERSION > 1 int -#else -/* Temporarily allow library to compile with either 1.x or 2.0 API. */ -void -#endif -archive_read_finish(struct archive *_a) +_archive_read_finish(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int i; @@ -664,7 +772,9 @@ archive_read_finish(struct archive *_a) int __archive_read_register_format(struct archive_read *a, void *format_data, + const char *name, int (*bid)(struct archive_read *), + int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), int (*read_data_skip)(struct archive_read *), @@ -683,11 +793,13 @@ __archive_read_register_format(struct archive_read *a, return (ARCHIVE_WARN); /* We've already installed */ if (a->formats[i].bid == NULL) { a->formats[i].bid = bid; + a->formats[i].options = options; a->formats[i].read_header = read_header; a->formats[i].read_data = read_data; a->formats[i].read_data_skip = read_data_skip; a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; + a->formats[i].name = name; return (ARCHIVE_OK); } } @@ -700,26 +812,21 @@ __archive_read_register_format(struct archive_read *a, * Used internally by decompression routines to register their bid and * initialization functions. */ -struct decompressor_t * -__archive_read_register_compression(struct archive_read *a, - int (*bid)(const void *, size_t), - int (*init)(struct archive_read *, const void *, size_t)) +struct archive_read_filter_bidder * +__archive_read_get_bidder(struct archive_read *a) { int i, number_slots; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "__archive_read_register_compression"); + "__archive_read_get_bidder"); - number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]); + number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]); for (i = 0; i < number_slots; i++) { - if (a->decompressors[i].bid == bid) - return (a->decompressors + i); - if (a->decompressors[i].bid == NULL) { - a->decompressors[i].bid = bid; - a->decompressors[i].init = init; - return (a->decompressors + i); + if (a->bidders[i].bid == NULL) { + memset(a->bidders + i, 0, sizeof(a->bidders[0])); + return (a->bidders + i); } } @@ -727,13 +834,358 @@ __archive_read_register_compression(struct archive_read *a, return (NULL); /* Never actually executed. */ } -/* used internally to simplify read-ahead */ +/* + * The next three functions comprise the peek/consume internal I/O + * system used by archive format readers. This system allows fairly + * flexible read-ahead and allows the I/O code to operate in a + * zero-copy manner most of the time. + * + * In the ideal case, filters generate blocks of data + * and __archive_read_ahead() just returns pointers directly into + * those blocks. Then __archive_read_consume() just bumps those + * pointers. Only if your request would span blocks does the I/O + * layer use a copy buffer to provide you with a contiguous block of + * data. The __archive_read_skip() is an optimization; it scans ahead + * very quickly (it usually translates into a seek() operation if + * you're reading uncompressed disk files). + * + * A couple of useful idioms: + * * "I just want some data." Ask for 1 byte and pay attention to + * the "number of bytes available" from __archive_read_ahead(). + * You can consume more than you asked for; you just can't consume + * more than is available. If you consume everything that's + * immediately available, the next read_ahead() call will pull + * the next block. + * * "I want to output a large block of data." As above, ask for 1 byte, + * emit all that's available (up to whatever limit you have), then + * repeat until you're done. + * * "I want to peek ahead by a large amount." Ask for 4k or so, then + * double and repeat until you get an error or have enough. Note + * that the I/O layer will likely end up expanding its copy buffer + * to fit your request, so use this technique cautiously. This + * technique is used, for example, by some of the format tasting + * code that has uncertain look-ahead needs. + * + * TODO: Someday, provide a more generic __archive_read_seek() for + * those cases where it's useful. This is tricky because there are lots + * of cases where seek() is not available (reading gzip data from a + * network socket, for instance), so there needs to be a good way to + * communicate whether seek() is available and users of that interface + * need to use non-seeking strategies whenever seek() is not available. + */ + +/* + * Looks ahead in the input stream: + * * If 'avail' pointer is provided, that returns number of bytes available + * in the current buffer, which may be much larger than requested. + * * If end-of-file, *avail gets set to zero. + * * If error, *avail gets error code. + * * If request can be met, returns pointer to data, returns NULL + * if request is not met. + * + * Note: If you just want "some data", ask for 1 byte and pay attention + * to *avail, which will have the actual amount available. If you + * know exactly how many bytes you need, just ask for that and treat + * a NULL return as an error. + * + * Important: This does NOT move the file pointer. See + * __archive_read_consume() below. + */ + +/* + * This is tricky. We need to provide our clients with pointers to + * contiguous blocks of memory but we want to avoid copying whenever + * possible. + * + * Mostly, this code returns pointers directly into the block of data + * provided by the client_read routine. It can do this unless the + * request would split across blocks. In that case, we have to copy + * into an internal buffer to combine reads. + */ +const void * +__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) +{ + return (__archive_read_filter_ahead(a->filter, min, avail)); +} + const void * -__archive_read_ahead(struct archive_read *a, size_t len) +__archive_read_filter_ahead(struct archive_read_filter *filter, + size_t min, ssize_t *avail) { - const void *h; + ssize_t bytes_read; + size_t tocopy; - if ((a->decompressor->read_ahead)(a, &h, len) < (ssize_t)len) + if (filter->fatal) { + if (avail) + *avail = ARCHIVE_FATAL; return (NULL); - return (h); + } + + /* + * Keep pulling more data until we can satisfy the request. + */ + for (;;) { + + /* + * If we can satisfy from the copy buffer, we're done. + */ + if (filter->avail >= min) { + if (avail != NULL) + *avail = filter->avail; + return (filter->next); + } + + /* + * We can satisfy directly from client buffer if everything + * currently in the copy buffer is still in the client buffer. + */ + if (filter->client_total >= filter->client_avail + filter->avail + && filter->client_avail + filter->avail >= min) { + /* "Roll back" to client buffer. */ + filter->client_avail += filter->avail; + filter->client_next -= filter->avail; + /* Copy buffer is now empty. */ + filter->avail = 0; + filter->next = filter->buffer; + /* Return data from client buffer. */ + if (avail != NULL) + *avail = filter->client_avail; + return (filter->client_next); + } + + /* Move data forward in copy buffer if necessary. */ + if (filter->next > filter->buffer && + filter->next + min > filter->buffer + filter->buffer_size) { + if (filter->avail > 0) + memmove(filter->buffer, filter->next, filter->avail); + filter->next = filter->buffer; + } + + /* If we've used up the client data, get more. */ + if (filter->client_avail <= 0) { + if (filter->end_of_file) { + if (avail != NULL) + *avail = 0; + return (NULL); + } + bytes_read = (filter->read)(filter, + &filter->client_buff); + if (bytes_read < 0) { /* Read error. */ + filter->client_total = filter->client_avail = 0; + filter->client_next = filter->client_buff = NULL; + filter->fatal = 1; + if (avail != NULL) + *avail = ARCHIVE_FATAL; + return (NULL); + } + if (bytes_read == 0) { /* Premature end-of-file. */ + filter->client_total = filter->client_avail = 0; + filter->client_next = filter->client_buff = NULL; + filter->end_of_file = 1; + /* Return whatever we do have. */ + if (avail != NULL) + *avail = filter->avail; + return (NULL); + } + filter->position += bytes_read; + filter->client_total = bytes_read; + filter->client_avail = filter->client_total; + filter->client_next = filter->client_buff; + } + else + { + /* + * We can't satisfy the request from the copy + * buffer or the existing client data, so we + * need to copy more client data over to the + * copy buffer. + */ + + /* Ensure the buffer is big enough. */ + if (min > filter->buffer_size) { + size_t s, t; + char *p; + + /* Double the buffer; watch for overflow. */ + s = t = filter->buffer_size; + if (s == 0) + s = min; + while (s < min) { + t *= 2; + if (t <= s) { /* Integer overflow! */ + archive_set_error( + &filter->archive->archive, + ENOMEM, + "Unable to allocate copy buffer"); + filter->fatal = 1; + if (avail != NULL) + *avail = ARCHIVE_FATAL; + return (NULL); + } + s = t; + } + /* Now s >= min, so allocate a new buffer. */ + p = (char *)malloc(s); + if (p == NULL) { + archive_set_error( + &filter->archive->archive, + ENOMEM, + "Unable to allocate copy buffer"); + filter->fatal = 1; + if (avail != NULL) + *avail = ARCHIVE_FATAL; + return (NULL); + } + /* Move data into newly-enlarged buffer. */ + if (filter->avail > 0) + memmove(p, filter->next, filter->avail); + free(filter->buffer); + filter->next = filter->buffer = p; + filter->buffer_size = s; + } + + /* We can add client data to copy buffer. */ + /* First estimate: copy to fill rest of buffer. */ + tocopy = (filter->buffer + filter->buffer_size) + - (filter->next + filter->avail); + /* Don't waste time buffering more than we need to. */ + if (tocopy + filter->avail > min) + tocopy = min - filter->avail; + /* Don't copy more than is available. */ + if (tocopy > filter->client_avail) + tocopy = filter->client_avail; + + memcpy(filter->next + filter->avail, filter->client_next, + tocopy); + /* Remove this data from client buffer. */ + filter->client_next += tocopy; + filter->client_avail -= tocopy; + /* add it to copy buffer. */ + filter->avail += tocopy; + } + } +} + +/* + * Move the file pointer forward. This should be called after + * __archive_read_ahead() returns data to you. Don't try to move + * ahead by more than the amount of data available according to + * __archive_read_ahead(). + */ +/* + * Mark the appropriate data as used. Note that the request here will + * often be much smaller than the size of the previous read_ahead + * request. + */ +ssize_t +__archive_read_consume(struct archive_read *a, size_t request) +{ + ssize_t r; + r = __archive_read_filter_consume(a->filter, request); + a->archive.file_position += r; + return (r); +} + +ssize_t +__archive_read_filter_consume(struct archive_read_filter * filter, + size_t request) +{ + if (filter->avail > 0) { + /* Read came from copy buffer. */ + filter->next += request; + filter->avail -= request; + } else { + /* Read came from client buffer. */ + filter->client_next += request; + filter->client_avail -= request; + } + return (request); +} + +/* + * Move the file pointer ahead by an arbitrary amount. If you're + * reading uncompressed data from a disk file, this will actually + * translate into a seek() operation. Even in cases where seek() + * isn't feasible, this at least pushes the read-and-discard loop + * down closer to the data source. + */ +int64_t +__archive_read_skip(struct archive_read *a, int64_t request) +{ + return (__archive_read_filter_skip(a->filter, request)); +} + +int64_t +__archive_read_filter_skip(struct archive_read_filter *filter, int64_t request) +{ + off_t bytes_skipped, total_bytes_skipped = 0; + size_t min; + + if (filter->fatal) + return (-1); + /* + * If there is data in the buffers already, use that first. + */ + if (filter->avail > 0) { + min = minimum(request, (off_t)filter->avail); + bytes_skipped = __archive_read_consume(filter->archive, min); + request -= bytes_skipped; + total_bytes_skipped += bytes_skipped; + } + if (filter->client_avail > 0) { + min = minimum(request, (off_t)filter->client_avail); + bytes_skipped = __archive_read_consume(filter->archive, min); + request -= bytes_skipped; + total_bytes_skipped += bytes_skipped; + } + if (request == 0) + return (total_bytes_skipped); + /* + * If a client_skipper was provided, try that first. + */ +#if ARCHIVE_API_VERSION < 2 + if ((filter->skip != NULL) && (request < SSIZE_MAX)) { +#else + if (filter->skip != NULL) { +#endif + bytes_skipped = (filter->skip)(filter, request); + if (bytes_skipped < 0) { /* error */ + filter->client_total = filter->client_avail = 0; + filter->client_next = filter->client_buff = NULL; + filter->fatal = 1; + return (bytes_skipped); + } + filter->archive->archive.file_position += bytes_skipped; + total_bytes_skipped += bytes_skipped; + request -= bytes_skipped; + filter->client_next = filter->client_buff; + filter->client_avail = filter->client_total = 0; + } + /* + * Note that client_skipper will usually not satisfy the + * full request (due to low-level blocking concerns), + * so even if client_skipper is provided, we may still + * have to use ordinary reads to finish out the request. + */ + while (request > 0) { + const void* dummy_buffer; + ssize_t bytes_read; + dummy_buffer = __archive_read_ahead(filter->archive, + 1, &bytes_read); + if (bytes_read < 0) + return (bytes_read); + if (bytes_read == 0) { + /* We hit EOF before we satisfied the skip request. */ + archive_set_error(&filter->archive->archive, + ARCHIVE_ERRNO_MISC, + "Truncated input file (need to skip %jd bytes)", + (intmax_t)request); + return (ARCHIVE_FATAL); + } + min = (size_t)(minimum(bytes_read, request)); + bytes_read = __archive_read_consume(filter->archive, min); + total_bytes_skipped += bytes_read; + request -= bytes_read; + } + return (total_bytes_skipped); } diff --git a/contrib/libarchive/libarchive/archive_read_disk.3 b/contrib/libarchive/libarchive/archive_read_disk.3 new file mode 100644 index 0000000000..fd53095358 --- /dev/null +++ b/contrib/libarchive/libarchive/archive_read_disk.3 @@ -0,0 +1,308 @@ +.\" Copyright (c) 2003-2009 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. +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2009 +.Dt archive_read_disk 3 +.Os +.Sh NAME +.Nm archive_read_disk_new , +.Nm archive_read_disk_set_symlink_logical , +.Nm archive_read_disk_set_symlink_physical , +.Nm archive_read_disk_set_symlink_hybrid , +.Nm archive_read_disk_entry_from_file , +.Nm archive_read_disk_gname , +.Nm archive_read_disk_uname , +.Nm archive_read_disk_set_uname_lookup , +.Nm archive_read_disk_set_gname_lookup , +.Nm archive_read_disk_set_standard_lookup , +.Nm archive_read_close , +.Nm archive_read_finish +.Nd functions for reading objects from disk +.Sh SYNOPSIS +.In archive.h +.Ft struct archive * +.Fn archive_read_disk_new "void" +.Ft int +.Fn archive_read_disk_set_symlink_logical "struct archive *" +.Ft int +.Fn archive_read_disk_set_symlink_physical "struct archive *" +.Ft int +.Fn archive_read_disk_set_symlink_hybrid "struct archive *" +.Ft int +.Fn archive_read_disk_gname "struct archive *" "gid_t" +.Ft int +.Fn archive_read_disk_uname "struct archive *" "uid_t" +.Ft int +.Fo archive_read_disk_set_gname_lookup +.Fa "struct archive *" +.Fa "void *" +.Fa "const char *(*lookup)(void *, gid_t)" +.Fa "void (*cleanup)(void *)" +.Fc +.Ft int +.Fo archive_read_disk_set_uname_lookup +.Fa "struct archive *" +.Fa "void *" +.Fa "const char *(*lookup)(void *, uid_t)" +.Fa "void (*cleanup)(void *)" +.Fc +.Ft int +.Fn archive_read_disk_set_standard_lookup "struct archive *" +.Ft int +.Fo archive_read_disk_entry_from_file +.Fa "struct archive *" +.Fa "struct archive_entry *" +.Fa "int fd" +.Fa "const struct stat *" +.Fc +.Ft int +.Fn archive_read_close "struct archive *" +.Ft int +.Fn archive_read_finish "struct archive *" +.Sh DESCRIPTION +These functions provide an API for reading information about +objects on disk. +In particular, they provide an interface for populating +.Tn struct archive_entry +objects. +.Bl -tag -width indent +.It Fn archive_read_disk_new +Allocates and initializes a +.Tn struct archive +object suitable for reading object information from disk. +.It Xo +.Fn archive_read_disk_set_symlink_logical , +.Fn archive_read_disk_set_symlink_physical , +.Fn archive_read_disk_set_symlink_hybrid +.Xc +This sets the mode used for handling symbolic links. +The +.Dq logical +mode follows all symbolic links. +The +.Dq physical +mode does not follow any symbolic links. +The +.Dq hybrid +mode currently behaves identically to the +.Dq logical +mode. +.It Xo +.Fn archive_read_disk_gname , +.Fn archive_read_disk_uname +.Xc +Returns a user or group name given a gid or uid value. +By default, these always return a NULL string. +.It Xo +.Fn archive_read_disk_set_gname_lookup , +.Fn archive_read_disk_set_uname_lookup +.Xc +These allow you to override the functions used for +user and group name lookups. +You may also provide a +.Tn void * +pointer to a private data structure and a cleanup function for +that data. +The cleanup function will be invoked when the +.Tn struct archive +object is destroyed or when new lookup functions are registered. +.It Fn archive_read_disk_set_standard_lookup +This convenience function installs a standard set of user +and group name lookup functions. +These functions use +.Xr getpwid 3 +and +.Xr getgrid 3 +to convert ids to names, defaulting to NULL if the names cannot +be looked up. +These functions also implement a simple memory cache to reduce +the number of calls to +.Xr getpwid 3 +and +.Xr getgrid 3 . +.It Fn archive_read_disk_entry_from_file +Populates a +.Tn struct archive_entry +object with information about a particular file. +The +.Tn archive_entry +object must have already been created with +.Xr archive_entry_new 3 +and at least one of the source path or path fields must already be set. +(If both are set, the source path will be used.) +.Pp +Information is read from disk using the path name from the +.Tn struct archive_entry +object. +If a file descriptor is provided, some information will be obtained using +that file descriptor, on platforms that support the appropriate +system calls. +.Pp +If a pointer to a +.Tn struct stat +is provided, information from that structure will be used instead +of reading from the disk where appropriate. +This can provide performance benefits in scenarios where +.Tn struct stat +information has already been read from the disk as a side effect +of some other operation. +(For example, directory traversal libraries often provide this information.) +.Pp +Where necessary, user and group ids are converted to user and group names +using the currently registered lookup functions above. +This affects the file ownership fields and ACL values in the +.Tn struct archive_entry +object. +.It Fn archive_read_close +This currently does nothing. +.It Fn archive_write_finish +Invokes +.Fn archive_write_close +if it was not invoked manually, then releases all resources. +.El +More information about the +.Va struct archive +object and the overall design of the library can be found in the +.Xr libarchive 3 +overview. +.Sh EXAMPLE +The following illustrates basic usage of the library by +showing how to use it to copy an item on disk into an archive. +.Bd -literal -offset indent +void +file_to_archive(struct archive *a, const char *name) +{ + char buff[8192]; + size_t bytes_read; + struct archive *ard; + struct archive_entry *entry; + int fd; + + ard = archive_read_disk_new(); + archive_read_disk_set_standard_lookup(ard); + entry = archive_entry_new(); + fd = open(name, O_RDONLY); + if (fd < 0) + return; + archive_entry_copy_sourcepath(entry, name); + archive_read_disk_entry_from_file(ard, entry, fd, NULL); + archive_write_header(a, entry); + while ((bytes_read = read(fd, buff, sizeof(buff))) > 0) + archive_write_data(a, buff, bytes_read); + archive_write_finish_entry(a); + archive_read_finish(ard); + archive_entry_free(entry); +} +.Ed +.Sh RETURN VALUES +Most functions return +.Cm ARCHIVE_OK +(zero) on success, or one of several negative +error codes for errors. +Specific error codes include: +.Cm ARCHIVE_RETRY +for operations that might succeed if retried, +.Cm ARCHIVE_WARN +for unusual conditions that do not prevent further operations, and +.Cm ARCHIVE_FATAL +for serious errors that make remaining operations impossible. +The +.Xr archive_errno 3 +and +.Xr archive_error_string 3 +functions can be used to retrieve an appropriate error code and a +textual error message. +(See +.Xr archive_util 3 +for details.) +.Pp +.Fn archive_read_disk_new +returns a pointer to a newly-allocated +.Tn struct archive +object or NULL if the allocation failed for any reason. +.Pp +.Fn archive_read_disk_gname +and +.Fn archive_read_disk_uname +return +.Tn const char * +pointers to the textual name or NULL if the lookup failed for any reason. +The returned pointer points to internal storage that +may be reused on the next call to either of these functions; +callers should copy the string if they need to continue accessing it. +.Pp +.Sh SEE ALSO +.Xr archive_read 3 , +.Xr archive_write 3 , +.Xr archive_write_disk 3 , +.Xr tar 1 , +.Xr libarchive 3 +.Sh HISTORY +The +.Nm libarchive +library first appeared in +.Fx 5.3 . +The +.Nm archive_read_disk +interface was added to +.Nm libarchive 2.6 +and first appeared in +.Fx 8.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm libarchive +library was written by +.An Tim Kientzle Aq kientzle@freebsd.org . +.Sh BUGS +The +.Dq standard +user name and group name lookup functions are not the defaults because +.Xr getgrid 3 +and +.Xr getpwid 3 +are sometimes too large for particular applications. +The current design allows the application author to use a more +compact implementation when appropriate. +.Pp +The full list of metadata read from disk by +.Fn archive_read_disk_entry_from_file +is necessarily system-dependent. +.Pp +The +.Fn archive_read_disk_entry_from_file +function reads as much information as it can from disk. +Some method should be provided to limit this so that clients who +do not need ACLs, for instance, can avoid the extra work needed +to look up such information. +.Pp +This API should provide a set of methods for walking a directory tree. +That would make it a direct parallel of the +.Xr archive_read 3 +API. +When such methods are implemented, the +.Dq hybrid +symbolic link mode will make sense. diff --git a/contrib/libarchive/libarchive/archive_read_disk.c b/contrib/libarchive/libarchive/archive_read_disk.c new file mode 100644 index 0000000000..3c0b815af5 --- /dev/null +++ b/contrib/libarchive/libarchive/archive_read_disk.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2003-2009 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 "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#include "archive.h" +#include "archive_string.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" + +static int _archive_read_finish(struct archive *); +static int _archive_read_close(struct archive *); +static const char *trivial_lookup_gname(void *, gid_t gid); +static const char *trivial_lookup_uname(void *, uid_t uid); + +static struct archive_vtable * +archive_read_disk_vtable(void) +{ + static struct archive_vtable av; + static int inited = 0; + + if (!inited) { + av.archive_finish = _archive_read_finish; + av.archive_close = _archive_read_close; + } + return (&av); +} + +const char * +archive_read_disk_gname(struct archive *_a, gid_t gid) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + if (a->lookup_gname != NULL) + return ((*a->lookup_gname)(a->lookup_gname_data, gid)); + return (NULL); +} + +const char * +archive_read_disk_uname(struct archive *_a, uid_t uid) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + if (a->lookup_uname != NULL) + return ((*a->lookup_uname)(a->lookup_uname_data, uid)); + return (NULL); +} + +int +archive_read_disk_set_gname_lookup(struct archive *_a, + void *private_data, + const char * (*lookup_gname)(void *private, gid_t gid), + void (*cleanup_gname)(void *private)) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); + + if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) + (a->cleanup_gname)(a->lookup_gname_data); + + a->lookup_gname = lookup_gname; + a->cleanup_gname = cleanup_gname; + a->lookup_gname_data = private_data; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_uname_lookup(struct archive *_a, + void *private_data, + const char * (*lookup_uname)(void *private, uid_t uid), + void (*cleanup_uname)(void *private)) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); + + if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) + (a->cleanup_uname)(a->lookup_uname_data); + + a->lookup_uname = lookup_uname; + a->cleanup_uname = cleanup_uname; + a->lookup_uname_data = private_data; + return (ARCHIVE_OK); +} + +/* + * Create a new archive_read_disk object and initialize it with global state. + */ +struct archive * +archive_read_disk_new(void) +{ + struct archive_read_disk *a; + + a = (struct archive_read_disk *)malloc(sizeof(*a)); + if (a == NULL) + return (NULL); + memset(a, 0, sizeof(*a)); + a->archive.magic = ARCHIVE_READ_DISK_MAGIC; + /* We're ready to write a header immediately. */ + a->archive.state = ARCHIVE_STATE_HEADER; + a->archive.vtable = archive_read_disk_vtable(); + a->lookup_uname = trivial_lookup_uname; + a->lookup_gname = trivial_lookup_gname; + return (&a->archive); +} + +static int +_archive_read_finish(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + + if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) + (a->cleanup_gname)(a->lookup_gname_data); + if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) + (a->cleanup_uname)(a->lookup_uname_data); + archive_string_free(&a->archive.error_string); + free(a); + return (ARCHIVE_OK); +} + +static int +_archive_read_close(struct archive *_a) +{ + (void)_a; /* UNUSED */ + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_logical(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'L'; + a->follow_symlinks = 1; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_physical(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'P'; + a->follow_symlinks = 0; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_symlink_hybrid(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + a->symlink_mode = 'H'; + a->follow_symlinks = 1; /* Follow symlinks initially. */ + return (ARCHIVE_OK); +} + +/* + * Trivial implementations of gname/uname lookup functions. + * These are normally overridden by the client, but these stub + * versions ensure that we always have something that works. + */ +static const char * +trivial_lookup_gname(void *private_data, gid_t gid) +{ + (void)private_data; /* UNUSED */ + (void)gid; /* UNUSED */ + return (NULL); +} + +static const char * +trivial_lookup_uname(void *private_data, uid_t uid) +{ + (void)private_data; /* UNUSED */ + (void)uid; /* UNUSED */ + return (NULL); +} diff --git a/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c new file mode 100644 index 0000000000..6e12517b5e --- /dev/null +++ b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c @@ -0,0 +1,548 @@ +/*- + * Copyright (c) 2003-2009 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. + * 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$"); + +#ifdef HAVE_SYS_TYPES_H +/* Mac OSX requires sys/types.h before sys/acl.h. */ +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_XATTR_H +#include +#endif +#ifdef HAVE_ACL_LIBACL_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" + +/* + * Linux and FreeBSD plug this obvious hole in POSIX.1e in + * different ways. + */ +#if HAVE_ACL_GET_PERM +#define ACL_GET_PERM acl_get_perm +#elif HAVE_ACL_GET_PERM_NP +#define ACL_GET_PERM acl_get_perm_np +#endif + +static int setup_acls_posix1e(struct archive_read_disk *, + struct archive_entry *, int fd); +static int setup_xattrs(struct archive_read_disk *, + struct archive_entry *, int fd); + +int +archive_read_disk_entry_from_file(struct archive *_a, + struct archive_entry *entry, + int fd, const struct stat *st) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + const char *path, *name; + struct stat s; + int initial_fd = fd; + int r, r1; + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + +#ifdef EXT2_IOC_GETFLAGS + /* Linux requires an extra ioctl to pull the flags. Although + * this is an extra step, it has a nice side-effect: We get an + * open file descriptor which we can use in the subsequent lookups. */ + if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { + if (fd < 0) + fd = open(pathname, O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + unsigned long stflags; + int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); + if (r == 0 && stflags != 0) + archive_entry_set_fflags(entry, stflags, 0); + } + } +#endif + + if (st == NULL) { +#if HAVE_FSTAT + if (fd >= 0) { + if (fstat(fd, &s) != 0) + return (ARCHIVE_FATAL); + } else +#endif +#if HAVE_LSTAT + if (!a->follow_symlinks) { + if (lstat(path, &s) != 0) + return (ARCHIVE_FATAL); + } else +#endif + if (stat(path, &s) != 0) + return (ARCHIVE_FATAL); + st = &s; + } + archive_entry_copy_stat(entry, st); + + /* Lookup uname/gname */ + name = archive_read_disk_uname(_a, archive_entry_uid(entry)); + if (name != NULL) + archive_entry_copy_uname(entry, name); + name = archive_read_disk_gname(_a, archive_entry_gid(entry)); + if (name != NULL) + archive_entry_copy_gname(entry, name); + +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + /* On FreeBSD, we get flags for free with the stat. */ + /* TODO: Does this belong in copy_stat()? */ + if (st->st_flags != 0) + archive_entry_set_fflags(entry, st->st_flags, 0); +#endif + +#ifdef HAVE_READLINK + if (S_ISLNK(st->st_mode)) { + char linkbuffer[PATH_MAX + 1]; + int lnklen = readlink(path, linkbuffer, PATH_MAX); + if (lnklen < 0) { + archive_set_error(&a->archive, errno, + "Couldn't read link data"); + return (ARCHIVE_WARN); + } + linkbuffer[lnklen] = 0; + archive_entry_set_symlink(entry, linkbuffer); + } +#endif + + r = setup_acls_posix1e(a, entry, fd); + r1 = setup_xattrs(a, entry, fd); + if (r1 < r) + r = r1; + /* If we opened the file earlier in this function, close it. */ + if (initial_fd != fd) + close(fd); + return (r); +} + +#ifdef HAVE_POSIX_ACL +static void setup_acl_posix1e(struct archive_read_disk *a, + struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); + +static int +setup_acls_posix1e(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + const char *accpath; + acl_t acl; + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + archive_entry_acl_clear(entry); + + /* Retrieve access ACL from file. */ + if (fd >= 0) + acl = acl_get_fd(fd); +#if HAVE_ACL_GET_LINK_NP + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); +#endif + else + acl = acl_get_file(accpath, ACL_TYPE_ACCESS); + if (acl != NULL) { + setup_acl_posix1e(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + acl_free(acl); + } + + /* Only directories can have default ACLs. */ + if (S_ISDIR(archive_entry_mode(entry))) { + acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); + if (acl != NULL) { + setup_acl_posix1e(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + acl_free(acl); + } + } + return (ARCHIVE_OK); +} + +/* + * Translate POSIX.1e ACL into libarchive internal structure. + */ +static void +setup_acl_posix1e(struct archive_read_disk *a, + struct archive_entry *entry, acl_t acl, int archive_entry_acl_type) +{ + acl_tag_t acl_tag; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int s, ae_id, ae_tag, ae_perm; + const char *ae_name; + + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); + while (s == 1) { + ae_id = -1; + ae_name = NULL; + + acl_get_tag_type(acl_entry, &acl_tag); + if (acl_tag == ACL_USER) { + ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); + ae_name = archive_read_disk_uname(&a->archive, ae_id); + ae_tag = ARCHIVE_ENTRY_ACL_USER; + } else if (acl_tag == ACL_GROUP) { + ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); + ae_name = archive_read_disk_gname(&a->archive, ae_id); + ae_tag = ARCHIVE_ENTRY_ACL_GROUP; + } else if (acl_tag == ACL_MASK) { + ae_tag = ARCHIVE_ENTRY_ACL_MASK; + } else if (acl_tag == ACL_USER_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + } else if (acl_tag == ACL_GROUP_OBJ) { + ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + } else if (acl_tag == ACL_OTHER) { + ae_tag = ARCHIVE_ENTRY_ACL_OTHER; + } else { + /* Skip types that libarchive can't support. */ + continue; + } + + acl_get_permset(acl_entry, &acl_permset); + ae_perm = 0; + /* + * acl_get_perm() is spelled differently on different + * platforms; see above. + */ + if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) + ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; + if (ACL_GET_PERM(acl_permset, ACL_READ)) + ae_perm |= ARCHIVE_ENTRY_ACL_READ; + if (ACL_GET_PERM(acl_permset, ACL_WRITE)) + ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; + + archive_entry_acl_add_entry(entry, + archive_entry_acl_type, ae_perm, ae_tag, + ae_id, ae_name); + + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + } +} +#else +static int +setup_acls_posix1e(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)fd; /* UNUSED */ + return (ARCHIVE_OK); +} +#endif + +#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR + +/* + * Linux extended attribute support. + * + * TODO: By using a stack-allocated buffer for the first + * call to getxattr(), we might be able to avoid the second + * call entirely. We only need the second call if the + * stack-allocated buffer is too small. But a modest buffer + * of 1024 bytes or so will often be big enough. Same applies + * to listxattr(). + */ + + +static int +setup_xattr(struct archive_read_disk *a, + struct archive_entry *entry, const char *name, int fd) +{ + ssize_t size; + void *value = NULL; + const char *accpath; + + (void)fd; /* UNUSED */ + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + size = lgetxattr(accpath, name, NULL, 0); + else + size = getxattr(accpath, name, NULL, 0); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't query extended attribute"); + return (ARCHIVE_WARN); + } + + if (size > 0 && (value = malloc(size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + size = lgetxattr(accpath, name, value, size); + else + size = getxattr(accpath, name, value, size); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't read extended attribute"); + return (ARCHIVE_WARN); + } + + archive_entry_xattr_add_entry(entry, name, value, size); + + free(value); + return (ARCHIVE_OK); +} + +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + char *list, *p; + const char *path; + ssize_t list_size; + + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + list_size = llistxattr(path, NULL, 0); + else + list_size = listxattr(path, NULL, 0); + + if (list_size == -1) { + if (errno == ENOTSUP) + return (ARCHIVE_OK); + archive_set_error(&a->archive, errno, + "Couldn't list extended attributes"); + return (ARCHIVE_WARN); + } + + if (list_size == 0) + return (ARCHIVE_OK); + + if ((list = malloc(list_size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + list_size = llistxattr(path, list, list_size); + else + list_size = listxattr(path, list, list_size); + + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't retrieve extended attributes"); + free(list); + return (ARCHIVE_WARN); + } + + for (p = list; (p - list) < list_size; p += strlen(p) + 1) { + if (strncmp(p, "system.", 7) == 0 || + strncmp(p, "xfsroot.", 8) == 0) + continue; + setup_xattr(a, entry, p, fd); + } + + free(list); + return (ARCHIVE_OK); +} + +#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE + +/* + * FreeBSD extattr interface. + */ + +/* TODO: Implement this. Follow the Linux model above, but + * with FreeBSD-specific system calls, of course. Be careful + * to not include the system extattrs that hold ACLs; we handle + * those separately. + */ +int +setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, + int namespace, const char *name, const char *fullname, int fd); + +int +setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, + int namespace, const char *name, const char *fullname, int fd) +{ + ssize_t size; + void *value = NULL; + const char *accpath; + + (void)fd; /* UNUSED */ + + accpath = archive_entry_sourcepath(entry); + if (accpath == NULL) + accpath = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + size = extattr_get_link(accpath, namespace, name, NULL, 0); + else + size = extattr_get_file(accpath, namespace, name, NULL, 0); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't query extended attribute"); + return (ARCHIVE_WARN); + } + + if (size > 0 && (value = malloc(size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + size = extattr_get_link(accpath, namespace, name, value, size); + else + size = extattr_get_file(accpath, namespace, name, value, size); + + if (size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't read extended attribute"); + return (ARCHIVE_WARN); + } + + archive_entry_xattr_add_entry(entry, fullname, value, size); + + free(value); + return (ARCHIVE_OK); +} + +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + char buff[512]; + char *list, *p; + ssize_t list_size; + const char *path; + int namespace = EXTATTR_NAMESPACE_USER; + + path = archive_entry_sourcepath(entry); + if (path == NULL) + path = archive_entry_pathname(entry); + + if (!a->follow_symlinks) + list_size = extattr_list_link(path, namespace, NULL, 0); + else + list_size = extattr_list_file(path, namespace, NULL, 0); + + if (list_size == -1 && errno == EOPNOTSUPP) + return (ARCHIVE_OK); + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't list extended attributes"); + return (ARCHIVE_WARN); + } + + if (list_size == 0) + return (ARCHIVE_OK); + + if ((list = malloc(list_size)) == NULL) { + archive_set_error(&a->archive, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + + if (!a->follow_symlinks) + list_size = extattr_list_link(path, namespace, list, list_size); + else + list_size = extattr_list_file(path, namespace, list, list_size); + + if (list_size == -1) { + archive_set_error(&a->archive, errno, + "Couldn't retrieve extended attributes"); + free(list); + return (ARCHIVE_WARN); + } + + p = list; + while ((p - list) < list_size) { + size_t len = 255 & (int)*p; + char *name; + + strcpy(buff, "user."); + name = buff + strlen(buff); + memcpy(name, p + 1, len); + name[len] = '\0'; + setup_xattr(a, entry, namespace, name, buff, fd); + p += 1 + len; + } + + free(list); + return (ARCHIVE_OK); +} + +#else + +/* + * Generic (stub) extended attribute support. + */ +static int +setup_xattrs(struct archive_read_disk *a, + struct archive_entry *entry, int fd) +{ + (void)a; /* UNUSED */ + (void)entry; /* UNUSED */ + (void)fd; /* UNUSED */ + return (ARCHIVE_OK); +} + +#endif diff --git a/contrib/libarchive/cpio/pathmatch.h b/contrib/libarchive/libarchive/archive_read_disk_private.h similarity index 59% copy from contrib/libarchive/cpio/pathmatch.h copy to contrib/libarchive/libarchive/archive_read_disk_private.h index 990fa1fa1e..fd385a6b77 100644 --- a/contrib/libarchive/cpio/pathmatch.h +++ b/contrib/libarchive/libarchive/archive_read_disk_private.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2009 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,12 +26,33 @@ * $FreeBSD$ */ -#ifndef PATHMATCH_H -#define PATHMATCH_H +#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED -#define PATHMATCH_NO_ANCHOR_START 1 -#define PATHMATCH_NO_ANCHOR_END 2 +struct archive_read_disk { + struct archive archive; -int pathmatch(const char *p, const char *s, int flags); + /* + * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, + * following an old BSD convention. 'L' follows all symlinks, + * 'P' follows none, 'H' follows symlinks only for the first + * item. + */ + char symlink_mode; + + /* + * Since symlink interaction changes, we need to track whether + * we're following symlinks for the current item. 'L' mode above + * sets this true, 'P' sets it false, 'H' changes it as we traverse. + */ + char follow_symlinks; /* Either 'L' or 'P'. */ + + const char * (*lookup_gname)(void *private, gid_t gid); + void (*cleanup_gname)(void *private); + void *lookup_gname_data; + const char * (*lookup_uname)(void *private, gid_t gid); + void (*cleanup_uname)(void *private); + void *lookup_uname_data; +}; #endif diff --git a/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c b/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c new file mode 100644 index 0000000000..60054f918f --- /dev/null +++ b/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c @@ -0,0 +1,270 @@ +/*- + * 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. + * 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$"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive.h" + +#if defined(_WIN32) && !defined(__CYGWIN__) +int +archive_read_disk_set_standard_lookup(struct archive *a) +{ + archive_set_error(a, -1, "Standard lookups not available on Windows"); + return (ARCHIVE_FATAL); +} +#else /* ! (_WIN32 && !__CYGWIN__) */ +#define name_cache_size 127 + +static const char * const NO_NAME = "(noname)"; + +struct name_cache { + struct archive *archive; + char *buff; + size_t buff_size; + int probes; + int hits; + size_t size; + struct { + id_t id; + const char *name; + } cache[name_cache_size]; +}; + +static const char * lookup_gname(void *, gid_t); +static const char * lookup_uname(void *, uid_t); +static void cleanup(void *); +static const char * lookup_gname_helper(struct name_cache *, id_t gid); +static const char * lookup_uname_helper(struct name_cache *, id_t uid); + +/* + * Installs functions that use getpwuid()/getgrgid()---along with + * a simple cache to accelerate such lookups---into the archive_read_disk + * object. This is in a separate file because getpwuid()/getgrgid() + * can pull in a LOT of library code (including NIS/LDAP functions, which + * pull in DNS resolveers, etc). This can easily top 500kB, which makes + * it inappropriate for some space-constrained applications. + * + * Applications that are size-sensitive may want to just use the + * real default functions (defined in archive_read_disk.c) that just + * use the uid/gid without the lookup. Or define your own custom functions + * if you prefer. + */ +int +archive_read_disk_set_standard_lookup(struct archive *a) +{ + struct name_cache *ucache = malloc(sizeof(struct name_cache)); + struct name_cache *gcache = malloc(sizeof(struct name_cache)); + + if (ucache == NULL || gcache == NULL) { + archive_set_error(a, ENOMEM, + "Can't allocate uname/gname lookup cache"); + free(ucache); + free(gcache); + return (ARCHIVE_FATAL); + } + + memset(ucache, 0, sizeof(*ucache)); + ucache->archive = a; + ucache->size = name_cache_size; + memset(gcache, 0, sizeof(*gcache)); + gcache->archive = a; + gcache->size = name_cache_size; + + archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup); + archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup); + + return (ARCHIVE_OK); +} + +static void +cleanup(void *data) +{ + struct name_cache *cache = (struct name_cache *)data; + size_t i; + + if (cache != NULL) { + for (i = 0; i < cache->size; i++) { + if (cache->cache[i].name != NULL && + cache->cache[i].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[i].name); + } + free(cache->buff); + free(cache); + } +} + +/* + * Lookup uid/gid from uname/gname, return NULL if no match. + */ +static const char * +lookup_name(struct name_cache *cache, + const char * (*lookup_fn)(struct name_cache *, id_t), id_t id) +{ + const char *name; + int slot; + + + cache->probes++; + + slot = id % cache->size; + if (cache->cache[slot].name != NULL) { + if (cache->cache[slot].id == id) { + cache->hits++; + if (cache->cache[slot].name == NO_NAME) + return (NULL); + return (cache->cache[slot].name); + } + if (cache->cache[slot].name != NO_NAME) + free((void *)(uintptr_t)cache->cache[slot].name); + cache->cache[slot].name = NULL; + } + + name = (lookup_fn)(cache, id); + if (name == NULL) { + /* Cache and return the negative response. */ + cache->cache[slot].name = NO_NAME; + cache->cache[slot].id = id; + return (NULL); + } + + cache->cache[slot].name = name; + cache->cache[slot].id = id; + return (cache->cache[slot].name); +} + +static const char * +lookup_uname(void *data, uid_t uid) +{ + struct name_cache *uname_cache = (struct name_cache *)data; + return (lookup_name(uname_cache, + &lookup_uname_helper, (id_t)uid)); +} + +static const char * +lookup_uname_helper(struct name_cache *cache, id_t id) +{ + struct passwd pwent, *result; + int r; + + if (cache->buff_size == 0) { + cache->buff_size = 256; + cache->buff = malloc(cache->buff_size); + } + if (cache->buff == NULL) + return (NULL); + for (;;) { + r = getpwuid_r((uid_t)id, &pwent, + cache->buff, cache->buff_size, &result); + if (r == 0) + break; + if (r != ERANGE) + break; + /* ERANGE means our buffer was too small, but POSIX + * doesn't tell us how big the buffer should be, so + * we just double it and try again. Because the buffer + * is kept around in the cache object, we shouldn't + * have to do this very often. */ + cache->buff_size *= 2; + cache->buff = realloc(cache->buff, cache->buff_size); + if (cache->buff == NULL) + break; + } + if (r != 0) { + archive_set_error(cache->archive, errno, + "Can't lookup user for id %d", (int)id); + return (NULL); + } + if (result == NULL) + return (NULL); + + return strdup(result->pw_name); +} + +static const char * +lookup_gname(void *data, gid_t gid) +{ + struct name_cache *gname_cache = (struct name_cache *)data; + return (lookup_name(gname_cache, + &lookup_gname_helper, (id_t)gid)); +} + +static const char * +lookup_gname_helper(struct name_cache *cache, id_t id) +{ + struct group grent, *result; + int r; + + if (cache->buff_size == 0) { + cache->buff_size = 256; + cache->buff = malloc(cache->buff_size); + } + if (cache->buff == NULL) + return (NULL); + for (;;) { + r = getgrgid_r((gid_t)id, &grent, + cache->buff, cache->buff_size, &result); + if (r == 0) + break; + if (r != ERANGE) + break; + /* ERANGE means our buffer was too small, but POSIX + * doesn't tell us how big the buffer should be, so + * we just double it and try again. */ + cache->buff_size *= 2; + cache->buff = realloc(cache->buff, cache->buff_size); + if (cache->buff == NULL) + break; + } + if (r != 0) { + archive_set_error(cache->archive, errno, + "Can't lookup group for id %d", (int)id); + return (NULL); + } + if (result == NULL) + return (NULL); + + return strdup(result->gr_name); +} +#endif /* ! (_WIN32 && !__CYGWIN__) */ diff --git a/contrib/libarchive/libarchive/archive_read_open_fd.c b/contrib/libarchive/libarchive/archive_read_open_fd.c index 2ebd46d6cd..c7f9d693b4 100644 --- a/contrib/libarchive/libarchive/archive_read_open_fd.c +++ b/contrib/libarchive/libarchive/archive_read_open_fd.c @@ -52,7 +52,6 @@ struct read_fd_data { }; static int file_close(struct archive *, void *); -static int file_open(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); @@ -63,50 +62,41 @@ static off_t file_skip(struct archive *, void *, off_t request); int archive_read_open_fd(struct archive *a, int fd, size_t block_size) { + struct stat st; struct read_fd_data *mine; + void *b; - mine = (struct read_fd_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); + if (fstat(fd, &st) != 0) { + archive_set_error(a, errno, "Can't stat fd %d", fd); return (ARCHIVE_FATAL); } - mine->block_size = block_size; - mine->buffer = malloc(mine->block_size); - if (mine->buffer == NULL) { + + mine = (struct read_fd_data *)malloc(sizeof(*mine)); + b = malloc(block_size); + if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); + free(b); return (ARCHIVE_FATAL); } + mine->block_size = block_size; + mine->buffer = b; mine->fd = fd; - /* lseek() hardly ever works, so disable it by default. See below. */ - mine->can_skip = 0; - return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); -} - -static int -file_open(struct archive *a, void *client_data) -{ - struct read_fd_data *mine = (struct read_fd_data *)client_data; - struct stat st; - - if (fstat(mine->fd, &st) != 0) { - archive_set_error(a, errno, "Can't stat fd %d", mine->fd); - return (ARCHIVE_FATAL); - } - + /* + * Skip support is a performance optimization for anything + * that supports lseek(). On FreeBSD, only regular files and + * raw disk devices support lseek() and there's no portable + * way to determine if a device is a raw disk device, so we + * only enable this optimization for regular files. + */ if (S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); - /* - * Enabling skip here is a performance optimization for - * anything that supports lseek(). On FreeBSD, only - * regular files and raw disk devices support lseek() and - * there's no portable way to determine if a device is - * a raw disk device, so we only enable this optimization - * for regular files. - */ mine->can_skip = 1; - } - return (ARCHIVE_OK); + } else + mine->can_skip = 0; + + return (archive_read_open2(a, mine, + NULL, file_read, file_skip, file_close)); } static ssize_t @@ -180,8 +170,7 @@ file_close(struct archive *a, void *client_data) struct read_fd_data *mine = (struct read_fd_data *)client_data; (void)a; /* UNUSED */ - if (mine->buffer != NULL) - free(mine->buffer); + free(mine->buffer); free(mine); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_open_file.c b/contrib/libarchive/libarchive/archive_read_open_file.c index 55c431c7da..bcc13b188f 100644 --- a/contrib/libarchive/libarchive/archive_read_open_file.c +++ b/contrib/libarchive/libarchive/archive_read_open_file.c @@ -55,7 +55,6 @@ struct read_FILE_data { }; static int file_close(struct archive *, void *); -static int file_open(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); @@ -66,45 +65,36 @@ static off_t file_skip(struct archive *, void *, off_t request); int archive_read_open_FILE(struct archive *a, FILE *f) { + struct stat st; struct read_FILE_data *mine; + size_t block_size = 128 * 1024; + void *b; mine = (struct read_FILE_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->block_size = 128 * 1024; - mine->buffer = malloc(mine->block_size); - if (mine->buffer == NULL) { + b = malloc(block_size); + if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); + free(b); return (ARCHIVE_FATAL); } + mine->block_size = block_size; + mine->buffer = b; mine->f = f; - /* Suppress skip by default. See below. */ - mine->can_skip = 0; - return (archive_read_open2(a, mine, file_open, file_read, - file_skip, file_close)); -} - -static int -file_open(struct archive *a, void *client_data) -{ - struct read_FILE_data *mine = (struct read_FILE_data *)client_data; - struct stat st; - /* - * If we can't fstat() the file, it may just be that - * it's not a file. (FILE * objects can wrap many kinds - * of I/O streams.) + * If we can't fstat() the file, it may just be that it's not + * a file. (FILE * objects can wrap many kinds of I/O + * streams, some of which don't support fileno()).) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); - /* Enable the seek optimization for regular files. */ + /* Enable the seek optimization only for regular files. */ mine->can_skip = 1; - } + } else + mine->can_skip = 0; - return (ARCHIVE_OK); + return (archive_read_open2(a, mine, NULL, file_read, + file_skip, file_close)); } static ssize_t diff --git a/contrib/libarchive/libarchive/archive_read_open_filename.c b/contrib/libarchive/libarchive/archive_read_open_filename.c index 3d6376fbcf..04efb0f5a9 100644 --- a/contrib/libarchive/libarchive/archive_read_open_filename.c +++ b/contrib/libarchive/libarchive/archive_read_open_filename.c @@ -61,7 +61,6 @@ struct read_file_data { }; static int file_close(struct archive *, void *); -static int file_open(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); #if ARCHIVE_API_VERSION < 2 static ssize_t file_skip(struct archive *, void *, size_t request); @@ -80,78 +79,54 @@ int archive_read_open_filename(struct archive *a, const char *filename, size_t block_size) { + struct stat st; struct read_file_data *mine; + void *b; + int fd; - if (filename == NULL || filename[0] == '\0') { - mine = (struct read_file_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->filename[0] = '\0'; - } else { - mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - strcpy(mine->filename, filename); - } - mine->block_size = block_size; - mine->buffer = NULL; - mine->fd = -1; - /* lseek() almost never works; disable it by default. See below. */ - mine->can_skip = 0; - return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); -} + if (filename == NULL || filename[0] == '\0') + return (archive_read_open_fd(a, 0, block_size)); -static int -file_open(struct archive *a, void *client_data) -{ - struct read_file_data *mine = (struct read_file_data *)client_data; - struct stat st; - - mine->buffer = malloc(mine->block_size); - if (mine->buffer == NULL) { - archive_set_error(a, ENOMEM, "No memory"); + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + archive_set_error(a, errno, "Failed to open '%s'", filename); return (ARCHIVE_FATAL); } - if (mine->filename[0] != '\0') - mine->fd = open(mine->filename, O_RDONLY | O_BINARY); - else - mine->fd = 0; /* Fake "open" for stdin. */ - if (mine->fd < 0) { - archive_set_error(a, errno, "Failed to open '%s'", - mine->filename); + if (fstat(fd, &st) != 0) { + archive_set_error(a, errno, "Can't stat '%s'", filename); return (ARCHIVE_FATAL); } - if (fstat(mine->fd, &st) == 0) { - /* If we're reading a file from disk, ensure that we don't - overwrite it with an extracted file. */ - if (S_ISREG(st.st_mode)) { - archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); - /* - * Enabling skip here is a performance - * optimization for anything that supports - * lseek(). On FreeBSD, only regular files - * and raw disk devices support lseek() and - * there's no portable way to determine if a - * device is a raw disk device, so we only - * enable this optimization for regular files. - */ - mine->can_skip = 1; - } - /* Remember mode so close can decide whether to flush. */ - mine->st_mode = st.st_mode; - } else { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Can't stat stdin"); - else - archive_set_error(a, errno, "Can't stat '%s'", - mine->filename); + + mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); + b = malloc(block_size); + if (mine == NULL || b == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + free(mine); + free(b); return (ARCHIVE_FATAL); } - return (0); + strcpy(mine->filename, filename); + mine->block_size = block_size; + mine->buffer = b; + mine->fd = fd; + /* Remember mode so close can decide whether to flush. */ + mine->st_mode = st.st_mode; + /* If we're reading a file from disk, ensure that we don't + overwrite it with an extracted file. */ + if (S_ISREG(st.st_mode)) { + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + /* + * Skip is a performance optimization for anything + * that supports lseek(). Generally, that only + * includes regular files and possibly raw disk + * devices, but there's no good portable way to detect + * raw disks. + */ + mine->can_skip = 1; + } else + mine->can_skip = 0; + return (archive_read_open2(a, mine, + NULL, file_read, file_skip, file_close)); } static ssize_t @@ -163,11 +138,8 @@ file_read(struct archive *a, void *client_data, const void **buff) *buff = mine->buffer; bytes_read = read(mine->fd, mine->buffer, mine->block_size); if (bytes_read < 0) { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Error reading stdin"); - else - archive_set_error(a, errno, "Error reading '%s'", - mine->filename); + archive_set_error(a, errno, "Error reading '%s'", + mine->filename); } return (bytes_read); } @@ -217,15 +189,8 @@ file_skip(struct archive *a, void *client_data, off_t request) * likely caused by a programmer error (too large request) * or a corrupted archive file. */ - if (mine->filename[0] == '\0') - /* - * Should never get here, since lseek() on stdin ought - * to return an ESPIPE error. - */ - archive_set_error(a, errno, "Error seeking in stdin"); - else - archive_set_error(a, errno, "Error seeking in '%s'", - mine->filename); + archive_set_error(a, errno, "Error seeking in '%s'", + mine->filename); return (-1); } return (new_offset - old_offset); @@ -238,30 +203,30 @@ file_close(struct archive *a, void *client_data) (void)a; /* UNUSED */ - /* - * Sometimes, we should flush the input before closing. - * Regular files: faster to just close without flush. - * Devices: must not flush (user might need to - * read the "next" item on a non-rewind device). - * Pipes and sockets: must flush (otherwise, the - * program feeding the pipe or socket may complain). - * Here, I flush everything except for regular files and - * device nodes. - */ - if (!S_ISREG(mine->st_mode) - && !S_ISCHR(mine->st_mode) - && !S_ISBLK(mine->st_mode)) { - ssize_t bytesRead; - do { - bytesRead = read(mine->fd, mine->buffer, - mine->block_size); - } while (bytesRead > 0); - } - /* If a named file was opened, then it needs to be closed. */ - if (mine->filename[0] != '\0') + /* Only flush and close if open succeeded. */ + if (mine->fd >= 0) { + /* + * Sometimes, we should flush the input before closing. + * Regular files: faster to just close without flush. + * Devices: must not flush (user might need to + * read the "next" item on a non-rewind device). + * Pipes and sockets: must flush (otherwise, the + * program feeding the pipe or socket may complain). + * Here, I flush everything except for regular files and + * device nodes. + */ + if (!S_ISREG(mine->st_mode) + && !S_ISCHR(mine->st_mode) + && !S_ISBLK(mine->st_mode)) { + ssize_t bytesRead; + do { + bytesRead = read(mine->fd, mine->buffer, + mine->block_size); + } while (bytesRead > 0); + } close(mine->fd); - if (mine->buffer != NULL) - free(mine->buffer); + } + free(mine->buffer); free(mine); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_private.h b/contrib/libarchive/libarchive/archive_read_private.h index f4d0274b50..10def7264b 100644 --- a/contrib/libarchive/libarchive/archive_read_private.h +++ b/contrib/libarchive/libarchive/archive_read_private.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.6 2008/03/15 11:09:16 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.7 2008/12/06 06:45:15 kientzle Exp $ */ #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED @@ -32,6 +32,86 @@ #include "archive_string.h" #include "archive_private.h" +struct archive_read; +struct archive_read_filter_bidder; +struct archive_read_filter; + +/* + * How bidding works for filters: + * * The bid manager reads the first block from the current source. + * * It shows that block to each registered bidder. + * * The bid manager creates a new filter structure for the winning + * bidder and gives the winning bidder a chance to initialize it. + * * The new filter becomes the top filter in the archive_read structure + * and we repeat the process. + * This ends only when no bidder provides a non-zero bid. + */ +struct archive_read_filter_bidder { + /* Configuration data for the bidder. */ + void *data; + /* Taste the upstream filter to see if we handle this. */ + int (*bid)(struct archive_read_filter_bidder *, + struct archive_read_filter *); + /* Initialize a newly-created filter. */ + int (*init)(struct archive_read_filter *); + /* Set an option for the filter bidder. */ + int (*options)(struct archive_read_filter_bidder *, + const char *key, const char *value); + /* Release the bidder's configuration data. */ + int (*free)(struct archive_read_filter_bidder *); +}; + +/* + * This structure is allocated within the archive_read core + * and initialized by archive_read and the init() method of the + * corresponding bidder above. + */ +struct archive_read_filter { + /* Essentially all filters will need these values, so + * just declare them here. */ + struct archive_read_filter_bidder *bidder; /* My bidder. */ + struct archive_read_filter *upstream; /* Who I read from. */ + struct archive_read *archive; /* Associated archive. */ + /* Return next block. */ + ssize_t (*read)(struct archive_read_filter *, const void **); + /* Skip forward this many bytes. */ + int64_t (*skip)(struct archive_read_filter *self, int64_t request); + /* Close (just this filter) and free(self). */ + int (*close)(struct archive_read_filter *self); + /* My private data. */ + void *data; + + const char *name; + int code; + + /* Used by reblocking logic. */ + char *buffer; + size_t buffer_size; + char *next; /* Current read location. */ + size_t avail; /* Bytes in my buffer. */ + const void *client_buff; /* Client buffer information. */ + size_t client_total; + const char *client_next; + size_t client_avail; + int64_t position; + char end_of_file; + char fatal; +}; + +/* + * The client looks a lot like a filter, so we just wrap it here. + * + * TODO: Make archive_read_filter and archive_read_client identical so + * that users of the library can easily register their own + * transformation filters. This will probably break the API/ABI and + * so should be deferred at least until libarchive 3.0. + */ +struct archive_read_client { + archive_read_callback *reader; + archive_skip_callback *skipper; + archive_close_callback *closer; +}; + struct archive_read { struct archive archive; @@ -50,47 +130,18 @@ struct archive_read { off_t read_data_output_offset; size_t read_data_remaining; - /* Callbacks to open/read/write/close archive stream. */ - archive_open_callback *client_opener; - archive_read_callback *client_reader; - archive_skip_callback *client_skipper; - archive_close_callback *client_closer; - void *client_data; + /* Callbacks to open/read/write/close client archive stream. */ + struct archive_read_client client; + + /* Registered filter bidders. */ + struct archive_read_filter_bidder bidders[8]; + + /* Last filter in chain */ + struct archive_read_filter *filter; /* File offset of beginning of most recently-read header. */ off_t header_position; - /* - * Decompressors have a very specific lifecycle: - * public setup function initializes a slot in this table - * 'config' holds minimal configuration data - * bid() examines a block of data and returns a bid [1] - * init() is called for successful bidder - * 'data' is initialized by init() - * read() returns a pointer to the next block of data - * consume() indicates how much data is used - * skip() ignores bytes of data - * finish() cleans up and frees 'data' and 'config' - * - * [1] General guideline: bid the number of bits that you actually - * test, e.g., 16 if you test a 2-byte magic value. - */ - struct decompressor_t { - void *config; - void *data; - int (*bid)(const void *buff, size_t); - int (*init)(struct archive_read *, - const void *buff, size_t); - int (*finish)(struct archive_read *); - ssize_t (*read_ahead)(struct archive_read *, - const void **, size_t); - ssize_t (*consume)(struct archive_read *, size_t); - off_t (*skip)(struct archive_read *, off_t); - } decompressors[4]; - - /* Pointer to current decompressor. */ - struct decompressor_t *decompressor; - /* * Format detection is mostly the same as compression * detection, with one significant difference: The bidders @@ -101,7 +152,10 @@ struct archive_read { struct archive_format_descriptor { void *data; + const char *name; int (*bid)(struct archive_read *); + int (*options)(struct archive_read *, const char *key, + const char *value); int (*read_header)(struct archive_read *, struct archive_entry *); int (*read_data)(struct archive_read *, const void **, size_t *, off_t *); int (*read_data_skip)(struct archive_read *); @@ -118,18 +172,23 @@ struct archive_read { int __archive_read_register_format(struct archive_read *a, void *format_data, + const char *name, int (*bid)(struct archive_read *), + int (*options)(struct archive_read *, const char *, const char *), int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, off_t *), int (*read_data_skip)(struct archive_read *), int (*cleanup)(struct archive_read *)); -struct decompressor_t - *__archive_read_register_compression(struct archive_read *a, - int (*bid)(const void *, size_t), - int (*init)(struct archive_read *, const void *, size_t)); - -const void - *__archive_read_ahead(struct archive_read *, size_t); - +struct archive_read_filter_bidder + *__archive_read_get_bidder(struct archive_read *a); + +const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *); +const void *__archive_read_filter_ahead(struct archive_read_filter *, + size_t, ssize_t *); +ssize_t __archive_read_consume(struct archive_read *, size_t); +ssize_t __archive_read_filter_consume(struct archive_read_filter *, size_t); +int64_t __archive_read_skip(struct archive_read *, int64_t); +int64_t __archive_read_filter_skip(struct archive_read_filter *, int64_t); +int __archive_read_program(struct archive_read_filter *, const char *); #endif diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_all.c b/contrib/libarchive/libarchive/archive_read_support_compression_all.c index da2b246bed..063c288978 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_all.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_all.c @@ -24,20 +24,31 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.6 2007/01/09 08:05:55 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.7 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" int archive_read_support_compression_all(struct archive *a) { -#if HAVE_BZLIB_H + /* Bzip falls back to "bunzip2" command-line */ archive_read_support_compression_bzip2(a); -#endif /* The decompress code doesn't use an outside library. */ archive_read_support_compression_compress(a); -#if HAVE_ZLIB_H + /* Gzip decompress falls back to "gunzip" command-line. */ archive_read_support_compression_gzip(a); -#endif + /* The LZMA file format has a very weak signature, so it + * may not be feasible to keep this here, but we'll try. + * This will come back out if there are problems. */ + /* Lzma falls back to "unlzma" command-line program. */ + archive_read_support_compression_lzma(a); + /* Xz falls back to "unxz" command-line program. */ + archive_read_support_compression_xz(a); + + /* Note: We always return ARCHIVE_OK here, even if some of the + * above return ARCHIVE_WARN. The intent here is to enable + * "as much as possible." Clients who need specific + * compression should enable those individually so they can + * verify the level of support. */ return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_bzip2.c b/contrib/libarchive/libarchive/archive_read_support_compression_bzip2.c index 824b0cdeb5..082cf49c5f 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_bzip2.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_bzip2.c @@ -25,7 +25,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.18 2008/05/26 17:00:22 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.19 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -51,30 +51,54 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c, #if HAVE_BZLIB_H struct private_data { bz_stream stream; - char *uncompressed_buffer; - size_t uncompressed_buffer_size; - char *read_next; - int64_t total_out; + char *out_block; + size_t out_block_size; + char valid; /* True = decompressor is initialized */ char eof; /* True = found end of compressed data. */ }; -static int finish(struct archive_read *); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int drive_decompressor(struct archive_read *a, struct private_data *); +/* Bzip2 filter */ +static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **); +static int bzip2_filter_close(struct archive_read_filter *); #endif -/* These two functions are defined even if we lack the library. See below. */ -static int bid(const void *, size_t); -static int init(struct archive_read *, const void *, size_t); +/* + * Note that we can detect bzip2 archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * error messages.) So the bid framework here gets compiled even + * if bzlib is unavailable. + */ +static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); +static int bzip2_reader_init(struct archive_read_filter *); +static int bzip2_reader_free(struct archive_read_filter_bidder *); int archive_read_support_compression_bzip2(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_read_filter_bidder *reader = __archive_read_get_bidder(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = bzip2_reader_bid; + reader->init = bzip2_reader_init; + reader->options = NULL; + reader->free = bzip2_reader_free; +#if HAVE_BZLIB_H + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external bunzip2 program"); + return (ARCHIVE_WARN); +#endif +} + +static int +bzip2_reader_free(struct archive_read_filter_bidder *self){ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); } /* @@ -85,59 +109,38 @@ archive_read_support_compression_bzip2(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) { const unsigned char *buffer; + ssize_t avail; int bits_checked; - if (len < 1) - return (0); - - buffer = (const unsigned char *)buff; - bits_checked = 0; - if (buffer[0] != 'B') /* Verify first ID byte. */ - return (0); - bits_checked += 8; - if (len < 2) - return (bits_checked); + (void)self; /* UNUSED */ - if (buffer[1] != 'Z') /* Verify second ID byte. */ + /* Minimal bzip2 archive is 14 bytes. */ + buffer = __archive_read_filter_ahead(filter, 14, &avail); + if (buffer == NULL) return (0); - bits_checked += 8; - if (len < 3) - return (bits_checked); - if (buffer[2] != 'h') /* Verify third ID byte. */ + /* First three bytes must be "BZh" */ + bits_checked = 0; + if (buffer[0] != 'B' || buffer[1] != 'Z' || buffer[2] != 'h') return (0); - bits_checked += 8; - if (len < 4) - return (bits_checked); + bits_checked += 24; + /* Next follows a compression flag which must be an ASCII digit. */ if (buffer[3] < '1' || buffer[3] > '9') return (0); bits_checked += 5; - if (len < 5) - return (bits_checked); /* After BZh[1-9], there must be either a data block * which begins with 0x314159265359 or an end-of-data * marker of 0x177245385090. */ - - if (buffer[4] == 0x31) { - /* Verify the data block signature. */ - size_t s = len; - if (s > 10) s = 10; - if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0) - return (0); - bits_checked += 8 * (s - 4); - } else if (buffer[4] == 0x17) { - /* Verify the end-of-data marker. */ - size_t s = len; - if (s > 10) s = 10; - if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0) - return (0); - bits_checked += 8 * (s - 4); - } else + if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0) + bits_checked += 48; + else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0) + bits_checked += 48; + else return (0); return (bits_checked); @@ -151,15 +154,17 @@ bid(const void *buff, size_t len) * and emit a useful message. */ static int -init(struct archive_read *a, const void *buff, size_t n) +bzip2_reader_init(struct archive_read_filter *self) { - (void)a; /* UNUSED */ - (void)buff; /* UNUSED */ - (void)n; /* UNUSED */ - - archive_set_error(&a->archive, -1, - "This version of libarchive was compiled without bzip2 support"); - return (ARCHIVE_FATAL); + int r; + + r = __archive_read_program(self, "bunzip2"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_COMPRESSION_BZIP2; + self->name = "bzip2"; + return (r); } @@ -169,258 +174,181 @@ init(struct archive_read *a, const void *buff, size_t n) * Setup the callbacks. */ static int -init(struct archive_read *a, const void *buff, size_t n) +bzip2_reader_init(struct archive_read_filter *self) { + static const size_t out_block_size = 64 * 1024; + void *out_block; struct private_data *state; - int ret; - a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; - a->archive.compression_name = "bzip2"; + self->code = ARCHIVE_COMPRESSION_BZIP2; + self->name = "bzip2"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate data for %s decompression", - a->archive.compression_name); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = (char *)malloc(state->uncompressed_buffer_size); - state->stream.next_out = state->uncompressed_buffer; - state->read_next = state->uncompressed_buffer; - state->stream.avail_out = state->uncompressed_buffer_size; - - if (state->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate %s decompression buffers", - a->archive.compression_name); + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (self == NULL || state == NULL || out_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for bzip2 decompression"); + free(out_block); free(state); return (ARCHIVE_FATAL); } - /* - * A bug in bzlib.h: stream.next_in should be marked 'const' - * but isn't (the library never alters data through the - * next_in pointer, only reads it). The result: this ugly - * cast to remove 'const'. - */ - state->stream.next_in = (char *)(uintptr_t)(const void *)buff; - state->stream.avail_in = n; - - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; - - /* Initialize compression library. */ - ret = BZ2_bzDecompressInit(&(state->stream), - 0 /* library verbosity */, - 0 /* don't use slow low-mem algorithm */); - - /* If init fails, try using low-memory algorithm instead. */ - if (ret == BZ_MEM_ERROR) { - ret = BZ2_bzDecompressInit(&(state->stream), - 0 /* library verbosity */, - 1 /* do use slow low-mem algorithm */); - } - - if (ret == BZ_OK) { - a->decompressor->data = state; - return (ARCHIVE_OK); - } + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->read = bzip2_filter_read; + self->skip = NULL; /* not supported */ + self->close = bzip2_filter_close; - /* Library setup failed: Clean up. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing %s library", - a->archive.compression_name); - free(state->uncompressed_buffer); - free(state); - - /* Override the error message if we know what really went wrong. */ - switch (ret) { - case BZ_PARAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid setup parameter"); - break; - case BZ_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - case BZ_CONFIG_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "mis-compiled library"); - break; - } - - return (ARCHIVE_FATAL); + return (ARCHIVE_OK); } /* - * Return a block of data from the decompression buffer. Decompress more - * as necessary. + * Return the next block of decompressed data. */ static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +bzip2_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; - size_t read_avail, was_avail; - int ret; - - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } + size_t read_avail, decompressed; + const char *read_buf; + ssize_t ret; - read_avail = state->stream.next_out - state->read_next; + state = (struct private_data *)self->data; + read_avail = 0; - if (read_avail + state->stream.avail_out < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = state->uncompressed_buffer; - state->stream.next_out = state->read_next + read_avail; - state->stream.avail_out - = state->uncompressed_buffer_size - read_avail; + if (state->eof) { + *p = NULL; + return (0); } - while (read_avail < min && /* Haven't satisfied min. */ - read_avail < state->uncompressed_buffer_size) { /* !full */ - was_avail = read_avail; - if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK) - return (ret); - if (ret == ARCHIVE_EOF) - break; /* Break on EOF even if we haven't met min. */ - read_avail = state->stream.next_out - state->read_next; - if (was_avail == read_avail) /* No progress? */ - break; - } + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - *p = state->read_next; - return (read_avail); -} + /* Try to fill the output buffer. */ + for (;;) { + if (!state->valid) { + if (bzip2_reader_bid(self->bidder, self->upstream) == 0) { + state->eof = 1; + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + return (decompressed); + } + /* Initialize compression library. */ + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 0 /* don't use low-mem algorithm */); + + /* If init fails, try low-memory algorithm instead. */ + if (ret == BZ_MEM_ERROR) + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 1 /* do use low-mem algo */); + + if (ret != BZ_OK) { + const char *detail = NULL; + int err = ARCHIVE_ERRNO_MISC; + switch (ret) { + case BZ_PARAM_ERROR: + detail = "invalid setup parameter"; + break; + case BZ_MEM_ERROR: + err = ENOMEM; + detail = "out of memory"; + break; + case BZ_CONFIG_ERROR: + detail = "mis-compiled library"; + break; + } + archive_set_error(&self->archive->archive, err, + "Internal error initializing decompressor%s%s", + detail == NULL ? "" : ": ", + detail); + return (ARCHIVE_FATAL); + } + state->valid = 1; + } -/* - * Mark a previously-returned block of data as read. - */ -static ssize_t -read_consume(struct archive_read *a, size_t n) -{ - struct private_data *state; + /* stream.next_in is really const, but bzlib + * doesn't declare it so. */ + read_buf = + __archive_read_filter_ahead(self->upstream, 1, &ret); + if (read_buf == NULL) + return (ARCHIVE_FATAL); + state->stream.next_in = (char *)(uintptr_t)read_buf; + state->stream.avail_in = ret; + /* There is no more data, return whatever we have. */ + if (ret == 0) { + state->eof = 1; + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + return (decompressed); + } - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->stream.next_out) - __archive_errx(1, "Request to consume too many " - "bytes from bzip2 decompressor"); - return (n); -} + /* Decompress as much as we can in one pass. */ + ret = BZ2_bzDecompress(&(state->stream)); + __archive_read_filter_consume(self->upstream, + state->stream.next_in - read_buf); -/* - * Clean up the decompressor. - */ -static int -finish(struct archive_read *a) -{ - struct private_data *state; - int ret; - - state = (struct private_data *)a->decompressor->data; - ret = ARCHIVE_OK; - switch (BZ2_bzDecompressEnd(&(state->stream))) { - case BZ_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - a->archive.compression_name); - ret = ARCHIVE_FATAL; + switch (ret) { + case BZ_STREAM_END: /* Found end of stream. */ + switch (BZ2_bzDecompressEnd(&(state->stream))) { + case BZ_OK: + break; + default: + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, + "Failed to clean up decompressor"); + return (ARCHIVE_FATAL); + } + state->valid = 0; + /* FALLTHROUGH */ + case BZ_OK: /* Decompressor made some progress. */ + /* If we filled our buffer, update stats and return. */ + if (state->stream.avail_out == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + return (decompressed); + } + break; + default: /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "bzip decompression failed"); + return (ARCHIVE_FATAL); + } } - - free(state->uncompressed_buffer); - free(state); - - a->decompressor->data = NULL; - return (ret); } /* - * Utility function to pull data through decompressor, reading input - * blocks as necessary. + * Clean up the decompressor. */ static int -drive_decompressor(struct archive_read *a, struct private_data *state) +bzip2_filter_close(struct archive_read_filter *self) { - ssize_t ret; - int decompressed, total_decompressed; - char *output; - const void *read_buf; - - if (state->eof) - return (ARCHIVE_EOF); - total_decompressed = 0; - for (;;) { - if (state->stream.avail_in == 0) { - read_buf = state->stream.next_in; - ret = (a->client_reader)(&a->archive, a->client_data, - &read_buf); - state->stream.next_in = (void *)(uintptr_t)read_buf; - if (ret < 0) { - /* - * TODO: Find a better way to handle - * this read failure. - */ - goto fatal; - } - if (ret == 0 && total_decompressed == 0) { - archive_set_error(&a->archive, EIO, - "Premature end of %s compressed data", - a->archive.compression_name); - return (ARCHIVE_FATAL); - } - a->archive.raw_position += ret; - state->stream.avail_in = ret; - } - - { - output = state->stream.next_out; - - /* Decompress some data. */ - ret = BZ2_bzDecompress(&(state->stream)); - decompressed = state->stream.next_out - output; + struct private_data *state; + int ret = ARCHIVE_OK; - /* Accumulate the total bytes of output. */ - state->total_out += decompressed; - total_decompressed += decompressed; + state = (struct private_data *)self->data; - switch (ret) { - case BZ_OK: /* Decompressor made some progress. */ - if (decompressed > 0) - return (ARCHIVE_OK); - break; - case BZ_STREAM_END: /* Found end of stream. */ - state->eof = 1; - return (ARCHIVE_OK); - default: - /* Any other return value is an error. */ - goto fatal; - } + if (state->valid) { + switch (BZ2_bzDecompressEnd(&state->stream)) { + case BZ_OK: + break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up decompressor"); + ret = ARCHIVE_FATAL; } } - return (ARCHIVE_OK); - /* Return a fatal error. */ -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s decompression failed", a->archive.compression_name); - return (ARCHIVE_FATAL); + free(state->out_block); + free(state); + return (ARCHIVE_OK); } #endif /* HAVE_BZLIB_H */ diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_compress.c b/contrib/libarchive/libarchive/archive_read_support_compression_compress.c index 050099b210..752ddda344 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_compress.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_compress.c @@ -64,7 +64,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.10 2007/05/29 01:00:19 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.11 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -100,11 +100,8 @@ struct private_data { size_t bytes_in_section; /* Output variables. */ - size_t uncompressed_buffer_size; - void *uncompressed_buffer; - unsigned char *read_next; /* Data for client. */ - unsigned char *next_out; /* Where to write new data. */ - size_t avail_out; /* Space at end of buffer. */ + size_t out_block_size; + void *out_block; /* Decompression status variables. */ int use_reset_code; @@ -133,21 +130,31 @@ struct private_data { unsigned char stack[65300]; }; -static int bid(const void *, size_t); -static int finish(struct archive_read *); -static int init(struct archive_read *, const void *, size_t); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int getbits(struct archive_read *, struct private_data *, int n); -static int next_code(struct archive_read *a, struct private_data *state); +static int compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); +static int compress_bidder_init(struct archive_read_filter *); +static int compress_bidder_free(struct archive_read_filter_bidder *); + +static ssize_t compress_filter_read(struct archive_read_filter *, const void **); +static int compress_filter_close(struct archive_read_filter *); + +static int getbits(struct archive_read_filter *, int n); +static int next_code(struct archive_read_filter *); int archive_read_support_compression_compress(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); + + if (bidder == NULL) + return (ARCHIVE_FATAL); + + bidder->data = NULL; + bidder->bid = compress_bidder_bid; + bidder->init = compress_bidder_init; + bidder->options = NULL; + bidder->free = compress_bidder_free; + return (ARCHIVE_OK); } /* @@ -158,27 +165,28 @@ archive_read_support_compression_compress(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +compress_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) { const unsigned char *buffer; + ssize_t avail; int bits_checked; - if (len < 1) + (void)self; /* UNUSED */ + + buffer = __archive_read_filter_ahead(filter, 2, &avail); + + if (buffer == NULL) return (0); - buffer = (const unsigned char *)buff; bits_checked = 0; if (buffer[0] != 037) /* Verify first ID byte. */ return (0); bits_checked += 8; - if (len < 2) - return (bits_checked); if (buffer[1] != 0235) /* Verify second ID byte. */ return (0); bits_checked += 8; - if (len < 3) - return (bits_checked); /* * TODO: Verify more. @@ -191,60 +199,40 @@ bid(const void *buff, size_t len) * Setup the callbacks. */ static int -init(struct archive_read *a, const void *buff, size_t n) +compress_bidder_init(struct archive_read_filter *self) { struct private_data *state; + static const size_t out_block_size = 64 * 1024; + void *out_block; int code; - a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS; - a->archive.compression_name = "compress (.Z)"; - - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; + self->code = ARCHIVE_COMPRESSION_COMPRESS; + self->name = "compress (.Z)"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { - archive_set_error(&a->archive, ENOMEM, + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = malloc(out_block_size); + if (state == NULL || out_block == NULL) { + free(out_block); + free(state); + archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate data for %s decompression", - a->archive.compression_name); + self->name); return (ARCHIVE_FATAL); } - memset(state, 0, sizeof(*state)); - a->decompressor->data = state; - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = malloc(state->uncompressed_buffer_size); + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->read = compress_filter_read; + self->skip = NULL; /* not supported */ + self->close = compress_filter_close; - if (state->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate %s decompression buffers", - a->archive.compression_name); - goto fatal; - } + /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */ - state->next_in = (const unsigned char *)buff; - state->avail_in = n; - state->read_next = state->next_out = (unsigned char *)state->uncompressed_buffer; - state->avail_out = state->uncompressed_buffer_size; - - code = getbits(a, state, 8); - if (code != 037) /* This should be impossible. */ - goto fatal; - - code = getbits(a, state, 8); - if (code != 0235) { - /* This can happen if the library is receiving 1-byte - * blocks and gzip and compress are both enabled. - * You can't distinguish gzip and compress only from - * the first byte. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Compress signature did not match."); - goto fatal; - } + code = getbits(self, 8); /* Skip first signature byte. */ + code = getbits(self, 8); /* Skip second signature byte. */ - code = getbits(a, state, 8); + code = getbits(self, 8); state->maxcode_bits = code & 0x1f; state->maxcode = (1 << state->maxcode_bits); state->use_reset_code = code & 0x80; @@ -261,12 +249,9 @@ init(struct archive_read *a, const void *buff, size_t n) state->prefix[code] = 0; state->suffix[code] = code; } - next_code(a, state); - return (ARCHIVE_OK); + next_code(self); -fatal: - finish(a); - return (ARCHIVE_FATAL); + return (ARCHIVE_OK); } /* @@ -274,93 +259,57 @@ fatal: * as necessary. */ static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +compress_filter_read(struct archive_read_filter *self, const void **pblock) { struct private_data *state; - size_t read_avail; + unsigned char *p, *start, *end; int ret; - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); - return (ARCHIVE_FATAL); - } - - read_avail = state->next_out - state->read_next; - - if (read_avail < min && state->end_of_stream) { - if (state->end_of_stream == ARCHIVE_EOF) - return (0); - else - return (-1); + state = (struct private_data *)self->data; + if (state->end_of_stream) { + *pblock = NULL; + return (0); } - - if (read_avail < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = (unsigned char *)state->uncompressed_buffer; - state->next_out = state->read_next + read_avail; - state->avail_out - = state->uncompressed_buffer_size - read_avail; - - while (read_avail < state->uncompressed_buffer_size - && !state->end_of_stream) { - if (state->stackp > state->stack) { - *state->next_out++ = *--state->stackp; - state->avail_out--; - read_avail++; - } else { - ret = next_code(a, state); - if (ret == ARCHIVE_EOF) - state->end_of_stream = ret; - else if (ret != ARCHIVE_OK) - return (ret); - } + p = start = (unsigned char *)state->out_block; + end = start + state->out_block_size; + + while (p < end && !state->end_of_stream) { + if (state->stackp > state->stack) { + *p++ = *--state->stackp; + } else { + ret = next_code(self); + if (ret == -1) + state->end_of_stream = ret; + else if (ret != ARCHIVE_OK) + return (ret); } } - *p = state->read_next; - return (read_avail); + *pblock = start; + return (p - start); } /* - * Mark a previously-returned block of data as read. + * Clean up the reader. */ -static ssize_t -read_consume(struct archive_read *a, size_t n) +static int +compress_bidder_free(struct archive_read_filter_bidder *self) { - struct private_data *state; - - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->next_out) - __archive_errx(1, "Request to consume too many " - "bytes from compress decompressor"); - return (n); + self->data = NULL; + return (ARCHIVE_OK); } /* - * Clean up the decompressor. + * Close and release the filter. */ static int -finish(struct archive_read *a) +compress_filter_close(struct archive_read_filter *self) { - struct private_data *state; - int ret = ARCHIVE_OK; + struct private_data *state = (struct private_data *)self->data; - state = (struct private_data *)a->decompressor->data; - - if (state != NULL) { - if (state->uncompressed_buffer != NULL) - free(state->uncompressed_buffer); - free(state); - } - - a->decompressor->data = NULL; - return (ret); + free(state->out_block); + free(state); + return (ARCHIVE_OK); } /* @@ -369,14 +318,15 @@ finish(struct archive_read *a) * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise. */ static int -next_code(struct archive_read *a, struct private_data *state) +next_code(struct archive_read_filter *self) { + struct private_data *state = (struct private_data *)self->data; int code, newcode; static int debug_buff[1024]; static unsigned debug_index; - code = newcode = getbits(a, state, state->bits); + code = newcode = getbits(self, state->bits); if (code < 0) return (code); @@ -398,7 +348,7 @@ next_code(struct archive_read *a, struct private_data *state) skip_bytes %= state->bits; state->bits_avail = 0; /* Discard rest of this byte. */ while (skip_bytes-- > 0) { - code = getbits(a, state, 8); + code = getbits(self, 8); if (code < 0) return (code); } @@ -408,12 +358,13 @@ next_code(struct archive_read *a, struct private_data *state) state->section_end_code = (1 << state->bits) - 1; state->free_ent = 257; state->oldcode = -1; - return (next_code(a, state)); + return (next_code(self)); } if (code > state->free_ent) { /* An invalid code is a fatal error. */ - archive_set_error(&a->archive, -1, "Invalid compressed data"); + archive_set_error(&(self->archive->archive), -1, + "Invalid compressed data"); return (ARCHIVE_FATAL); } @@ -457,27 +408,27 @@ next_code(struct archive_read *a, struct private_data *state) * -1 indicates end of available data. */ static int -getbits(struct archive_read *a, struct private_data *state, int n) +getbits(struct archive_read_filter *self, int n) { - int code, ret; + struct private_data *state = (struct private_data *)self->data; + int code; + ssize_t ret; static const int mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; - const void *read_buf; while (state->bits_avail < n) { if (state->avail_in <= 0) { - read_buf = state->next_in; - ret = (a->client_reader)(&a->archive, a->client_data, - &read_buf); - state->next_in = read_buf; - if (ret < 0) - return (ARCHIVE_FATAL); + state->next_in + = __archive_read_filter_ahead(self->upstream, + 1, &ret); if (ret == 0) - return (ARCHIVE_EOF); - a->archive.raw_position += ret; + return (-1); + if (ret < 0 || state->next_in == NULL) + return (ARCHIVE_FATAL); state->avail_in = ret; + __archive_read_filter_consume(self->upstream, ret); } state->bit_buffer |= *state->next_in++ << state->bits_avail; state->avail_in--; diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_gzip.c b/contrib/libarchive/libarchive/archive_read_support_compression_gzip.c index 2dac54da0c..2222478935 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_gzip.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_gzip.c @@ -25,7 +25,7 @@ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.16 2008/02/19 05:44:59 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.17 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H @@ -51,499 +51,412 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v #ifdef HAVE_ZLIB_H struct private_data { z_stream stream; - unsigned char *uncompressed_buffer; - size_t uncompressed_buffer_size; - unsigned char *read_next; + char in_stream; + unsigned char *out_block; + size_t out_block_size; int64_t total_out; unsigned long crc; - char header_done; char eof; /* True = found end of compressed data. */ }; -static int finish(struct archive_read *); -static ssize_t read_ahead(struct archive_read *, const void **, size_t); -static ssize_t read_consume(struct archive_read *, size_t); -static int drive_decompressor(struct archive_read *a, struct private_data *); +/* Gzip Filter. */ +static ssize_t gzip_filter_read(struct archive_read_filter *, const void **); +static int gzip_filter_close(struct archive_read_filter *); #endif -/* These two functions are defined even if we lack the library. See below. */ -static int bid(const void *, size_t); -static int init(struct archive_read *, const void *, size_t); +/* + * Note that we can detect gzip archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * error messages.) So the bid framework here gets compiled even + * if zlib is unavailable. + * + * TODO: If zlib is unavailable, gzip_bidder_init() should + * use the compress_program framework to try to fire up an external + * gunzip program. + */ +static int gzip_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int gzip_bidder_init(struct archive_read_filter *); int archive_read_support_compression_gzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, bid, init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); + struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); + + if (bidder == NULL) + return (ARCHIVE_FATAL); + + bidder->data = NULL; + bidder->bid = gzip_bidder_bid; + bidder->init = gzip_bidder_init; + bidder->options = NULL; + bidder->free = NULL; /* No data, so no cleanup necessary. */ + /* Signal the extent of gzip support with the return value here. */ +#if HAVE_ZLIB_H + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external gunzip program"); + return (ARCHIVE_WARN); +#endif } /* - * Test whether we can handle this data. + * Read and verify the header. * - * This logic returns zero if any part of the signature fails. It - * also tries to Do The Right Thing if a very short buffer prevents us - * from verifying as much as we would like. + * Returns zero if the header couldn't be validated, else returns + * number of bytes in header. If pbits is non-NULL, it receives a + * count of bits verified, suitable for use by bidder. */ static int -bid(const void *buff, size_t len) +peek_at_header(struct archive_read_filter *filter, int *pbits) { - const unsigned char *buffer; - int bits_checked; - - if (len < 1) + const unsigned char *p; + ssize_t avail, len; + int bits = 0; + int header_flags; + + /* Start by looking at the first ten bytes of the header, which + * is all fixed layout. */ + len = 10; + p = __archive_read_filter_ahead(filter, len, &avail); + if (p == NULL || avail == 0) return (0); - - buffer = (const unsigned char *)buff; - bits_checked = 0; - if (buffer[0] != 037) /* Verify first ID byte. */ + if (p[0] != 037) return (0); - bits_checked += 8; - if (len < 2) - return (bits_checked); - - if (buffer[1] != 0213) /* Verify second ID byte. */ + bits += 8; + if (p[1] != 0213) return (0); - bits_checked += 8; - if (len < 3) - return (bits_checked); - - if (buffer[2] != 8) /* Compression must be 'deflate'. */ + bits += 8; + if (p[2] != 8) /* We only support deflation. */ return (0); - bits_checked += 8; - if (len < 4) - return (bits_checked); + bits += 8; + if ((p[3] & 0xE0)!= 0) /* No reserved flags set. */ + return (0); + bits += 3; + header_flags = p[3]; + /* Bytes 4-7 are mod time. */ + /* Byte 8 is deflate flags. */ + /* XXXX TODO: return deflate flags back to consume_header for use + in initializing the decompressor. */ + /* Byte 9 is OS. */ + + /* Optional extra data: 2 byte length plus variable body. */ + if (header_flags & 4) { + p = __archive_read_filter_ahead(filter, len + 2, &avail); + if (p == NULL) + return (0); + len += ((int)p[len + 1] << 8) | (int)p[len]; + } - if ((buffer[3] & 0xE0)!= 0) /* No reserved flags set. */ + /* Null-terminated optional filename. */ + if (header_flags & 8) { + do { + ++len; + if (avail < len) + p = __archive_read_filter_ahead(filter, + len, &avail); + if (p == NULL) + return (0); + } while (p[len - 1] != 0); + } + + /* Null-terminated optional comment. */ + if (header_flags & 16) { + do { + ++len; + if (avail < len) + p = __archive_read_filter_ahead(filter, + len, &avail); + if (p == NULL) + return (0); + } while (p[len - 1] != 0); + } + + /* Optional header CRC */ + if ((header_flags & 2)) { + p = __archive_read_filter_ahead(filter, len + 2, &avail); + if (p == NULL) + return (0); +#if 0 + int hcrc = ((int)p[len + 1] << 8) | (int)p[len]; + int crc = /* XXX TODO: Compute header CRC. */; + if (crc != hcrc) return (0); - bits_checked += 3; - if (len < 5) - return (bits_checked); + bits += 16; +#endif + len += 2; + } + + if (pbits != NULL) + *pbits = bits; + return (len); +} + +/* + * Bidder just verifies the header and returns the number of verified bits. + */ +static int +gzip_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + int bits_checked; - /* - * TODO: Verify more; in particular, gzip has an optional - * header CRC, which would give us 16 more verified bits. We - * may also be able to verify certain constraints on other - * fields. - */ + (void)self; /* UNUSED */ - return (bits_checked); + if (peek_at_header(filter, &bits_checked)) + return (bits_checked); + return (0); } #ifndef HAVE_ZLIB_H /* - * If we don't have the library on this system, we can't actually do the - * decompression. We can, however, still detect compressed archives - * and emit a useful message. + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run gunzip + * in case that's available. */ static int -init(struct archive_read *a, const void *buff, size_t n) +gzip_bidder_init(struct archive_read_filter *self) { - (void)a; /* UNUSED */ - (void)buff; /* UNUSED */ - (void)n; /* UNUSED */ - - archive_set_error(&a->archive, -1, - "This version of libarchive was compiled without gzip support"); - return (ARCHIVE_FATAL); + int r; + + r = __archive_read_program(self, "gunzip"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_COMPRESSION_GZIP; + self->name = "gzip"; + return (r); } - #else /* - * Setup the callbacks. + * Initialize the filter object. */ static int -init(struct archive_read *a, const void *buff, size_t n) +gzip_bidder_init(struct archive_read_filter *self) { struct private_data *state; - int ret; + static const size_t out_block_size = 64 * 1024; + void *out_block; - a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; - a->archive.compression_name = "gzip"; + self->code = ARCHIVE_COMPRESSION_GZIP; + self->name = "gzip"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate data for %s decompression", - a->archive.compression_name); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->crc = crc32(0L, NULL, 0); - state->header_done = 0; /* We've not yet begun to parse header... */ - - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = (unsigned char *)malloc(state->uncompressed_buffer_size); - state->stream.next_out = state->uncompressed_buffer; - state->read_next = state->uncompressed_buffer; - state->stream.avail_out = state->uncompressed_buffer_size; - - if (state->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate %s decompression buffers", - a->archive.compression_name); + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (state == NULL || out_block == NULL) { + free(out_block); free(state); + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for gzip decompression"); return (ARCHIVE_FATAL); } - /* - * A bug in zlib.h: stream.next_in should be marked 'const' - * but isn't (the library never alters data through the - * next_in pointer, only reads it). The result: this ugly - * cast to remove 'const'. - */ - state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; - state->stream.avail_in = n; - - a->decompressor->read_ahead = read_ahead; - a->decompressor->consume = read_consume; - a->decompressor->skip = NULL; /* not supported */ - a->decompressor->finish = finish; - - /* - * TODO: Do I need to parse the gzip header before calling - * inflateInit2()? In particular, one of the header bytes - * marks "best compression" or "fastest", which may be - * appropriate for setting the second parameter here. - * However, I think the only penalty for not setting it - * correctly is wasted memory. If this is necessary, it - * should probably go into drive_decompressor() below. - */ + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->read = gzip_filter_read; + self->skip = NULL; /* not supported */ + self->close = gzip_filter_close; + + state->in_stream = 0; /* We're not actually within a stream yet. */ + + return (ARCHIVE_OK); +} + +static int +consume_header(struct archive_read_filter *self) +{ + struct private_data *state; + ssize_t avail; + size_t len; + int ret; + + state = (struct private_data *)self->data; + + /* If this is a real header, consume it. */ + len = peek_at_header(self->upstream, NULL); + if (len == 0) + return (ARCHIVE_EOF); + __archive_read_filter_consume(self->upstream, len); + + /* Initialize CRC accumulator. */ + state->crc = crc32(0L, NULL, 0); /* Initialize compression library. */ + state->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(self->upstream, 1, &avail); + state->stream.avail_in = avail; ret = inflateInit2(&(state->stream), -15 /* Don't check for zlib header */); - if (ret == Z_OK) { - a->decompressor->data = state; - return (ARCHIVE_OK); - } - - /* Library setup failed: Clean up. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing %s library", - a->archive.compression_name); - free(state->uncompressed_buffer); - free(state); - /* Override the error message if we know what really went wrong. */ + /* Decipher the error code. */ switch (ret) { + case Z_OK: + state->in_stream = 1; + return (ARCHIVE_OK); case Z_STREAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid setup parameter"); break; case Z_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, + archive_set_error(&self->archive->archive, ENOMEM, "Internal error initializing compression library: " "out of memory"); break; case Z_VERSION_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid library version"); break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + " Zlib error %d", ret); + break; } - return (ARCHIVE_FATAL); } -/* - * Return a block of data from the decompression buffer. Decompress more - * as necessary. - */ -static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +static int +consume_trailer(struct archive_read_filter *self) { struct private_data *state; - size_t read_avail, was_avail; - int ret; + const unsigned char *p; + ssize_t avail; + + state = (struct private_data *)self->data; - state = (struct private_data *)a->decompressor->data; - if (!a->client_reader) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No read callback is registered? " - "This is probably an internal programming error."); + state->in_stream = 0; + switch (inflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to clean up gzip decompressor"); return (ARCHIVE_FATAL); } - read_avail = state->stream.next_out - state->read_next; + /* GZip trailer is a fixed 8 byte structure. */ + p = __archive_read_filter_ahead(self->upstream, 8, &avail); + if (p == NULL || avail == 0) + return (ARCHIVE_FATAL); - if (read_avail + state->stream.avail_out < min) { - memmove(state->uncompressed_buffer, state->read_next, - read_avail); - state->read_next = state->uncompressed_buffer; - state->stream.next_out = state->read_next + read_avail; - state->stream.avail_out - = state->uncompressed_buffer_size - read_avail; - } + /* XXX TODO: Verify the length and CRC. */ - while (read_avail < min && /* Haven't satisfied min. */ - read_avail < state->uncompressed_buffer_size) { /* !full */ - was_avail = read_avail; - if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK) - return (ret); - if (ret == ARCHIVE_EOF) - break; /* Break on EOF even if we haven't met min. */ - read_avail = state->stream.next_out - state->read_next; - if (was_avail == read_avail) /* No progress? */ - break; - } + /* We've verified the trailer, so consume it now. */ + __archive_read_filter_consume(self->upstream, 8); - *p = state->read_next; - return (read_avail); + return (ARCHIVE_OK); } -/* - * Mark a previously-returned block of data as read. - */ static ssize_t -read_consume(struct archive_read *a, size_t n) +gzip_filter_read(struct archive_read_filter *self, const void **p) { struct private_data *state; + size_t decompressed; + ssize_t avail_in; + int ret; - state = (struct private_data *)a->decompressor->data; - a->archive.file_position += n; - state->read_next += n; - if (state->read_next > state->stream.next_out) - __archive_errx(1, "Request to consume too many " - "bytes from gzip decompressor"); - return (n); -} + state = (struct private_data *)self->data; -/* - * Clean up the decompressor. - */ -static int -finish(struct archive_read *a) -{ - struct private_data *state; - int ret; + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - state = (struct private_data *)a->decompressor->data; - ret = ARCHIVE_OK; - switch (inflateEnd(&(state->stream))) { - case Z_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up %s compressor", - a->archive.compression_name); - ret = ARCHIVE_FATAL; - } + /* Try to fill the output buffer. */ + while (state->stream.avail_out > 0 && !state->eof) { + /* If we're not in a stream, read a header + * and initialize the decompression library. */ + if (!state->in_stream) { + ret = consume_header(self); + if (ret == ARCHIVE_EOF) { + state->eof = 1; + break; + } + if (ret < ARCHIVE_OK) + return (ret); + } - free(state->uncompressed_buffer); - free(state); + /* Peek at the next available data. */ + /* ZLib treats stream.next_in as const but doesn't declare + * it so, hence this ugly cast. */ + state->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(self->upstream, 1, &avail_in); + if (state->stream.next_in == NULL) + return (ARCHIVE_FATAL); + state->stream.avail_in = avail_in; + + /* Decompress and consume some of that data. */ + ret = inflate(&(state->stream), 0); + switch (ret) { + case Z_OK: /* Decompressor made some progress. */ + __archive_read_filter_consume(self->upstream, + avail_in - state->stream.avail_in); + break; + case Z_STREAM_END: /* Found end of stream. */ + __archive_read_filter_consume(self->upstream, + avail_in - state->stream.avail_in); + /* Consume the stream trailer; release the + * decompression library. */ + ret = consume_trailer(self); + break; + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "gzip decompression failed"); + return (ARCHIVE_FATAL); + } + } - a->decompressor->data = NULL; - return (ret); + /* We've read as much as we can. */ + decompressed = state->stream.next_out - state->out_block; + state->total_out += decompressed; + if (decompressed == 0) + *p = NULL; + else + *p = state->out_block; + return (decompressed); } /* - * Utility function to pull data through decompressor, reading input - * blocks as necessary. + * Clean up the decompressor. */ static int -drive_decompressor(struct archive_read *a, struct private_data *state) +gzip_filter_close(struct archive_read_filter *self) { - ssize_t ret; - size_t decompressed, total_decompressed; - int count, flags, header_state; - unsigned char *output; - unsigned char b; - const void *read_buf; - - if (state->eof) - return (ARCHIVE_EOF); - flags = 0; - count = 0; - header_state = 0; - total_decompressed = 0; - for (;;) { - if (state->stream.avail_in == 0) { - read_buf = state->stream.next_in; - ret = (a->client_reader)(&a->archive, a->client_data, - &read_buf); - state->stream.next_in = (unsigned char *)(uintptr_t)read_buf; - if (ret < 0) { - /* - * TODO: Find a better way to handle - * this read failure. - */ - goto fatal; - } - if (ret == 0 && total_decompressed == 0) { - archive_set_error(&a->archive, EIO, - "Premature end of %s compressed data", - a->archive.compression_name); - return (ARCHIVE_FATAL); - } - a->archive.raw_position += ret; - state->stream.avail_in = ret; - } + struct private_data *state; + int ret; - if (!state->header_done) { - /* - * If still parsing the header, interpret the - * next byte. - */ - b = *(state->stream.next_in++); - state->stream.avail_in--; - - /* - * Yes, this is somewhat crude, but it works, - * GZip format isn't likely to change anytime - * in the near future, and header parsing is - * certainly not a performance issue, so - * there's little point in making this more - * elegant. Of course, if you see an easy way - * to make this more elegant, please let me - * know.. ;-) - */ - switch (header_state) { - case 0: /* First byte of signature. */ - if (b != 037) - goto fatal; - header_state = 1; - break; - case 1: /* Second byte of signature. */ - if (b != 0213) - goto fatal; - header_state = 2; - break; - case 2: /* Compression type must be 8. */ - if (b != 8) - goto fatal; - header_state = 3; - break; - case 3: /* GZip flags. */ - flags = b; - header_state = 4; - break; - case 4: case 5: case 6: case 7: /* Mod time. */ - header_state++; - break; - case 8: /* Deflate flags. */ - header_state = 9; - break; - case 9: /* OS. */ - header_state = 10; - break; - case 10: /* Optional Extra: First byte of Length. */ - if ((flags & 4)) { - count = 255 & (int)b; - header_state = 11; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 11: /* Optional Extra: Second byte of Length. */ - if ((flags & 4)) { - count = (0xff00 & ((int)b << 8)) | count; - header_state = 12; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 12: /* Optional Extra Field: counted length. */ - if ((flags & 4)) { - --count; - if (count == 0) header_state = 13; - else header_state = 12; - break; - } - /* - * Fall through if there is no - * Optional Extra field. - */ - case 13: /* Optional Original Filename. */ - if ((flags & 8)) { - if (b == 0) header_state = 14; - else header_state = 13; - break; - } - /* - * Fall through if no Optional - * Original Filename. - */ - case 14: /* Optional Comment. */ - if ((flags & 16)) { - if (b == 0) header_state = 15; - else header_state = 14; - break; - } - /* Fall through if no Optional Comment. */ - case 15: /* Optional Header CRC: First byte. */ - if ((flags & 2)) { - header_state = 16; - break; - } - /* Fall through if no Optional Header CRC. */ - case 16: /* Optional Header CRC: Second byte. */ - if ((flags & 2)) { - header_state = 17; - break; - } - /* Fall through if no Optional Header CRC. */ - case 17: /* First byte of compressed data. */ - state->header_done = 1; /* done with header */ - state->stream.avail_in++; - state->stream.next_in--; - } + state = (struct private_data *)self->data; + ret = ARCHIVE_OK; - /* - * TODO: Consider moving the inflateInit2 call - * here so it can include the compression type - * from the header? - */ - } else { - output = state->stream.next_out; - - /* Decompress some data. */ - ret = inflate(&(state->stream), 0); - decompressed = state->stream.next_out - output; - - /* Accumulate the CRC of the uncompressed data. */ - state->crc = crc32(state->crc, output, decompressed); - - /* Accumulate the total bytes of output. */ - state->total_out += decompressed; - total_decompressed += decompressed; - - switch (ret) { - case Z_OK: /* Decompressor made some progress. */ - if (decompressed > 0) - return (ARCHIVE_OK); - break; - case Z_STREAM_END: /* Found end of stream. */ - /* - * TODO: Verify gzip trailer - * (uncompressed length and CRC). - */ - state->eof = 1; - return (ARCHIVE_OK); - default: - /* Any other return value is an error. */ - goto fatal; - } + if (state->in_stream) { + switch (inflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, + "Failed to clean up gzip compressor"); + ret = ARCHIVE_FATAL; } } - return (ARCHIVE_OK); - /* Return a fatal error. */ -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s decompression failed", a->archive.compression_name); - return (ARCHIVE_FATAL); + free(state->out_block); + free(state); + return (ret); } #endif /* HAVE_ZLIB_H */ diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_none.c b/contrib/libarchive/libarchive/archive_read_support_compression_none.c index 3f17756aba..2fddece48b 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_none.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_none.c @@ -24,347 +24,17 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.19 2007/12/30 04:58:21 kientzle Exp $"); - -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_LIMITS_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.20 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" -#include "archive_private.h" -#include "archive_read_private.h" - -struct archive_decompress_none { - char *buffer; - size_t buffer_size; - char *next; /* Current read location. */ - size_t avail; /* Bytes in my buffer. */ - const void *client_buff; /* Client buffer information. */ - size_t client_total; - const char *client_next; - size_t client_avail; - char end_of_file; - char fatal; -}; /* - * Size of internal buffer used for combining short reads. This is - * also an upper limit on the size of a read request. Recall, - * however, that we can (and will!) return blocks of data larger than - * this. The read semantics are: you ask for a minimum, I give you a - * pointer to my best-effort match and tell you how much data is - * there. It could be less than you asked for, it could be much more. - * For example, a client might use mmap() to "read" the entire file as - * a single block. In that case, I will return that entire block to - * my clients. + * Uncompressed streams are handled implicitly by the read core, + * so this is now a no-op. */ -#define BUFFER_SIZE 65536 - -#define minimum(a, b) (a < b ? a : b) - -static int archive_decompressor_none_bid(const void *, size_t); -static int archive_decompressor_none_finish(struct archive_read *); -static int archive_decompressor_none_init(struct archive_read *, - const void *, size_t); -static ssize_t archive_decompressor_none_read_ahead(struct archive_read *, - const void **, size_t); -static ssize_t archive_decompressor_none_read_consume(struct archive_read *, - size_t); -static off_t archive_decompressor_none_skip(struct archive_read *, off_t); - int -archive_read_support_compression_none(struct archive *_a) +archive_read_support_compression_none(struct archive *a) { - struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_compression(a, - archive_decompressor_none_bid, - archive_decompressor_none_init) != NULL) - return (ARCHIVE_OK); - return (ARCHIVE_FATAL); -} - -/* - * Try to detect an "uncompressed" archive. - */ -static int -archive_decompressor_none_bid(const void *buff, size_t len) -{ - (void)buff; - (void)len; - - return (1); /* Default: We'll take it if noone else does. */ -} - -static int -archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t n) -{ - struct archive_decompress_none *state; - - a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; - a->archive.compression_name = "none"; - - state = (struct archive_decompress_none *)malloc(sizeof(*state)); - if (!state) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate input data"); - return (ARCHIVE_FATAL); - } - memset(state, 0, sizeof(*state)); - - state->buffer_size = BUFFER_SIZE; - state->buffer = (char *)malloc(state->buffer_size); - state->next = state->buffer; - if (state->buffer == NULL) { - free(state); - archive_set_error(&a->archive, ENOMEM, "Can't allocate input buffer"); - return (ARCHIVE_FATAL); - } - - /* Save reference to first block of data. */ - state->client_buff = buff; - state->client_total = n; - state->client_next = state->client_buff; - state->client_avail = state->client_total; - - a->decompressor->data = state; - a->decompressor->read_ahead = archive_decompressor_none_read_ahead; - a->decompressor->consume = archive_decompressor_none_read_consume; - a->decompressor->skip = archive_decompressor_none_skip; - a->decompressor->finish = archive_decompressor_none_finish; - - return (ARCHIVE_OK); -} - -/* - * We just pass through pointers to the client buffer if we can. - * If the client buffer is short, then we copy stuff to our internal - * buffer to combine reads. - */ -static ssize_t -archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff, - size_t min) -{ - struct archive_decompress_none *state; - ssize_t bytes_read; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->fatal) - return (-1); - - /* - * Don't make special efforts to handle requests larger than - * the copy buffer. - */ - if (min > state->buffer_size) - min = state->buffer_size; - - /* - * Keep pulling more data until we can satisfy the request. - */ - for (;;) { - - /* - * If we can satisfy from the copy buffer, we're done. - */ - if (state->avail >= min) { - *buff = state->next; - return (state->avail); - } - - /* - * We can satisfy directly from client buffer if everything - * currently in the copy buffer is still in the client buffer. - */ - if (state->client_total >= state->client_avail + state->avail - && state->client_avail + state->avail >= min) { - /* "Roll back" to client buffer. */ - state->client_avail += state->avail; - state->client_next -= state->avail; - /* Copy buffer is now empty. */ - state->avail = 0; - state->next = state->buffer; - /* Return data from client buffer. */ - *buff = state->client_next; - return (state->client_avail); - } - - /* Move data forward in copy buffer if necessary. */ - if (state->next > state->buffer && - state->next + min > state->buffer + state->buffer_size) { - if (state->avail > 0) - memmove(state->buffer, state->next, state->avail); - state->next = state->buffer; - } - - /* If we've used up the client data, get more. */ - if (state->client_avail <= 0) { - bytes_read = (a->client_reader)(&a->archive, - a->client_data, &state->client_buff); - if (bytes_read < 0) { /* Read error. */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->fatal = 1; - return (-1); - } - if (bytes_read == 0) { /* End-of-file. */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->end_of_file = 1; - /* Return whatever we do have. */ - *buff = state->next; - return (state->avail); - } - a->archive.raw_position += bytes_read; - state->client_total = bytes_read; - state->client_avail = state->client_total; - state->client_next = state->client_buff; - } - else - { - /* We can add client data to copy buffer. */ - /* First estimate: copy to fill rest of buffer. */ - size_t tocopy = (state->buffer + state->buffer_size) - - (state->next + state->avail); - /* Don't copy more than is available. */ - if (tocopy > state->client_avail) - tocopy = state->client_avail; - memcpy(state->next + state->avail, state->client_next, - tocopy); - /* Remove this data from client buffer. */ - state->client_next += tocopy; - state->client_avail -= tocopy; - /* add it to copy buffer. */ - state->avail += tocopy; - } - } -} - -/* - * Mark the appropriate data as used. Note that the request here will - * often be much smaller than the size of the previous read_ahead - * request. - */ -static ssize_t -archive_decompressor_none_read_consume(struct archive_read *a, size_t request) -{ - struct archive_decompress_none *state; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->avail > 0) { - /* Read came from copy buffer. */ - state->next += request; - state->avail -= request; - } else { - /* Read came from client buffer. */ - state->client_next += request; - state->client_avail -= request; - } - a->archive.file_position += request; - return (request); -} - -/* - * Skip forward by exactly the requested bytes or else return - * ARCHIVE_FATAL. Note that this differs from the contract for - * read_ahead, which does not guarantee a minimum count. - */ -static off_t -archive_decompressor_none_skip(struct archive_read *a, off_t request) -{ - struct archive_decompress_none *state; - off_t bytes_skipped, total_bytes_skipped = 0; - size_t min; - - state = (struct archive_decompress_none *)a->decompressor->data; - if (state->fatal) - return (-1); - /* - * If there is data in the buffers already, use that first. - */ - if (state->avail > 0) { - min = minimum(request, (off_t)state->avail); - bytes_skipped = archive_decompressor_none_read_consume(a, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; - } - if (state->client_avail > 0) { - min = minimum(request, (off_t)state->client_avail); - bytes_skipped = archive_decompressor_none_read_consume(a, min); - request -= bytes_skipped; - total_bytes_skipped += bytes_skipped; - } - if (request == 0) - return (total_bytes_skipped); - /* - * If a client_skipper was provided, try that first. - */ -#if ARCHIVE_API_VERSION < 2 - if ((a->client_skipper != NULL) && (request < SSIZE_MAX)) { -#else - if (a->client_skipper != NULL) { -#endif - bytes_skipped = (a->client_skipper)(&a->archive, - a->client_data, request); - if (bytes_skipped < 0) { /* error */ - state->client_total = state->client_avail = 0; - state->client_next = state->client_buff = NULL; - state->fatal = 1; - return (bytes_skipped); - } - total_bytes_skipped += bytes_skipped; - a->archive.file_position += bytes_skipped; - request -= bytes_skipped; - state->client_next = state->client_buff; - a->archive.raw_position += bytes_skipped; - state->client_avail = state->client_total = 0; - } - /* - * Note that client_skipper will usually not satisfy the - * full request (due to low-level blocking concerns), - * so even if client_skipper is provided, we may still - * have to use ordinary reads to finish out the request. - */ - while (request > 0) { - const void* dummy_buffer; - ssize_t bytes_read; - bytes_read = archive_decompressor_none_read_ahead(a, - &dummy_buffer, 1); - if (bytes_read < 0) - return (bytes_read); - if (bytes_read == 0) { - /* We hit EOF before we satisfied the skip request. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated input file (need to skip %jd bytes)", - (intmax_t)request); - return (ARCHIVE_FATAL); - } - min = (size_t)(minimum(bytes_read, request)); - bytes_read = archive_decompressor_none_read_consume(a, min); - total_bytes_skipped += bytes_read; - request -= bytes_read; - } - return (total_bytes_skipped); -} - -static int -archive_decompressor_none_finish(struct archive_read *a) -{ - struct archive_decompress_none *state; - - state = (struct archive_decompress_none *)a->decompressor->data; - free(state->buffer); - free(state); - a->decompressor->data = NULL; + (void)a; /* UNUSED */ return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_program.c b/contrib/libarchive/libarchive/archive_read_support_compression_program.c index 4f25d6ed56..11ee805497 100644 --- a/contrib/libarchive/libarchive/archive_read_support_compression_program.c +++ b/contrib/libarchive/libarchive/archive_read_support_compression_program.c @@ -24,25 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.4 2008/06/15 10:45:57 kientzle Exp $"); - -/* This capability is only available on POSIX systems. */ -#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK)) - -/* - * On non-Posix systems, allow the program to build, but choke if - * this function is actually invoked. - */ -int -archive_read_support_compression_program(struct archive *_a, const char *cmd) -{ - archive_set_error(_a, -1, - "External compression programs not supported on this platform"); - return (ARCHIVE_FATAL); -} - -#else +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.6 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_SYS_WAIT_H # include @@ -56,6 +38,9 @@ archive_read_support_compression_program(struct archive *_a, const char *cmd) #ifdef HAVE_LIMITS_H # include #endif +#ifdef HAVE_SIGNAL_H +# include +#endif #ifdef HAVE_STDLIB_H # include #endif @@ -70,272 +55,403 @@ archive_read_support_compression_program(struct archive *_a, const char *cmd) #include "archive_private.h" #include "archive_read_private.h" +int +archive_read_support_compression_program(struct archive *a, const char *cmd) +{ + return (archive_read_support_compression_program_signature(a, cmd, NULL, 0)); +} + + +/* This capability is only available on POSIX systems. */ +#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ + !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) + +/* + * On non-Posix systems, allow the program to build, but choke if + * this function is actually invoked. + */ +int +archive_read_support_compression_program_signature(struct archive *_a, + const char *cmd, void *signature, size_t signature_len) +{ + (void)_a; /* UNUSED */ + (void)cmd; /* UNUSED */ + (void)signature; /* UNUSED */ + (void)signature_len; /* UNUSED */ + + archive_set_error(_a, -1, + "External compression programs not supported on this platform"); + return (ARCHIVE_FATAL); +} + +int +__archive_read_program(struct archive_read_filter *self, const char *cmd) +{ + (void)self; /* UNUSED */ + (void)cmd; /* UNUSED */ + + archive_set_error(&self->archive->archive, -1, + "External compression programs not supported on this platform"); + return (ARCHIVE_FATAL); +} + +#else + #include "filter_fork.h" -struct archive_decompress_program { +/* + * The bidder object stores the command and the signature to watch for. + * The 'inhibit' entry here is used to ensure that unchecked filters never + * bid twice in the same pipeline. + */ +struct program_bidder { + char *cmd; + void *signature; + size_t signature_len; + int inhibit; +}; + +static int program_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *upstream); +static int program_bidder_init(struct archive_read_filter *); +static int program_bidder_free(struct archive_read_filter_bidder *); + +/* + * The actual filter needs to track input and output data. + */ +struct program_filter { char *description; pid_t child; + int exit_status; + int waitpid_return; int child_stdin, child_stdout; - char *child_out_buf; - char *child_out_buf_next; - size_t child_out_buf_len, child_out_buf_avail; - - const char *child_in_buf; - size_t child_in_buf_avail; + char *out_buf; + size_t out_buf_len; }; -static int archive_decompressor_program_bid(const void *, size_t); -static int archive_decompressor_program_finish(struct archive_read *); -static int archive_decompressor_program_init(struct archive_read *, - const void *, size_t); -static ssize_t archive_decompressor_program_read_ahead(struct archive_read *, - const void **, size_t); -static ssize_t archive_decompressor_program_read_consume(struct archive_read *, - size_t); +static ssize_t program_filter_read(struct archive_read_filter *, + const void **); +static int program_filter_close(struct archive_read_filter *); int -archive_read_support_compression_program(struct archive *_a, const char *cmd) +archive_read_support_compression_program_signature(struct archive *_a, + const char *cmd, const void *signature, size_t signature_len) { struct archive_read *a = (struct archive_read *)_a; - struct decompressor_t *decompressor; + struct archive_read_filter_bidder *bidder; + struct program_bidder *state; + + /* + * Get a bidder object from the read core. + */ + bidder = __archive_read_get_bidder(a); + if (bidder == NULL) + return (ARCHIVE_FATAL); - if (cmd == NULL || *cmd == '\0') - return (ARCHIVE_WARN); + /* + * Allocate our private state. + */ + state = (struct program_bidder *)calloc(sizeof (*state), 1); + if (state == NULL) + return (ARCHIVE_FATAL); + state->cmd = strdup(cmd); + if (signature != NULL && signature_len > 0) { + state->signature_len = signature_len; + state->signature = malloc(signature_len); + memcpy(state->signature, signature, signature_len); + } - decompressor = __archive_read_register_compression(a, - archive_decompressor_program_bid, - archive_decompressor_program_init); - if (decompressor == NULL) - return (ARCHIVE_WARN); + /* + * Fill in the bidder object. + */ + bidder->data = state; + bidder->bid = program_bidder_bid; + bidder->init = program_bidder_init; + bidder->options = NULL; + bidder->free = program_bidder_free; + return (ARCHIVE_OK); +} - decompressor->config = strdup(cmd); +static int +program_bidder_free(struct archive_read_filter_bidder *self) +{ + struct program_bidder *state = (struct program_bidder *)self->data; + free(state->cmd); + free(state->signature); + free(self->data); return (ARCHIVE_OK); } /* - * If the user used us to register, they must really want us to - * handle it, so this module always bids INT_MAX. + * If we do have a signature, bid only if that matches. + * + * If there's no signature, we bid INT_MAX the first time + * we're called, then never bid again. */ static int -archive_decompressor_program_bid(const void *buff, size_t len) +program_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *upstream) { - (void)buff; /* UNUSED */ - (void)len; /* UNUSED */ + struct program_bidder *state = self->data; + const char *p; + + /* If we have a signature, use that to match. */ + if (state->signature_len > 0) { + p = __archive_read_filter_ahead(upstream, + state->signature_len, NULL); + if (p == NULL) + return (0); + /* No match, so don't bid. */ + if (memcmp(p, state->signature, state->signature_len) != 0) + return (0); + return (state->signature_len * 8); + } - return (INT_MAX); /* Default: We'll take it. */ + /* Otherwise, bid once and then never bid again. */ + if (state->inhibit) + return (0); + state->inhibit = 1; + return (INT_MAX); } -static ssize_t -child_read(struct archive_read *a, char *buf, size_t buf_len) +/* + * Shut down the child, return ARCHIVE_OK if it exited normally. + * + * Note that the return value is sticky; if we're called again, + * we won't reap the child again, but we will return the same status + * (including error message if the child came to a bad end). + */ +static int +child_stop(struct archive_read_filter *self, struct program_filter *state) { - struct archive_decompress_program *state = a->decompressor->data; - ssize_t ret, requested; - const void *child_buf; + /* Close our side of the I/O with the child. */ + if (state->child_stdin != -1) { + close(state->child_stdin); + state->child_stdin = -1; + } + if (state->child_stdout != -1) { + close(state->child_stdout); + state->child_stdout = -1; + } - if (state->child_stdout == -1) - return (-1); + if (state->child != 0) { + /* Reap the child. */ + do { + state->waitpid_return + = waitpid(state->child, &state->exit_status, 0); + } while (state->waitpid_return == -1 && errno == EINTR); + state->child = 0; + } - if (buf_len == 0) - return (-1); + if (state->waitpid_return < 0) { + /* waitpid() failed? This is ugly. */ + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Child process exited badly"); + return (ARCHIVE_WARN); + } -restart_read: - requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; + if (WIFSIGNALED(state->exit_status)) { +#ifdef SIGPIPE + /* If the child died because we stopped reading before + * it was done, that's okay. Some archive formats + * have padding at the end that we routinely ignore. */ + /* The alternative to this would be to add a step + * before close(child_stdout) above to read from the + * child until the child has no more to write. */ + if (WTERMSIG(state->exit_status) == SIGPIPE) + return (ARCHIVE_OK); +#endif + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Child process exited with signal %d", + WTERMSIG(state->exit_status)); + return (ARCHIVE_WARN); + } - do { - ret = read(state->child_stdout, buf, requested); - } while (ret == -1 && errno == EINTR); + if (WIFEXITED(state->exit_status)) { + if (WEXITSTATUS(state->exit_status) == 0) + return (ARCHIVE_OK); - if (ret > 0) - return (ret); - if (ret == 0 || (ret == -1 && errno == EPIPE)) { - close(state->child_stdout); - state->child_stdout = -1; - return (0); + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Child process exited with status %d", + WEXITSTATUS(state->exit_status)); + return (ARCHIVE_WARN); } - if (ret == -1 && errno != EAGAIN) - return (-1); - if (state->child_in_buf_avail == 0) { - child_buf = state->child_in_buf; - ret = (a->client_reader)(&a->archive, - a->client_data,&child_buf); - state->child_in_buf = (const char *)child_buf; + return (ARCHIVE_WARN); +} + +/* + * Use select() to decide whether the child is ready for read or write. + */ +static ssize_t +child_read(struct archive_read_filter *self, char *buf, size_t buf_len) +{ + struct program_filter *state = self->data; + ssize_t ret, requested, avail; + const char *p; + + requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; - if (ret < 0) { + for (;;) { + do { + ret = read(state->child_stdout, buf, requested); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) + return (ret); + if (ret == 0 || (ret == -1 && errno == EPIPE)) + /* Child has closed its output; reap the child + * and return the status. */ + return (child_stop(self, state)); + if (ret == -1 && errno != EAGAIN) + return (-1); + + if (state->child_stdin == -1) { + /* Block until child has some I/O ready. */ + __archive_check_child(state->child_stdin, + state->child_stdout); + continue; + } + + /* Get some more data from upstream. */ + p = __archive_read_filter_ahead(self->upstream, 1, &avail); + if (p == NULL) { close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); - return (-1); + if (avail < 0) + return (avail); + continue; } - if (ret == 0) { + + do { + ret = write(state->child_stdin, p, avail); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) { + /* Consume whatever we managed to write. */ + __archive_read_filter_consume(self->upstream, ret); + } else if (ret == -1 && errno == EAGAIN) { + /* Block until child has some I/O ready. */ + __archive_check_child(state->child_stdin, + state->child_stdout); + } else { + /* Write failed. */ close(state->child_stdin); state->child_stdin = -1; fcntl(state->child_stdout, F_SETFL, 0); - goto restart_read; + /* If it was a bad error, we're done; otherwise + * it was EPIPE or EOF, and we can still read + * from the child. */ + if (ret == -1 && errno != EPIPE) + return (-1); } - state->child_in_buf_avail = ret; - } - - if (state->child_stdin == -1) { - fcntl(state->child_stdout, F_SETFL, 0); - __archive_check_child(state->child_stdin, state->child_stdout); - goto restart_read; - } - - do { - ret = write(state->child_stdin, state->child_in_buf, - state->child_in_buf_avail); - } while (ret == -1 && errno == EINTR); - - if (ret > 0) { - state->child_in_buf += ret; - state->child_in_buf_avail -= ret; - goto restart_read; - } else if (ret == -1 && errno == EAGAIN) { - __archive_check_child(state->child_stdin, state->child_stdout); - goto restart_read; - } else if (ret == 0 || (ret == -1 && errno == EPIPE)) { - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); - goto restart_read; - } else { - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); - return (-1); } } -static int -archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n) +int +__archive_read_program(struct archive_read_filter *self, const char *cmd) { - struct archive_decompress_program *state; - const char *cmd = a->decompressor->config; + struct program_filter *state; + static const size_t out_buf_len = 65536; + char *out_buf; + char *description; const char *prefix = "Program: "; - - state = (struct archive_decompress_program *)malloc(sizeof(*state)); - if (!state) { - archive_set_error(&a->archive, ENOMEM, + state = (struct program_filter *)calloc(1, sizeof(*state)); + out_buf = (char *)malloc(out_buf_len); + description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); + if (state == NULL || out_buf == NULL || description == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate input data"); + free(state); + free(out_buf); + free(description); return (ARCHIVE_FATAL); } - a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; - state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); + self->code = ARCHIVE_COMPRESSION_PROGRAM; + state->description = description; strcpy(state->description, prefix); strcat(state->description, cmd); - a->archive.compression_name = state->description; - - state->child_out_buf_next = state->child_out_buf = malloc(65536); - if (!state->child_out_buf) { - free(state); - archive_set_error(&a->archive, ENOMEM, - "Can't allocate filter buffer"); - return (ARCHIVE_FATAL); - } - state->child_out_buf_len = 65536; - state->child_out_buf_avail = 0; + self->name = state->description; - state->child_in_buf = buff; - state->child_in_buf_avail = n; + state->out_buf = out_buf; + state->out_buf_len = out_buf_len; if ((state->child = __archive_create_child(cmd, &state->child_stdin, &state->child_stdout)) == -1) { - free(state->child_out_buf); + free(state->out_buf); free(state); - archive_set_error(&a->archive, EINVAL, + archive_set_error(&self->archive->archive, EINVAL, "Can't initialise filter"); return (ARCHIVE_FATAL); } - a->decompressor->data = state; - a->decompressor->read_ahead = archive_decompressor_program_read_ahead; - a->decompressor->consume = archive_decompressor_program_read_consume; - a->decompressor->skip = NULL; - a->decompressor->finish = archive_decompressor_program_finish; + self->data = state; + self->read = program_filter_read; + self->skip = NULL; + self->close = program_filter_close; /* XXX Check that we can read at least one byte? */ return (ARCHIVE_OK); } -static ssize_t -archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff, - size_t min) +static int +program_bidder_init(struct archive_read_filter *self) { - struct archive_decompress_program *state; - ssize_t bytes_read; - - state = (struct archive_decompress_program *)a->decompressor->data; + struct program_bidder *bidder_state; - if (min > state->child_out_buf_len) - min = state->child_out_buf_len; - - while (state->child_stdout != -1 && min > state->child_out_buf_avail) { - if (state->child_out_buf != state->child_out_buf_next) { - memmove(state->child_out_buf, state->child_out_buf_next, - state->child_out_buf_avail); - state->child_out_buf_next = state->child_out_buf; - } - - bytes_read = child_read(a, - state->child_out_buf + state->child_out_buf_avail, - state->child_out_buf_len - state->child_out_buf_avail); - if (bytes_read == -1) - return (-1); - if (bytes_read == 0) - break; - state->child_out_buf_avail += bytes_read; - a->archive.raw_position += bytes_read; - } - - *buff = state->child_out_buf_next; - return (state->child_out_buf_avail); + bidder_state = (struct program_bidder *)self->bidder->data; + return (__archive_read_program(self, bidder_state->cmd)); } static ssize_t -archive_decompressor_program_read_consume(struct archive_read *a, size_t request) +program_filter_read(struct archive_read_filter *self, const void **buff) { - struct archive_decompress_program *state; - - state = (struct archive_decompress_program *)a->decompressor->data; - - state->child_out_buf_next += request; - state->child_out_buf_avail -= request; + struct program_filter *state; + ssize_t bytes; + size_t total; + char *p; + + state = (struct program_filter *)self->data; + + total = 0; + p = state->out_buf; + while (state->child_stdout != -1 && total < state->out_buf_len) { + bytes = child_read(self, p, state->out_buf_len - total); + if (bytes < 0) + /* No recovery is possible if we can no longer + * read from the child. */ + return (ARCHIVE_FATAL); + if (bytes == 0) + /* We got EOF from the child. */ + break; + total += bytes; + p += bytes; + } - a->archive.file_position += request; - return (request); + *buff = state->out_buf; + return (total); } static int -archive_decompressor_program_finish(struct archive_read *a) +program_filter_close(struct archive_read_filter *self) { - struct archive_decompress_program *state; - int status; - - state = (struct archive_decompress_program *)a->decompressor->data; + struct program_filter *state; + int e; - /* Release our configuration data. */ - free(a->decompressor->config); - a->decompressor->config = NULL; - - /* Shut down the child. */ - if (state->child_stdin != -1) - close(state->child_stdin); - if (state->child_stdout != -1) - close(state->child_stdout); - while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) - continue; + state = (struct program_filter *)self->data; + e = child_stop(self, state); /* Release our private data. */ - free(state->child_out_buf); + free(state->out_buf); free(state->description); free(state); - a->decompressor->data = NULL; - return (ARCHIVE_OK); + return (e); } #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/contrib/libarchive/libarchive/archive_read_support_compression_xz.c b/contrib/libarchive/libarchive/archive_read_support_compression_xz.c new file mode 100644 index 0000000000..a3c9553c92 --- /dev/null +++ b/contrib/libarchive/libarchive/archive_read_support_compression_xz.c @@ -0,0 +1,642 @@ +/*- + * Copyright (c) 2009 Michihiro NAKAJIMA + * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna + * 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$"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#if HAVE_LZMA_H +#include +#elif HAVE_LZMADEC_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#if HAVE_LZMA_H && HAVE_LIBLZMA + +struct private_data { + lzma_stream stream; + unsigned char *out_block; + size_t out_block_size; + int64_t total_out; + char eof; /* True = found end of compressed data. */ +}; + +/* Combined lzma/xz filter */ +static ssize_t xz_filter_read(struct archive_read_filter *, const void **); +static int xz_filter_close(struct archive_read_filter *); +static int xz_lzma_bidder_init(struct archive_read_filter *); + +#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC + +struct private_data { + lzmadec_stream stream; + unsigned char *out_block; + size_t out_block_size; + int64_t total_out; + char eof; /* True = found end of compressed data. */ +}; + +/* Lzma-only filter */ +static ssize_t lzma_filter_read(struct archive_read_filter *, const void **); +static int lzma_filter_close(struct archive_read_filter *); +#endif + +/* + * Note that we can detect xz and lzma compressed files even if we + * can't decompress them. (In fact, we like detecting them because we + * can give better error messages.) So the bid framework here gets + * compiled even if no lzma library is available. + */ +static int xz_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int xz_bidder_init(struct archive_read_filter *); +static int lzma_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int lzma_bidder_init(struct archive_read_filter *); + +int +archive_read_support_compression_xz(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); + + if (bidder == NULL) + return (ARCHIVE_FATAL); + + bidder->data = NULL; + bidder->bid = xz_bidder_bid; + bidder->init = xz_bidder_init; + bidder->options = NULL; + bidder->free = NULL; +#if HAVE_LZMA_H && HAVE_LIBLZMA + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external unxz program for xz decompression"); + return (ARCHIVE_WARN); +#endif +} + +int +archive_read_support_compression_lzma(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *bidder = __archive_read_get_bidder(a); + + if (bidder == NULL) + return (ARCHIVE_FATAL); + + bidder->data = NULL; + bidder->bid = lzma_bidder_bid; + bidder->init = lzma_bidder_init; + bidder->options = NULL; + bidder->free = NULL; +#if HAVE_LZMA_H && HAVE_LIBLZMA + return (ARCHIVE_OK); +#elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external unlzma program for lzma decompression"); + return (ARCHIVE_WARN); +#endif +} + +/* + * Test whether we can handle this data. + */ +static int +xz_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *buffer; + ssize_t avail; + int bits_checked; + + (void)self; /* UNUSED */ + + buffer = __archive_read_filter_ahead(filter, 6, &avail); + if (buffer == NULL) + return (0); + + /* + * Verify Header Magic Bytes : FD 37 7A 58 5A 00 + */ + bits_checked = 0; + if (buffer[0] != 0xFD) + return (0); + bits_checked += 8; + if (buffer[1] != 0x37) + return (0); + bits_checked += 8; + if (buffer[2] != 0x7A) + return (0); + bits_checked += 8; + if (buffer[3] != 0x58) + return (0); + bits_checked += 8; + if (buffer[4] != 0x5A) + return (0); + bits_checked += 8; + if (buffer[5] != 0x00) + return (0); + bits_checked += 8; + + return (bits_checked); +} + +/* + * Test whether we can handle this data. + * + * LZMA has a rather poor file signature. Zeros do not + * make good signature bytes as a rule, and the only non-zero byte + * here is an ASCII character. For example, an uncompressed tar + * archive whose first file is ']' would satisfy this check. It may + * be necessary to exclude LZMA from compression_all() because of + * this. Clients of libarchive would then have to explicitly enable + * LZMA checking instead of (or in addition to) compression_all() when + * they have other evidence (file name, command-line option) to go on. + */ +static int +lzma_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *buffer; + ssize_t avail; + int bits_checked; + + (void)self; /* UNUSED */ + + buffer = __archive_read_filter_ahead(filter, 6, &avail); + if (buffer == NULL) + return (0); + + /* First byte of raw LZMA stream is always 0x5d. */ + bits_checked = 0; + if (buffer[0] != 0x5d) + return (0); + bits_checked += 8; + + /* Second through fifth bytes are dictionary code, stored in + * little-endian order. The two least-significant bytes are + * always zero. */ + if (buffer[1] != 0 || buffer[2] != 0) + return (0); + bits_checked += 16; + + /* ??? TODO: Fix this. ??? */ + /* NSIS format check uses this, but I've seen tar.lzma + * archives where this byte is 0xff, not 0. Can it + * ever be anything other than 0 or 0xff? + */ +#if 0 + if (buffer[5] != 0) + return (0); + bits_checked += 8; +#endif + + /* TODO: The above test is still very weak. It would be + * good to do better. */ + + return (bits_checked); +} + +#if HAVE_LZMA_H && HAVE_LIBLZMA + +/* + * liblzma 4.999.7 and later support both lzma and xz streams. + */ +static int +xz_bidder_init(struct archive_read_filter *self) +{ + self->code = ARCHIVE_COMPRESSION_XZ; + self->name = "xz"; + return (xz_lzma_bidder_init(self)); +} + +static int +lzma_bidder_init(struct archive_read_filter *self) +{ + self->code = ARCHIVE_COMPRESSION_LZMA; + self->name = "lzma"; + return (xz_lzma_bidder_init(self)); +} + +/* + * Setup the callbacks. + */ +static int +xz_lzma_bidder_init(struct archive_read_filter *self) +{ + static const size_t out_block_size = 64 * 1024; + void *out_block; + struct private_data *state; + int ret; + + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (state == NULL || out_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for xz decompression"); + free(out_block); + free(state); + return (ARCHIVE_FATAL); + } + + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->read = xz_filter_read; + self->skip = NULL; /* not supported */ + self->close = xz_filter_close; + + state->stream.avail_in = 0; + + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Initialize compression library. + * TODO: I don't know what value is best for memlimit. + * maybe, it needs to check memory size which + * running system has. + */ + if (self->code == ARCHIVE_COMPRESSION_XZ) + ret = lzma_stream_decoder(&(state->stream), + (1U << 23) + (1U << 21),/* memlimit */ + LZMA_CONCATENATED); + else + ret = lzma_alone_decoder(&(state->stream), + (1U << 23) + (1U << 21));/* memlimit */ + + if (ret == LZMA_OK) + return (ARCHIVE_OK); + + /* Library setup failed: Choose an error message and clean up. */ + switch (ret) { + case LZMA_MEM_ERROR: + archive_set_error(&self->archive->archive, ENOMEM, + "Internal error initializing compression library: " + "Cannot allocate memory"); + break; + case LZMA_OPTIONS_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "Invalid or unsupported options"); + break; + default: + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing lzma library"); + break; + } + + free(state->out_block); + free(state); + self->data = NULL; + return (ARCHIVE_FATAL); +} + +/* + * Return the next block of decompressed data. + */ +static ssize_t +xz_filter_read(struct archive_read_filter *self, const void **p) +{ + struct private_data *state; + size_t decompressed; + ssize_t avail_in; + int ret; + + state = (struct private_data *)self->data; + + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Try to fill the output buffer. */ + while (state->stream.avail_out > 0 && !state->eof) { + state->stream.next_in = + __archive_read_filter_ahead(self->upstream, 1, &avail_in); + if (state->stream.next_in == NULL && avail_in < 0) + return (ARCHIVE_FATAL); + state->stream.avail_in = avail_in; + + /* Decompress as much as we can in one pass. */ + ret = lzma_code(&(state->stream), + (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN); + switch (ret) { + case LZMA_STREAM_END: /* Found end of stream. */ + state->eof = 1; + /* FALL THROUGH */ + case LZMA_OK: /* Decompressor made some progress. */ + __archive_read_filter_consume(self->upstream, + avail_in - state->stream.avail_in); + break; + case LZMA_MEM_ERROR: + archive_set_error(&self->archive->archive, ENOMEM, + "Lzma library error: Cannot allocate memory"); + return (ARCHIVE_FATAL); + case LZMA_MEMLIMIT_ERROR: + archive_set_error(&self->archive->archive, ENOMEM, + "Lzma library error: Out of memory"); + return (ARCHIVE_FATAL); + case LZMA_FORMAT_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma library error: format not recognized"); + return (ARCHIVE_FATAL); + case LZMA_OPTIONS_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma library error: Invalid options"); + return (ARCHIVE_FATAL); + case LZMA_DATA_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma library error: Corrupted input data"); + return (ARCHIVE_FATAL); + case LZMA_BUF_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma library error: No progress is possible"); + return (ARCHIVE_FATAL); + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma decompression failed: Unknown error"); + return (ARCHIVE_FATAL); + } + } + + decompressed = state->stream.next_out - state->out_block; + state->total_out += decompressed; + if (decompressed == 0) + *p = NULL; + else + *p = state->out_block; + return (decompressed); +} + +/* + * Clean up the decompressor. + */ +static int +xz_filter_close(struct archive_read_filter *self) +{ + struct private_data *state; + + state = (struct private_data *)self->data; + lzma_end(&(state->stream)); + free(state->out_block); + free(state); + return (ARCHIVE_OK); +} + +#else + +#if HAVE_LZMADEC_H && HAVE_LIBLZMADEC + +/* + * If we have the older liblzmadec library, then we can handle + * LZMA streams but not XZ streams. + */ + +/* + * Setup the callbacks. + */ +static int +lzma_bidder_init(struct archive_read_filter *self) +{ + static const size_t out_block_size = 64 * 1024; + void *out_block; + struct private_data *state; + ssize_t ret, avail_in; + + self->code = ARCHIVE_COMPRESSION_LZMA; + self->name = "lzma"; + + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (state == NULL || out_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lzma decompression"); + free(out_block); + free(state); + return (ARCHIVE_FATAL); + } + + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->read = lzma_filter_read; + self->skip = NULL; /* not supported */ + self->close = lzma_filter_close; + + /* Prime the lzma library with 18 bytes of input. */ + state->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(self->upstream, 18, &avail_in); + if (state->stream.next_in == NULL) + return (ARCHIVE_FATAL); + state->stream.avail_in = avail_in; + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Initialize compression library. */ + ret = lzmadec_init(&(state->stream)); + __archive_read_filter_consume(self->upstream, + avail_in - state->stream.avail_in); + if (ret == LZMADEC_OK) + return (ARCHIVE_OK); + + /* Library setup failed: Clean up. */ + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing lzma library"); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case LZMADEC_HEADER_ERROR: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid header"); + break; + case LZMADEC_MEM_ERROR: + archive_set_error(&self->archive->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + } + + free(state->out_block); + free(state); + self->data = NULL; + return (ARCHIVE_FATAL); +} + +/* + * Return the next block of decompressed data. + */ +static ssize_t +lzma_filter_read(struct archive_read_filter *self, const void **p) +{ + struct private_data *state; + size_t decompressed; + ssize_t avail_in, ret; + + state = (struct private_data *)self->data; + + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Try to fill the output buffer. */ + while (state->stream.avail_out > 0 && !state->eof) { + state->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(self->upstream, 1, &avail_in); + if (state->stream.next_in == NULL && avail_in < 0) + return (ARCHIVE_FATAL); + state->stream.avail_in = avail_in; + + /* Decompress as much as we can in one pass. */ + ret = lzmadec_decode(&(state->stream), avail_in == 0); + switch (ret) { + case LZMADEC_STREAM_END: /* Found end of stream. */ + state->eof = 1; + /* FALL THROUGH */ + case LZMADEC_OK: /* Decompressor made some progress. */ + __archive_read_filter_consume(self->upstream, + avail_in - state->stream.avail_in); + break; + case LZMADEC_BUF_ERROR: /* Insufficient input data? */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Insufficient compressed data"); + return (ARCHIVE_FATAL); + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Lzma decompression failed"); + return (ARCHIVE_FATAL); + } + } + + decompressed = state->stream.next_out - state->out_block; + state->total_out += decompressed; + if (decompressed == 0) + *p = NULL; + else + *p = state->out_block; + return (decompressed); +} + +/* + * Clean up the decompressor. + */ +static int +lzma_filter_close(struct archive_read_filter *self) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)self->data; + ret = ARCHIVE_OK; + switch (lzmadec_end(&(state->stream))) { + case LZMADEC_OK: + break; + default: + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, + "Failed to clean up %s compressor", + self->archive->archive.compression_name); + ret = ARCHIVE_FATAL; + } + + free(state->out_block); + free(state); + return (ret); +} + +#else + +/* + * + * If we have no suitable library on this system, we can't actually do + * the decompression. We can, however, still detect compressed + * archives and emit a useful message. + * + */ +static int +lzma_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "unlzma"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_COMPRESSION_LZMA; + self->name = "lzma"; + return (r); +} + +#endif /* HAVE_LZMADEC_H */ + + +static int +xz_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "unxz"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_COMPRESSION_XZ; + self->name = "xz"; + return (r); +} + + +#endif /* HAVE_LZMA_H */ diff --git a/contrib/libarchive/libarchive/archive_read_support_format_ar.c b/contrib/libarchive/libarchive/archive_read_support_format_ar.c index baf80bd62f..587aad266d 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_ar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_ar.c @@ -26,7 +26,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.10 2008/05/26 17:00:22 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.12 2008/12/17 19:02:42 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -72,8 +72,6 @@ struct ar { #define AR_fmag_offset 58 #define AR_fmag_size 2 -#define isdigit(x) (x) >= '0' && (x) <= '9' - static int archive_read_format_ar_bid(struct archive_read *a); static int archive_read_format_ar_cleanup(struct archive_read *a); static int archive_read_format_ar_read_data(struct archive_read *a, @@ -105,7 +103,9 @@ archive_read_support_format_ar(struct archive *_a) r = __archive_read_register_format(a, ar, + "ar", archive_read_format_ar_bid, + NULL, archive_read_format_ar_read_header, archive_read_format_ar_read_data, archive_read_format_ar_skip, @@ -135,7 +135,6 @@ static int archive_read_format_ar_bid(struct archive_read *a) { struct ar *ar; - ssize_t bytes_read; const void *h; if (a->archive.archive_format != 0 && @@ -149,8 +148,7 @@ archive_read_format_ar_bid(struct archive_read *a) * Verify the 8-byte file signature. * TODO: Do we need to check more than this? */ - bytes_read = (a->decompressor->read_ahead)(a, &h, 8); - if (bytes_read < 8) + if ((h = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (strncmp((const char*)h, "!\n", 8) == 0) { return (64); @@ -166,7 +164,7 @@ archive_read_format_ar_read_header(struct archive_read *a, struct ar *ar; uint64_t number; /* Used to hold parsed numbers before validation. */ ssize_t bytes_read; - size_t bsd_name_length, entry_size, s; + size_t bsd_name_length, entry_size; char *p, *st; const void *b; const char *h; @@ -179,24 +177,22 @@ archive_read_format_ar_read_header(struct archive_read *a, * We are now at the beginning of the archive, * so we need first consume the ar global header. */ - (a->decompressor->consume)(a, 8); + __archive_read_consume(a, 8); /* Set a default format code for now. */ a->archive.archive_format = ARCHIVE_FORMAT_AR; } /* Read the header for the next file entry. */ - bytes_read = (a->decompressor->read_ahead)(a, &b, 60); - if (bytes_read < 60) { + if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL) /* Broken header. */ return (ARCHIVE_EOF); - } - (a->decompressor->consume)(a, 60); + __archive_read_consume(a, 60); h = (const char *)b; /* Verify the magic signature on the file header. */ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { archive_set_error(&a->archive, EINVAL, - "Consistency check failed"); + "Incorrect file header signature"); return (ARCHIVE_WARN); } @@ -296,16 +292,10 @@ archive_read_format_ar_read_header(struct archive_read *a, } ar->strtab = st; ar->strtab_size = entry_size; - for (s = entry_size; s > 0; s -= bytes_read) { - bytes_read = (a->decompressor->read_ahead)(a, &b, s); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - if (bytes_read > (ssize_t)s) - bytes_read = s; - memcpy(st, b, bytes_read); - st += bytes_read; - (a->decompressor->consume)(a, bytes_read); - } + if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL) + return (ARCHIVE_FATAL); + memcpy(st, b, entry_size); + __archive_read_consume(a, entry_size); /* All contents are consumed. */ ar->entry_bytes_remaining = 0; archive_entry_set_size(entry, ar->entry_bytes_remaining); @@ -317,8 +307,10 @@ archive_read_format_ar_read_header(struct archive_read *a, /* * GNU variant handles long filenames by storing / * to indicate a name stored in the filename table. + * XXX TODO: Verify that it's all digits... Don't be fooled + * by "/9xyz" XXX */ - if (filename[0] == '/' && isdigit(filename[1])) { + if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') { number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1); /* * If we can't look up the real name, warn and return @@ -365,15 +357,12 @@ archive_read_format_ar_read_header(struct archive_read *a, archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Read the long name into memory. */ - bytes_read = (a->decompressor->read_ahead)(a, &b, bsd_name_length); - if (bytes_read <= 0) - return (ARCHIVE_FATAL); - if ((size_t)bytes_read < bsd_name_length) { + if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, bsd_name_length); + __archive_read_consume(a, bsd_name_length); /* Store it in the entry. */ p = (char *)malloc(bsd_name_length + 1); @@ -453,7 +442,7 @@ archive_read_format_ar_read_data(struct archive_read *a, ar = (struct ar *)(a->format->data); if (ar->entry_bytes_remaining > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated ar archive"); @@ -467,16 +456,16 @@ archive_read_format_ar_read_data(struct archive_read *a, *offset = ar->entry_offset; ar->entry_offset += bytes_read; ar->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, (size_t)bytes_read); + __archive_read_consume(a, (size_t)bytes_read); return (ARCHIVE_OK); } else { while (ar->entry_padding > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > ar->entry_padding) bytes_read = (ssize_t)ar->entry_padding; - (a->decompressor->consume)(a, (size_t)bytes_read); + __archive_read_consume(a, (size_t)bytes_read); ar->entry_padding -= bytes_read; } *buff = NULL; @@ -491,20 +480,11 @@ archive_read_format_ar_skip(struct archive_read *a) { off_t bytes_skipped; struct ar* ar; - int r = ARCHIVE_OK; - const void *b; /* Dummy variables */ - size_t s; - off_t o; ar = (struct ar *)(a->format->data); - if (a->decompressor->skip == NULL) { - while (r == ARCHIVE_OK) - r = archive_read_format_ar_read_data(a, &b, &s, &o); - return (r); - } - bytes_skipped = (a->decompressor->skip)(a, ar->entry_bytes_remaining + - ar->entry_padding); + bytes_skipped = __archive_read_skip(a, + ar->entry_bytes_remaining + ar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); @@ -533,11 +513,10 @@ ar_parse_gnu_filename_table(struct archive_read *a) } } /* - * Sanity check, last two chars must be `/\n' or '\n\n', - * depending on whether the string table is padded by a '\n' - * (string table produced by GNU ar always has a even size). + * GNU ar always pads the table to an even size. + * The pad character is either '\n' or '`'. */ - if (p != ar->strtab + size && *p != '\n') + if (p != ar->strtab + size && *p != '\n' && *p != '`') goto bad_string_table; /* Enforce zero termination. */ diff --git a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c index 2c50abc6b8..56247dd1c6 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_cpio.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_cpio.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.26 2008/01/15 04:56:48 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.27 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -150,7 +150,9 @@ archive_read_support_format_cpio(struct archive *_a) r = __archive_read_register_format(a, cpio, + "cpio", archive_read_format_cpio_bid, + NULL, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, NULL, @@ -165,7 +167,6 @@ archive_read_support_format_cpio(struct archive *_a) static int archive_read_format_cpio_bid(struct archive_read *a) { - int bytes_read; const void *h; const unsigned char *p; struct cpio *cpio; @@ -173,11 +174,7 @@ archive_read_format_cpio_bid(struct archive_read *a) cpio = (struct cpio *)(a->format->data); - bytes_read = (a->decompressor->read_ahead)(a, &h, 6); - /* Convert error code into error return. */ - if (bytes_read < 0) - return ((int)bytes_read); - if (bytes_read < 6) + if ((h = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); p = (const unsigned char *)h; @@ -228,7 +225,6 @@ archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; - size_t bytes; const void *h; size_t namelength; size_t name_pad; @@ -241,21 +237,20 @@ archive_read_format_cpio_read_header(struct archive_read *a, return (r); /* Read name from buffer. */ - bytes = (a->decompressor->read_ahead)(a, &h, namelength + name_pad); - if (bytes < namelength + name_pad) + h = __archive_read_ahead(a, namelength + name_pad, NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, namelength + name_pad); + __archive_read_consume(a, namelength + name_pad); archive_strncpy(&cpio->entry_name, (const char *)h, namelength); archive_entry_set_pathname(entry, cpio->entry_name.s); cpio->entry_offset = 0; /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { - bytes = (a->decompressor->read_ahead)(a, &h, - cpio->entry_bytes_remaining); - if ((off_t)bytes < cpio->entry_bytes_remaining) + h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, cpio->entry_bytes_remaining); + __archive_read_consume(a, cpio->entry_bytes_remaining); archive_strncpy(&cpio->entry_linkname, (const char *)h, cpio->entry_bytes_remaining); archive_entry_set_symlink(entry, cpio->entry_linkname.s); @@ -284,7 +279,7 @@ archive_read_format_cpio_read_data(struct archive_read *a, cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_remaining > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) @@ -293,16 +288,16 @@ archive_read_format_cpio_read_data(struct archive_read *a, *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } else { while (cpio->entry_padding > 0) { - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_padding) bytes_read = cpio->entry_padding; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); cpio->entry_padding -= bytes_read; } *buff = NULL; @@ -336,11 +331,12 @@ find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, bytes, skipped = 0; + size_t skip, skipped = 0; + ssize_t bytes; for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 2048); - if (bytes < sizeof(struct cpio_newc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes); + if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; @@ -362,7 +358,7 @@ find_newc_header(struct archive_read *a) if (memcmp("07070", p, 5) == 0 && is_hex(p, sizeof(struct cpio_newc_header))) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, @@ -385,7 +381,7 @@ find_newc_header(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; } } @@ -396,7 +392,6 @@ header_newc(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_newc_header *header; - size_t bytes; int r; r = find_newc_header(a); @@ -404,10 +399,10 @@ header_newc(struct archive_read *a, struct cpio *cpio, return (r); /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header)); - if (bytes < sizeof(struct cpio_newc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_newc_header)); + __archive_read_consume(a, sizeof(struct cpio_newc_header)); /* Parse out hex fields. */ header = (const struct cpio_newc_header *)h; @@ -471,11 +466,12 @@ find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, bytes, skipped = 0; + size_t skip, skipped = 0; + ssize_t bytes; for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 512); - if (bytes < sizeof(struct cpio_odc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes); + if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; @@ -495,7 +491,7 @@ find_odc_header(struct archive_read *a) if (memcmp("070707", p, 6) == 0 && is_octal(p, sizeof(struct cpio_odc_header))) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, @@ -518,7 +514,7 @@ find_odc_header(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); skipped += skip; } } @@ -530,7 +526,6 @@ header_odc(struct archive_read *a, struct cpio *cpio, const void *h; int r; const struct cpio_odc_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; @@ -541,10 +536,10 @@ header_odc(struct archive_read *a, struct cpio *cpio, return (r); /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header)); - if (bytes < sizeof(struct cpio_odc_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_odc_header)); + __archive_read_consume(a, sizeof(struct cpio_odc_header)); /* Parse out octal fields. */ header = (const struct cpio_odc_header *)h; @@ -578,16 +573,15 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_bin_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_bin_header)); - if (bytes < sizeof(struct cpio_bin_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; @@ -615,17 +609,15 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, { const void *h; const struct cpio_bin_header *header; - size_t bytes; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ - bytes = (a->decompressor->read_ahead)(a, &h, - sizeof(struct cpio_bin_header)); - if (bytes < sizeof(struct cpio_bin_header)) + h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL); + if (h == NULL) return (ARCHIVE_FATAL); - (a->decompressor->consume)(a, sizeof(struct cpio_bin_header)); + __archive_read_consume(a, sizeof(struct cpio_bin_header)); /* Parse out binary fields. */ header = (const struct cpio_bin_header *)h; diff --git a/contrib/libarchive/libarchive/archive_read_support_format_empty.c b/contrib/libarchive/libarchive/archive_read_support_format_empty.c index 837fdef997..583e5a3f9a 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_empty.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_empty.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.3 2007/05/29 01:00:19 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.4 2008/12/06 06:45:15 kientzle Exp $"); #include "archive.h" #include "archive_entry.h" @@ -43,8 +43,10 @@ archive_read_support_format_empty(struct archive *_a) int r; r = __archive_read_register_format(a, + NULL, NULL, archive_read_format_empty_bid, + NULL, archive_read_format_empty_read_header, archive_read_format_empty_read_data, NULL, @@ -57,11 +59,11 @@ archive_read_support_format_empty(struct archive *_a) static int archive_read_format_empty_bid(struct archive_read *a) { - int bytes_read; const void *h; + ssize_t avail; - bytes_read = (a->decompressor->read_ahead)(a, &h, 1); - if (bytes_read > 0) + h = __archive_read_ahead(a, 1, &avail); + if (avail != 0) return (-1); return (1); } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c index 481220dd47..c45872a8bc 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2009 Andreas Henriksson * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.26 2008/05/26 17:00:22 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.30 2008/12/06 06:57:45 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -138,6 +139,37 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1 #define PVD_reserved4_size 1 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size) #define PVD_application_data_size 512 +#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size) +#define PVD_reserved5_size (2048 - PVD_reserved5_offset) + +/* TODO: It would make future maintenance easier to just hardcode the + * above values. In particular, ECMA119 states the offsets as part of + * the standard. That would eliminate the need for the following check.*/ +#if PVD_reserved5_offset != 1395 +#error PVD offset and size definitions are wrong. +#endif + + +/* Structure of optional on-disk supplementary volume descriptor. */ +#define SVD_type_offset 0 +#define SVD_type_size 1 +#define SVD_id_offset (SVD_type_offset + SVD_type_size) +#define SVD_id_size 5 +#define SVD_version_offset (SVD_id_offset + SVD_id_size) +#define SVD_version_size 1 +/* ... */ +#define SVD_volume_space_size_offset 80 +#define SVD_volume_space_size_size 8 +#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size) +#define SVD_escape_sequences_size 32 +/* ... */ +#define SVD_logical_block_size_offset 128 +#define SVD_logical_block_size_size 4 +/* ... */ +#define SVD_root_directory_record_offset 156 +#define SVD_root_directory_record_size 34 +/* ... */ +/* FIXME: validate correctness of last SVD entry offset. */ /* Structure of an on-disk directory record. */ /* Note: ISO9660 stores each multi-byte integer twice, once in @@ -166,10 +198,6 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1 #define DR_name_len_size 1 #define DR_name_offset 33 -/* - * Our private data. - */ - /* In-memory storage for a directory record. */ struct file_info { struct file_info *parent; @@ -178,26 +206,33 @@ struct file_info { uint64_t size; /* File size in bytes. */ uint64_t ce_offset; /* Offset of CE */ uint64_t ce_size; /* Size of CE */ + time_t birthtime; /* File created time. */ time_t mtime; /* File last modified time. */ time_t atime; /* File last accessed time. */ - time_t ctime; /* File creation time. */ + time_t ctime; /* File attribute change time. */ uint64_t rdev; /* Device number */ mode_t mode; uid_t uid; gid_t gid; ino_t inode; int nlinks; - char *name; /* Null-terminated filename. */ + struct archive_string name; /* Pathname */ + char name_continues; /* Non-zero if name continues */ struct archive_string symlink; + char symlink_continues; /* Non-zero if link continues */ }; struct iso9660 { int magic; #define ISO9660_MAGIC 0x96609660 + + int option_ignore_joliet; + struct archive_string pathname; char seenRockridge; /* Set true if RR extensions are used. */ unsigned char suspOffset; + char seenJoliet; uint64_t previous_offset; uint64_t previous_size; @@ -210,6 +245,7 @@ struct iso9660 { uint64_t current_position; ssize_t logical_block_size; + uint64_t volume_size; /* Total size of volume in bytes. */ off_t entry_sparse_offset; int64_t entry_bytes_remaining; @@ -217,6 +253,8 @@ struct iso9660 { static void add_entry(struct iso9660 *iso9660, struct file_info *file); static int archive_read_format_iso9660_bid(struct archive_read *); +static int archive_read_format_iso9660_options(struct archive_read *, + const char *, const char *); static int archive_read_format_iso9660_cleanup(struct archive_read *); static int archive_read_format_iso9660_read_data(struct archive_read *, const void **, size_t *, off_t *); @@ -224,10 +262,13 @@ static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); static const char *build_pathname(struct archive_string *, struct file_info *); +#if DEBUG static void dump_isodirrec(FILE *, const unsigned char *isodirrec); +#endif static time_t time_from_tm(struct tm *); static time_t isodate17(const unsigned char *); static time_t isodate7(const unsigned char *); +static int isJolietSVD(struct iso9660 *, const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); static struct file_info *next_entry(struct iso9660 *); static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, @@ -238,6 +279,12 @@ static struct file_info * static void parse_rockridge(struct iso9660 *iso9660, struct file_info *file, const unsigned char *start, const unsigned char *end); +static void parse_rockridge_NM1(struct file_info *, + const unsigned char *, int); +static void parse_rockridge_SL1(struct file_info *, + const unsigned char *, int); +static void parse_rockridge_TF1(struct file_info *, + const unsigned char *, int); static void release_file(struct iso9660 *, struct file_info *); static unsigned toi(const void *p, int n); @@ -258,7 +305,9 @@ archive_read_support_format_iso9660(struct archive *_a) r = __archive_read_register_format(a, iso9660, + "iso9660", archive_read_format_iso9660_bid, + archive_read_format_iso9660_options, archive_read_format_iso9660_read_header, archive_read_format_iso9660_read_data, archive_read_format_iso9660_read_data_skip, @@ -276,9 +325,9 @@ static int archive_read_format_iso9660_bid(struct archive_read *a) { struct iso9660 *iso9660; - ssize_t bytes_read; + ssize_t bytes_read, brsvd; const void *h; - const unsigned char *p; + const unsigned char *p, *psvd; int bid; iso9660 = (struct iso9660 *)(a->format->data); @@ -288,8 +337,8 @@ archive_read_format_iso9660_bid(struct archive_read *a) * 8 sectors of the volume descriptor table. Of course, * if the I/O layer gives us more, we'll take it. */ - bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048); - if (bytes_read < 32768 + 8*2048) + h = __archive_read_ahead(a, 32768 + 8*2048, &bytes_read); + if (h == NULL) return (-1); p = (const unsigned char *)h; @@ -297,6 +346,17 @@ archive_read_format_iso9660_bid(struct archive_read *a) bytes_read -= 32768; p += 32768; + /* Check each volume descriptor to locate possible SVD with Joliet. */ + for (brsvd = bytes_read, psvd = p; + !iso9660->option_ignore_joliet && brsvd > 2048; + brsvd -= 2048, psvd += 2048) { + bid = isJolietSVD(iso9660, psvd); + if (bid > 0) + return (bid); + if (*p == '\177') /* End-of-volume-descriptor marker. */ + break; + } + /* Check each volume descriptor to locate the PVD. */ for (; bytes_read > 2048; bytes_read -= 2048, p += 2048) { bid = isPVD(iso9660, p); @@ -310,17 +370,147 @@ archive_read_format_iso9660_bid(struct archive_read *a) return (0); } +static int +archive_read_format_iso9660_options(struct archive_read *a, + const char *key, const char *val) +{ + struct iso9660 *iso9660; + + iso9660 = (struct iso9660 *)(a->format->data); + + if (strcmp(key, "joliet") == 0) { + if (val == NULL || strcmp(val, "off") == 0 || + strcmp(val, "ignore") == 0 || + strcmp(val, "disable") == 0 || + strcmp(val, "0") == 0) + iso9660->option_ignore_joliet = 1; + else + iso9660->option_ignore_joliet = 0; + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if noone used this option. */ + return (ARCHIVE_WARN); +} + +static int +isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) +{ + struct file_info *file; + const unsigned char *p; + + /* Type 2 means it's a SVD. */ + if (h[SVD_type_offset] != 2) + return (0); + + /* ID must be "CD001" */ + if (memcmp(h + SVD_id_offset, "CD001", 5) != 0) + return (0); + + /* FIXME: do more validations according to joliet spec. */ + + /* check if this SVD contains joliet extension! */ + p = h + SVD_escape_sequences_offset; + /* N.B. Joliet spec says p[1] == '\\', but.... */ + if (p[0] == '%' && p[1] == '/') { + int level = 0; + + if (p[2] == '@') + level = 1; + else if (p[2] == 'C') + level = 2; + else if (p[2] == 'E') + level = 3; + else /* not joliet */ + return (0); + + iso9660->seenJoliet = level; + + } else /* not joliet */ + return (0); + + iso9660->logical_block_size = toi(h + SVD_logical_block_size_offset, 2); + if (iso9660->logical_block_size <= 0) + return (0); + + iso9660->volume_size = iso9660->logical_block_size + * (uint64_t)toi(h + SVD_volume_space_size_offset, 4); + +#if DEBUG + fprintf(stderr, "Joliet UCS-2 level %d with " + "logical block size:%d, volume size:%d\n", + iso9660->seenJoliet, + iso9660->logical_block_size, iso9660->volume_size); +#endif + + /* Store the root directory in the pending list. */ + file = parse_file_info(iso9660, NULL, h + SVD_root_directory_record_offset); + add_entry(iso9660, file); + + return (48); +} + static int isPVD(struct iso9660 *iso9660, const unsigned char *h) { struct file_info *file; + int i; + + /* Type of the Primary Volume Descriptor must be 1. */ + if (h[PVD_type_offset] != 1) + return (0); - if (h[0] != 1) + /* ID must be "CD001" */ + if (memcmp(h + PVD_id_offset, "CD001", 5) != 0) return (0); - if (memcmp(h+1, "CD001", 5) != 0) + + /* PVD version must be 1. */ + if (h[PVD_version_offset] != 1) + return (0); + + /* Reserved field must be 0. */ + if (h[PVD_reserved1_offset] != 0) return (0); + /* Reserved field must be 0. */ + for (i = 0; i < PVD_reserved2_size; ++i) + if (h[PVD_reserved2_offset + i] != 0) + return (0); + + /* Reserved field must be 0. */ + for (i = 0; i < PVD_reserved3_size; ++i) + if (h[PVD_reserved3_offset + i] != 0) + return (0); + + /* Logical block size must be > 0. */ + /* I've looked at Ecma 119 and can't find any stronger + * restriction on this field. */ iso9660->logical_block_size = toi(h + PVD_logical_block_size_offset, 2); + if (iso9660->logical_block_size <= 0) + return (0); + + iso9660->volume_size = iso9660->logical_block_size + * (uint64_t)toi(h + PVD_volume_space_size_offset, 4); + + /* File structure version must be 1 for ISO9660/ECMA119. */ + if (h[PVD_file_structure_version_offset] != 1) + return (0); + + + /* Reserved field must be 0. */ + for (i = 0; i < PVD_reserved4_size; ++i) + if (h[PVD_reserved4_offset + i] != 0) + return (0); + + /* Reserved field must be 0. */ + for (i = 0; i < PVD_reserved5_size; ++i) + if (h[PVD_reserved5_offset + i] != 0) + return (0); + + /* XXX TODO: Check other values for sanity; reject more + * malformed PVDs. XXX */ /* Store the root directory in the pending list. */ file = parse_file_info(iso9660, NULL, h + PVD_root_directory_record_offset); @@ -334,7 +524,6 @@ archive_read_format_iso9660_read_header(struct archive_read *a, { struct iso9660 *iso9660; struct file_info *file; - ssize_t bytes_read; int r; iso9660 = (struct iso9660 *)(a->format->data); @@ -346,18 +535,30 @@ archive_read_format_iso9660_read_header(struct archive_read *a, /* Get the next entry that appears after the current offset. */ r = next_entry_seek(a, iso9660, &file); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + release_file(iso9660, file); return (r); + } iso9660->entry_bytes_remaining = file->size; iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */ + if (file->offset + file->size > iso9660->volume_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "File is beyond end-of-media: %s", file->name); + iso9660->entry_bytes_remaining = 0; + iso9660->entry_sparse_offset = 0; + release_file(iso9660, file); + return (ARCHIVE_WARN); + } + /* Set up the entry structure with information about this entry. */ archive_entry_set_mode(entry, file->mode); archive_entry_set_uid(entry, file->uid); archive_entry_set_gid(entry, file->gid); archive_entry_set_nlink(entry, file->nlinks); archive_entry_set_ino(entry, file->inode); + archive_entry_set_birthtime(entry, file->birthtime, 0); archive_entry_set_mtime(entry, file->mtime, 0); archive_entry_set_ctime(entry, file->ctime, 0); archive_entry_set_atime(entry, file->atime, 0); @@ -388,7 +589,10 @@ archive_read_format_iso9660_read_header(struct archive_read *a, * seek backwards to extract it, so issue a warning. */ if (file->offset < iso9660->current_position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignoring out-of-order file"); + "Ignoring out-of-order file @%x (%s) %jd < %jd", + file, + iso9660->pathname.s, + file->offset, iso9660->current_position); iso9660->entry_bytes_remaining = 0; iso9660->entry_sparse_offset = 0; release_file(iso9660, file); @@ -407,23 +611,26 @@ archive_read_format_iso9660_read_header(struct archive_read *a, ssize_t step = iso9660->logical_block_size; if (step > iso9660->entry_bytes_remaining) step = iso9660->entry_bytes_remaining; - bytes_read = (a->decompressor->read_ahead)(a, &block, step); - if (bytes_read < step) { + block = __archive_read_ahead(a, step, NULL); + if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning ISO9660 directory list"); release_file(iso9660, file); return (ARCHIVE_FATAL); } - if (bytes_read > step) - bytes_read = step; - (a->decompressor->consume)(a, bytes_read); - iso9660->current_position += bytes_read; - iso9660->entry_bytes_remaining -= bytes_read; + __archive_read_consume(a, step); + iso9660->current_position += step; + iso9660->entry_bytes_remaining -= step; for (p = (const unsigned char *)block; - *p != 0 && p < (const unsigned char *)block + bytes_read; + *p != 0 && p < (const unsigned char *)block + step; p += *p) { struct file_info *child; + /* N.B.: these special directory identifiers + * are 8 bit "values" even on a + * Joliet CD with UCS-2 (16bit) encoding. + */ + /* Skip '.' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\0') @@ -472,11 +679,11 @@ archive_read_format_iso9660_read_data(struct archive_read *a, return (ARCHIVE_EOF); } - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); - if (bytes_read <= 0) + if (buff == NULL) return (ARCHIVE_FATAL); if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = iso9660->entry_bytes_remaining; @@ -485,7 +692,7 @@ archive_read_format_iso9660_read_data(struct archive_read *a, iso9660->entry_sparse_offset += bytes_read; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } @@ -517,6 +724,8 @@ parse_file_info(struct iso9660 *iso9660, struct file_info *parent, { struct file_info *file; size_t name_len; + const unsigned char *rr_start, *rr_end; + const unsigned char *p; int flags; /* TODO: Sanity check that name_len doesn't exceed length, etc. */ @@ -529,38 +738,83 @@ parse_file_info(struct iso9660 *iso9660, struct file_info *parent, file->parent = parent; if (parent != NULL) parent->refcount++; - file->offset = toi(isodirrec + DR_extent_offset, DR_extent_size) + file->offset = (uint64_t)toi(isodirrec + DR_extent_offset, DR_extent_size) * iso9660->logical_block_size; file->size = toi(isodirrec + DR_size_offset, DR_size_size); file->mtime = isodate7(isodirrec + DR_date_offset); file->ctime = file->atime = file->mtime; - name_len = (size_t)*(const unsigned char *)(isodirrec + DR_name_len_offset); - file->name = (char *)malloc(name_len + 1); - if (file->name == NULL) { - free(file); - return (NULL); + + name_len = (size_t)isodirrec[DR_name_len_offset]; + p = isodirrec + DR_name_offset; + /* Rockridge extensions (if any) follow name. Compute this + * before fidgeting the name_len below. */ + rr_start = p + name_len + (name_len & 1 ? 0 : 1) + iso9660->suspOffset; + rr_end = isodirrec + isodirrec[DR_length_offset]; + + if (iso9660->seenJoliet) { + /* Joliet names are max 64 chars (128 bytes) according to spec, + * but genisoimage (and others?) will allow you to have more. + */ + wchar_t wbuff[64+1], *wp; + const unsigned char *c; + + /* TODO: warn when name_len > 128 ? */ + + /* convert BE UTF-16 to wchar_t */ + for (c = p, wp = wbuff; + c < (p + name_len) && + wp < (wbuff + sizeof(wbuff)/sizeof(*wbuff) - 1); + c += 2) { + *wp++ = (((255 & (int)c[0]) << 8) | (255 & (int)c[1])); + } + *wp = L'\0'; + +#if 0 /* untested code, is it at all useful on Joliet? */ + /* trim trailing first version and dot from filename. + * + * Remember we where in UTF-16BE land! + * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both + * 16 bits big endian characters on Joliet. + * + * TODO: sanitize filename? + * Joliet allows any UCS-2 char except: + * *, /, :, ;, ? and \. + */ + /* Chop off trailing ';1' from files. */ + if (*(wp-2) == ';' && *(wp-1) == '1') { + wp-=2; + *wp = L'\0'; + } + + /* Chop off trailing '.' from filenames. */ + if (*(wp-1) == '.') + *(--wp) = L'\0'; +#endif + + /* store the result in the file name field. */ + archive_strappend_w_utf8(&file->name, wbuff); + } else { + /* Chop off trailing ';1' from files. */ + if (name_len > 2 && p[name_len - 2] == ';' && + p[name_len - 1] == '1') + name_len -= 2; + /* Chop off trailing '.' from filenames. */ + if (name_len > 1 && p[name_len - 1] == '.') + --name_len; + + archive_strncpy(&file->name, (const char *)p, name_len); } - memcpy(file->name, isodirrec + DR_name_offset, name_len); - file->name[name_len] = '\0'; - flags = *(isodirrec + DR_flags_offset); + + flags = isodirrec[DR_flags_offset]; if (flags & 0x02) file->mode = AE_IFDIR | 0700; else file->mode = AE_IFREG | 0400; /* Rockridge extensions overwrite information from above. */ - { - const unsigned char *rr_start, *rr_end; - rr_end = (const unsigned char *)isodirrec - + *(isodirrec + DR_length_offset); - rr_start = (const unsigned char *)(isodirrec + DR_name_offset - + name_len); - if ((name_len & 1) == 0) - rr_start++; - rr_start += iso9660->suspOffset; - parse_rockridge(iso9660, file, rr_start, rr_end); - } + parse_rockridge(iso9660, file, rr_start, rr_end); +#if DEBUG /* DEBUGGING: Warn about attributes I don't yet fully support. */ if ((flags & ~0x02) != 0) { fprintf(stderr, "\n ** Unrecognized flag: "); @@ -583,7 +837,7 @@ parse_file_info(struct iso9660 *iso9660, struct file_info *parent, dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } - +#endif return (file); } @@ -619,10 +873,13 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, const unsigned char *p, const unsigned char *end) { (void)iso9660; /* UNUSED */ + file->name_continues = 0; + file->symlink_continues = 0; while (p + 4 < end /* Enough space for another entry. */ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ + && p[2] >= 4 /* Sanity-check length. */ && p + p[2] <= end) { /* Sanity-check length. */ const unsigned char *data = p + 4; int data_length = p[2] - 4; @@ -635,61 +892,54 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, */ switch(p[0]) { case 'C': - if (p[0] == 'C' && p[1] == 'E' && version == 1) { - /* - * CE extension comprises: - * 8 byte sector containing extension - * 8 byte offset w/in above sector - * 8 byte length of continuation - */ - file->ce_offset = toi(data, 4) - * iso9660->logical_block_size - + toi(data + 8, 4); - file->ce_size = toi(data + 16, 4); + if (p[0] == 'C' && p[1] == 'E') { + if (version == 1 && data_length == 24) { + /* + * CE extension comprises: + * 8 byte sector containing extension + * 8 byte offset w/in above sector + * 8 byte length of continuation + */ + file->ce_offset = (uint64_t)toi(data, 4) + * iso9660->logical_block_size + + toi(data + 8, 4); + file->ce_size = toi(data + 16, 4); + /* If the result is rediculous, + * ignore it. */ + if (file->ce_offset + file->ce_size + > iso9660->volume_size) { + file->ce_offset = 0; + file->ce_size = 0; + } + } break; } /* FALLTHROUGH */ case 'N': - if (p[0] == 'N' && p[1] == 'M' && version == 1 - && *data == 0) { - /* NM extension with flag byte == 0 */ - /* - * NM extension comprises: - * one byte flag - * rest is long name - */ - /* TODO: Obey flags. */ - char *old_name = file->name; - - data++; /* Skip flag byte. */ - data_length--; - file->name = (char *)malloc(data_length + 1); - if (file->name != NULL) { - free(old_name); - memcpy(file->name, data, data_length); - file->name[data_length] = '\0'; - } else - file->name = old_name; + if (p[0] == 'N' && p[1] == 'M') { + if (version == 1) + parse_rockridge_NM1(file, + data, data_length); break; } /* FALLTHROUGH */ case 'P': - if (p[0] == 'P' && p[1] == 'D' && version == 1) { + if (p[0] == 'P' && p[1] == 'D') { /* * PD extension is padding; * contents are always ignored. */ break; } - if (p[0] == 'P' && p[1] == 'N' && version == 1) { - if (data_length == 16) { + if (p[0] == 'P' && p[1] == 'N') { + if (version == 1 && data_length == 16) { file->rdev = toi(data,4); file->rdev <<= 32; file->rdev |= toi(data + 8, 4); } break; } - if (p[0] == 'P' && p[1] == 'X' && version == 1) { + if (p[0] == 'P' && p[1] == 'X') { /* * PX extension comprises: * 8 bytes for mode, @@ -698,12 +948,22 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, * 8 bytes for gid, * 8 bytes for inode. */ - if (data_length == 32) { - file->mode = toi(data, 4); - file->nlinks = toi(data + 8, 4); - file->uid = toi(data + 16, 4); - file->gid = toi(data + 24, 4); - file->inode = toi(data + 32, 4); + if (version == 1) { + if (data_length >= 8) + file->mode + = toi(data, 4); + if (data_length >= 16) + file->nlinks + = toi(data + 8, 4); + if (data_length >= 24) + file->uid + = toi(data + 16, 4); + if (data_length >= 32) + file->gid + = toi(data + 24, 4); + if (data_length >= 40) + file->inode + = toi(data + 32, 4); } break; } @@ -720,56 +980,14 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, } /* FALLTHROUGH */ case 'S': - if (p[0] == 'S' && p[1] == 'L' && version == 1 - && *data == 0) { - int cont = 1; - /* SL extension with flags == 0 */ - /* TODO: handle non-zero flag values. */ - data++; /* Skip flag byte. */ - data_length--; - while (data_length > 0) { - unsigned char flag = *data++; - unsigned char nlen = *data++; - data_length -= 2; - - if (cont == 0) - archive_strcat(&file->symlink, "/"); - cont = 0; - - switch(flag) { - case 0x01: /* Continue */ - archive_strncat(&file->symlink, - (const char *)data, nlen); - cont = 1; - break; - case 0x02: /* Current */ - archive_strcat(&file->symlink, "."); - break; - case 0x04: /* Parent */ - archive_strcat(&file->symlink, ".."); - break; - case 0x08: /* Root */ - case 0x10: /* Volume root */ - archive_string_empty(&file->symlink); - break; - case 0x20: /* Hostname */ - archive_strcat(&file->symlink, "hostname"); - break; - case 0: - archive_strncat(&file->symlink, - (const char *)data, nlen); - break; - default: - /* TODO: issue a warning ? */ - break; - } - data += nlen; - data_length -= nlen; - } + if (p[0] == 'S' && p[1] == 'L') { + if (version == 1) + parse_rockridge_SL1(file, + data, data_length); break; } if (p[0] == 'S' && p[1] == 'P' - && version == 1 && data_length == 7 + && version == 1 && data_length == 3 && data[0] == (unsigned char)'\xbe' && data[1] == (unsigned char)'\xef') { /* @@ -805,66 +1023,27 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, return; } case 'T': - if (p[0] == 'T' && p[1] == 'F' && version == 1) { - char flag = data[0]; - /* - * TF extension comprises: - * one byte flag - * create time (optional) - * modify time (optional) - * access time (optional) - * attribute time (optional) - * Time format and presence of fields - * is controlled by flag bits. - */ - data++; - if (flag & 0x80) { - /* Use 17-byte time format. */ - if (flag & 1) /* Create time. */ - data += 17; - if (flag & 2) { /* Modify time. */ - file->mtime = isodate17(data); - data += 17; - } - if (flag & 4) { /* Access time. */ - file->atime = isodate17(data); - data += 17; - } - if (flag & 8) { /* Attribute time. */ - file->ctime = isodate17(data); - data += 17; - } - } else { - /* Use 7-byte time format. */ - if (flag & 1) /* Create time. */ - data += 7; - if (flag & 2) { /* Modify time. */ - file->mtime = isodate7(data); - data += 7; - } - if (flag & 4) { /* Access time. */ - file->atime = isodate7(data); - data += 7; - } - if (flag & 8) { /* Attribute time. */ - file->ctime = isodate7(data); - data += 7; - } - } + if (p[0] == 'T' && p[1] == 'F') { + if (version == 1) + parse_rockridge_TF1(file, + data, data_length); break; } /* FALLTHROUGH */ default: /* The FALLTHROUGHs above leave us here for * any unsupported extension. */ +#if DEBUG { const unsigned char *t; - fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name); + fprintf(stderr, "\nUnsupported RRIP extension for %s\n", file->name.s); fprintf(stderr, " %c%c(%d):", p[0], p[1], data_length); for (t = data; t < data + data_length && t < data + 16; t++) fprintf(stderr, " %02x", *t); fprintf(stderr, "\n"); } +#endif + break; } @@ -873,15 +1052,226 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file, } } +static void +parse_rockridge_NM1(struct file_info *file, + const unsigned char *data, int data_length) +{ + if (!file->name_continues) + archive_string_empty(&file->name); + file->name_continues = 0; + if (data_length < 1) + return; + /* + * NM version 1 extension comprises: + * 1 byte flag, value is one of: + * = 0: remainder is name + * = 1: remainder is name, next NM entry continues name + * = 2: "." + * = 4: ".." + * = 32: Implementation specific + * All other values are reserved. + */ + switch(data[0]) { + case 0: + if (data_length < 2) + return; + archive_strncat(&file->name, (const char *)data + 1, data_length - 1); + break; + case 1: + if (data_length < 2) + return; + archive_strncat(&file->name, (const char *)data + 1, data_length - 1); + file->name_continues = 1; + break; + case 2: + archive_strcat(&file->name, "."); + break; + case 4: + archive_strcat(&file->name, ".."); + break; + default: + return; + } + +} + +static void +parse_rockridge_TF1(struct file_info *file, const unsigned char *data, + int data_length) +{ + char flag; + /* + * TF extension comprises: + * one byte flag + * create time (optional) + * modify time (optional) + * access time (optional) + * attribute time (optional) + * Time format and presence of fields + * is controlled by flag bits. + */ + if (data_length < 1) + return; + flag = data[0]; + ++data; + --data_length; + if (flag & 0x80) { + /* Use 17-byte time format. */ + if ((flag & 1) && data_length >= 17) { + /* Create time. */ + file->birthtime = isodate17(data); + data += 17; + data_length -= 17; + } + if ((flag & 2) && data_length >= 17) { + /* Modify time. */ + file->mtime = isodate17(data); + data += 17; + data_length -= 17; + } + if ((flag & 4) && data_length >= 17) { + /* Access time. */ + file->atime = isodate17(data); + data += 17; + data_length -= 17; + } + if ((flag & 8) && data_length >= 17) { + /* Attribute change time. */ + file->ctime = isodate17(data); + data += 17; + data_length -= 17; + } + } else { + /* Use 7-byte time format. */ + if ((flag & 1) && data_length >= 7) { + /* Create time. */ + file->birthtime = isodate17(data); + data += 7; + data_length -= 7; + } + if ((flag & 2) && data_length >= 7) { + /* Modify time. */ + file->mtime = isodate7(data); + data += 7; + data_length -= 7; + } + if ((flag & 4) && data_length >= 7) { + /* Access time. */ + file->atime = isodate7(data); + data += 7; + data_length -= 7; + } + if ((flag & 8) && data_length >= 7) { + /* Attribute change time. */ + file->ctime = isodate7(data); + data += 7; + data_length -= 7; + } + } +} + +static void +parse_rockridge_SL1(struct file_info *file, const unsigned char *data, + int data_length) +{ + int component_continues = 1; + + if (!file->symlink_continues) + archive_string_empty(&file->symlink); + else + archive_strcat(&file->symlink, "/"); + file->symlink_continues = 0; + + /* + * Defined flag values: + * 0: This is the last SL record for this symbolic link + * 1: this symbolic link field continues in next SL entry + * All other values are reserved. + */ + if (data_length < 1) + return; + switch(*data) { + case 0: + break; + case 1: + file->symlink_continues = 1; + break; + default: + return; + } + ++data; /* Skip flag byte. */ + --data_length; + + /* + * SL extension body stores "components". + * Basically, this is a complicated way of storing + * a POSIX path. It also interferes with using + * symlinks for storing non-path data. + * + * Each component is 2 bytes (flag and length) + * possibly followed by name data. + */ + while (data_length >= 2) { + unsigned char flag = *data++; + unsigned char nlen = *data++; + data_length -= 2; + + if (!component_continues) + archive_strcat(&file->symlink, "/"); + component_continues = 0; + + switch(flag) { + case 0: /* Usual case, this is text. */ + if (data_length < nlen) + return; + archive_strncat(&file->symlink, + (const char *)data, nlen); + break; + case 0x01: /* Text continues in next component. */ + if (data_length < nlen) + return; + archive_strncat(&file->symlink, + (const char *)data, nlen); + component_continues = 1; + break; + case 0x02: /* Current dir. */ + archive_strcat(&file->symlink, "."); + break; + case 0x04: /* Parent dir. */ + archive_strcat(&file->symlink, ".."); + break; + case 0x08: /* Root of filesystem. */ + archive_string_empty(&file->symlink); + archive_strcat(&file->symlink, "/"); + break; + case 0x10: /* Undefined (historically "volume root" */ + archive_string_empty(&file->symlink); + archive_strcat(&file->symlink, "ROOT"); + break; + case 0x20: /* Undefined (historically "hostname") */ + archive_strcat(&file->symlink, "hostname"); + break; + default: + /* TODO: issue a warning ? */ + return; + } + data += nlen; + data_length -= nlen; + } +} + + static void release_file(struct iso9660 *iso9660, struct file_info *file) { struct file_info *parent; + if (file == NULL) + return; + if (file->refcount == 0) { parent = file->parent; - if (file->name) - free(file->name); + archive_string_free(&file->name); archive_string_free(&file->symlink); free(file); if (parent != NULL) { @@ -906,7 +1296,7 @@ next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, /* CE area precedes actual file data? Ignore it. */ if (file->ce_offset > file->offset) { -fprintf(stderr, " *** Discarding CE data.\n"); + /* fprintf(stderr, " *** Discarding CE data.\n"); */ file->ce_offset = 0; file->ce_size = 0; } @@ -926,7 +1316,7 @@ fprintf(stderr, " *** Discarding CE data.\n"); if (iso9660->current_position < offset) { off_t step = offset - iso9660->current_position; off_t bytes_read; - bytes_read = (a->decompressor->skip)(a, step); + bytes_read = __archive_read_skip(a, step); if (bytes_read < 0) return (bytes_read); iso9660->current_position = offset; @@ -940,19 +1330,18 @@ fprintf(stderr, " *** Discarding CE data.\n"); if (offset == file->ce_offset) { const void *p; ssize_t size = file->ce_size; - ssize_t bytes_read; const unsigned char *rr_start; file->ce_offset = 0; file->ce_size = 0; - bytes_read = (a->decompressor->read_ahead)(a, &p, size); - if (bytes_read > size) - bytes_read = size; + p = __archive_read_ahead(a, size, NULL); + if (p == NULL) + return (ARCHIVE_FATAL); rr_start = (const unsigned char *)p; parse_rockridge(iso9660, file, rr_start, - rr_start + bytes_read); - (a->decompressor->consume)(a, bytes_read); - iso9660->current_position += bytes_read; + rr_start + size); + __archive_read_consume(a, size); + iso9660->current_position += size; add_entry(iso9660, file); } } @@ -1071,17 +1460,18 @@ time_from_tm(struct tm *t) static const char * build_pathname(struct archive_string *as, struct file_info *file) { - if (file->parent != NULL && file->parent->name[0] != '\0') { + if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { build_pathname(as, file->parent); archive_strcat(as, "/"); } - if (file->name[0] == '\0') + if (archive_strlen(&file->name) == 0) archive_strcat(as, "."); else - archive_strcat(as, file->name); + archive_string_concat(as, &file->name); return (as->s); } +#if DEBUG static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { @@ -1106,3 +1496,4 @@ dump_isodirrec(FILE *out, const unsigned char *isodirrec) fprintf(out, " `%.*s'", toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); } +#endif diff --git a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c index 33597e0de6..0f0e116e0e 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c @@ -25,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_mtree.c,v 1.9 2008/06/21 19:06:37 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_mtree.c,v 1.11 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -148,8 +148,8 @@ archive_read_support_format_mtree(struct archive *_a) memset(mtree, 0, sizeof(*mtree)); mtree->fd = -1; - r = __archive_read_register_format(a, mtree, - mtree_bid, read_header, read_data, skip, cleanup); + r = __archive_read_register_format(a, mtree, "mtree", + mtree_bid, NULL, read_header, read_data, skip, cleanup); if (r != ARCHIVE_OK) free(mtree); @@ -187,32 +187,17 @@ cleanup(struct archive_read *a) static int mtree_bid(struct archive_read *a) { - struct mtree *mtree; - ssize_t bytes_read; - const void *h; const char *signature = "#mtree"; const char *p; - int bid; - - mtree = (struct mtree *)(a->format->data); /* Now let's look at the actual header and see if it matches. */ - bytes_read = (a->decompressor->read_ahead)(a, &h, strlen(signature)); - - if (bytes_read <= 0) - return (bytes_read); - - p = h; - bid = 0; - while (bytes_read > 0 && *signature != '\0') { - if (*p != *signature) - return (bid = 0); - bid += 8; - p++; - signature++; - bytes_read--; - } - return (bid); + p = __archive_read_ahead(a, strlen(signature), NULL); + if (p == NULL) + return (-1); + + if (strncmp(p, signature, strlen(signature)) == 0) + return (8 * strlen(signature)); + return (0); } /* @@ -408,7 +393,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) struct mtree_entry *last_entry; int r; - mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1; + mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; @@ -419,10 +404,13 @@ read_mtree(struct archive_read *a, struct mtree *mtree) len = readline(a, mtree, &p, 256); if (len == 0) { mtree->this_entry = mtree->entries; + free_options(global); return (ARCHIVE_OK); } - if (len < 0) + if (len < 0) { + free_options(global); return (len); + } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { ++p; @@ -447,13 +435,16 @@ read_mtree(struct archive_read *a, struct mtree *mtree) } else break; - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + free_options(global); return r; + } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't parse line %ju", counter); - return ARCHIVE_FATAL; + free_options(global); + return (ARCHIVE_FATAL); } /* @@ -687,6 +678,15 @@ parse_file(struct archive_read *a, struct archive_entry *entry, #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); +#elif HAVE_STRUCT_STAT_ST_MTIME_N + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtime_n); +#elif HAVE_STRUCT_STAT_ST_UMTIME + archive_entry_set_mtime(entry, st->st_mtime, + st->st_umtime*1000); +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif @@ -897,8 +897,17 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, break; } if (strcmp(key, "time") == 0) { + time_t m; + long ns; + *parsed_kws |= MTREE_HAS_MTIME; - archive_entry_set_mtime(entry, mtree_atol10(&val), 0); + m = (time_t)mtree_atol10(&val); + if (*val == '.') { + ++val; + ns = (long)mtree_atol10(&val); + } else + ns = 0; + archive_entry_set_mtime(entry, m, ns); break; } if (strcmp(key, "type") == 0) { @@ -1231,6 +1240,7 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi { ssize_t bytes_read; ssize_t total_size = 0; + ssize_t find_off = 0; const void *t; const char *s; void *p; @@ -1239,8 +1249,8 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); - if (bytes_read == 0) + t = __archive_read_ahead(a, 1, &bytes_read); + if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); @@ -1263,14 +1273,12 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi return (ARCHIVE_FATAL); } memcpy(mtree->line.s + total_size, t, bytes_read); - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); total_size += bytes_read; /* Null terminate. */ mtree->line.s[total_size] = '\0'; /* If we found an unescaped '\n', clean up and return. */ - if (p == NULL) - continue; - for (u = mtree->line.s; *u; ++u) { + for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { *start = mtree->line.s; return total_size; @@ -1291,8 +1299,12 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi memmove(u, u + 1, total_size - (u - mtree->line.s) + 1); --total_size; - continue; + ++u; + break; } + if (u[1] == '\0') + break; } + find_off = u - mtree->line.s; } } diff --git a/contrib/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libarchive/libarchive/archive_read_support_format_tar.c index 0c2f0077c1..60ad8147ee 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_tar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_tar.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.69 2008/05/27 04:46:12 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.72 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -253,8 +253,9 @@ archive_read_support_format_tar(struct archive *_a) } memset(tar, 0, sizeof(*tar)); - r = __archive_read_register_format(a, tar, + r = __archive_read_register_format(a, tar, "tar", archive_read_format_tar_bid, + NULL, archive_read_format_tar_read_header, archive_read_format_tar_read_data, archive_read_format_tar_skip, @@ -294,21 +295,15 @@ static int archive_read_format_tar_bid(struct archive_read *a) { int bid; - ssize_t bytes_read; const void *h; const struct archive_entry_header_ustar *header; bid = 0; /* Now let's look at the actual header and see if it matches. */ - if (a->decompressor->read_ahead != NULL) - bytes_read = (a->decompressor->read_ahead)(a, &h, 512); - else - bytes_read = 0; /* Empty file. */ - if (bytes_read < 0) - return (ARCHIVE_FATAL); - if (bytes_read < 512) - return (0); + h = __archive_read_ahead(a, 512, NULL); + if (h == NULL) + return (-1); /* If it's an end-of-archive mark, we can handle it. */ if ((*(const char *)h) == 0 @@ -479,7 +474,7 @@ archive_read_format_tar_read_data(struct archive_read *a, /* If we're at end of file, return EOF. */ if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) { - if ((a->decompressor->skip)(a, tar->entry_padding) < 0) + if (__archive_read_skip(a, tar->entry_padding) < 0) return (ARCHIVE_FATAL); tar->entry_padding = 0; *buff = NULL; @@ -488,14 +483,14 @@ archive_read_format_tar_read_data(struct archive_read *a, return (ARCHIVE_EOF); } - bytes_read = (a->decompressor->read_ahead)(a, buff, 1); - if (bytes_read == 0) { + *buff = __archive_read_ahead(a, 1, &bytes_read); + if (bytes_read < 0) + return (ARCHIVE_FATAL); + if (*buff == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } - if (bytes_read < 0) - return (ARCHIVE_FATAL); if (bytes_read > tar->entry_bytes_remaining) bytes_read = tar->entry_bytes_remaining; /* Don't read more than is available in the @@ -507,7 +502,7 @@ archive_read_format_tar_read_data(struct archive_read *a, tar->sparse_list->remaining -= bytes_read; tar->sparse_list->offset += bytes_read; tar->entry_bytes_remaining -= bytes_read; - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); return (ARCHIVE_OK); } @@ -524,8 +519,8 @@ archive_read_format_tar_skip(struct archive_read *a) * length requested or fail, so we can rely upon the entire entry * plus padding being skipped. */ - bytes_skipped = (a->decompressor->skip)(a, tar->entry_bytes_remaining + - tar->entry_padding); + bytes_skipped = __archive_read_skip(a, + tar->entry_bytes_remaining + tar->entry_padding); if (bytes_skipped < 0) return (ARCHIVE_FATAL); @@ -552,31 +547,35 @@ tar_read_header(struct archive_read *a, struct tar *tar, const struct archive_entry_header_ustar *header; /* Read 512-byte header record */ - bytes = (a->decompressor->read_ahead)(a, &h, 512); + h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) return (bytes); - if (bytes == 0) { - /* - * An archive that just ends without a proper - * end-of-archive marker. Yes, there are tar programs - * that do this; hold our nose and accept it. - */ - return (ARCHIVE_EOF); - } - if (bytes < 512) { + if (bytes < 512) { /* Short read or EOF. */ + /* Try requesting just one byte and see what happens. */ + h = __archive_read_ahead(a, 1, &bytes); + if (bytes == 0) { + /* + * The archive ends at a 512-byte boundary but + * without a proper end-of-archive marker. + * Yes, there are tar writers that do this; + * hold our nose and accept it. + */ + return (ARCHIVE_EOF); + } + /* Archive ends with a partial block; this is bad. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, 512); + __archive_read_consume(a, 512); /* Check for end-of-archive mark. */ if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) { /* Try to consume a second all-null record, as well. */ - bytes = (a->decompressor->read_ahead)(a, &h, 512); - if (bytes > 0) - (a->decompressor->consume)(a, bytes); + h = __archive_read_ahead(a, 512, NULL); + if (h != NULL) + __archive_read_consume(a, 512); archive_set_error(&a->archive, 0, NULL); if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; @@ -837,10 +836,8 @@ read_body_to_string(struct archive_read *a, struct tar *tar, struct archive_string *as, const void *h) { off_t size, padded_size; - ssize_t bytes_read, bytes_to_copy; const struct archive_entry_header_ustar *header; const void *src; - char *dest; (void)tar; /* UNUSED */ header = (const struct archive_entry_header_ustar *)h; @@ -858,27 +855,14 @@ read_body_to_string(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); } - /* Read the body into the string. */ + /* Read the body into the string. */ padded_size = (size + 511) & ~ 511; - dest = as->s; - while (padded_size > 0) { - bytes_read = (a->decompressor->read_ahead)(a, &src, padded_size); - if (bytes_read == 0) - return (ARCHIVE_EOF); - if (bytes_read < 0) - return (ARCHIVE_FATAL); - if (bytes_read > padded_size) - bytes_read = padded_size; - (a->decompressor->consume)(a, bytes_read); - bytes_to_copy = bytes_read; - if ((off_t)bytes_to_copy > size) - bytes_to_copy = (ssize_t)size; - memcpy(dest, src, bytes_to_copy); - dest += bytes_to_copy; - size -= bytes_to_copy; - padded_size -= bytes_read; - } - *dest = '\0'; + src = __archive_read_ahead(a, padded_size, NULL); + if (src == NULL) + return (ARCHIVE_FATAL); + memcpy(as->s, src, size); + __archive_read_consume(a, padded_size); + as->s[size] = '\0'; return (ARCHIVE_OK); } @@ -1451,6 +1435,10 @@ pax_attribute(struct tar *tar, struct archive_entry *entry, if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0) archive_entry_set_xxxxxx(entry, value); */ + if (strcmp(key, "LIBARCHIVE.creationtime")==0) { + pax_time(value, &s, &n); + archive_entry_set_birthtime(entry, s, n); + } if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0) pax_attribute_xattr(entry, key, value); break; @@ -1762,7 +1750,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, return (ARCHIVE_OK); do { - bytes_read = (a->decompressor->read_ahead)(a, &data, 512); + data = __archive_read_ahead(a, 512, &bytes_read); if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read < 512) { @@ -1771,7 +1759,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, "detected while reading sparse file data"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, 512); + __archive_read_consume(a, 512); ext = (const struct extended *)data; gnu_sparse_old_parse(tar, ext->sparse, 21); } while (ext->isextended[0] != 0); @@ -1949,7 +1937,7 @@ gnu_sparse_10_read(struct archive_read *a, struct tar *tar) /* Skip rest of block... */ bytes_read = tar->entry_bytes_remaining - remaining; to_skip = 0x1ff & -bytes_read; - if (to_skip != (a->decompressor->skip)(a, to_skip)) + if (to_skip != __archive_read_skip(a, to_skip)) return (ARCHIVE_FATAL); return (bytes_read + to_skip); } @@ -2104,7 +2092,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, const char *s; void *p; - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); + t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ @@ -2118,7 +2106,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, "Line too long"); return (ARCHIVE_FATAL); } - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); *start = s; return (bytes_read); } @@ -2136,7 +2124,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, return (ARCHIVE_FATAL); } memcpy(tar->line.s + total_size, t, bytes_read); - (a->decompressor->consume)(a, bytes_read); + __archive_read_consume(a, bytes_read); total_size += bytes_read; /* If we found '\n', clean up and return. */ if (p != NULL) { @@ -2144,7 +2132,7 @@ readline(struct archive_read *a, struct tar *tar, const char **start, return (total_size); } /* Read some more. */ - bytes_read = (a->decompressor->read_ahead)(a, &t, 1); + t = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); s = t; /* Start of line? */ @@ -2161,7 +2149,6 @@ utf8_decode(struct tar *tar, const char *src, size_t length) { wchar_t *dest; ssize_t n; - int err; /* Ensure pax_entry buffer is big enough. */ if (tar->pax_entry_length <= length) { @@ -2183,7 +2170,6 @@ utf8_decode(struct tar *tar, const char *src, size_t length) } dest = tar->pax_entry; - err = 0; while (length > 0) { n = UTF8_mbrtowc(dest, src, length); if (n < 0) diff --git a/contrib/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libarchive/libarchive/archive_read_support_format_zip.c index f04f69bb20..cd061eb0b8 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_zip.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_zip.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.24 2008/06/15 05:15:53 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.28 2008/12/06 06:45:15 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -36,6 +36,10 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.24 #include #ifdef HAVE_ZLIB_H #include +#else +/* Hmmm... This is necessary, but means that we can't correctly extract + * even uncompressed entries on platforms that lack zlib. */ +#define crc32(crc, buf, len) (unsigned long)0 #endif #include "archive.h" @@ -53,6 +57,9 @@ struct zip { int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; + /* Running CRC32 of the decompressed data */ + unsigned long entry_crc32; + unsigned version; unsigned system; unsigned flags; @@ -68,9 +75,8 @@ struct zip { /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; - char end_of_entry_cleanup; - long crc32; + unsigned long crc32; ssize_t filename_length; ssize_t extra_length; int64_t uncompressed_size; @@ -147,7 +153,9 @@ archive_read_support_format_zip(struct archive *_a) r = __archive_read_register_format(a, zip, + "zip", archive_read_format_zip_bid, + NULL, archive_read_format_zip_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip, @@ -164,9 +172,9 @@ archive_read_format_zip_bid(struct archive_read *a) { const char *p; const void *buff; - size_t bytes_avail; + ssize_t bytes_avail, offset; - if ((p = __archive_read_ahead(a, 4)) == NULL) + if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* @@ -186,42 +194,37 @@ archive_read_format_zip_bid(struct archive_read *a) /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward - * up to 64k for a 'PK\003\004' marker. + * up to 128k for a 'PK\003\004' marker. */ if (p[0] == 'M' && p[1] == 'Z') { /* - * TODO: Additional checks that this really is a PE - * file before we invoke the 128k lookahead below. - * No point in allocating a bigger lookahead buffer - * if we don't need to. - */ - /* - * TODO: Of course, the compression layer lookahead - * buffers aren't dynamically sized yet; they should be. - */ - bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024); - p = (const char *)buff; - - /* - * TODO: Optimize by jumping forward based on values - * in the PE header. Note that we don't need to be - * exact, but we mustn't skip too far. The search - * below will compensate if we undershoot. Skipping - * will also reduce the chance of false positives - * (which is not really all that high to begin with, - * so maybe skipping isn't really necessary). + * TODO: Optimize by initializing 'offset' to an + * estimate of the likely start of the archive data + * based on values in the PE header. Note that we + * don't need to be exact, but we mustn't skip too + * far. The search below will compensate if we + * undershoot. */ - - while (p < bytes_avail + (const char *)buff) { - if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */ - && p[2] == 3 && p[3] == 4 /* File entry */ - && p[8] == 8 /* compression == deflate */ - && p[9] == 0 /* High byte of compression */ - ) - { - return (30); + offset = 0; + while (offset < 124000) { + /* Get 4k of data beyond where we stopped. */ + buff = __archive_read_ahead(a, offset + 4096, + &bytes_avail); + if (bytes_avail < offset + 1) + break; + p = (const char *)buff + offset; + while (p + 9 < (const char *)buff + bytes_avail) { + if (p[0] == 'P' && p[1] == 'K' /* signature */ + && p[2] == 3 && p[3] == 4 /* File entry */ + && p[8] == 8 /* compression == deflate */ + && p[9] == 0 /* High byte of compression */ + ) + { + return (30); + } + ++p; } - ++p; + offset = p - (const char *)buff; } } @@ -238,7 +241,8 @@ skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, bytes; + size_t skip; + ssize_t bytes; /* * TODO: We should be able to skip forward by a bunch @@ -248,7 +252,7 @@ skip_sfx(struct archive_read *a) * reduce the chance of a false positive. */ for (;;) { - bytes = (a->decompressor->read_ahead)(a, &h, 4096); + h = __archive_read_ahead(a, 4, &bytes); if (bytes < 4) return (ARCHIVE_FATAL); p = h; @@ -264,7 +268,7 @@ skip_sfx(struct archive_read *a) /* TODO: Additional verification here. */ if (memcmp("PK\003\004", p, 4) == 0) { skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += 4; @@ -276,7 +280,7 @@ skip_sfx(struct archive_read *a) } } skip = p - (const char *)h; - (a->decompressor->consume)(a, skip); + __archive_read_consume(a, skip); } } @@ -296,10 +300,10 @@ archive_read_format_zip_read_header(struct archive_read *a, zip = (struct zip *)(a->format->data); zip->decompress_init = 0; zip->end_of_entry = 0; - zip->end_of_entry_cleanup = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; - if ((h = __archive_read_ahead(a, 4)) == NULL) + zip->entry_crc32 = crc32(0, NULL, 0); + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; @@ -308,7 +312,7 @@ archive_read_format_zip_read_header(struct archive_read *a, r = skip_sfx(a); if (r < ARCHIVE_WARN) return (r); - if ((h = __archive_read_ahead(a, 4)) == NULL) + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; } @@ -325,8 +329,8 @@ archive_read_format_zip_read_header(struct archive_read *a, * skip the PK00; the first real file header should follow. */ if (signature[2] == '0' && signature[3] == '0') { - (a->decompressor->consume)(a, 4); - if ((h = __archive_read_ahead(a, 4)) == NULL) + __archive_read_consume(a, 4); + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (ARCHIVE_FATAL); signature = (const char *)h; if (signature[0] != 'P' || signature[1] != 'K') { @@ -370,14 +374,14 @@ archive_read_format_zip_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } -int +static int zip_read_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const struct zip_file_header *p; const void *h; - if ((p = __archive_read_ahead(a, sizeof *p)) == NULL) { + if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); @@ -404,11 +408,11 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, zip->uncompressed_size = archive_le32dec(p->uncompressed_size); zip->compressed_size = archive_le32dec(p->compressed_size); - (a->decompressor->consume)(a, sizeof(struct zip_file_header)); + __archive_read_consume(a, sizeof(struct zip_file_header)); /* Read the filename. */ - if ((h = __archive_read_ahead(a, zip->filename_length)) == NULL) { + if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); @@ -416,7 +420,7 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL) __archive_errx(1, "Out of memory"); archive_strncpy(&zip->pathname, h, zip->filename_length); - (a->decompressor->consume)(a, zip->filename_length); + __archive_read_consume(a, zip->filename_length); archive_entry_set_pathname(entry, zip->pathname.s); if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/') @@ -425,13 +429,13 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, zip->mode = AE_IFREG | 0777; /* Read the extra data. */ - if ((h = __archive_read_ahead(a, zip->extra_length)) == NULL) { + if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } process_extra(h, zip); - (a->decompressor->consume)(a, zip->extra_length); + __archive_read_consume(a, zip->extra_length); /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip->mode); @@ -440,7 +444,9 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry, archive_entry_set_mtime(entry, zip->mtime, 0); archive_entry_set_ctime(entry, zip->ctime, 0); archive_entry_set_atime(entry, zip->atime, 0); - archive_entry_set_size(entry, zip->uncompressed_size); + /* Set the size only if it's meaningful. */ + if (0 == (zip->flags & ZIP_LENGTH_AT_END)) + archive_entry_set_size(entry, zip->uncompressed_size); zip->entry_bytes_remaining = zip->compressed_size; zip->entry_offset = 0; @@ -494,46 +500,6 @@ archive_read_format_zip_read_data(struct archive_read *a, * ARCHIVE_EOF this time. */ if (zip->end_of_entry) { - if (!zip->end_of_entry_cleanup) { - if (zip->flags & ZIP_LENGTH_AT_END) { - const char *p; - - if ((p = __archive_read_ahead(a, 16)) == NULL) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP end-of-file record"); - return (ARCHIVE_FATAL); - } - zip->crc32 = archive_le32dec(p + 4); - zip->compressed_size = archive_le32dec(p + 8); - zip->uncompressed_size = archive_le32dec(p + 12); - (a->decompressor->consume)(a, 16); - } - - /* Check file size, CRC against these values. */ - if (zip->compressed_size != zip->entry_compressed_bytes_read) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP compressed data is wrong size"); - return (ARCHIVE_WARN); - } - /* Size field only stores the lower 32 bits of the actual size. */ - if ((zip->uncompressed_size & UINT32_MAX) - != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP uncompressed data is wrong size"); - return (ARCHIVE_WARN); - } -/* TODO: Compute CRC. */ -/* - if (zip->crc32 != zip->entry_crc32_calculated) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP data CRC error"); - return (ARCHIVE_WARN); - } -*/ - /* End-of-entry cleanup done. */ - zip->end_of_entry_cleanup = 1; - } *offset = zip->entry_uncompressed_bytes_read; *size = 0; *buff = NULL; @@ -570,7 +536,54 @@ archive_read_format_zip_read_data(struct archive_read *a, } break; } - return (r); + if (r != ARCHIVE_OK) + return (r); + /* Update checksum */ + if (*size) + zip->entry_crc32 = + crc32(zip->entry_crc32, *buff, *size); + /* If we hit the end, swallow any end-of-data marker. */ + if (zip->end_of_entry) { + if (zip->flags & ZIP_LENGTH_AT_END) { + const char *p; + + if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP end-of-file record"); + return (ARCHIVE_FATAL); + } + zip->crc32 = archive_le32dec(p + 4); + zip->compressed_size = archive_le32dec(p + 8); + zip->uncompressed_size = archive_le32dec(p + 12); + __archive_read_consume(a, 16); + } + /* Check file size, CRC against these values. */ + if (zip->compressed_size != zip->entry_compressed_bytes_read) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP compressed data is wrong size"); + return (ARCHIVE_WARN); + } + /* Size field only stores the lower 32 bits of the actual size. */ + if ((zip->uncompressed_size & UINT32_MAX) + != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP uncompressed data is wrong size"); + return (ARCHIVE_WARN); + } + /* Check computed CRC against header */ + if (zip->crc32 != zip->entry_crc32) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP bad CRC: 0x%lx should be 0x%lx", + zip->entry_crc32, zip->crc32); + return (ARCHIVE_WARN); + } + } + + /* Return EOF immediately if this is a non-regular file. */ + if (AE_IFREG != (zip->mode & AE_IFMT)) + return (ARCHIVE_EOF); + return (ARCHIVE_OK); } /* @@ -608,7 +621,7 @@ zip_read_data_none(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - bytes_avail = (a->decompressor->read_ahead)(a, buff, 1); + *buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); @@ -616,7 +629,7 @@ zip_read_data_none(struct archive_read *a, const void **buff, } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = zip->entry_bytes_remaining; - (a->decompressor->consume)(a, bytes_avail); + __archive_read_consume(a, bytes_avail); *size = bytes_avail; *offset = zip->entry_offset; zip->entry_offset += *size; @@ -674,7 +687,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - bytes_avail = (a->decompressor->read_ahead)(a, &compressed_buff, 1); + compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); @@ -713,7 +726,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; - (a->decompressor->consume)(a, bytes_avail); + __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; @@ -748,7 +761,7 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) zip = (struct zip *)(a->format->data); /* If we've already read to end of data, we're done. */ - if (zip->end_of_entry_cleanup) + if (zip->end_of_entry) return (ARCHIVE_OK); /* @@ -770,12 +783,12 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) * If the length is at the beginning, we can skip the * compressed data much more quickly. */ - bytes_skipped = (a->decompressor->skip)(a, zip->entry_bytes_remaining); + bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* This entry is finished and done. */ - zip->end_of_entry_cleanup = zip->end_of_entry = 1; + zip->end_of_entry = 1; return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_string.c b/contrib/libarchive/libarchive/archive_string.c index 21fe571151..9b7e19948d 100644 --- a/contrib/libarchive/libarchive/archive_string.c +++ b/contrib/libarchive/libarchive/archive_string.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.16 2008/06/15 11:28:56 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.17 2008/12/06 05:56:43 kientzle Exp $"); /* * Basic resizable string support, to simplify manipulating arbitrary-sized @@ -40,6 +40,9 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.16 2008/06/15 11:28: #ifdef HAVE_WCHAR_H #include #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#endif #include "archive_private.h" #include "archive_string.h" @@ -69,6 +72,18 @@ __archive_string_copy(struct archive_string *dest, struct archive_string *src) } } +void +__archive_string_concat(struct archive_string *dest, struct archive_string *src) +{ + if (src->length > 0) { + if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL) + __archive_errx(1, "Out of memory"); + memcpy(dest->s + dest->length, src->s, src->length); + dest->length += src->length; + dest->s[dest->length] = 0; + } +} + void __archive_string_free(struct archive_string *as) { @@ -100,11 +115,11 @@ __archive_string_ensure(struct archive_string *as, size_t s) as->buffer_length = 32; else if (as->buffer_length < 8192) /* Buffers under 8k are doubled for speed. */ - as->buffer_length *= 2; + as->buffer_length += as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ size_t old_length = as->buffer_length; - as->buffer_length = (as->buffer_length * 5) / 4; + as->buffer_length += as->buffer_length / 4; /* Be safe: If size wraps, release buffer and return NULL. */ if (as->buffer_length < old_length) { free(as->s); @@ -127,10 +142,12 @@ __archive_string_ensure(struct archive_string *as, size_t s) } struct archive_string * -__archive_strncat(struct archive_string *as, const char *p, size_t n) +__archive_strncat(struct archive_string *as, const void *_p, size_t n) { size_t s; - const char *pp; + const char *p, *pp; + + p = (const char *)_p; /* Like strlen(p), except won't examine positions beyond p[n]. */ s = 0; @@ -148,70 +165,18 @@ __archive_strappend_char(struct archive_string *as, char c) return (__archive_string_append(as, &c, 1)); } -struct archive_string * -__archive_strappend_int(struct archive_string *as, int d, int base) -{ - static const char *digits = "0123456789abcdef"; - - if (d < 0) { - __archive_strappend_char(as, '-'); - d = -d; - } - if (d >= base) - __archive_strappend_int(as, d/base, base); - __archive_strappend_char(as, digits[d % base]); - return (as); -} - /* - * Home-grown wctomb for UTF-8. + * Translates a wide character string into UTF-8 and appends + * to the archive_string. Note: returns NULL if conversion fails, + * but still leaves a best-effort conversion in the argument as. */ -static int -my_wctomb_utf8(char *p, wchar_t wc) -{ - if (p == NULL) - /* UTF-8 doesn't use shift states. */ - return (0); - if (wc <= 0x7f) { - p[0] = (char)wc; - return (1); - } - if (wc <= 0x7ff) { - p[0] = 0xc0 | ((wc >> 6) & 0x1f); - p[1] = 0x80 | (wc & 0x3f); - return (2); - } - if (wc <= 0xffff) { - p[0] = 0xe0 | ((wc >> 12) & 0x0f); - p[1] = 0x80 | ((wc >> 6) & 0x3f); - p[2] = 0x80 | (wc & 0x3f); - return (3); - } - if (wc <= 0x1fffff) { - p[0] = 0xf0 | ((wc >> 18) & 0x07); - p[1] = 0x80 | ((wc >> 12) & 0x3f); - p[2] = 0x80 | ((wc >> 6) & 0x3f); - p[3] = 0x80 | (wc & 0x3f); - return (4); - } - /* Unicode has no codes larger than 0x1fffff. */ - /* - * Awkward point: UTF-8 <-> wchar_t conversions - * can actually fail. - */ - return (-1); -} - -static int -my_wcstombs(struct archive_string *as, const wchar_t *w, - int (*func)(char *, wchar_t)) +struct archive_string * +__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) { - int n; char *p; + unsigned wc; char buff[256]; - - /* Clear the shift state before starting. */ - (*func)(NULL, L'\0'); + struct archive_string *return_val = as; /* * Convert one wide char at a time into 'buff', whenever that @@ -226,67 +191,50 @@ my_wcstombs(struct archive_string *as, const wchar_t *w, archive_strcat(as, buff); p = buff; } - n = (*func)(p, *w++); - if (n == -1) - return (-1); - p += n; + wc = *w++; + /* If this is a surrogate pair, assemble the full code point.*/ + /* Note: wc must not be wchar_t here, because the full code + * point can be more than 16 bits! */ + if (wc >= 0xD800 && wc <= 0xDBff + && *w >= 0xDC00 && *w <= 0xDFFF) { + wc -= 0xD800; + wc *= 0x400; + wc += (*w - 0xDC00); + wc += 0x10000; + ++w; + } + /* Translate code point to UTF8 */ + if (wc <= 0x7f) { + *p++ = (char)wc; + } else if (wc <= 0x7ff) { + *p++ = 0xc0 | ((wc >> 6) & 0x1f); + *p++ = 0x80 | (wc & 0x3f); + } else if (wc <= 0xffff) { + *p++ = 0xe0 | ((wc >> 12) & 0x0f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | (wc & 0x3f); + } else if (wc <= 0x1fffff) { + *p++ = 0xf0 | ((wc >> 18) & 0x07); + *p++ = 0x80 | ((wc >> 12) & 0x3f); + *p++ = 0x80 | ((wc >> 6) & 0x3f); + *p++ = 0x80 | (wc & 0x3f); + } else { + /* Unicode has no codes larger than 0x1fffff. */ + /* TODO: use \uXXXX escape here instead of ? */ + *p++ = '?'; + return_val = NULL; + } } *p = '\0'; archive_strcat(as, buff); - return (0); + return (return_val); } -/* - * Translates a wide character string into UTF-8 and appends - * to the archive_string. Note: returns NULL if conversion fails. - */ -struct archive_string * -__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) -{ - if (my_wcstombs(as, w, my_wctomb_utf8)) - return (NULL); - return (as); -} - -/* - * Translates a wide character string into current locale character set - * and appends to the archive_string. Note: returns NULL if conversion - * fails. - */ -struct archive_string * -__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) -{ -#if HAVE_WCTOMB - if (my_wcstombs(as, w, wctomb)) - return (NULL); -#else - /* TODO: Can we do better than this? Are there platforms - * that have locale support but don't have wctomb()? */ - if (my_wcstombs(as, w, my_wctomb_utf8)) - return (NULL); -#endif - return (as); -} - - -/* - * Home-grown mbtowc for UTF-8. Some systems lack UTF-8 - * (or even lack mbtowc()) and we need UTF-8 support for pax - * format. So please don't replace this with a call to the - * standard mbtowc() function! - */ static int -my_mbtowc_utf8(wchar_t *pwc, const char *s, size_t n) +utf8_to_unicode(int *pwc, const char *s, size_t n) { int ch; - /* Standard behavior: a NULL value for 's' just resets shift state. */ - if (s == NULL) - return (0); - /* If length argument is zero, don't look at the first character. */ - if (n <= 0) - return (-1); - /* * Decode 1-4 bytes depending on the value of the first byte. */ @@ -332,13 +280,15 @@ my_mbtowc_utf8(wchar_t *pwc, const char *s, size_t n) } /* - * Return a wide-character string by converting this archive_string - * from UTF-8. + * Return a wide-character Unicode string by converting this archive_string + * from UTF-8. We assume that systems with 16-bit wchar_t always use + * UTF16 and systems with 32-bit wchar_t can accept UCS4. */ wchar_t * __archive_string_utf8_w(struct archive_string *as) { wchar_t *ws, *dest; + int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */ const char *src; int n; int err; @@ -350,16 +300,156 @@ __archive_string_utf8_w(struct archive_string *as) dest = ws; src = as->s; while (*src != '\0') { - n = my_mbtowc_utf8(dest, src, 8); + n = utf8_to_unicode(&wc, src, 8); if (n == 0) break; if (n < 0) { free(ws); return (NULL); } - dest++; src += n; + if (wc >= 0xDC00 && wc <= 0xDBFF) { + /* This is a leading surrogate; some idiot + * has translated UTF16 to UTF8 without combining + * surrogates; rebuild the full code point before + * continuing. */ + n = utf8_to_unicode(&wc2, src, 8); + if (n < 0) { + free(ws); + return (NULL); + } + if (n == 0) /* Ignore the leading surrogate */ + break; + if (wc2 < 0xDC00 || wc2 > 0xDFFF) { + /* If the second character isn't a + * trailing surrogate, then someone + * has really screwed up and this is + * invalid. */ + free(ws); + return (NULL); + } else { + src += n; + wc -= 0xD800; + wc *= 0x400; + wc += wc2 - 0xDC00; + wc += 0x10000; + } + } + if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) { + /* We have a code point that won't fit into a + * wchar_t; convert it to a surrogate pair. */ + wc -= 0x10000; + *dest++ = ((wc >> 10) & 0x3ff) + 0xD800; + *dest++ = (wc & 0x3ff) + 0xDC00; + } else + *dest++ = wc; } *dest++ = L'\0'; return (ws); } + +#if defined(_WIN32) && !defined(__CYGWIN__) + +/* + * Translates a wide character string into current locale character set + * and appends to the archive_string. Note: returns NULL if conversion + * fails. + * + * Win32 builds use WideCharToMultiByte from the Windows API. + * (Maybe Cygwin should too? WideCharToMultiByte will know a + * lot more about local character encodings than the wcrtomb() + * wrapper is going to know.) + */ +struct archive_string * +__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) +{ + char *p; + int l, wl; + BOOL useDefaultChar = FALSE; + + wl = (int)wcslen(w); + l = wl * 4 + 4; + p = malloc(l); + if (p == NULL) + __archive_errx(1, "Out of memory"); + /* To check a useDefaultChar is to simulate error handling of + * the my_wcstombs() which is running on non Windows system with + * wctomb(). + * And to set NULL for last argument is necessary when a codepage + * is not CP_ACP(current locale). + */ + l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar); + if (l == 0) { + free(p); + return (NULL); + } + __archive_string_append(as, p, l); + free(p); + return (as); +} + +#else + +/* + * Translates a wide character string into current locale character set + * and appends to the archive_string. Note: returns NULL if conversion + * fails. + * + * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion + * one character at a time. If a non-Windows platform doesn't have + * either of these, fall back to the built-in UTF8 conversion. + */ +struct archive_string * +__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) +{ +#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB) + /* If there's no built-in locale support, fall back to UTF8 always. */ + return __archive_strappend_w_utf8(as, w); +#else + /* We cannot use the standard wcstombs() here because it + * cannot tell us how big the output buffer should be. So + * I've built a loop around wcrtomb() or wctomb() that + * converts a character at a time and resizes the string as + * needed. We prefer wcrtomb() when it's available because + * it's thread-safe. */ + int n; + char *p; + char buff[256]; +#if HAVE_WCRTOMB + mbstate_t shift_state; + + memset(&shift_state, 0, sizeof(shift_state)); +#else + /* Clear the shift state before starting. */ + wctomb(NULL, L'\0'); +#endif + + /* + * Convert one wide char at a time into 'buff', whenever that + * fills, append it to the string. + */ + p = buff; + while (*w != L'\0') { + /* Flush the buffer when we have <=16 bytes free. */ + /* (No encoding has a single character >16 bytes.) */ + if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) { + *p = '\0'; + archive_strcat(as, buff); + p = buff; + } +#if HAVE_WCRTOMB + n = wcrtomb(p, *w++, &shift_state); +#else + n = wctomb(p, *w++); +#endif + if (n == -1) + return (NULL); + p += n; + } + *p = '\0'; + archive_strcat(as, buff); + return (as); +#endif +} + +#endif /* _WIN32 && ! __CYGWIN__ */ diff --git a/contrib/libarchive/libarchive/archive_string.h b/contrib/libarchive/libarchive/archive_string.h index e59c33af19..9b6f22232f 100644 --- a/contrib/libarchive/libarchive/archive_string.h +++ b/contrib/libarchive/libarchive/archive_string.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.12 2008/06/15 05:11:08 kientzle Exp $ + * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.13 2008/12/06 05:56:43 kientzle Exp $ * */ @@ -66,11 +66,6 @@ struct archive_string * __archive_strappend_char(struct archive_string *, char); #define archive_strappend_char __archive_strappend_char -/* Append an integer in the specified base (2 <= base <= 16). */ -struct archive_string * -__archive_strappend_int(struct archive_string *as, int d, int base); -#define archive_strappend_int __archive_strappend_int - /* Convert a wide-char string to UTF-8 and append the result. */ struct archive_string * __archive_strappend_w_utf8(struct archive_string *, const wchar_t *); @@ -92,14 +87,24 @@ __archive_string_copy(struct archive_string *dest, struct archive_string *src); #define archive_string_copy(dest, src) \ __archive_string_copy(dest, src) +/* Concatenate one archive_string to another */ +void +__archive_string_concat(struct archive_string *dest, struct archive_string *src); +#define archive_string_concat(dest, src) \ + __archive_string_concat(dest, src) + /* Ensure that the underlying buffer is at least as large as the request. */ struct archive_string * __archive_string_ensure(struct archive_string *, size_t); #define archive_string_ensure __archive_string_ensure /* Append C string, which may lack trailing \0. */ +/* The source is declared void * here because this gets used with + * "signed char *", "unsigned char *" and "char *" arguments. + * Declaring it "char *" as with some of the other functions just + * leads to a lot of extra casts. */ struct archive_string * -__archive_strncat(struct archive_string *, const char *, size_t); +__archive_strncat(struct archive_string *, const void *, size_t); #define archive_strncat __archive_strncat /* Append a C string to an archive_string, resizing as necessary. */ diff --git a/contrib/libarchive/libarchive/archive_string_sprintf.c b/contrib/libarchive/libarchive/archive_string_sprintf.c index 6f77a36a1b..067c79bf64 100644 --- a/contrib/libarchive/libarchive/archive_string_sprintf.c +++ b/contrib/libarchive/libarchive/archive_string_sprintf.c @@ -32,7 +32,7 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.10 2008/03/1 * implementing this function in terms of vsnprintf() requires * two calls (one to determine the size, another to format the * result), which in turn requires duplicating the argument list - * using va_copy, which isn't yet universally available. + * using va_copy, which isn't yet universally available. * * So, I've implemented a bare minimum of printf()-like capability * here. This is only used to format error messages, so doesn't @@ -44,6 +44,30 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.10 2008/03/1 #include "archive_string.h" #include "archive_private.h" +/* + * Utility functions to format signed/unsigned integers and append + * them to an archive_string. + */ +static void +append_uint(struct archive_string *as, uintmax_t d, unsigned base) +{ + static const char *digits = "0123456789abcdef"; + if (d >= base) + append_uint(as, d/base, base); + archive_strappend_char(as, digits[d % base]); +} + +static void +append_int(struct archive_string *as, intmax_t d, unsigned base) +{ + if (d < 0) { + archive_strappend_char(as, '-'); + d = -d; + } + append_uint(as, d, base); +} + + void __archive_string_sprintf(struct archive_string *as, const char *fmt, ...) { @@ -75,7 +99,6 @@ __archive_string_vsprintf(struct archive_string *as, const char *fmt, return; } - long_flag = '\0'; for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; @@ -86,6 +109,7 @@ __archive_string_vsprintf(struct archive_string *as, const char *fmt, p++; + long_flag = '\0'; switch(*p) { case 'j': long_flag = 'j'; @@ -111,7 +135,7 @@ __archive_string_vsprintf(struct archive_string *as, const char *fmt, case 'l': s = va_arg(ap, long); break; default: s = va_arg(ap, int); break; } - archive_strappend_int(as, s, 10); + append_int(as, s, 10); break; case 's': p2 = va_arg(ap, char *); @@ -126,9 +150,9 @@ __archive_string_vsprintf(struct archive_string *as, const char *fmt, } /* Format it in the correct base. */ switch (*p) { - case 'o': archive_strappend_int(as, u, 8); break; - case 'u': archive_strappend_int(as, u, 10); break; - default: archive_strappend_int(as, u, 16); break; + case 'o': append_uint(as, u, 8); break; + case 'u': append_uint(as, u, 10); break; + default: append_uint(as, u, 16); break; } break; default: diff --git a/contrib/libarchive/libarchive/archive_util.c b/contrib/libarchive/libarchive/archive_util.c index 7fb4696771..efaebb677a 100644 --- a/contrib/libarchive/libarchive/archive_util.c +++ b/contrib/libarchive/libarchive/archive_util.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2009 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * @@ -24,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.18 2008/05/26 17:00:22 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.19 2008/10/21 12:10:30 des Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -155,10 +156,6 @@ void archive_set_error(struct archive *a, int error_number, const char *fmt, ...) { va_list ap; -#ifdef HAVE_STRERROR_R - char errbuff[512]; -#endif - char *errp; a->archive_error_number = error_number; if (fmt == NULL) { @@ -168,23 +165,8 @@ archive_set_error(struct archive *a, int error_number, const char *fmt, ...) va_start(ap, fmt); archive_string_vsprintf(&(a->error_string), fmt, ap); - if (error_number > 0) { - archive_strcat(&(a->error_string), ": "); -#ifdef HAVE_STRERROR_R -#ifdef STRERROR_R_CHAR_P - errp = strerror_r(error_number, errbuff, sizeof(errbuff)); -#else - strerror_r(error_number, errbuff, sizeof(errbuff)); - errp = errbuff; -#endif -#else - /* Note: this is not threadsafe! */ - errp = strerror(error_number); -#endif - archive_strcat(&(a->error_string), errp); - } - a->error = a->error_string.s; va_end(ap); + a->error = a->error_string.s; } void @@ -205,3 +187,195 @@ __archive_errx(int retvalue, const char *msg) write(2, "\n", 1); exit(retvalue); } + +/* + * Parse option strings + * Detail of option format. + * - The option can accept: + * "opt-name", "!opt-name", "opt-name=value". + * + * - The option entries are separated by comma. + * e.g "compression=9,opt=XXX,opt-b=ZZZ" + * + * - The name of option string consist of '-' and alphabet + * but character '-' cannot be used for the first character. + * (Regular expression is [a-z][-a-z]+) + * + * - For a specfic format/filter, using the format name with ':'. + * e.g "zip:compression=9" + * (This "compression=9" option entry is for "zip" format only) + * + * If another entries follow it, those are not for + * the specfic format/filter. + * e.g handle "zip:compression=9,opt=XXX,opt-b=ZZZ" + * "zip" format/filter handler will get "compression=9" + * all format/filter handler will get "opt=XXX" + * all format/filter handler will get "opt-b=ZZZ" + * + * - Whitespace and tab are bypassed. + * + */ +int +__archive_parse_options(const char *p, const char *fn, int keysize, char *key, + int valsize, char *val) +{ + const char *p_org; + int apply; + int kidx, vidx; + int negative; + enum { + /* Requested for initialization. */ + INIT, + /* Finding format/filter-name and option-name. */ + F_BOTH, + /* Finding option-name only. + * (already detected format/filter-name) */ + F_NAME, + /* Getting option-value. */ + G_VALUE, + } state; + + p_org = p; + state = INIT; + kidx = vidx = negative = 0; + apply = 1; + while (*p) { + switch (state) { + case INIT: + kidx = vidx = 0; + negative = 0; + apply = 1; + state = F_BOTH; + break; + case F_BOTH: + case F_NAME: + if ((*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || *p == '-') { + if (kidx == 0 && !(*p >= 'a' && *p <= 'z')) + /* Illegal sequence. */ + return (-1); + if (kidx >= keysize -1) + /* Too many characters. */ + return (-1); + key[kidx++] = *p++; + } else if (*p == '!') { + if (kidx != 0) + /* Illegal sequence. */ + return (-1); + negative = 1; + ++p; + } else if (*p == ',') { + if (kidx == 0) + /* Illegal sequence. */ + return (-1); + if (!negative) + val[vidx++] = '1'; + /* We have got boolean option data. */ + ++p; + if (apply) + goto complete; + else + /* This option does not apply to the + * format which the fn variable + * indicate. */ + state = INIT; + } else if (*p == ':') { + /* obuf data is format name */ + if (state == F_NAME) + /* We already found it. */ + return (-1); + if (kidx == 0) + /* Illegal sequence. */ + return (-1); + if (negative) + /* We cannot accept "!format-name:". */ + return (-1); + key[kidx] = '\0'; + if (strcmp(fn, key) != 0) + /* This option does not apply to the + * format which the fn variable + * indicate. */ + apply = 0; + kidx = 0; + ++p; + state = F_NAME; + } else if (*p == '=') { + if (kidx == 0) + /* Illegal sequence. */ + return (-1); + if (negative) + /* We cannot accept "!opt-name=value". */ + return (-1); + ++p; + state = G_VALUE; + } else if (*p == ' ') { + /* Pass the space character */ + ++p; + } else { + /* Illegal character. */ + return (-1); + } + break; + case G_VALUE: + if (*p == ',') { + if (vidx == 0) + /* Illegal sequence. */ + return (-1); + /* We have got option data. */ + ++p; + if (apply) + goto complete; + else + /* This option does not apply to the + * format which the fn variable + * indicate. */ + state = INIT; + } else if (*p == ' ') { + /* Pass the space character */ + ++p; + } else { + if (vidx >= valsize -1) + /* Too many characters. */ + return (-1); + val[vidx++] = *p++; + } + break; + } + } + + switch (state) { + case F_BOTH: + case F_NAME: + if (kidx != 0) { + if (!negative) + val[vidx++] = '1'; + /* We have got boolean option. */ + if (apply) + /* This option apply to the format which the + * fn variable indicate. */ + goto complete; + } + break; + case G_VALUE: + if (vidx == 0) + /* Illegal sequence. */ + return (-1); + /* We have got option value. */ + if (apply) + /* This option apply to the format which the fn + * variable indicate. */ + goto complete; + break; + case INIT:/* nothing */ + break; + } + + /* End of Option string. */ + return (0); + +complete: + key[kidx] = '\0'; + val[vidx] = '\0'; + /* Return a size which we've consumed for detecting option */ + return ((int)(p - p_org)); +} diff --git a/contrib/libarchive/libarchive/archive_virtual.c b/contrib/libarchive/libarchive/archive_virtual.c index a431058c67..41904e6de2 100644 --- a/contrib/libarchive/libarchive/archive_virtual.c +++ b/contrib/libarchive/libarchive/archive_virtual.c @@ -33,24 +33,36 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_virtual.c,v 1.1 2007/03/03 07:37: int archive_write_close(struct archive *a) { - return ((a->vtable->archive_write_close)(a)); + return ((a->vtable->archive_close)(a)); +} + +int +archive_read_close(struct archive *a) +{ + return ((a->vtable->archive_close)(a)); } #if ARCHIVE_API_VERSION > 1 int archive_write_finish(struct archive *a) { - return ((a->vtable->archive_write_finish)(a)); + return ((a->vtable->archive_finish)(a)); } #else /* Temporarily allow library to compile with either 1.x or 2.0 API. */ void archive_write_finish(struct archive *a) { - (void)(a->vtable->archive_write_finish)(a); + (void)(a->vtable->archive_finish)(a); } #endif +int +archive_read_finish(struct archive *a) +{ + return ((a->vtable->archive_finish)(a)); +} + int archive_write_header(struct archive *a, struct archive_entry *entry) { diff --git a/contrib/libarchive/libarchive/archive_write.3 b/contrib/libarchive/libarchive/archive_write.3 index 14003f43c3..9f42b93537 100644 --- a/contrib/libarchive/libarchive/archive_write.3 +++ b/contrib/libarchive/libarchive/archive_write.3 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.24 2008/05/26 17:00:23 kientzle Exp $ +.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.25 2008/11/01 19:11:21 kientzle Exp $ .\" .Dd May 11, 2008 .Dt archive_write 3 @@ -43,6 +43,9 @@ .Nm archive_write_set_compression_gzip , .Nm archive_write_set_compression_none , .Nm archive_write_set_compression_program , +.Nm archive_write_set_compressor_options , +.Nm archive_write_set_format_options , +.Nm archive_write_set_options , .Nm archive_write_open , .Nm archive_write_open_fd , .Nm archive_write_open_FILE , @@ -73,10 +76,7 @@ .Ft int .Fn archive_write_set_compression_none "struct archive *" .Ft int -.Fo archive_write_set_compression_program -.Fa "struct archive *" -.Fa "const char * cmd" -.Fc +.Fn archive_write_set_compression_program "struct archive *" "const char * cmd" .Ft int .Fn archive_write_set_format_cpio "struct archive *" .Ft int @@ -90,6 +90,12 @@ .Ft int .Fn archive_write_set_format_ustar "struct archive *" .Ft int +.Fn archive_write_set_format_options "struct archive *" "const char *" +.Ft int +.Fn archive_write_set_compressor_options "struct archive *" "const char *" +.Ft int +.Fn archive_write_set_options "struct archive *" "const char *" +.Ft int .Fo archive_write_open .Fa "struct archive *" .Fa "void *client_data" @@ -210,6 +216,68 @@ Note that the compressed output is always properly blocked. The archive will be fed into the specified compression program. The output of that program is blocked and written to the client write callbacks. +.It Xo +.Fn archive_write_set_compressor_options , +.Fn archive_write_set_format_options , +.Fn archive_write_set_options +.Xc +Specifies options that will be passed to the currently-enabled +compressor and/or format writer. +The argument is a comma-separated list of individual options. +Individual options have one of the following forms: +.Bl -tag -compact -width indent +.It Ar option=value +The option/value pair will be provided to every module. +Modules that do not accept an option with this name will ignore it. +.It Ar option +The option will be provided to every module with a value of +.Dq 1 . +.It Ar !option +The option will be provided to every module with a NULL value. +.It Ar module:option=value , Ar module:option , Ar module:!option +As above, but the corresponding option and value will be provided +only to modules whose name matches +.Ar module . +.El +The return value will be +.Cm ARCHIVE_OK +if any module accepts the option, or +.Cm ARCHIVE_WARN +if no module accepted the option, or +.Cm ARCHIVE_FATAL +if there was a fatal error while attempting to process the option. +.Pp +The currently supported options are: +.Bl -tag -compact -width indent +.It Compressor gzip +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +gzip compression level. +.El +.It Compressor xz +.Bl -tag -compact -width indent +.It Cm compression-level +The value is interpreted as a decimal integer specifying the +compression level. +.El +.It Format mtree +.Bl -tag -compact -width indent +.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname +Enable a particular keyword in the mtree output. +Prefix with an exclamation mark to disable the corresponding keyword. +The default is equivalent to +.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . +.It Cm all +Enables all of the above keywords. +.It Cm use-set +Enables generation of +.Cm /set +lines that specify default values for the following files and/or directories. +.It Cm indent +XXX needs explanation XXX +.El +.El .It Fn archive_write_open Freeze the settings, open the archive, and prepare for writing entries. This is the most generic form of this function, which accepts @@ -342,7 +410,7 @@ to register an error code and message and return .Fo archive_write_callback .Fa "struct archive *" .Fa "void *client_data" -.Fa "void *buffer" +.Fa "const void *buffer" .Fa "size_t length" .Fc .El @@ -410,7 +478,7 @@ myopen(struct archive *a, void *client_data) } ssize_t -mywrite(struct archive *a, void *client_data, void *buff, size_t n) +mywrite(struct archive *a, void *client_data, const void *buff, size_t n) { struct mydata *mydata = client_data; diff --git a/contrib/libarchive/libarchive/archive_write.c b/contrib/libarchive/libarchive/archive_write.c index 1a3ddc9a5e..60d42e65ec 100644 --- a/contrib/libarchive/libarchive/archive_write.c +++ b/contrib/libarchive/libarchive/archive_write.c @@ -72,8 +72,8 @@ archive_write_vtable(void) static int inited = 0; if (!inited) { - av.archive_write_close = _archive_write_close; - av.archive_write_finish = _archive_write_finish; + av.archive_close = _archive_write_close; + av.archive_finish = _archive_write_finish; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; @@ -124,6 +124,115 @@ archive_write_new(void) return (&a->archive); } +/* + * Set write options for the format. Returns 0 if successful. + */ +int +archive_write_set_format_options(struct archive *_a, const char *s) +{ + struct archive_write *a = (struct archive_write *)_a; + char key[64], val[64]; + int len, r, ret = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_format_options"); + archive_clear_error(&a->archive); + + if (s == NULL || *s == '\0') + return (ARCHIVE_OK); + if (a->format_options == NULL) + /* This format does not support option. */ + return (ARCHIVE_OK); + + while ((len = __archive_parse_options(s, a->format_name, + sizeof(key), key, sizeof(val), val)) > 0) { + if (val[0] == '\0') + r = a->format_options(a, key, NULL); + else + r = a->format_options(a, key, val); + if (r == ARCHIVE_FATAL) + return (r); + if (r < ARCHIVE_OK) { /* This key was not handled. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unsupported option ``%s''", key); + ret = ARCHIVE_WARN; + } + s += len; + } + if (len < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Malformed options string."); + return (ARCHIVE_WARN); + } + return (ret); +} + +/* + * Set write options for the compressor. Returns 0 if successful. + */ +int +archive_write_set_compressor_options(struct archive *_a, const char *s) +{ + struct archive_write *a = (struct archive_write *)_a; + char key[64], val[64]; + int len, r; + int ret = ARCHIVE_OK; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compressor_options"); + archive_clear_error(&a->archive); + + if (s == NULL || *s == '\0') + return (ARCHIVE_OK); + if (a->compressor.options == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unsupported option ``%s''", s); + /* This compressor does not support option. */ + return (ARCHIVE_WARN); + } + + while ((len = __archive_parse_options(s, a->archive.compression_name, + sizeof(key), key, sizeof(val), val)) > 0) { + if (val[0] == '\0') + r = a->compressor.options(a, key, NULL); + else + r = a->compressor.options(a, key, val); + if (r == ARCHIVE_FATAL) + return (r); + if (r < ARCHIVE_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unsupported option ``%s''", key); + ret = ARCHIVE_WARN; + } + s += len; + } + if (len < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Illegal format options."); + return (ARCHIVE_WARN); + } + return (ret); +} + +/* + * Set write options for the format and the compressor. Returns 0 if successful. + */ +int +archive_write_set_options(struct archive *_a, const char *s) +{ + int r1, r2; + + r1 = archive_write_set_format_options(_a, s); + if (r1 < ARCHIVE_WARN) + return (r1); + r2 = archive_write_set_compressor_options(_a, s); + if (r2 < ARCHIVE_WARN) + return (r2); + if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN) + return (ARCHIVE_WARN); + return (ARCHIVE_OK); +} + /* * Set the block size. Returns 0 if successful. */ diff --git a/contrib/libarchive/libarchive/archive_write_disk.3 b/contrib/libarchive/libarchive/archive_write_disk.3 index d68a0ffca3..5ed4a5038f 100644 --- a/contrib/libarchive/libarchive/archive_write_disk.3 +++ b/contrib/libarchive/libarchive/archive_write_disk.3 @@ -22,9 +22,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.3 2008/05/26 17:00:23 kientzle Exp $ +.\" $FreeBSD: src/lib/libarchive/archive_write_disk.3,v 1.4 2008/09/04 05:22:00 kientzle Exp $ .\" -.Dd March 2, 2007 +.Dd August 5, 2008 .Dt archive_write_disk 3 .Os .Sh NAME @@ -169,11 +169,11 @@ The default is to not refuse such paths. Note that paths ending in .Pa .. always cause an error, regardless of this flag. -.El .It Cm ARCHIVE_EXTRACT_SPARSE Scan data for blocks of NUL bytes and try to recreate them with holes. This results in sparse files, independent of whether the archive format supports or uses them. +.El .It Xo .Fn archive_write_disk_set_group_lookup , .Fn archive_write_disk_set_user_lookup diff --git a/contrib/libarchive/libarchive/archive_write_disk.c b/contrib/libarchive/libarchive/archive_write_disk.c index 238173c42a..54fab828a3 100644 --- a/contrib/libarchive/libarchive/archive_write_disk.c +++ b/contrib/libarchive/libarchive/archive_write_disk.c @@ -25,7 +25,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.26 2008/06/21 19:05:29 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.42 2008/12/06 05:55:46 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -33,6 +33,12 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.26 2008/06/21 19 #ifdef HAVE_SYS_ACL_H #include #endif +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif +#ifdef HAVE_SYS_XATTR_H +#include +#endif #ifdef HAVE_ATTR_XATTR_H #include #endif @@ -48,10 +54,6 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.26 2008/06/21 19 #ifdef HAVE_SYS_UTIME_H #include #endif - -#ifdef HAVE_EXT2FS_EXT2_FS_H -#include /* for Linux file flags */ -#endif #ifdef HAVE_ERRNO_H #include #endif @@ -64,6 +66,16 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.26 2008/06/21 19 #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif +/* + * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. + * As the include guards don't agree, the order of include is important. + */ +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) +#include /* Linux file flags, broken on Cygwin */ +#endif #ifdef HAVE_LIMITS_H #include #endif @@ -96,10 +108,12 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.26 2008/06/21 19 struct fixup_entry { struct fixup_entry *next; mode_t mode; - int64_t mtime; int64_t atime; - unsigned long mtime_nanos; + int64_t birthtime; + int64_t mtime; unsigned long atime_nanos; + unsigned long birthtime_nanos; + unsigned long mtime_nanos; unsigned long fflags_set; int fixup; /* bitmask of what needs fixing */ char *name; @@ -140,6 +154,7 @@ struct archive_write_disk { uid_t user_uid; dev_t skip_file_dev; ino_t skip_file_ino; + time_t start_time; gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid); void (*cleanup_gid)(void *private); @@ -175,7 +190,9 @@ struct archive_write_disk { int fd; /* Current offset for writing data to the file. */ off_t offset; - /* Maximum size of file. */ + /* Last offset actually written to disk. */ + off_t fd_offset; + /* Maximum size of file, -1 if unknown. */ off_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; @@ -184,8 +201,6 @@ struct archive_write_disk { /* UID/GID to use in restoring this entry. */ uid_t uid; gid_t gid; - /* Last offset written to disk. */ - off_t last_offset; }; /* @@ -226,11 +241,13 @@ static int set_fflags_platform(struct archive_write_disk *, int fd, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); -static int set_time(struct archive_write_disk *); +static int set_time(int, int, const char *, time_t, long, time_t, long); +static int set_times(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static gid_t trivial_lookup_gid(void *, const char *, gid_t); static uid_t trivial_lookup_uid(void *, const char *, uid_t); - +static ssize_t write_data_block(struct archive_write_disk *, + const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); @@ -273,8 +290,8 @@ archive_write_disk_vtable(void) static int inited = 0; if (!inited) { - av.archive_write_close = _archive_write_close; - av.archive_write_finish = _archive_write_finish; + av.archive_close = _archive_write_close; + av.archive_finish = _archive_write_finish; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; av.archive_write_data = _archive_write_data; @@ -332,11 +349,14 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) } a->entry = archive_entry_clone(entry); a->fd = -1; - a->last_offset = 0; + a->fd_offset = 0; a->offset = 0; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); - a->filesize = archive_entry_size(a->entry); + if (archive_entry_size_is_set(a->entry)) + a->filesize = archive_entry_size(a->entry); + else + a->filesize = -1; archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); @@ -397,12 +417,16 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; +#endif if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) a->todo |= TODO_ACLS; + if (a->flags & ARCHIVE_EXTRACT_XATTR) + a->todo |= TODO_XATTR; if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { @@ -417,6 +441,17 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) ret = restore_entry(a); + /* + * On the GNU tar mailing list, some people working with new + * Linux filesystems observed that system xattrs used as + * layout hints need to be restored before the file contents + * are written, so this can't be done at file close. + */ + if (a->todo & TODO_XATTR) { + int r2 = set_xattrs(a); + if (r2 < ret) ret = r2; + } + #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { @@ -438,13 +473,35 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) fe->mode = a->mode; } - if (a->deferred & TODO_TIMES) { + if ((a->deferred & TODO_TIMES) + && (archive_entry_mtime_is_set(entry) + || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); fe->fixup |= TODO_TIMES; - fe->mtime = archive_entry_mtime(entry); - fe->mtime_nanos = archive_entry_mtime_nsec(entry); - fe->atime = archive_entry_atime(entry); - fe->atime_nanos = archive_entry_atime_nsec(entry); + if (archive_entry_atime_is_set(entry)) { + fe->atime = archive_entry_atime(entry); + fe->atime_nanos = archive_entry_atime_nsec(entry); + } else { + /* If atime is unset, use start time. */ + fe->atime = a->start_time; + fe->atime_nanos = 0; + } + if (archive_entry_mtime_is_set(entry)) { + fe->mtime = archive_entry_mtime(entry); + fe->mtime_nanos = archive_entry_mtime_nsec(entry); + } else { + /* If mtime is unset, use start time. */ + fe->mtime = a->start_time; + fe->mtime_nanos = 0; + } + if (archive_entry_birthtime_is_set(entry)) { + fe->birthtime = archive_entry_birthtime(entry); + fe->birthtime_nanos = archive_entry_birthtime_nsec(entry); + } else { + /* If birthtime is unset, use mtime. */ + fe->birthtime = fe->mtime; + fe->birthtime_nanos = fe->mtime_nanos; + } } if (a->deferred & TODO_FFLAGS) { @@ -454,7 +511,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) } /* We've created the object and are ready to pour data into it. */ - if (ret == ARCHIVE_OK) + if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. @@ -483,87 +540,125 @@ archive_write_disk_set_skip_file(struct archive *_a, dev_t d, ino_t i) } static ssize_t -_archive_write_data_block(struct archive *_a, - const void *buff, size_t size, off_t offset) +write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { - struct archive_write_disk *a = (struct archive_write_disk *)_a; + uint64_t start_size = size; ssize_t bytes_written = 0; - ssize_t block_size, bytes_to_write; - int r = ARCHIVE_OK; + ssize_t block_size = 0, bytes_to_write; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, - ARCHIVE_STATE_DATA, "archive_write_disk_block"); - if (a->fd < 0) { - archive_set_error(&a->archive, 0, "File not open"); + if (size == 0) + return (ARCHIVE_OK); + + if (a->filesize == 0 || a->fd < 0) { + archive_set_error(&a->archive, 0, + "Attempt to write to an empty file"); return (ARCHIVE_WARN); } - archive_clear_error(&a->archive); if (a->flags & ARCHIVE_EXTRACT_SPARSE) { +#if HAVE_STRUCT_STAT_ST_BLKSIZE + int r; if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; - } else - block_size = -1; - - if ((off_t)(offset + size) > a->filesize) { - size = (size_t)(a->filesize - a->offset); - archive_set_error(&a->archive, 0, - "Write request too large"); - r = ARCHIVE_WARN; +#else + /* XXX TODO XXX Is there a more appropriate choice here ? */ + /* This needn't match the filesystem allocation size. */ + block_size = 16*1024; +#endif } + /* If this write would run beyond the file size, truncate it. */ + if (a->filesize >= 0 && (off_t)(a->offset + size) > a->filesize) + start_size = size = (size_t)(a->filesize - a->offset); + /* Write the data. */ while (size > 0) { - if (block_size != -1) { - const char *buf; + if (block_size == 0) { + bytes_to_write = size; + } else { + /* We're sparsifying the file. */ + const char *p, *end; + off_t block_end; - for (buf = buff; size; ++buf, --size, ++offset) { - if (*buf != '\0') + /* Skip leading zero bytes. */ + for (p = buff, end = buff + size; p < end; ++p) { + if (*p != '\0') break; } + a->offset += p - buff; + size -= p - buff; + buff = p; if (size == 0) break; - bytes_to_write = block_size - offset % block_size; - buff = buf; - } else + + /* Calculate next block boundary after offset. */ + block_end + = (a->offset / block_size + 1) * block_size; + + /* If the adjusted write would cross block boundary, + * truncate it to the block boundary. */ bytes_to_write = size; + if (a->offset + bytes_to_write > block_end) + bytes_to_write = block_end - a->offset; + } /* Seek if necessary to the specified offset. */ - if (offset != a->last_offset) { - if (lseek(a->fd, offset, SEEK_SET) < 0) { - archive_set_error(&a->archive, errno, "Seek failed"); + if (a->offset != a->fd_offset) { + if (lseek(a->fd, a->offset, SEEK_SET) < 0) { + archive_set_error(&a->archive, errno, + "Seek failed"); return (ARCHIVE_FATAL); } + a->fd_offset = a->offset; + a->archive.file_position = a->offset; + a->archive.raw_position = a->offset; } - bytes_written = write(a->fd, buff, size); + bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } - buff = (const char *)buff + bytes_written; + buff += bytes_written; size -= bytes_written; - offset += bytes_written; - a->last_offset = a->offset = offset; + a->offset += bytes_written; + a->archive.file_position += bytes_written; + a->archive.raw_position += bytes_written; + a->fd_offset = a->offset; } + return (start_size - size); +} + +static ssize_t +_archive_write_data_block(struct archive *_a, + const void *buff, size_t size, off_t offset) +{ + struct archive_write_disk *a = (struct archive_write_disk *)_a; + ssize_t r; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_disk_block"); + a->offset = offset; - return (r); + r = write_data_block(a, buff, size); + if (r < ARCHIVE_OK) + return (r); + if ((size_t)r < size) { + archive_set_error(&a->archive, 0, + "Write request too large"); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); } static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; - int r; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); - if (a->fd < 0) - return (ARCHIVE_OK); - r = _archive_write_data_block(_a, buff, size, a->offset); - if (r < ARCHIVE_OK) - return (r); - return size; + return (write_data_block(a, buff, size)); } static int @@ -579,13 +674,23 @@ _archive_write_finish_entry(struct archive *_a) return (ARCHIVE_OK); archive_clear_error(&a->archive); - if (a->last_offset != a->filesize && a->fd >= 0) { + /* Pad or truncate file to the right size. */ + if (a->fd < 0) { + /* There's no file. */ + } else if (a->filesize < 0) { + /* File size is unknown, so we can't set the size. */ + } else if (a->fd_offset == a->filesize) { + /* Last write ended at exactly the filesize; we're done. */ + /* Hopefully, this is the common case. */ + } else { +#if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } +#endif /* * Explicitly stat the file as some platforms might not * implement the XSI option to extend files via ftruncate. @@ -596,7 +701,8 @@ _archive_write_finish_entry(struct archive *_a) if (a->st.st_size != a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) { - archive_set_error(&a->archive, errno, "Seek failed"); + archive_set_error(&a->archive, errno, + "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { @@ -604,6 +710,7 @@ _archive_write_finish_entry(struct archive *_a) "Write to restore size failed"); return (ARCHIVE_FATAL); } + a->pst = NULL; } } @@ -636,22 +743,26 @@ _archive_write_finish_entry(struct archive *_a) int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } - if (a->todo & TODO_TIMES) { - int r2 = set_time(a); - if (r2 < ret) ret = r2; - } if (a->todo & TODO_ACLS) { int r2 = set_acls(a); if (r2 < ret) ret = r2; } - if (a->todo & TODO_XATTR) { - int r2 = set_xattrs(a); - if (r2 < ret) ret = r2; - } + /* + * Some flags prevent file modification; they must be restored after + * file contents are written. + */ if (a->todo & TODO_FFLAGS) { int r2 = set_fflags(a); if (r2 < ret) ret = r2; } + /* + * Time has to be restored after all other metadata; + * otherwise atime will get changed. + */ + if (a->todo & TODO_TIMES) { + int r2 = set_times(a); + if (r2 < ret) ret = r2; + } /* If there's an fd, we can close it now. */ if (a->fd >= 0) { @@ -718,6 +829,7 @@ archive_write_disk_new(void) a->archive.vtable = archive_write_disk_vtable(); a->lookup_uid = trivial_lookup_uid; a->lookup_gid = trivial_lookup_gid; + a->start_time = time(NULL); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ @@ -768,7 +880,7 @@ edit_deep_directories(struct archive_write_disk *a) *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; @@ -809,7 +921,7 @@ restore_entry(struct archive_write_disk *a) /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); - return(ARCHIVE_WARN); + return(ARCHIVE_FAILED); } } @@ -828,7 +940,7 @@ restore_entry(struct archive_write_disk *a) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ archive_set_error(&a->archive, en, "Already exists"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } /* @@ -843,7 +955,7 @@ restore_entry(struct archive_write_disk *a) if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ @@ -853,15 +965,31 @@ restore_entry(struct archive_write_disk *a) * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ - if (lstat(a->name, &a->st) != 0) { + int r = 0; + /* + * The SECURE_SYMLINK logic has already removed a + * symlink to a dir if the client wants that. So + * follow the symlink if we're creating a dir. + */ + if (S_ISDIR(a->mode)) + r = stat(a->name, &a->st); + /* + * If it's not a dir (or it's a broken symlink), + * then don't follow it. + */ + if (r != 0 || !S_ISDIR(a->mode)) + r = lstat(a->name, &a->st); + if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } - /* TODO: if it's a symlink... */ - - if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) { + /* + * NO_OVERWRITE_NEWER doesn't apply to directories. + */ + if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) + && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_set_error(&a->archive, 0, "File on disk is not older; skipping."); @@ -883,7 +1011,7 @@ restore_entry(struct archive_write_disk *a) if (unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ @@ -893,7 +1021,7 @@ restore_entry(struct archive_write_disk *a) if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); @@ -915,8 +1043,9 @@ restore_entry(struct archive_write_disk *a) if (en) { /* Everything failed; give up here. */ - archive_set_error(&a->archive, en, "Can't create '%s'", a->name); - return (ARCHIVE_WARN); + archive_set_error(&a->archive, en, "Can't create '%s'", + a->name); + return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ @@ -953,7 +1082,7 @@ create_filesystem_object(struct archive_write_disk *a) * If the hardlink does carry data, let the last * archive entry decide ownership. */ - if (r == 0 && a->filesize == 0) { + if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } if (r == 0 && a->filesize > 0) { @@ -1091,11 +1220,24 @@ _archive_write_close(struct archive *_a) if (p->fixup & TODO_TIMES) { #ifdef HAVE_UTIMES /* {f,l,}utimes() are preferred, when available. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + struct __timeval times[2]; +#else struct timeval times[2]; - times[1].tv_sec = p->mtime; - times[1].tv_usec = p->mtime_nanos / 1000; +#endif times[0].tv_sec = p->atime; times[0].tv_usec = p->atime_nanos / 1000; +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME + /* if it's valid and not mtime, push the birthtime first */ + if (((times[1].tv_sec = p->birthtime) < p->mtime) && + (p->birthtime > 0)) + { + times[1].tv_usec = p->birthtime_nanos / 1000; + utimes(p->name, times); + } +#endif + times[1].tv_sec = p->mtime; + times[1].tv_usec = p->mtime_nanos / 1000; #ifdef HAVE_LUTIMES lutimes(p->name, times); #else @@ -1136,6 +1278,8 @@ _archive_write_finish(struct archive *_a) (a->cleanup_gid)(a->lookup_gid_data); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); + if (a->entry) + archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); @@ -1293,7 +1437,7 @@ check_symlinks(struct archive_write_disk *a) "Could not remove symlink %s", a->name); pn[0] = c; - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } a->pst = NULL; /* @@ -1317,7 +1461,7 @@ check_symlinks(struct archive_write_disk *a) "Cannot remove intervening symlink %s", a->name); pn[0] = c; - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } a->pst = NULL; } else { @@ -1325,7 +1469,7 @@ check_symlinks(struct archive_write_disk *a) "Cannot extract through symlink %s", a->name); pn[0] = c; - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } } } @@ -1335,6 +1479,57 @@ check_symlinks(struct archive_write_disk *a) return (ARCHIVE_OK); } +#if defined(_WIN32) || defined(__CYGWIN__) +/* + * 1. Convert a path separator from '\' to '/' . + * We shouldn't check multi-byte character directly because some + * character-set have been using the '\' character for a part of + * its multibyte character code. + * 2. Replace unusable characters in Windows with underscore('_'). + * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx + */ +static void +cleanup_pathname_win(struct archive_write_disk *a) +{ + wchar_t wc; + char *p; + size_t alen, l; + + alen = 0; + l = 0; + for (p = a->name; *p != '\0'; p++) { + ++alen; + if (*p == '\\') + l = 1; + /* Rewrite the path name if its character is a unusable. */ + if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || + *p == '<' || *p == '>' || *p == '|') + *p = '_'; + } + if (alen == 0 || l == 0) + return; + /* + * Convert path separator. + */ + p = a->name; + while (*p != '\0' && alen) { + l = mbtowc(&wc, p, alen); + if (l == -1) { + while (*p != '\0') { + if (*p == '\\') + *p = '/'; + ++p; + } + break; + } + if (l == 1 && wc == L'\\') + *p = '/'; + p += l; + alen -= l; + } +} +#endif + /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an @@ -1346,7 +1541,6 @@ cleanup_pathname(struct archive_write_disk *a) { char *dest, *src; char separator = '\0'; - int lastdotdot = 0; /* True if last elt copied was '..' */ dest = src = a->name; if (*src == '\0') { @@ -1355,6 +1549,9 @@ cleanup_pathname(struct archive_write_disk *a) return (ARCHIVE_FAILED); } +#if defined(_WIN32) || defined(__CYGWIN__) + cleanup_pathname_win(a); +#endif /* Skip leading '/'. */ if (*src == '/') separator = *src++; @@ -1385,9 +1582,7 @@ cleanup_pathname(struct archive_write_disk *a) "Path contains '..'"); return (ARCHIVE_FAILED); } - lastdotdot = 1; - } else - lastdotdot = 0; + } /* * Note: Under no circumstances do we * remove '..' elements. In @@ -1395,10 +1590,8 @@ cleanup_pathname(struct archive_write_disk *a) * '/foo/../bar/' should create the * 'foo' dir as a side-effect. */ - } else - lastdotdot = 0; - } else - lastdotdot = 0; + } + } /* Copy current element, including leading '/'. */ if (separator) @@ -1417,13 +1610,6 @@ cleanup_pathname(struct archive_write_disk *a) * We've just copied zero or more path elements, not including the * final '/'. */ - if (lastdotdot) { - /* Trailing '..' is always wrong. */ - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Path contains trailing '..'"); - return (ARCHIVE_FAILED); - } if (dest == a->name) { /* * Nothing got copied. The path must have been something @@ -1463,7 +1649,7 @@ create_parent_dir(struct archive_write_disk *a, char *path) * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. - * Otherwise, returns ARCHIVE_WARN. + * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int @@ -1508,18 +1694,18 @@ create_dir(struct archive_write_disk *a, char *path) if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed"); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); @@ -1559,8 +1745,9 @@ create_dir(struct archive_write_disk *a, char *path) if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); - archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); - return (ARCHIVE_WARN); + archive_set_error(&a->archive, errno, "Failed to create dir '%s'", + path); + return (ARCHIVE_FAILED); } /* @@ -1576,12 +1763,17 @@ create_dir(struct archive_write_disk *a, char *path) static int set_ownership(struct archive_write_disk *a) { +#ifndef __CYGWIN__ +/* unfortunately, on win32 there is no 'root' user with uid 0, + so we just have to try the chown and see if it works */ + /* If we know we can't change it, don't bother trying. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%d", a->uid); return (ARCHIVE_WARN); } +#endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ @@ -1621,41 +1813,35 @@ set_ownership(struct archive_write_disk *a) * when they're available. */ static int -set_time(struct archive_write_disk *a) +set_time(int fd, int mode, const char *name, + time_t atime, long atime_nsec, + time_t mtime, long mtime_nsec) { +#if defined(_WIN32) && !defined(__CYGWIN__) + struct __timeval times[2]; +#else struct timeval times[2]; +#endif - times[1].tv_sec = archive_entry_mtime(a->entry); - times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000; - - times[0].tv_sec = archive_entry_atime(a->entry); - times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000; + times[0].tv_sec = atime; + times[0].tv_usec = atime_nsec / 1000; + times[1].tv_sec = mtime; + times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES - if (a->fd >= 0 && futimes(a->fd, times) == 0) { - return (ARCHIVE_OK); - } + if (fd >= 0) + return (futimes(fd, times)); +#else + (void)fd; /* UNUSED */ #endif - #ifdef HAVE_LUTIMES - if (lutimes(a->name, times) != 0) + (void)mode; /* UNUSED */ + return (lutimes(name, times)); #else - if (!S_ISLNK(a->mode) && utimes(a->name, times) != 0) + if (S_ISLNK(mode)) + return (0); + return (utimes(name, times)); #endif - { - archive_set_error(&a->archive, errno, "Can't update time for %s", - a->name); - return (ARCHIVE_WARN); - } - - /* - * Note: POSIX does not provide a portable way to restore ctime. - * (Apart from resetting the system clock, which is distasteful.) - * So, any restoration of ctime will necessarily be OS-specific. - */ - - /* XXX TODO: Can FreeBSD restore ctime? XXX */ - return (ARCHIVE_OK); } #elif defined(HAVE_UTIME) /* @@ -1663,31 +1849,93 @@ set_time(struct archive_write_disk *a) * if utimes() isn't available. */ static int -set_time(struct archive_write_disk *a) +set_time(int fd, int mode, const char *name, + time_t atime, long atime_nsec, + time_t mtime, long mtime_nsec) { struct utimbuf times; - - times.modtime = archive_entry_mtime(a->entry); - times.actime = archive_entry_atime(a->entry); - if (!S_ISLNK(a->mode) && utime(a->name, ×) != 0) { - archive_set_error(&a->archive, errno, - "Can't update time for %s", a->name); - return (ARCHIVE_WARN); - } - return (ARCHIVE_OK); + (void)fd; /* UNUSED */ + (void)name; /* UNUSED */ + (void)atime_nsec; /* UNUSED */ + (void)mtime_nsec; /* UNUSED */ + times.actime = atime; + times.modtime = mtime; + if (S_ISLNK(mode)) + return (ARCHIVE_OK); + return (utime(name, ×)); } #else -/* This platform doesn't give us a way to restore the time. */ static int -set_time(struct archive_write_disk *a) +set_time(int fd, int mode, const char *name, + time_t atime, long atime_nsec, + time_t mtime, long mtime_nsec) { - (void)a; /* UNUSED */ - archive_set_error(&a->archive, errno, - "Can't update time for %s", a->name); return (ARCHIVE_WARN); } #endif +static int +set_times(struct archive_write_disk *a) +{ + time_t atime = a->start_time, mtime = a->start_time; + long atime_nsec = 0, mtime_nsec = 0; + + /* If no time was provided, we're done. */ + if (!archive_entry_atime_is_set(a->entry) +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + && !archive_entry_birthtime_is_set(a->entry) +#endif + && !archive_entry_mtime_is_set(a->entry)) + return (ARCHIVE_OK); + + /* If no atime was specified, use start time instead. */ + /* In theory, it would be marginally more correct to use + * time(NULL) here, but that would cost us an extra syscall + * for little gain. */ + if (archive_entry_atime_is_set(a->entry)) { + atime = archive_entry_atime(a->entry); + atime_nsec = archive_entry_atime_nsec(a->entry); + } + + /* + * If you have struct stat.st_birthtime, we assume BSD birthtime + * semantics, in which {f,l,}utimes() updates birthtime to earliest + * mtime. So we set the time twice, first using the birthtime, + * then using the mtime. + */ +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + /* If birthtime is set, flush that through to disk first. */ + if (archive_entry_birthtime_is_set(a->entry)) + if (set_time(a->fd, a->mode, a->name, atime, atime_nsec, + archive_entry_birthtime(a->entry), + archive_entry_birthtime_nsec(a->entry))) { + archive_set_error(&a->archive, errno, + "Can't update time for %s", + a->name); + return (ARCHIVE_WARN); + } +#endif + + if (archive_entry_mtime_is_set(a->entry)) { + mtime = archive_entry_mtime(a->entry); + mtime_nsec = archive_entry_mtime_nsec(a->entry); + } + if (set_time(a->fd, a->mode, a->name, + atime, atime_nsec, mtime, mtime_nsec)) { + archive_set_error(&a->archive, errno, + "Can't update time for %s", + a->name); + return (ARCHIVE_WARN); + } + + /* + * Note: POSIX does not provide a portable way to restore ctime. + * (Apart from resetting the system clock, which is distasteful.) + * So, any restoration of ctime will necessarily be OS-specific. + */ + + return (ARCHIVE_OK); +} static int set_mode(struct archive_write_disk *a, int mode) @@ -1706,6 +1954,7 @@ set_mode(struct archive_write_disk *a, int mode) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you @@ -1718,16 +1967,19 @@ set_mode(struct archive_write_disk *a, int mode) "Can't restore SGID bit"); r = ARCHIVE_WARN; } +#endif } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } +#endif } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; @@ -1739,11 +1991,13 @@ set_mode(struct archive_write_disk *a, int mode) */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; +#if !defined(_WIN32) || defined(__CYGWIN__) if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } +#endif } a->todo &= ~TODO_SUID_CHECK; } @@ -1861,7 +2115,10 @@ set_fflags(struct archive_write_disk *a) } -#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux) +#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) +/* + * BSD reads flags using stat() and sets them with one of {f,l,}chflags() + */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) @@ -1910,11 +2167,9 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, return (ARCHIVE_WARN); } -#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) - +#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) /* - * Linux has flags too, but uses ioctl() to access them instead of - * having a separate chflags() system call. + * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, @@ -1982,7 +2237,7 @@ cleanup: return (ret); } -#else /* Not HAVE_CHFLAGS && Not __linux */ +#else /* * Of course, some systems have neither BSD chflags() nor Linux' flags @@ -2171,6 +2426,71 @@ set_xattrs(struct archive_write_disk *a) } return (ret); } +#elif HAVE_EXTATTR_SET_FILE +/* + * Restore extended attributes - FreeBSD implementation + */ +static int +set_xattrs(struct archive_write_disk *a) +{ + struct archive_entry *entry = a->entry; + static int warning_done = 0; + int ret = ARCHIVE_OK; + int i = archive_entry_xattr_reset(entry); + + while (i--) { + const char *name; + const void *value; + size_t size; + archive_entry_xattr_next(entry, &name, &value, &size); + if (name != NULL) { + int e; + int namespace; + + if (strncmp(name, "user.", 5) == 0) { + /* "user." attributes go to user namespace */ + name += 5; + namespace = EXTATTR_NAMESPACE_USER; + } else { + /* Warn about other extended attributes. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Can't restore extended attribute ``%s''", + name); + ret = ARCHIVE_WARN; + continue; + } + errno = 0; +#if HAVE_EXTATTR_SET_FD + if (a->fd >= 0) + e = extattr_set_fd(a->fd, namespace, name, value, size); + else +#endif + /* TODO: should we use extattr_set_link() instead? */ + { + e = extattr_set_file(archive_entry_pathname(entry), + namespace, name, value, size); + } + if (e != (int)size) { + if (errno == ENOTSUP) { + if (!warning_done) { + warning_done = 1; + archive_set_error(&a->archive, errno, + "Cannot restore extended " + "attributes on this file " + "system"); + } + } else { + archive_set_error(&a->archive, errno, + "Failed to set extended attribute"); + } + + ret = ARCHIVE_WARN; + } + } + } + return (ret); +} #else /* * Restore extended attributes - stub implementation for unsupported systems @@ -2233,19 +2553,25 @@ older(struct stat *st, struct archive_entry *entry) /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); - /* Definitely younger. */ - if (st->st_mtimespec.tv_nsec > archive_entry_mtime_nsec(entry)) - return (0); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); - /* Definitely older. */ - if (st->st_mtim.tv_nsec > archive_entry_mtime_nsec(entry)) - return (0); +#elif HAVE_STRUCT_STAT_ST_MTIME_N + /* older. */ + if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) + return (1); +#elif HAVE_STRUCT_STAT_ST_UMTIME + /* older. */ + if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) + return (1); +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC + /* older. */ + if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) + return (1); #else /* This system doesn't have high-res timestamps. */ #endif - /* Same age, so not older. */ + /* Same age or newer, so not older. */ return (0); } diff --git a/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c b/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c index 427b87615a..55373b8cf3 100644 --- a/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c +++ b/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c @@ -118,12 +118,34 @@ lookup_gid(void *private_data, const char *gname, gid_t gid) b->hash = h; #if HAVE_GRP_H { - struct group *grent = getgrnam(gname); - if (grent != NULL) - gid = grent->gr_gid; + char _buffer[128]; + size_t bufsize = 128; + char *buffer = _buffer; + struct group grent, *result; + int r; + + for (;;) { + r = getgrnam_r(gname, &grent, buffer, bufsize, &result); + if (r == 0) + break; + if (r != ERANGE) + break; + bufsize *= 2; + if (buffer != _buffer) + free(buffer); + buffer = malloc(bufsize); + if (buffer == NULL) + break; + } + if (result != NULL) + gid = result->gr_gid; + if (buffer != _buffer) + free(buffer); } -#elif _WIN32 +#elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a gname->gid lookup for Windows. */ +#else + #error No way to perform gid lookups on this platform #endif b->id = gid; @@ -155,12 +177,34 @@ lookup_uid(void *private_data, const char *uname, uid_t uid) b->hash = h; #if HAVE_PWD_H { - struct passwd *pwent = getpwnam(uname); - if (pwent != NULL) - uid = pwent->pw_uid; + char _buffer[128]; + size_t bufsize = 128; + char *buffer = _buffer; + struct passwd pwent, *result; + int r; + + for (;;) { + r = getpwnam_r(uname, &pwent, buffer, bufsize, &result); + if (r == 0) + break; + if (r != ERANGE) + break; + bufsize *= 2; + if (buffer != _buffer) + free(buffer); + buffer = malloc(bufsize); + if (buffer == NULL) + break; + } + if (result != NULL) + uid = result->pw_uid; + if (buffer != _buffer) + free(buffer); } -#elif _WIN32 +#elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a uname->uid lookup for Windows. */ +#else + #error No way to look up uids on this platform #endif b->id = uid; diff --git a/contrib/libarchive/libarchive/archive_write_open_filename.c b/contrib/libarchive/libarchive/archive_write_open_filename.c index 72eeb54053..5536eb0410 100644 --- a/contrib/libarchive/libarchive/archive_write_open_filename.c +++ b/contrib/libarchive/libarchive/archive_write_open_filename.c @@ -71,24 +71,18 @@ archive_write_open_filename(struct archive *a, const char *filename) { struct write_file_data *mine; - if (filename == NULL || filename[0] == '\0') { - mine = (struct write_file_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->filename[0] = '\0'; /* Record that we're using stdout. */ - } else { - mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - strcpy(mine->filename, filename); + if (filename == NULL || filename[0] == '\0') + return (archive_write_open_fd(a, 1)); + + mine = (struct write_file_data *)malloc(sizeof(*mine) + strlen(filename)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); } + strcpy(mine->filename, filename); mine->fd = -1; return (archive_write_open(a, mine, - file_open, file_write, file_close)); + file_open, file_write, file_close)); } static int @@ -104,21 +98,11 @@ file_open(struct archive *a, void *client_data) /* * Open the file. */ - if (mine->filename[0] != '\0') { - mine->fd = open(mine->filename, flags, 0666); - if (mine->fd < 0) { - archive_set_error(a, errno, "Failed to open '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - } else { - /* - * NULL filename is stdout. - */ - mine->fd = 1; - /* By default, pad archive when writing to stdout. */ - if (archive_write_get_bytes_in_last_block(a) < 0) - archive_write_set_bytes_in_last_block(a, 0); + mine->fd = open(mine->filename, flags, 0666); + if (mine->fd < 0) { + archive_set_error(a, errno, "Failed to open '%s'", + mine->filename); + return (ARCHIVE_FATAL); } if (fstat(mine->fd, &st) != 0) { @@ -172,8 +156,7 @@ file_close(struct archive *a, void *client_data) struct write_file_data *mine = (struct write_file_data *)client_data; (void)a; /* UNUSED */ - if (mine->filename[0] != '\0') - close(mine->fd); + close(mine->fd); free(mine); return (ARCHIVE_OK); } diff --git a/contrib/libarchive/libarchive/archive_write_private.h b/contrib/libarchive/libarchive/archive_write_private.h index 55c24ad96c..58a96757d4 100644 --- a/contrib/libarchive/libarchive/archive_write_private.h +++ b/contrib/libarchive/libarchive/archive_write_private.h @@ -77,6 +77,8 @@ struct archive_write { void *data; void *config; int (*init)(struct archive_write *); + int (*options)(struct archive_write *, + const char *key, const char *value); int (*finish)(struct archive_write *); int (*write)(struct archive_write *, const void *, size_t); } compressor; @@ -86,7 +88,10 @@ struct archive_write { * initialized by archive_write_set_format_XXX() calls. */ void *format_data; + const char *format_name; int (*format_init)(struct archive_write *); + int (*format_options)(struct archive_write *, + const char *key, const char *value); int (*format_finish)(struct archive_write *); int (*format_destroy)(struct archive_write *); int (*format_finish_entry)(struct archive_write *); diff --git a/contrib/libarchive/libarchive/archive_write_set_compression_bzip2.c b/contrib/libarchive/libarchive/archive_write_set_compression_bzip2.c index 272ae26e52..17f6a0a4d3 100644 --- a/contrib/libarchive/libarchive/archive_write_set_compression_bzip2.c +++ b/contrib/libarchive/libarchive/archive_write_set_compression_bzip2.c @@ -25,9 +25,6 @@ #include "archive_platform.h" -/* Don't compile this if we don't have bzlib. */ -#if HAVE_BZLIB_H - __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.13 2007/12/30 04:58:21 kientzle Exp $"); #ifdef HAVE_ERRNO_H @@ -48,6 +45,17 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1 #include "archive_private.h" #include "archive_write_private.h" +#ifndef HAVE_BZLIB_H +int +archive_write_set_compression_bzip2(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "bzip2 compression not supported on this platform"); + return (ARCHIVE_FATAL); +} +#else +/* Don't compile this if we don't have bzlib. */ + struct private_data { bz_stream stream; int64_t total_in; @@ -55,6 +63,9 @@ struct private_data { size_t compressed_buffer_size; }; +struct private_config { + int compression_level; +}; /* * Yuck. bzlib.h is not const-correct, so I need this one bit @@ -65,6 +76,8 @@ struct private_data { static int archive_compressor_bzip2_finish(struct archive_write *); static int archive_compressor_bzip2_init(struct archive_write *); +static int archive_compressor_bzip2_options(struct archive_write *, + const char *, const char *); static int archive_compressor_bzip2_write(struct archive_write *, const void *, size_t); static int drive_compressor(struct archive_write *, struct private_data *, @@ -77,9 +90,21 @@ int archive_write_set_compression_bzip2(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; + struct private_config *config; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); + config = malloc(sizeof(*config)); + if (config == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + a->compressor.config = config; + a->compressor.finish = archive_compressor_bzip2_finish; + config->compression_level = 9; /* default */ a->compressor.init = &archive_compressor_bzip2_init; + a->compressor.options = &archive_compressor_bzip2_options; + a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; + a->archive.compression_name = "bzip2"; return (ARCHIVE_OK); } @@ -91,10 +116,9 @@ archive_compressor_bzip2_init(struct archive_write *a) { int ret; struct private_data *state; + struct private_config *config; - a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; - a->archive.compression_name = "bzip2"; - + config = (struct private_config *)a->compressor.config; if (a->client_opener != NULL) { ret = (a->client_opener)(&a->archive, a->client_data); if (ret != 0) @@ -122,10 +146,10 @@ archive_compressor_bzip2_init(struct archive_write *a) state->stream.next_out = state->compressed; state->stream.avail_out = state->compressed_buffer_size; a->compressor.write = archive_compressor_bzip2_write; - a->compressor.finish = archive_compressor_bzip2_finish; /* Initialize compression library */ - ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30); + ret = BZ2_bzCompressInit(&(state->stream), + config->compression_level, 0, 30); if (ret == BZ_OK) { a->compressor.data = state; return (ARCHIVE_OK); @@ -160,6 +184,32 @@ archive_compressor_bzip2_init(struct archive_write *a) } +/* + * Set write options. + */ +static int +archive_compressor_bzip2_options(struct archive_write *a, const char *key, + const char *value) +{ + struct private_config *config; + + config = (struct private_config *)a->compressor.config; + if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + config->compression_level = value[0] - '0'; + /* Make '0' be a synonym for '1'. */ + /* This way, bzip2 compressor supports the same 0..9 + * range of levels as gzip. */ + if (config->compression_level < 1) + config->compression_level = 1; + return (ARCHIVE_OK); + } + + return (ARCHIVE_WARN); +} + /* * Write data to the compressed stream. * @@ -205,83 +255,88 @@ archive_compressor_bzip2_finish(struct archive_write *a) ssize_t bytes_written; unsigned tocopy; - state = (struct private_data *)a->compressor.data; ret = ARCHIVE_OK; - if (a->client_writer == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered?\n" - "This is probably an internal programming error."); - ret = ARCHIVE_FATAL; - goto cleanup; - } + state = (struct private_data *)a->compressor.data; + if (state != NULL) { + if (a->client_writer == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered?\n" + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } - /* By default, always pad the uncompressed data. */ - if (a->pad_uncompressed) { - tocopy = a->bytes_per_block - - (state->total_in % a->bytes_per_block); - while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { - SET_NEXT_IN(state, a->nulls); - state->stream.avail_in = tocopy < a->null_length ? - tocopy : a->null_length; - state->total_in += state->stream.avail_in; - tocopy -= state->stream.avail_in; - ret = drive_compressor(a, state, 0); - if (ret != ARCHIVE_OK) - goto cleanup; + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + SET_NEXT_IN(state, a->nulls); + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } } - } - /* Finish compression cycle. */ - if ((ret = drive_compressor(a, state, 1))) - goto cleanup; - - /* Optionally, pad the final compressed block. */ - block_length = state->stream.next_out - state->compressed; - - - /* Tricky calculation to determine size of last block. */ - target_block_length = block_length; - if (a->bytes_in_last_block <= 0) - /* Default or Zero: pad to full block */ - target_block_length = a->bytes_per_block; - else - /* Round length to next multiple of bytes_in_last_block. */ - target_block_length = a->bytes_in_last_block * - ( (block_length + a->bytes_in_last_block - 1) / - a->bytes_in_last_block); - if (target_block_length > a->bytes_per_block) - target_block_length = a->bytes_per_block; - if (block_length < target_block_length) { - memset(state->stream.next_out, 0, - target_block_length - block_length); - block_length = target_block_length; - } + /* Finish compression cycle. */ + if ((ret = drive_compressor(a, state, 1))) + goto cleanup; + + /* Optionally, pad the final compressed block. */ + block_length = state->stream.next_out - state->compressed; + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } - /* Write the last block */ - bytes_written = (a->client_writer)(&a->archive, a->client_data, - state->compressed, block_length); + /* Write the last block */ + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, block_length); - /* TODO: Handle short write of final block. */ - if (bytes_written <= 0) - ret = ARCHIVE_FATAL; - else { - a->archive.raw_position += ret; - ret = ARCHIVE_OK; - } + /* TODO: Handle short write of final block. */ + if (bytes_written <= 0) + ret = ARCHIVE_FATAL; + else { + a->archive.raw_position += ret; + ret = ARCHIVE_OK; + } - /* Cleanup: shut down compressor, release memory, etc. */ + /* Cleanup: shut down compressor, release memory, etc. */ cleanup: - switch (BZ2_bzCompressEnd(&(state->stream))) { - case BZ_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Failed to clean up compressor"); - ret = ARCHIVE_FATAL; - } + switch (BZ2_bzCompressEnd(&(state->stream))) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } - free(state->compressed); - free(state); + free(state->compressed); + free(state); + } + /* Free configuration data even if we were never fully initialized. */ + free(a->compressor.config); + a->compressor.config = NULL; return (ret); } diff --git a/contrib/libarchive/libarchive/archive_write_set_compression_gzip.c b/contrib/libarchive/libarchive/archive_write_set_compression_gzip.c index 18abbdf67b..a8b1c33360 100644 --- a/contrib/libarchive/libarchive/archive_write_set_compression_gzip.c +++ b/contrib/libarchive/libarchive/archive_write_set_compression_gzip.c @@ -25,9 +25,6 @@ #include "archive_platform.h" -/* Don't compile this if we don't have zlib. */ -#if HAVE_ZLIB_H - __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.16 2008/02/21 03:21:50 kientzle Exp $"); #ifdef HAVE_ERRNO_H @@ -48,6 +45,17 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1. #include "archive_private.h" #include "archive_write_private.h" +#ifndef HAVE_ZLIB_H +int +archive_write_set_compression_gzip(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "gzip compression not supported on this platform"); + return (ARCHIVE_FATAL); +} +#else +/* Don't compile this if we don't have zlib. */ + struct private_data { z_stream stream; int64_t total_in; @@ -56,6 +64,10 @@ struct private_data { unsigned long crc; }; +struct private_config { + int compression_level; +}; + /* * Yuck. zlib.h is not const-correct, so I need this one bit @@ -66,6 +78,8 @@ struct private_data { static int archive_compressor_gzip_finish(struct archive_write *); static int archive_compressor_gzip_init(struct archive_write *); +static int archive_compressor_gzip_options(struct archive_write *, + const char *, const char *); static int archive_compressor_gzip_write(struct archive_write *, const void *, size_t); static int drive_compressor(struct archive_write *, struct private_data *, @@ -79,9 +93,19 @@ int archive_write_set_compression_gzip(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; + struct private_config *config; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); + config = malloc(sizeof(*config)); + if (config == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + a->compressor.config = config; + a->compressor.finish = &archive_compressor_gzip_finish; + config->compression_level = Z_DEFAULT_COMPRESSION; a->compressor.init = &archive_compressor_gzip_init; + a->compressor.options = &archive_compressor_gzip_options; a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; a->archive.compression_name = "gzip"; return (ARCHIVE_OK); @@ -95,10 +119,10 @@ archive_compressor_gzip_init(struct archive_write *a) { int ret; struct private_data *state; + struct private_config *config; time_t t; - a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; - a->archive.compression_name = "gzip"; + config = (struct private_config *)a->compressor.config; if (a->client_opener != NULL) { ret = (a->client_opener)(&a->archive, a->client_data); @@ -163,11 +187,10 @@ archive_compressor_gzip_init(struct archive_write *a) state->stream.avail_out -= 10; a->compressor.write = archive_compressor_gzip_write; - a->compressor.finish = archive_compressor_gzip_finish; /* Initialize compression library. */ ret = deflateInit2(&(state->stream), - Z_DEFAULT_COMPRESSION, + config->compression_level, Z_DEFLATED, -15 /* < 0 to suppress zlib header */, 8, @@ -205,6 +228,27 @@ archive_compressor_gzip_init(struct archive_write *a) return (ARCHIVE_FATAL); } +/* + * Set write options. + */ +static int +archive_compressor_gzip_options(struct archive_write *a, const char *key, + const char *value) +{ + struct private_config *config; + + config = (struct private_config *)a->compressor.config; + if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + config->compression_level = value[0] - '0'; + return (ARCHIVE_OK); + } + + return (ARCHIVE_WARN); +} + /* * Write data to the compressed stream. */ @@ -237,7 +281,6 @@ archive_compressor_gzip_write(struct archive_write *a, const void *buff, return (ARCHIVE_OK); } - /* * Finish the compression... */ @@ -252,113 +295,118 @@ archive_compressor_gzip_finish(struct archive_write *a) state = (struct private_data *)a->compressor.data; ret = 0; - if (a->client_writer == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "No write callback is registered? " - "This is probably an internal programming error."); - ret = ARCHIVE_FATAL; - goto cleanup; - } + if (state != NULL) { + if (a->client_writer == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + SET_NEXT_IN(state, a->nulls); + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->crc = crc32(state->crc, a->nulls, + state->stream.avail_in); + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } + } + + /* Finish compression cycle */ + if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) + goto cleanup; - /* By default, always pad the uncompressed data. */ - if (a->pad_uncompressed) { - tocopy = a->bytes_per_block - - (state->total_in % a->bytes_per_block); - while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { - SET_NEXT_IN(state, a->nulls); - state->stream.avail_in = tocopy < a->null_length ? - tocopy : a->null_length; - state->crc = crc32(state->crc, a->nulls, - state->stream.avail_in); - state->total_in += state->stream.avail_in; - tocopy -= state->stream.avail_in; - ret = drive_compressor(a, state, 0); - if (ret != ARCHIVE_OK) + /* Build trailer: 4-byte CRC and 4-byte length. */ + trailer[0] = (state->crc)&0xff; + trailer[1] = (state->crc >> 8)&0xff; + trailer[2] = (state->crc >> 16)&0xff; + trailer[3] = (state->crc >> 24)&0xff; + trailer[4] = (state->total_in)&0xff; + trailer[5] = (state->total_in >> 8)&0xff; + trailer[6] = (state->total_in >> 16)&0xff; + trailer[7] = (state->total_in >> 24)&0xff; + + /* Add trailer to current block. */ + tocopy = 8; + if (tocopy > state->stream.avail_out) + tocopy = state->stream.avail_out; + memcpy(state->stream.next_out, trailer, tocopy); + state->stream.next_out += tocopy; + state->stream.avail_out -= tocopy; + + /* If it overflowed, flush and start a new block. */ + if (tocopy < 8) { + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, state->compressed_buffer_size); + if (bytes_written <= 0) { + ret = ARCHIVE_FATAL; goto cleanup; + } + a->archive.raw_position += bytes_written; + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); + state->stream.next_out += 8-tocopy; + state->stream.avail_out -= 8-tocopy; } - } - /* Finish compression cycle */ - if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) - goto cleanup; - - /* Build trailer: 4-byte CRC and 4-byte length. */ - trailer[0] = (state->crc)&0xff; - trailer[1] = (state->crc >> 8)&0xff; - trailer[2] = (state->crc >> 16)&0xff; - trailer[3] = (state->crc >> 24)&0xff; - trailer[4] = (state->total_in)&0xff; - trailer[5] = (state->total_in >> 8)&0xff; - trailer[6] = (state->total_in >> 16)&0xff; - trailer[7] = (state->total_in >> 24)&0xff; - - /* Add trailer to current block. */ - tocopy = 8; - if (tocopy > state->stream.avail_out) - tocopy = state->stream.avail_out; - memcpy(state->stream.next_out, trailer, tocopy); - state->stream.next_out += tocopy; - state->stream.avail_out -= tocopy; - - /* If it overflowed, flush and start a new block. */ - if (tocopy < 8) { + /* Optionally, pad the final compressed block. */ + block_length = state->stream.next_out - state->compressed; + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } + + /* Write the last block */ bytes_written = (a->client_writer)(&a->archive, a->client_data, - state->compressed, state->compressed_buffer_size); + state->compressed, block_length); if (bytes_written <= 0) { ret = ARCHIVE_FATAL; goto cleanup; } a->archive.raw_position += bytes_written; - state->stream.next_out = state->compressed; - state->stream.avail_out = state->compressed_buffer_size; - memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); - state->stream.next_out += 8-tocopy; - state->stream.avail_out -= 8-tocopy; - } - /* Optionally, pad the final compressed block. */ - block_length = state->stream.next_out - state->compressed; - - - /* Tricky calculation to determine size of last block. */ - target_block_length = block_length; - if (a->bytes_in_last_block <= 0) - /* Default or Zero: pad to full block */ - target_block_length = a->bytes_per_block; - else - /* Round length to next multiple of bytes_in_last_block. */ - target_block_length = a->bytes_in_last_block * - ( (block_length + a->bytes_in_last_block - 1) / - a->bytes_in_last_block); - if (target_block_length > a->bytes_per_block) - target_block_length = a->bytes_per_block; - if (block_length < target_block_length) { - memset(state->stream.next_out, 0, - target_block_length - block_length); - block_length = target_block_length; - } - - /* Write the last block */ - bytes_written = (a->client_writer)(&a->archive, a->client_data, - state->compressed, block_length); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->archive.raw_position += bytes_written; - - /* Cleanup: shut down compressor, release memory, etc. */ -cleanup: - switch (deflateEnd(&(state->stream))) { - case Z_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up compressor"); - ret = ARCHIVE_FATAL; + /* Cleanup: shut down compressor, release memory, etc. */ + cleanup: + switch (deflateEnd(&(state->stream))) { + case Z_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } + free(state->compressed); + free(state); } - free(state->compressed); - free(state); + /* Clean up config area even if we never initialized. */ + free(a->compressor.config); + a->compressor.config = NULL; return (ret); } diff --git a/contrib/libarchive/libarchive/archive_write_set_compression_program.c b/contrib/libarchive/libarchive/archive_write_set_compression_program.c index 69c5d42863..0056ed1361 100644 --- a/contrib/libarchive/libarchive/archive_write_set_compression_program.c +++ b/contrib/libarchive/libarchive/archive_write_set_compression_program.c @@ -28,8 +28,8 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.3 2008/06/15 10:45:57 kientzle Exp $"); /* This capability is only available on POSIX systems. */ -#if !defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK)) +#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ + !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) #include "archive.h" /* diff --git a/contrib/libarchive/libarchive/archive_write_set_compression_xz.c b/contrib/libarchive/libarchive/archive_write_set_compression_xz.c new file mode 100644 index 0000000000..8396252527 --- /dev/null +++ b/contrib/libarchive/libarchive/archive_write_set_compression_xz.c @@ -0,0 +1,439 @@ +/*- + * Copyright (c) 2009 Michihiro NAKAJIMA + * 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. + * 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$"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_LZMA_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#ifndef HAVE_LZMA_H +int +archive_write_set_compression_xz(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "xz compression not supported on this platform"); + return (ARCHIVE_FATAL); +} + +int +archive_write_set_compression_lzma(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "lzma compression not supported on this platform"); + return (ARCHIVE_FATAL); +} +#else +/* Don't compile this if we don't have liblzma. */ + +struct private_data { + lzma_stream stream; + lzma_filter lzmafilters[2]; + lzma_options_lzma lzma_opt; + int64_t total_in; + unsigned char *compressed; + size_t compressed_buffer_size; +}; + +struct private_config { + int compression_level; +}; + +static int archive_compressor_xz_init(struct archive_write *); +static int archive_compressor_xz_options(struct archive_write *, + const char *, const char *); +static int archive_compressor_xz_finish(struct archive_write *); +static int archive_compressor_xz_write(struct archive_write *, + const void *, size_t); +static int drive_compressor(struct archive_write *, struct private_data *, + int finishing); + + +/* + * Allocate, initialize and return a archive object. + */ +int +archive_write_set_compression_xz(struct archive *_a) +{ + struct private_config *config; + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_xz"); + config = calloc(1, sizeof(*config)); + if (config == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + a->compressor.config = config; + a->compressor.finish = archive_compressor_xz_finish; + config->compression_level = LZMA_PRESET_DEFAULT; + a->compressor.init = &archive_compressor_xz_init; + a->compressor.options = &archive_compressor_xz_options; + a->archive.compression_code = ARCHIVE_COMPRESSION_XZ; + a->archive.compression_name = "xz"; + return (ARCHIVE_OK); +} + +/* LZMA is handled identically, we just need a different compression + * code set. (The liblzma setup looks at the code to determine + * the one place that XZ and LZMA require different handling.) */ +int +archive_write_set_compression_lzma(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = archive_write_set_compression_xz(_a); + if (r != ARCHIVE_OK) + return (r); + a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA; + a->archive.compression_name = "lzma"; + return (ARCHIVE_OK); +} + +static int +archive_compressor_xz_init_stream(struct archive_write *a, + struct private_data *state) +{ + int ret; + + state->stream = (lzma_stream)LZMA_STREAM_INIT; + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ) + ret = lzma_stream_encoder(&(state->stream), + state->lzmafilters, LZMA_CHECK_CRC64); + else + ret = lzma_alone_encoder(&(state->stream), &state->lzma_opt); + if (ret == LZMA_OK) + return (ARCHIVE_OK); + + switch (ret) { + case LZMA_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "Cannot allocate memory"); + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "It's a bug in liblzma"); + break; + } + return (ARCHIVE_FATAL); +} + +/* + * Setup callback. + */ +static int +archive_compressor_xz_init(struct archive_write *a) +{ + int ret; + struct private_data *state; + struct private_config *config; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != ARCHIVE_OK) + return (ret); + } + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + config = a->compressor.config; + + /* + * See comment above. We should set compressed_buffer_size to + * max(bytes_per_block, 65536), but the code can't handle that yet. + */ + state->compressed_buffer_size = a->bytes_per_block; + state->compressed = (unsigned char *)malloc(state->compressed_buffer_size); + if (state->compressed == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression buffer"); + free(state); + return (ARCHIVE_FATAL); + } + a->compressor.write = archive_compressor_xz_write; + + /* Initialize compression library. */ + if (lzma_lzma_preset(&state->lzma_opt, config->compression_level)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library"); + free(state->compressed); + free(state); + } + state->lzmafilters[0].id = LZMA_FILTER_LZMA2; + state->lzmafilters[0].options = &state->lzma_opt; + state->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ + ret = archive_compressor_xz_init_stream(a, state); + if (ret == LZMA_OK) { + a->compressor.data = state; + return (0); + } + /* Library setup failed: clean up. */ + free(state->compressed); + free(state); + + return (ARCHIVE_FATAL); +} + +/* + * Set write options. + */ +static int +archive_compressor_xz_options(struct archive_write *a, const char *key, + const char *value) +{ + struct private_config *config; + + config = (struct private_config *)a->compressor.config; + if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + config->compression_level = value[0] - '0'; + if (config->compression_level > 6) + config->compression_level = 6; + return (ARCHIVE_OK); + } + + return (ARCHIVE_WARN); +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_xz_write(struct archive_write *a, const void *buff, + size_t length) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)a->compressor.data; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + /* Update statistics */ + state->total_in += length; + + /* Compress input data to output buffer */ + state->stream.next_in = buff; + state->stream.avail_in = length; + if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK) + return (ret); + + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression... + */ +static int +archive_compressor_xz_finish(struct archive_write *a) +{ + ssize_t block_length, target_block_length, bytes_written; + int ret; + struct private_data *state; + unsigned tocopy; + + ret = ARCHIVE_OK; + state = (struct private_data *)a->compressor.data; + if (state != NULL) { + if (a->client_writer == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + state->stream.next_in = a->nulls; + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } + } + + /* Finish compression cycle */ + if (((ret = drive_compressor(a, state, 1))) != ARCHIVE_OK) + goto cleanup; + + /* Optionally, pad the final compressed block. */ + block_length = state->stream.next_out - state->compressed; + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } + + /* Write the last block */ + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, block_length); + if (bytes_written <= 0) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + a->archive.raw_position += bytes_written; + + /* Cleanup: shut down compressor, release memory, etc. */ + cleanup: + lzma_end(&(state->stream)); + free(state->compressed); + free(state); + } + free(a->compressor.config); + a->compressor.config = NULL; + return (ret); +} + +/* + * Utility function to push input data through compressor, + * writing full output blocks as necessary. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write *a, struct private_data *state, int finishing) +{ + ssize_t bytes_written; + int ret; + + for (;;) { + if (state->stream.avail_out == 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->compressed, + state->compressed_buffer_size); + if (bytes_written <= 0) { + /* TODO: Handle this write failure */ + return (ARCHIVE_FATAL); + } else if ((size_t)bytes_written < state->compressed_buffer_size) { + /* Short write: Move remaining to + * front of block and keep filling */ + memmove(state->compressed, + state->compressed + bytes_written, + state->compressed_buffer_size - bytes_written); + } + a->archive.raw_position += bytes_written; + state->stream.next_out + = state->compressed + + state->compressed_buffer_size - bytes_written; + state->stream.avail_out = bytes_written; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + + ret = lzma_code(&(state->stream), + finishing ? LZMA_FINISH : LZMA_RUN ); + + switch (ret) { + case LZMA_OK: + /* In non-finishing case, check if compressor + * consumed everything */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + /* In finishing case, this return always means + * there's more work */ + break; + case LZMA_STREAM_END: + /* This return can only occur in finishing case. */ + if (finishing) + return (ARCHIVE_OK); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma compression data error"); + return (ARCHIVE_FATAL); + case LZMA_MEMLIMIT_ERROR: + archive_set_error(&a->archive, ENOMEM, + "lzma compression error: " + "%ju MiB would have been needed", + (lzma_memusage(&(state->stream)) + 1024 * 1024 -1) + / (1024 * 1024)); + return (ARCHIVE_FATAL); + default: + /* Any other return value indicates an error. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma compression failed:" + " lzma_code() call returned status %d", + ret); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_LZMA_H */ diff --git a/contrib/libarchive/libarchive/archive_write_set_format.c b/contrib/libarchive/libarchive/archive_write_set_format.c index e5f89211a2..4f561c4376 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format.c +++ b/contrib/libarchive/libarchive/archive_write_set_format.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.5 2007/06/22 05:47:00 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.6 2008/08/31 07:21:46 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -44,6 +44,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio }, { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc }, { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio }, + { ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree }, { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump }, diff --git a/contrib/libarchive/libarchive/archive_write_set_format_ar.c b/contrib/libarchive/libarchive/archive_write_set_format_ar.c index 7176e6e2f4..4ed2c67917 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_ar.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_ar.c @@ -26,7 +26,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.7 2008/05/26 17:00:23 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.8 2008/08/10 02:06:28 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -125,6 +125,7 @@ archive_write_set_format_ar(struct archive_write *a) memset(ar, 0, sizeof(*ar)); a->format_data = ar; + a->format_name = "ar"; a->format_write_header = archive_write_ar_header; a->format_write_data = archive_write_ar_data; a->format_finish = archive_write_ar_finish; @@ -389,6 +390,9 @@ archive_write_ar_destroy(struct archive_write *a) ar = (struct ar_w *)a->format_data; + if (ar == NULL) + return (ARCHIVE_OK); + if (ar->has_strtab > 0) { free(ar->strtab); ar->strtab = NULL; diff --git a/contrib/libarchive/libarchive/archive_write_set_format_by_name.c b/contrib/libarchive/libarchive/archive_write_set_format_by_name.c index 0d7cae4867..6b6ff4541a 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_by_name.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_by_name.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.7 2007/06/22 05:47:00 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.9 2008/09/01 02:50:53 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -44,11 +44,12 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.7 static struct { const char *name; int (*setter)(struct archive *); } names[] = { - { "arbsd", archive_write_set_format_ar_bsd }, { "ar", archive_write_set_format_ar_bsd }, + { "arbsd", archive_write_set_format_ar_bsd }, { "argnu", archive_write_set_format_ar_svr4 }, { "arsvr4", archive_write_set_format_ar_svr4 }, { "cpio", archive_write_set_format_cpio }, + { "mtree", archive_write_set_format_mtree }, { "newc", archive_write_set_format_cpio_newc }, { "odc", archive_write_set_format_cpio }, { "pax", archive_write_set_format_pax }, diff --git a/contrib/libarchive/libarchive/archive_write_set_format_cpio.c b/contrib/libarchive/libarchive/archive_write_set_format_cpio.c index 61042999ef..3026b6c84f 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_cpio.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_cpio.c @@ -92,6 +92,7 @@ archive_write_set_format_cpio(struct archive *_a) a->format_data = cpio; a->pad_uncompressed = 1; + a->format_name = "cpio"; a->format_write_header = archive_write_cpio_header; a->format_write_data = archive_write_cpio_data; a->format_finish_entry = archive_write_cpio_finish_entry; diff --git a/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c b/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c index b5a2a02841..a826877ae3 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c @@ -97,6 +97,7 @@ archive_write_set_format_cpio_newc(struct archive *_a) a->format_data = cpio; a->pad_uncompressed = 1; + a->format_name = "cpio"; a->format_write_header = archive_write_newc_header; a->format_write_data = archive_write_newc_data; a->format_finish_entry = archive_write_newc_finish_entry; diff --git a/contrib/libarchive/libarchive/archive_write_set_format_mtree.c b/contrib/libarchive/libarchive/archive_write_set_format_mtree.c new file mode 100644 index 0000000000..22e4c56d9f --- /dev/null +++ b/contrib/libarchive/libarchive/archive_write_set_format_mtree.c @@ -0,0 +1,1101 @@ +/*- + * Copyright (c) 2009 Michihiro NAKAJIMA + * Copyright (c) 2008 Joerg Sonnenberger + * 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$"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +#include +#include +#ifdef HAVE_OPENSSL_MD5_H +#include +#else /* HAVE_OPENSSL_MD5_H */ +#ifdef HAVE_MD5_H +#include +#endif +#endif /* HAVE_OPENSSL_MD5_H */ +#ifdef HAVE_OPENSSL_RIPEMD_H +#include +#else /* HAVE_OPENSSL_RIPEMD_H */ +#ifdef HAVE_RIPEMD_H +#include +#endif +#ifdef HAVE_RMD160_H +#include +#endif +#endif /* HAVE_OPENSSL_RIPEMD_H */ +#ifdef HAVE_OPENSSL_SHA_H +#include +#else /* HAVE_OPENSSL_SHA_H */ +#ifdef HAVE_SHA_H +#include +#endif +#ifdef HAVE_SHA1_H +#include +#endif +#ifdef HAVE_SHA2_H +#include +#endif +#ifdef HAVE_SHA256_H +#include +#endif +#endif /* HAVE_OPENSSL_SHA_H */ + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#define INDENTNAMELEN 15 +#define MAXLINELEN 80 + +struct mtree_writer { + struct archive_entry *entry; + struct archive_string ebuf; + struct archive_string buf; + int first; + uint64_t entry_bytes_remaining; + struct { + int output; + int processed; + struct archive_string parent; + mode_t type; + int keys; + uid_t uid; + gid_t gid; + mode_t mode; + unsigned long fflags_set; + unsigned long fflags_clear; + } set; + /* chekc sum */ + int compute_sum; + uint32_t crc; + uint64_t crc_len; +#ifdef HAVE_MD5 + MD5_CTX md5ctx; +#endif +#if defined(HAVE_OPENSSL_RIPEMD_H) || defined(HAVE_RIPEMD_H) + RIPEMD160_CTX rmd160ctx; +#elif defined(HAVE_RMD160_H) + RMD160_CTX rmd160ctx; +#endif +#ifdef HAVE_SHA1 +#if defined(HAVE_OPENSSL_SHA_H) || defined(HAVE_SHA_H) + SHA_CTX sha1ctx; +#else + SHA1_CTX sha1ctx; +#endif +#endif +#ifdef HAVE_SHA256 + SHA256_CTX sha256ctx; +#endif +#ifdef HAVE_SHA384 +#if defined(HAVE_OPENSSL_SHA_H) + SHA512_CTX sha384ctx; +#else + SHA384_CTX sha384ctx; +#endif +#endif +#ifdef HAVE_SHA512 + SHA512_CTX sha512ctx; +#endif + /* Keyword options */ + int keys; +#define F_CKSUM 0x00000001 /* check sum */ +#define F_DEV 0x00000002 /* device type */ +#define F_DONE 0x00000004 /* directory done */ +#define F_FLAGS 0x00000008 /* file flags */ +#define F_GID 0x00000010 /* gid */ +#define F_GNAME 0x00000020 /* group name */ +#define F_IGN 0x00000040 /* ignore */ +#define F_MAGIC 0x00000080 /* name has magic chars */ +#define F_MD5 0x00000100 /* MD5 digest */ +#define F_MODE 0x00000200 /* mode */ +#define F_NLINK 0x00000400 /* number of links */ +#define F_NOCHANGE 0x00000800 /* If owner/mode "wrong", do + * not change */ +#define F_OPT 0x00001000 /* existence optional */ +#define F_RMD160 0x00002000 /* RIPEMD160 digest */ +#define F_SHA1 0x00004000 /* SHA-1 digest */ +#define F_SIZE 0x00008000 /* size */ +#define F_SLINK 0x00010000 /* symbolic link */ +#define F_TAGS 0x00020000 /* tags */ +#define F_TIME 0x00040000 /* modification time */ +#define F_TYPE 0x00080000 /* file type */ +#define F_UID 0x00100000 /* uid */ +#define F_UNAME 0x00200000 /* user name */ +#define F_VISIT 0x00400000 /* file visited */ +#define F_SHA256 0x00800000 /* SHA-256 digest */ +#define F_SHA384 0x01000000 /* SHA-384 digest */ +#define F_SHA512 0x02000000 /* SHA-512 digest */ + + /* Options */ + int dironly; /* if the dironly is 1, ignore everything except + * directory type files. like mtree(8) -d option. + */ + int indent; /* if the indent is 1, indent writing data. */ +}; + +#define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\ + | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\ + | F_UNAME) + +#define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] +static const uint32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static int +mtree_safe_char(char c) +{ + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) + return 1; + if (c >= '0' && c <= '9') + return 1; + if (c == 35 || c == 61 || c == 92) + return 0; /* #, = and \ are always quoted */ + + if (c >= 33 && c <= 47) /* !"$%&'()*+,-./ */ + return 1; + if (c >= 58 && c <= 64) /* :;<>?@ */ + return 1; + if (c >= 91 && c <= 96) /* []^_` */ + return 1; + if (c >= 123 && c <= 126) /* {|}~ */ + return 1; + return 0; +} + +static void +mtree_quote(struct archive_string *s, const char *str) +{ + const char *start; + char buf[4]; + unsigned char c; + + for (start = str; *str != '\0'; ++str) { + if (mtree_safe_char(*str)) + continue; + if (start != str) + archive_strncat(s, start, str - start); + c = (unsigned char)*str; + buf[0] = '\\'; + buf[1] = (c / 64) + '0'; + buf[2] = (c / 8 % 8) + '0'; + buf[3] = (c % 8) + '0'; + archive_strncat(s, buf, 4); + start = str + 1; + } + + if (start != str) + archive_strncat(s, start, str - start); +} + +static void +mtree_indent(struct mtree_writer *mtree) +{ + int i, fn; + const char *r, *s, *x; + + fn = 1; + s = r = mtree->ebuf.s; + x = NULL; + while (*r == ' ') + r++; + while ((r = strchr(r, ' ')) != NULL) { + if (fn) { + fn = 0; + archive_strncat(&mtree->buf, s, r - s); + if (r -s > INDENTNAMELEN) { + archive_strncat(&mtree->buf, " \\\n", 3); + for (i = 0; i < (INDENTNAMELEN + 1); i++) + archive_strappend_char(&mtree->buf, ' '); + } else { + for (i = r -s; i < (INDENTNAMELEN + 1); i++) + archive_strappend_char(&mtree->buf, ' '); + } + s = ++r; + x = NULL; + continue; + } + if (r - s <= MAXLINELEN - 3 - INDENTNAMELEN) + x = r++; + else { + if (x == NULL) + x = r; + archive_strncat(&mtree->buf, s, x - s); + archive_strncat(&mtree->buf, " \\\n", 3); + for (i = 0; i < (INDENTNAMELEN + 1); i++) + archive_strappend_char(&mtree->buf, ' '); + s = r = ++x; + x = NULL; + } + } + if (x != NULL && strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) { + /* Last keyword is longer. */ + archive_strncat(&mtree->buf, s, x - s); + archive_strncat(&mtree->buf, " \\\n", 3); + for (i = 0; i < (INDENTNAMELEN + 1); i++) + archive_strappend_char(&mtree->buf, ' '); + s = ++x; + } + archive_strcat(&mtree->buf, s); + archive_string_empty(&mtree->ebuf); +} + +#if !defined(_WIN32) || defined(__CYGWIN__) +static size_t +dir_len(struct archive_entry *entry) +{ + const char *path, *r; + + path = archive_entry_pathname(entry); + r = strrchr(path, '/'); + if (r == NULL) + return (0); + /* Include a separator size */ + return (r - path + 1); +} + +#else /* _WIN32 && !__CYGWIN__ */ +/* + * Note: We should use wide-character for findng '\' character, + * a directory separator on Windows, because some character-set have + * been using the '\' character for a part of its multibyte character + * code. + */ +static size_t +dir_len(struct archive_entry *entry) +{ + wchar_t wc; + const char *path; + const char *p, *rp; + size_t al, l, size; + + path = archive_entry_pathname(entry); + al = l = -1; + for (p = path; *p != '\0'; ++p) { + if (*p == '\\') + al = l = p - path; + else if (*p == '/') + al = p - path; + } + if (l == -1) + goto alen; + size = p - path; + rp = p = path; + while (*p != '\0') { + l = mbtowc(&wc, p, size); + if (l == -1) + goto alen; + if (l == 1 && (wc == L'/' || wc == L'\\')) + rp = p; + p += l; + size -= l; + } + return (rp - path + 1); +alen: + if (al == -1) + return (0); + return (al + 1); +} +#endif /* _WIN32 && !__CYGWIN__ */ + +static int +parent_dir_changed(struct archive_string *dir, struct archive_entry *entry) +{ + const char *path; + size_t l; + + l = dir_len(entry); + path = archive_entry_pathname(entry); + if (archive_strlen(dir) > 0) { + if (l == 0) { + archive_string_empty(dir); + return (1); + } + if (strncmp(dir->s, path, l) == 0) + return (0); /* The parent directory is the same. */ + } else if (l == 0) + return (0); /* The parent directory is the same. */ + archive_strncpy(dir, path, l); + return (1); +} + +/* + * Write /set keyword. It means set global datas. + * [directory-only mode] + * - It is only once to write /set keyword. It is using values of the + * first entry. + * [normal mode] + * - Write /set keyword. It is using values of the first entry whose + * filetype is a regular file. + * - When a parent directory of the entry whose filetype is the regular + * file is changed, check the global datas and write it again if its + * values are different from the entry's. + */ +static void +set_global(struct mtree_writer *mtree, struct archive_entry *entry) +{ + struct archive_string setstr; + struct archive_string unsetstr; + const char *name; + int keys, oldkeys, effkeys; + mode_t set_type = 0; + + switch (archive_entry_filetype(entry)) { + case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: + case AE_IFBLK: case AE_IFIFO: + break; + case AE_IFDIR: + if (mtree->dironly) + set_type = AE_IFDIR; + break; + case AE_IFREG: + default: /* Handle unknown file types as regular files. */ + if (!mtree->dironly) + set_type = AE_IFREG; + break; + } + if (set_type == 0) + return; + if (mtree->set.processed && + !parent_dir_changed(&mtree->set.parent, entry)) + return; + /* At first, save a parent directory of the entry for following + * entries. */ + if (!mtree->set.processed && set_type == AE_IFREG) + parent_dir_changed(&mtree->set.parent, entry); + + archive_string_init(&setstr); + archive_string_init(&unsetstr); + keys = mtree->keys & (F_FLAGS | F_GID | F_GNAME | F_NLINK | F_MODE + | F_TYPE | F_UID | F_UNAME); + oldkeys = mtree->set.keys; + effkeys = keys; + if (mtree->set.processed) { + /* + * Check the global datas for whether it needs updating. + */ + effkeys &= ~F_TYPE; + if ((oldkeys & (F_UNAME | F_UID)) != 0 && + mtree->set.uid == archive_entry_uid(entry)) + effkeys &= ~(F_UNAME | F_UID); + if ((oldkeys & (F_GNAME | F_GID)) != 0 && + mtree->set.gid == archive_entry_gid(entry)) + effkeys &= ~(F_GNAME | F_GID); + if ((oldkeys & F_MODE) != 0 && + mtree->set.mode == (archive_entry_mode(entry) & 07777)) + effkeys &= ~F_MODE; + if ((oldkeys & F_FLAGS) != 0) { + unsigned long fflags_set; + unsigned long fflags_clear; + + archive_entry_fflags(entry, &fflags_set, &fflags_clear); + if (fflags_set == mtree->set.fflags_set && + fflags_clear == mtree->set.fflags_clear) + effkeys &= ~F_FLAGS; + } + } + if ((keys & effkeys & F_TYPE) != 0) { + mtree->set.type = set_type; + if (set_type == AE_IFDIR) + archive_strcat(&setstr, " type=dir"); + else + archive_strcat(&setstr, " type=file"); + } + if ((keys & effkeys & F_UNAME) != 0) { + if ((name = archive_entry_uname(entry)) != NULL) { + archive_strcat(&setstr, " uname="); + mtree_quote(&setstr, name); + } else if ((oldkeys & F_UNAME) != 0) + archive_strcat(&unsetstr, " uname"); + else + keys &= ~F_UNAME; + } + if ((keys & effkeys & F_UID) != 0) { + mtree->set.uid = archive_entry_uid(entry); + archive_string_sprintf(&setstr, " uid=%jd", + (intmax_t)mtree->set.uid); + } + if ((keys & effkeys & F_GNAME) != 0) { + if ((name = archive_entry_gname(entry)) != NULL) { + archive_strcat(&setstr, " gname="); + mtree_quote(&setstr, name); + } else if ((oldkeys & F_GNAME) != 0) + archive_strcat(&unsetstr, " gname"); + else + keys &= ~F_GNAME; + } + if ((keys & effkeys & F_GID) != 0) { + mtree->set.gid = archive_entry_gid(entry); + archive_string_sprintf(&setstr, " gid=%jd", + (intmax_t)mtree->set.gid); + } + if ((keys & effkeys & F_MODE) != 0) { + mtree->set.mode = archive_entry_mode(entry) & 07777; + archive_string_sprintf(&setstr, " mode=%o", mtree->set.mode); + } + if ((keys & effkeys & F_FLAGS) != 0) { + if ((name = archive_entry_fflags_text(entry)) != NULL) { + archive_strcat(&setstr, " flags="); + mtree_quote(&setstr, name); + archive_entry_fflags(entry, &mtree->set.fflags_set, + &mtree->set.fflags_clear); + } else if ((oldkeys & F_FLAGS) != 0) + archive_strcat(&unsetstr, " flags"); + else + keys &= ~F_FLAGS; + } + if (unsetstr.length > 0) + archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s); + archive_string_free(&unsetstr); + if (setstr.length > 0) + archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s); + archive_string_free(&setstr); + mtree->set.keys = keys; + mtree->set.processed = 1; + /* On directory-only mode, it is only once to write /set keyword. */ + if (mtree->dironly) + mtree->set.output = 0; +} + +static int +get_keys(struct mtree_writer *mtree, struct archive_entry *entry) +{ + int keys; + + keys = mtree->keys; + if (mtree->set.keys == 0) + return (keys); + if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 && + mtree->set.gid == archive_entry_gid(entry)) + keys &= ~(F_GNAME | F_GID); + if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 && + mtree->set.uid == archive_entry_uid(entry)) + keys &= ~(F_UNAME | F_UID); + if (mtree->set.keys & F_FLAGS) { + unsigned long set, clear; + + archive_entry_fflags(entry, &set, &clear); + if (mtree->set.fflags_set == set && + mtree->set.fflags_clear == clear) + keys &= ~F_FLAGS; + } + if ((mtree->set.keys & F_MODE) != 0 && + mtree->set.mode == (archive_entry_mode(entry) & 07777)) + keys &= ~F_MODE; + + switch (archive_entry_filetype(entry)) { + case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR: + case AE_IFBLK: case AE_IFIFO: + break; + case AE_IFDIR: + if ((mtree->set.keys & F_TYPE) != 0 && + mtree->set.type == AE_IFDIR) + keys &= ~F_TYPE; + break; + case AE_IFREG: + default: /* Handle unknown file types as regular files. */ + if ((mtree->set.keys & F_TYPE) != 0 && + mtree->set.type == AE_IFREG) + keys &= ~F_TYPE; + break; + } + + return (keys); +} + +static int +archive_write_mtree_header(struct archive_write *a, + struct archive_entry *entry) +{ + struct mtree_writer *mtree= a->format_data; + struct archive_string *str; + const char *path; + + mtree->entry = archive_entry_clone(entry); + path = archive_entry_pathname(mtree->entry); + + if (mtree->first) { + mtree->first = 0; + archive_strcat(&mtree->buf, "#mtree\n"); + } + if (mtree->set.output) + set_global(mtree, entry); + + archive_string_empty(&mtree->ebuf); + str = (mtree->indent)? &mtree->ebuf : &mtree->buf; + if (!mtree->dironly || archive_entry_filetype(entry) == AE_IFDIR) + mtree_quote(str, path); + + mtree->entry_bytes_remaining = archive_entry_size(entry); + if ((mtree->keys & F_CKSUM) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_CKSUM; + mtree->crc = 0; + mtree->crc_len = 0; + } else + mtree->compute_sum &= ~F_CKSUM; +#ifdef HAVE_MD5 + if ((mtree->keys & F_MD5) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_MD5; + MD5_Init(&mtree->md5ctx); + } else + mtree->compute_sum &= ~F_MD5; +#endif +#ifdef HAVE_RMD160 + if ((mtree->keys & F_RMD160) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_RMD160; + RIPEMD160_Init(&mtree->rmd160ctx); + } else + mtree->compute_sum &= ~F_RMD160; +#endif +#ifdef HAVE_SHA1 + if ((mtree->keys & F_SHA1) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_SHA1; + SHA1_Init(&mtree->sha1ctx); + } else + mtree->compute_sum &= ~F_SHA1; +#endif +#ifdef HAVE_SHA256 + if ((mtree->keys & F_SHA256) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_SHA256; + SHA256_Init(&mtree->sha256ctx); + } else + mtree->compute_sum &= ~F_SHA256; +#endif +#ifdef HAVE_SHA384 + if ((mtree->keys & F_SHA384) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_SHA384; + SHA384_Init(&mtree->sha384ctx); + } else + mtree->compute_sum &= ~F_SHA384; +#endif +#ifdef HAVE_SHA512 + if ((mtree->keys & F_SHA512) != 0 && + archive_entry_filetype(entry) == AE_IFREG) { + mtree->compute_sum |= F_SHA512; + SHA512_Init(&mtree->sha512ctx); + } else + mtree->compute_sum &= ~F_SHA512; +#endif + + return (ARCHIVE_OK); +} + +#if defined(HAVE_MD5) || defined(HAVE_RMD160) || defined(HAVE_SHA1) || defined(HAVE_SHA256) || defined(HAVE_SHA384) || defined(HAVE_SHA512) +static void +strappend_bin(struct archive_string *s, const unsigned char *bin, int n) +{ + static const char hex[] = "0123456789abcdef"; + int i; + + for (i = 0; i < n; i++) { + archive_strappend_char(s, hex[bin[i] >> 4]); + archive_strappend_char(s, hex[bin[i] & 0x0f]); + } +} +#endif + +static int +archive_write_mtree_finish_entry(struct archive_write *a) +{ + struct mtree_writer *mtree = a->format_data; + struct archive_entry *entry; + struct archive_string *str; + const char *name; + int keys, ret; + + entry = mtree->entry; + if (entry == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Finished entry without being open first."); + return (ARCHIVE_FATAL); + } + mtree->entry = NULL; + + if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) { + archive_entry_free(entry); + return (ARCHIVE_OK); + } + + str = (mtree->indent)? &mtree->ebuf : &mtree->buf; + keys = get_keys(mtree, entry); + if ((keys & F_NLINK) != 0 && + archive_entry_nlink(entry) != 1 && + archive_entry_filetype(entry) != AE_IFDIR) + archive_string_sprintf(str, + " nlink=%u", archive_entry_nlink(entry)); + + if ((keys & F_GNAME) != 0 && + (name = archive_entry_gname(entry)) != NULL) { + archive_strcat(str, " gname="); + mtree_quote(str, name); + } + if ((keys & F_UNAME) != 0 && + (name = archive_entry_uname(entry)) != NULL) { + archive_strcat(str, " uname="); + mtree_quote(str, name); + } + if ((keys & F_FLAGS) != 0 && + (name = archive_entry_fflags_text(entry)) != NULL) { + archive_strcat(str, " flags="); + mtree_quote(str, name); + } + if ((keys & F_TIME) != 0) + archive_string_sprintf(str, " time=%jd.%jd", + (intmax_t)archive_entry_mtime(entry), + (intmax_t)archive_entry_mtime_nsec(entry)); + if ((keys & F_MODE) != 0) + archive_string_sprintf(str, " mode=%o", + archive_entry_mode(entry) & 07777); + if ((keys & F_GID) != 0) + archive_string_sprintf(str, " gid=%jd", + (intmax_t)archive_entry_gid(entry)); + if ((keys & F_UID) != 0) + archive_string_sprintf(str, " uid=%jd", + (intmax_t)archive_entry_uid(entry)); + + switch (archive_entry_filetype(entry)) { + case AE_IFLNK: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=link"); + if ((keys & F_SLINK) != 0) { + archive_strcat(str, " link="); + mtree_quote(str, archive_entry_symlink(entry)); + } + break; + case AE_IFSOCK: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=socket"); + break; + case AE_IFCHR: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=char"); + if ((keys & F_DEV) != 0) { + archive_string_sprintf(str, + " device=native,%d,%d", + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); + } + break; + case AE_IFBLK: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=block"); + if ((keys & F_DEV) != 0) { + archive_string_sprintf(str, + " device=native,%d,%d", + archive_entry_rdevmajor(entry), + archive_entry_rdevminor(entry)); + } + break; + case AE_IFDIR: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=dir"); + break; + case AE_IFIFO: + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=fifo"); + break; + case AE_IFREG: + default: /* Handle unknown file types as regular files. */ + if ((keys & F_TYPE) != 0) + archive_strcat(str, " type=file"); + if ((keys & F_SIZE) != 0) + archive_string_sprintf(str, " size=%jd", + (intmax_t)archive_entry_size(entry)); + break; + } + + if (mtree->compute_sum & F_CKSUM) { + uint64_t len; + /* Include the length of the file. */ + for (len = mtree->crc_len; len != 0; len >>= 8) + COMPUTE_CRC(mtree->crc, len & 0xff); + mtree->crc = ~mtree->crc; + archive_string_sprintf(str, " cksum=%ju", + (uintmax_t)mtree->crc); + } +#ifdef HAVE_MD5 + if (mtree->compute_sum & F_MD5) { + unsigned char buf[16]; + + MD5_Final(buf, &mtree->md5ctx); + archive_strcat(str, " md5digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif +#ifdef HAVE_RMD160 + if (mtree->compute_sum & F_RMD160) { + unsigned char buf[20]; + + RIPEMD160_Final(buf, &mtree->rmd160ctx); + archive_strcat(str, " rmd160digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif +#ifdef HAVE_SHA1 + if (mtree->compute_sum & F_SHA1) { + unsigned char buf[20]; + + SHA1_Final(buf, &mtree->sha1ctx); + archive_strcat(str, " sha1digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif +#ifdef HAVE_SHA256 + if (mtree->compute_sum & F_SHA256) { + unsigned char buf[32]; + + SHA256_Final(buf, &mtree->sha256ctx); + archive_strcat(str, " sha256digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif +#ifdef HAVE_SHA384 + if (mtree->compute_sum & F_SHA384) { + unsigned char buf[48]; + + SHA384_Final(buf, &mtree->sha384ctx); + archive_strcat(str, " sha384digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif +#ifdef HAVE_SHA512 + if (mtree->compute_sum & F_SHA512) { + unsigned char buf[64]; + + SHA512_Final(buf, &mtree->sha512ctx); + archive_strcat(str, " sha512digest="); + strappend_bin(str, buf, sizeof(buf)); + } +#endif + archive_strcat(str, "\n"); + if (mtree->indent) + mtree_indent(mtree); + + archive_entry_free(entry); + + if (mtree->buf.length > 32768) { + ret = (a->compressor.write)(a, mtree->buf.s, mtree->buf.length); + archive_string_empty(&mtree->buf); + } else + ret = ARCHIVE_OK; + + return (ret == ARCHIVE_OK ? ret : ARCHIVE_FATAL); +} + +static int +archive_write_mtree_finish(struct archive_write *a) +{ + struct mtree_writer *mtree= a->format_data; + + archive_write_set_bytes_in_last_block(&a->archive, 1); + + return (a->compressor.write)(a, mtree->buf.s, mtree->buf.length); +} + +static ssize_t +archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n) +{ + struct mtree_writer *mtree= a->format_data; + + if (n > mtree->entry_bytes_remaining) + n = mtree->entry_bytes_remaining; + if (mtree->dironly) + /* We don't need compute a regular file sum */ + return (n); + if (mtree->compute_sum & F_CKSUM) { + /* + * Compute a POSIX 1003.2 checksum + */ + const unsigned char *p; + int nn; + + for (nn = n, p = buff; nn--; ++p) + COMPUTE_CRC(mtree->crc, *p); + mtree->crc_len += n; + } +#ifdef HAVE_MD5 + if (mtree->compute_sum & F_MD5) + MD5_Update(&mtree->md5ctx, buff, n); +#endif +#ifdef HAVE_RMD160 + if (mtree->compute_sum & F_RMD160) + RIPEMD160_Update(&mtree->rmd160ctx, buff, n); +#endif +#ifdef HAVE_SHA1 + if (mtree->compute_sum & F_SHA1) + SHA1_Update(&mtree->sha1ctx, buff, n); +#endif +#ifdef HAVE_SHA256 + if (mtree->compute_sum & F_SHA256) + SHA256_Update(&mtree->sha256ctx, buff, n); +#endif +#ifdef HAVE_SHA384 + if (mtree->compute_sum & F_SHA384) + SHA384_Update(&mtree->sha384ctx, buff, n); +#endif +#ifdef HAVE_SHA512 + if (mtree->compute_sum & F_SHA512) + SHA512_Update(&mtree->sha512ctx, buff, n); +#endif + return (n); +} + +static int +archive_write_mtree_destroy(struct archive_write *a) +{ + struct mtree_writer *mtree= a->format_data; + + if (mtree == NULL) + return (ARCHIVE_OK); + + archive_entry_free(mtree->entry); + archive_string_free(&mtree->ebuf); + archive_string_free(&mtree->buf); + archive_string_free(&mtree->set.parent); + free(mtree); + a->format_data = NULL; + return (ARCHIVE_OK); +} + +static int +archive_write_mtree_options(struct archive_write *a, const char *key, + const char *value) +{ + struct mtree_writer *mtree= a->format_data; + int keybit = 0; + + switch (key[0]) { + case 'a': + if (strcmp(key, "all") == 0) + keybit = ~0; + break; + case 'c': + if (strcmp(key, "cksum") == 0) + keybit = F_CKSUM; + break; + case 'd': + if (strcmp(key, "device") == 0) + keybit = F_DEV; + else if (strcmp(key, "dironly") == 0) { + mtree->dironly = (value != NULL)? 1: 0; + return (ARCHIVE_OK); + } + break; + case 'f': + if (strcmp(key, "flags") == 0) + keybit = F_FLAGS; + break; + case 'g': + if (strcmp(key, "gid") == 0) + keybit = F_GID; + else if (strcmp(key, "gname") == 0) + keybit = F_GNAME; + break; + case 'i': + if (strcmp(key, "indent") == 0) { + mtree->indent = (value != NULL)? 1: 0; + return (ARCHIVE_OK); + } + break; + case 'l': + if (strcmp(key, "link") == 0) + keybit = F_SLINK; + break; + case 'm': +#ifdef HAVE_MD5 + if (strcmp(key, "md5") == 0 || + strcmp(key, "md5digest") == 0) + keybit = F_MD5; +#endif + if (strcmp(key, "mode") == 0) + keybit = F_MODE; + break; + case 'n': + if (strcmp(key, "nlink") == 0) + keybit = F_NLINK; + break; +#ifdef HAVE_RMD160 + case 'r': + if (strcmp(key, "ripemd160digest") == 0 || + strcmp(key, "rmd160") == 0 || + strcmp(key, "rmd160digest") == 0) + keybit = F_RMD160; + break; +#endif + case 's': +#ifdef HAVE_SHA1 + if (strcmp(key, "sha1") == 0 || + strcmp(key, "sha1digest") == 0) + keybit = F_SHA1; +#endif +#ifdef HAVE_SHA256 + if (strcmp(key, "sha256") == 0 || + strcmp(key, "sha256digest") == 0) + keybit = F_SHA256; +#endif +#ifdef HAVE_SHA384 + if (strcmp(key, "sha384") == 0 || + strcmp(key, "sha384digest") == 0) + keybit = F_SHA384; +#endif +#ifdef HAVE_SHA384 + if (strcmp(key, "sha512") == 0 || + strcmp(key, "sha512digest") == 0) + keybit = F_SHA512; +#endif + if (strcmp(key, "size") == 0) + keybit = F_SIZE; + break; + case 't': + if (strcmp(key, "time") == 0) + keybit = F_TIME; + else if (strcmp(key, "type") == 0) + keybit = F_TYPE; + break; + case 'u': + if (strcmp(key, "uid") == 0) + keybit = F_UID; + else if (strcmp(key, "uname") == 0) + keybit = F_UNAME; + else if (strcmp(key, "use-set") == 0) { + mtree->set.output = (value != NULL)? 1: 0; + return (ARCHIVE_OK); + } + break; + } + if (keybit != 0) { + if (value != NULL) + mtree->keys |= keybit; + else + mtree->keys &= ~keybit; + return (ARCHIVE_OK); + } + + return (ARCHIVE_WARN); +} + +int +archive_write_set_format_mtree(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct mtree_writer *mtree; + + if (a->format_destroy != NULL) + (a->format_destroy)(a); + + if ((mtree = malloc(sizeof(*mtree))) == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate mtree data"); + return (ARCHIVE_FATAL); + } + + mtree->entry = NULL; + mtree->first = 1; + memset(&(mtree->set), 0, sizeof(mtree->set)); + archive_string_init(&mtree->set.parent); + mtree->keys = DEFAULT_KEYS; + mtree->dironly = 0; + mtree->indent = 0; + archive_string_init(&mtree->ebuf); + archive_string_init(&mtree->buf); + a->format_data = mtree; + a->format_destroy = archive_write_mtree_destroy; + + a->pad_uncompressed = 0; + a->format_name = "mtree"; + a->format_options = archive_write_mtree_options; + a->format_write_header = archive_write_mtree_header; + a->format_finish = archive_write_mtree_finish; + a->format_write_data = archive_write_mtree_data; + a->format_finish_entry = archive_write_mtree_finish_entry; + a->archive.archive_format = ARCHIVE_FORMAT_MTREE; + a->archive.archive_format_name = "mtree"; + + return (ARCHIVE_OK); +} diff --git a/contrib/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libarchive/libarchive/archive_write_set_format_pax.c index 3e294cdf0a..c1cc3e74d2 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_pax.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_pax.c @@ -24,7 +24,7 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.47 2008/05/26 17:00:23 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.49 2008/09/30 03:57:07 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -111,6 +111,7 @@ archive_write_set_format_pax(struct archive *_a) a->format_data = pax; a->pad_uncompressed = 1; + a->format_name = "pax"; a->format_write_header = archive_write_pax_header; a->format_write_data = archive_write_pax_data; a->format_finish = archive_write_pax_finish; @@ -203,6 +204,16 @@ utf8_encode(const wchar_t *wval) utf8len = 0; for (wp = wval; *wp != L'\0'; ) { wc = *wp++; + + if (wc >= 0xd800 && wc <= 0xdbff + && *wp >= 0xdc00 && *wp <= 0xdfff) { + /* This is a surrogate pair. Combine into a + * full Unicode value before encoding into + * UTF-8. */ + wc = (wc - 0xd800) << 10; /* High 10 bits */ + wc += (*wp++ - 0xdc00); /* Low 10 bits */ + wc += 0x10000; /* Skip BMP */ + } if (wc <= 0x7f) utf8len++; else if (wc <= 0x7ff) @@ -226,6 +237,12 @@ utf8_encode(const wchar_t *wval) for (wp = wval, p = utf8_value; *wp != L'\0'; ) { wc = *wp++; + if (wc >= 0xd800 && wc <= 0xdbff + && *wp >= 0xdc00 && *wp <= 0xdfff) { + /* Combine surrogate pair. */ + wc = (wc - 0xd800) << 10; + wc += *wp++ - 0xdc00 + 0x10000; + } if (wc <= 0x7f) { *p++ = (char)wc; } else if (wc <= 0x7ff) { @@ -762,6 +779,15 @@ archive_write_pax_header(struct archive_write *a, archive_entry_atime(entry_main), archive_entry_atime_nsec(entry_main)); + /* Store birth/creationtime only if it's earlier than mtime */ + if (archive_entry_birthtime_is_set(entry_main) && + archive_entry_birthtime(entry_main) + < archive_entry_mtime(entry_main)) + add_pax_attr_time(&(pax->pax_header), + "LIBARCHIVE.creationtime", + archive_entry_birthtime(entry_main), + archive_entry_birthtime_nsec(entry_main)); + /* I use a star-compatible file flag attribute. */ p = archive_entry_fflags_text(entry_main); if (p != NULL && *p != '\0') @@ -1200,6 +1226,9 @@ archive_write_pax_destroy(struct archive_write *a) struct pax *pax; pax = (struct pax *)a->format_data; + if (pax == NULL) + return (ARCHIVE_OK); + archive_string_free(&pax->pax_header); free(pax); a->format_data = NULL; diff --git a/contrib/libarchive/libarchive/archive_write_set_format_shar.c b/contrib/libarchive/libarchive/archive_write_set_format_shar.c index b5d16e09d1..3c2852c18f 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_shar.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_shar.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2008 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,12 +25,11 @@ */ #include "archive_platform.h" -__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.19 2008/03/15 11:04:45 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.20 2008/08/31 07:10:40 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include #endif -#include #include #ifdef HAVE_STDLIB_H #include @@ -49,13 +49,14 @@ struct shar { struct archive_entry *entry; int has_data; char *last_dir; - char outbuff[1024]; - size_t outbytes; + + /* Line buffer for uuencoded dump format */ + char outbuff[45]; size_t outpos; - int uuavail; - char uubuffer[3]; + int wrote_header; struct archive_string work; + struct archive_string quoted_name; }; static int archive_write_shar_finish(struct archive_write *); @@ -67,23 +68,33 @@ static ssize_t archive_write_shar_data_sed(struct archive_write *, static ssize_t archive_write_shar_data_uuencode(struct archive_write *, const void * buff, size_t); static int archive_write_shar_finish_entry(struct archive_write *); -static int shar_printf(struct archive_write *, const char *fmt, ...); -static void uuencode_group(struct shar *); -static int -shar_printf(struct archive_write *a, const char *fmt, ...) +/* + * Copy the given string to the buffer, quoting all shell meta characters + * found. + */ +static void +shar_quote(struct archive_string *buf, const char *str, int in_shell) { - struct shar *shar; - va_list ap; - int ret; - - shar = (struct shar *)a->format_data; - va_start(ap, fmt); - archive_string_empty(&(shar->work)); - archive_string_vsprintf(&(shar->work), fmt, ap); - ret = ((a->compressor.write)(a, shar->work.s, strlen(shar->work.s))); - va_end(ap); - return (ret); + static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + size_t len; + + while (*str != '\0') { + if ((len = strcspn(str, meta)) != 0) { + archive_strncat(buf, str, len); + str += len; + } else if (*str == '\n') { + if (in_shell) + archive_strcat(buf, "\"\n\""); + else + archive_strcat(buf, "\\n"); + ++str; + } else { + archive_strappend_char(buf, '\\'); + archive_strappend_char(buf, *str); + ++str; + } + } } /* @@ -105,9 +116,12 @@ archive_write_set_format_shar(struct archive *_a) return (ARCHIVE_FATAL); } memset(shar, 0, sizeof(*shar)); + archive_string_init(&shar->work); + archive_string_init(&shar->quoted_name); a->format_data = shar; a->pad_uncompressed = 0; + a->format_name = "shar"; a->format_write_header = archive_write_shar_header; a->format_finish = archive_write_shar_finish; a->format_destroy = archive_write_shar_destroy; @@ -146,16 +160,11 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) const char *name; char *p, *pp; struct shar *shar; - int ret; shar = (struct shar *)a->format_data; if (!shar->wrote_header) { - ret = shar_printf(a, "#!/bin/sh\n"); - if (ret != ARCHIVE_OK) - return (ret); - ret = shar_printf(a, "# This is a shell archive\n"); - if (ret != ARCHIVE_OK) - return (ret); + archive_strcat(&shar->work, "#!/bin/sh\n"); + archive_strcat(&shar->work, "# This is a shell archive\n"); shar->wrote_header = 1; } @@ -192,10 +201,11 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) } } + archive_string_empty(&shar->quoted_name); + shar_quote(&shar->quoted_name, name, 1); + /* Stock preparation for all file types. */ - ret = shar_printf(a, "echo x %s\n", name); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s); if (archive_entry_filetype(entry) != AE_IFDIR) { /* Try to create the dir. */ @@ -210,12 +220,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) /* Don't try to "mkdir ." */ free(p); } else if (shar->last_dir == NULL) { - ret = shar_printf(a, - "mkdir -p %s > /dev/null 2>&1\n", p); - if (ret != ARCHIVE_OK) { - free(p); - return (ret); - } + archive_strcat(&shar->work, "mkdir -p "); + shar_quote(&shar->work, p, 1); + archive_strcat(&shar->work, + " > /dev/null 2>&1\n"); shar->last_dir = p; } else if (strcmp(p, shar->last_dir) == 0) { /* We've already created this exact dir. */ @@ -225,13 +233,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) /* We've already created a subdir. */ free(p); } else { - ret = shar_printf(a, - "mkdir -p %s > /dev/null 2>&1\n", p); - if (ret != ARCHIVE_OK) { - free(p); - return (ret); - } - free(shar->last_dir); + archive_strcat(&shar->work, "mkdir -p "); + shar_quote(&shar->work, p, 1); + archive_strcat(&shar->work, + " > /dev/null 2>&1\n"); shar->last_dir = p; } } else { @@ -242,51 +247,47 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) /* Handle file-type specific issues. */ shar->has_data = 0; if ((linkname = archive_entry_hardlink(entry)) != NULL) { - ret = shar_printf(a, "ln -f %s %s\n", linkname, name); - if (ret != ARCHIVE_OK) - return (ret); + archive_strcat(&shar->work, "ln -f "); + shar_quote(&shar->work, linkname, 1); + archive_string_sprintf(&shar->work, " %s\n", + shar->quoted_name.s); } else if ((linkname = archive_entry_symlink(entry)) != NULL) { - ret = shar_printf(a, "ln -fs %s %s\n", linkname, name); - if (ret != ARCHIVE_OK) - return (ret); + archive_strcat(&shar->work, "ln -fs "); + shar_quote(&shar->work, linkname, 1); + archive_string_sprintf(&shar->work, " %s\n", + shar->quoted_name.s); } else { switch(archive_entry_filetype(entry)) { case AE_IFREG: if (archive_entry_size(entry) == 0) { /* More portable than "touch." */ - ret = shar_printf(a, "test -e \"%s\" || :> \"%s\"\n", name, name); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, + "test -e \"%s\" || :> \"%s\"\n", + shar->quoted_name.s, shar->quoted_name.s); } else { if (shar->dump) { - ret = shar_printf(a, - "uudecode -o %s << 'SHAR_END'\n", - name); - if (ret != ARCHIVE_OK) - return (ret); - ret = shar_printf(a, "begin %o %s\n", - archive_entry_mode(entry) & 0777, - name); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, + "uudecode -p > %s << 'SHAR_END'\n", + shar->quoted_name.s); + archive_string_sprintf(&shar->work, + "begin %o ", + archive_entry_mode(entry) & 0777); + shar_quote(&shar->work, name, 0); + archive_strcat(&shar->work, "\n"); } else { - ret = shar_printf(a, + archive_string_sprintf(&shar->work, "sed 's/^X//' > %s << 'SHAR_END'\n", - name); - if (ret != ARCHIVE_OK) - return (ret); + shar->quoted_name.s); } shar->has_data = 1; shar->end_of_line = 1; shar->outpos = 0; - shar->outbytes = 0; } break; case AE_IFDIR: - ret = shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n", - name); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, + "mkdir -p %s > /dev/null 2>&1\n", + shar->quoted_name.s); /* Record that we just created this directory. */ if (shar->last_dir != NULL) free(shar->last_dir); @@ -302,23 +303,20 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) */ break; case AE_IFIFO: - ret = shar_printf(a, "mkfifo %s\n", name); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, + "mkfifo %s\n", shar->quoted_name.s); break; case AE_IFCHR: - ret = shar_printf(a, "mknod %s c %d %d\n", name, + archive_string_sprintf(&shar->work, + "mknod %s c %d %d\n", shar->quoted_name.s, archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); - if (ret != ARCHIVE_OK) - return (ret); break; case AE_IFBLK: - ret = shar_printf(a, "mknod %s b %d %d\n", name, + archive_string_sprintf(&shar->work, + "mknod %s b %d %d\n", shar->quoted_name.s, archive_entry_rdevmajor(entry), archive_entry_rdevminor(entry)); - if (ret != ARCHIVE_OK) - return (ret); break; default: return (ARCHIVE_WARN); @@ -328,69 +326,119 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_OK); } -/* XXX TODO: This could be more efficient XXX */ static ssize_t archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) { + static const size_t ensured = 65533; struct shar *shar; const char *src; + char *buf, *buf_end; int ret; size_t written = n; shar = (struct shar *)a->format_data; - if (!shar->has_data) + if (!shar->has_data || n == 0) return (0); src = (const char *)buff; - ret = ARCHIVE_OK; - shar->outpos = 0; - while (n-- > 0) { - if (shar->end_of_line) { - shar->outbuff[shar->outpos++] = 'X'; - shar->end_of_line = 0; + + /* + * ensure is the number of bytes in buffer before expanding the + * current character. Each operation writes the current character + * and optionally the start-of-new-line marker. This can happen + * twice before entering the loop, so make sure three additional + * bytes can be written. + */ + if (archive_string_ensure(&shar->work, ensured + 3) == NULL) + __archive_errx(1, "Out of memory"); + + if (shar->work.length > ensured) { + ret = (*a->compressor.write)(a, shar->work.s, + shar->work.length); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + archive_string_empty(&shar->work); + } + buf = shar->work.s + shar->work.length; + buf_end = shar->work.s + ensured; + + if (shar->end_of_line) { + *buf++ = 'X'; + shar->end_of_line = 0; + } + + while (n-- != 0) { + if ((*buf++ = *src++) == '\n') { + if (n == 0) + shar->end_of_line = 1; + else + *buf++ = 'X'; } - if (*src == '\n') - shar->end_of_line = 1; - shar->outbuff[shar->outpos++] = *src++; - if (shar->outpos > sizeof(shar->outbuff) - 2) { - ret = (a->compressor.write)(a, shar->outbuff, - shar->outpos); + if (buf >= buf_end) { + shar->work.length = buf - shar->work.s; + ret = (*a->compressor.write)(a, shar->work.s, + shar->work.length); if (ret != ARCHIVE_OK) - return (ret); - shar->outpos = 0; + return (ARCHIVE_FATAL); + archive_string_empty(&shar->work); + buf = shar->work.s; } } - if (shar->outpos > 0) - ret = (a->compressor.write)(a, shar->outbuff, shar->outpos); - if (ret != ARCHIVE_OK) - return (ret); + shar->work.length = buf - shar->work.s; + return (written); } #define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`') -/* XXX This could be a lot more efficient. XXX */ static void -uuencode_group(struct shar *shar) +uuencode_group(const char _in[3], char out[4]) +{ + const unsigned char *in = (const unsigned char *)_in; + int t; + + t = (in[0] << 16) | (in[1] << 8) | in[2]; + out[0] = UUENC( 0x3f & (t >> 18) ); + out[1] = UUENC( 0x3f & (t >> 12) ); + out[2] = UUENC( 0x3f & (t >> 6) ); + out[3] = UUENC( 0x3f & t ); +} + +static void +uuencode_line(struct shar *shar, const char *inbuf, size_t len) { - int t; - - t = 0; - if (shar->uuavail > 0) - t = 0xff0000 & (shar->uubuffer[0] << 16); - if (shar->uuavail > 1) - t |= 0x00ff00 & (shar->uubuffer[1] << 8); - if (shar->uuavail > 2) - t |= 0x0000ff & (shar->uubuffer[2]); - shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>18) ); - shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>12) ); - shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t>>6) ); - shar->outbuff[shar->outpos++] = UUENC( 0x3f & (t) ); - shar->uuavail = 0; - shar->outbytes += shar->uuavail; - shar->outbuff[shar->outpos] = 0; + char tmp_buf[3], *buf; + size_t alloc_len; + + /* len <= 45 -> expanded to 60 + len byte + new line */ + alloc_len = shar->work.length + 62; + if (archive_string_ensure(&shar->work, alloc_len) == NULL) + __archive_errx(1, "Out of memory"); + + buf = shar->work.s + shar->work.length; + *buf++ = UUENC(len); + while (len >= 3) { + uuencode_group(inbuf, buf); + len -= 3; + inbuf += 3; + buf += 4; + } + if (len != 0) { + tmp_buf[0] = inbuf[0]; + if (len == 1) + tmp_buf[1] = '\0'; + else + tmp_buf[1] = inbuf[1]; + tmp_buf[2] = '\0'; + uuencode_group(inbuf, buf); + buf += 4; + } + *buf++ = '\n'; + if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) + __archive_errx(1, "Buffer overflow"); + shar->work.length = buf - shar->work.s; } static ssize_t @@ -406,21 +454,39 @@ archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, if (!shar->has_data) return (ARCHIVE_OK); src = (const char *)buff; - n = length; - while (n-- > 0) { - if (shar->uuavail == 3) - uuencode_group(shar); - if (shar->outpos >= 60) { - ret = shar_printf(a, "%c%s\n", UUENC(shar->outbytes), - shar->outbuff); - if (ret != ARCHIVE_OK) - return (ret); - shar->outpos = 0; - shar->outbytes = 0; + + if (shar->outpos != 0) { + n = 45 - shar->outpos; + if (n > length) + n = length; + memcpy(shar->outbuff + shar->outpos, src, n); + if (shar->outpos + n < 45) { + shar->outpos += n; + return length; } + uuencode_line(shar, shar->outbuff, 45); + src += n; + n = length - n; + } else { + n = length; + } - shar->uubuffer[shar->uuavail++] = *src++; - shar->outbytes++; + while (n >= 45) { + uuencode_line(shar, src, 45); + src += 45; + n -= 45; + + if (shar->work.length < 65536) + continue; + ret = (*a->compressor.write)(a, shar->work.s, + shar->work.length); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + archive_string_empty(&shar->work); + } + if (n != 0) { + memcpy(shar->outbuff, src, n); + shar->outpos = n; } return (length); } @@ -439,54 +505,43 @@ archive_write_shar_finish_entry(struct archive_write *a) if (shar->dump) { /* Finish uuencoded data. */ if (shar->has_data) { - if (shar->uuavail > 0) - uuencode_group(shar); - if (shar->outpos > 0) { - ret = shar_printf(a, "%c%s\n", - UUENC(shar->outbytes), shar->outbuff); - if (ret != ARCHIVE_OK) - return (ret); - shar->outpos = 0; - shar->uuavail = 0; - shar->outbytes = 0; - } - ret = shar_printf(a, "%c\n", UUENC(0)); - if (ret != ARCHIVE_OK) - return (ret); - ret = shar_printf(a, "end\n", UUENC(0)); - if (ret != ARCHIVE_OK) - return (ret); - ret = shar_printf(a, "SHAR_END\n"); - if (ret != ARCHIVE_OK) - return (ret); + if (shar->outpos > 0) + uuencode_line(shar, shar->outbuff, + shar->outpos); + archive_strcat(&shar->work, "`\nend\n"); + archive_strcat(&shar->work, "SHAR_END\n"); } /* Restore file mode, owner, flags. */ /* * TODO: Don't immediately restore mode for * directories; defer that to end of script. */ - ret = shar_printf(a, "chmod %o %s\n", - archive_entry_mode(shar->entry) & 07777, - archive_entry_pathname(shar->entry)); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, "chmod %o ", + archive_entry_mode(shar->entry) & 07777); + shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); + archive_strcat(&shar->work, "\n"); u = archive_entry_uname(shar->entry); g = archive_entry_gname(shar->entry); if (u != NULL || g != NULL) { - ret = shar_printf(a, "chown %s%s%s %s\n", - (u != NULL) ? u : "", - (g != NULL) ? ":" : "", (g != NULL) ? g : "", - archive_entry_pathname(shar->entry)); - if (ret != ARCHIVE_OK) - return (ret); + archive_strcat(&shar->work, "chown "); + if (u != NULL) + shar_quote(&shar->work, u, 1); + if (g != NULL) { + archive_strcat(&shar->work, ":"); + shar_quote(&shar->work, g, 1); + } + shar_quote(&shar->work, + archive_entry_pathname(shar->entry), 1); + archive_strcat(&shar->work, "\n"); } if ((p = archive_entry_fflags_text(shar->entry)) != NULL) { - ret = shar_printf(a, "chflags %s %s\n", p, - archive_entry_pathname(shar->entry)); - if (ret != ARCHIVE_OK) - return (ret); + archive_string_sprintf(&shar->work, "chflags %s ", + p, archive_entry_pathname(shar->entry)); + shar_quote(&shar->work, + archive_entry_pathname(shar->entry), 1); + archive_strcat(&shar->work, "\n"); } /* TODO: restore ACLs */ @@ -494,20 +549,24 @@ archive_write_shar_finish_entry(struct archive_write *a) } else { if (shar->has_data) { /* Finish sed-encoded data: ensure last line ends. */ - if (!shar->end_of_line) { - ret = shar_printf(a, "\n"); - if (ret != ARCHIVE_OK) - return (ret); - } - ret = shar_printf(a, "SHAR_END\n"); - if (ret != ARCHIVE_OK) - return (ret); + if (!shar->end_of_line) + archive_strappend_char(&shar->work, '\n'); + archive_strcat(&shar->work, "SHAR_END\n"); } } archive_entry_free(shar->entry); shar->entry = NULL; - return (0); + + if (shar->work.length < 65536) + return (ARCHIVE_OK); + + ret = (*a->compressor.write)(a, shar->work.s, shar->work.length); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + archive_string_empty(&shar->work); + + return (ARCHIVE_OK); } static int @@ -529,17 +588,22 @@ archive_write_shar_finish(struct archive_write *a) * shar format, then sets another format (which would invoke * shar_finish to free the format-specific data). */ - if (shar->wrote_header) { - ret = shar_printf(a, "exit\n"); - if (ret != ARCHIVE_OK) - return (ret); - /* Shar output is never padded. */ - archive_write_set_bytes_in_last_block(&a->archive, 1); - /* - * TODO: shar should also suppress padding of - * uncompressed data within gzip/bzip2 streams. - */ - } + if (shar->wrote_header == 0) + return (ARCHIVE_OK); + + archive_strcat(&shar->work, "exit\n"); + + ret = (*a->compressor.write)(a, shar->work.s, shar->work.length); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + /* Shar output is never padded. */ + archive_write_set_bytes_in_last_block(&a->archive, 1); + /* + * TODO: shar should also suppress padding of + * uncompressed data within gzip/bzip2 streams. + */ + return (ARCHIVE_OK); } @@ -549,11 +613,13 @@ archive_write_shar_destroy(struct archive_write *a) struct shar *shar; shar = (struct shar *)a->format_data; - if (shar->entry != NULL) - archive_entry_free(shar->entry); - if (shar->last_dir != NULL) - free(shar->last_dir); + if (shar == NULL) + return (ARCHIVE_OK); + + archive_entry_free(shar->entry); + free(shar->last_dir); archive_string_free(&(shar->work)); + archive_string_free(&(shar->quoted_name)); free(shar); a->format_data = NULL; return (ARCHIVE_OK); diff --git a/contrib/libarchive/libarchive/archive_write_set_format_ustar.c b/contrib/libarchive/libarchive/archive_write_set_format_ustar.c index 8aed25d1f9..14c13f7057 100644 --- a/contrib/libarchive/libarchive/archive_write_set_format_ustar.c +++ b/contrib/libarchive/libarchive/archive_write_set_format_ustar.c @@ -181,6 +181,7 @@ archive_write_set_format_ustar(struct archive *_a) a->format_data = ustar; a->pad_uncompressed = 1; /* Mimic gtar in this respect. */ + a->format_name = "ustar"; a->format_write_header = archive_write_ustar_header; a->format_write_data = archive_write_ustar_data; a->format_finish = archive_write_ustar_finish; diff --git a/contrib/libarchive/libarchive/filter_fork.c b/contrib/libarchive/libarchive/filter_fork.c index c746a186f5..7ce4e5de81 100644 --- a/contrib/libarchive/libarchive/filter_fork.c +++ b/contrib/libarchive/libarchive/filter_fork.c @@ -29,7 +29,7 @@ #if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \ (defined(HAVE_FORK) || defined(HAVE_VFORK)) -__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.4 2008/06/15 10:45:57 kientzle Exp $"); +__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.5 2008/09/12 05:33:00 kientzle Exp $"); #if defined(HAVE_POLL) # if defined(HAVE_POLL_H) @@ -61,7 +61,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) if (pipe(stdin_pipe) == -1) goto state_allocated; - if (stdin_pipe[0] == STDOUT_FILENO) { + if (stdin_pipe[0] == 1 /* stdout */) { if ((tmp = dup(stdin_pipe[0])) == -1) goto stdin_opened; close(stdin_pipe[0]); @@ -69,7 +69,7 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) } if (pipe(stdout_pipe) == -1) goto stdin_opened; - if (stdout_pipe[1] == STDIN_FILENO) { + if (stdout_pipe[1] == 0 /* stdin */) { if ((tmp = dup(stdout_pipe[1])) == -1) goto stdout_opened; close(stdout_pipe[1]); @@ -86,16 +86,16 @@ __archive_create_child(const char *path, int *child_stdin, int *child_stdout) case 0: close(stdin_pipe[1]); close(stdout_pipe[0]); - if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) + if (dup2(stdin_pipe[0], 0 /* stdin */) == -1) _exit(254); - if (stdin_pipe[0] != STDIN_FILENO) + if (stdin_pipe[0] != 0 /* stdin */) close(stdin_pipe[0]); - if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) + if (dup2(stdout_pipe[1], 1 /* stdout */) == -1) _exit(254); - if (stdout_pipe[1] != STDOUT_FILENO) + if (stdout_pipe[1] != 1 /* stdout */) close(stdout_pipe[1]); execlp(path, path, (char *)NULL); - _exit(254); + _exit(254); default: close(stdin_pipe[0]); close(stdout_pipe[1]); diff --git a/contrib/libarchive/tar/COPYING b/contrib/libarchive/tar/COPYING new file mode 100644 index 0000000000..9c14fd209f --- /dev/null +++ b/contrib/libarchive/tar/COPYING @@ -0,0 +1,62 @@ +$FreeBSD: src/usr.bin/tar/COPYING,v 1.3 2008/01/02 00:19:49 kientzle Exp $ + +All of the C source code and documentation in this package is subject +to the following: + +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. +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. + +Some of the filename pattern matching code is based on code subject +to the following license: + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + */ diff --git a/contrib/libarchive/tar/bsdtar.1 b/contrib/libarchive/tar/bsdtar.1 index e790d60ccb..ec2d61c505 100644 --- a/contrib/libarchive/tar/bsdtar.1 +++ b/contrib/libarchive/tar/bsdtar.1 @@ -22,9 +22,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.43 2008/05/26 17:10:10 kientzle Exp $ +.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.46 2008/12/06 07:37:55 kientzle Exp $ .\" -.Dd May 15, 2008 +.Dd March 25, 2009 .Dt BSDTAR 1 .Os .Sh NAME @@ -37,12 +37,12 @@ .Nm .Brq Fl c .Op Ar options -.Op Ar files | directories +.Op Ar files | Ar directories .Nm .Brq Fl r | Fl u .Fl f Ar archive-file .Op Ar options -.Op Ar files | directories +.Op Ar files | Ar directories .Nm .Brq Fl t | Fl x .Op Ar options @@ -144,21 +144,21 @@ In c and r mode, this changes the directory before adding the following files. In x mode, change directories after opening the archive but before extracting entries from the archive. -.It Fl -check-links ( Fl W Cm check-links ) +.It Fl -check-links (c and r modes only) Issue a warning message unless all links to each file are archived. -.It Fl -chroot ( Fl W Cm chroot ) +.It Fl -chroot (x mode only) .Fn chroot to the current directory after processing any .Fl C options and before extracting any files. -.It Fl -exclude Ar pattern ( Fl W Cm exclude Ns = Ns Ar pattern ) +.It Fl -exclude Ar pattern Do not process files or directories that match the specified pattern. Note that exclusions take precedence over patterns or filenames specified on the command line. -.It Fl -format Ar format ( Fl W Cm format Ns = Ns Ar format ) +.It Fl -format Ar format (c, r, u mode only) Use the specified format for the created archive. Supported formats include @@ -193,7 +193,7 @@ Synonym for .It Fl I Synonym for .Fl T . -.It Fl -include Ar pattern ( Fl W Cm include Ns = Ns Ar pattern ) +.It Fl -include Ar pattern Process only files or directories that match the specified pattern. Note that exclusions specified with .Fl -exclude @@ -225,7 +225,7 @@ automatically when reading archives. Do not overwrite existing files. In particular, if a file appears more than once in an archive, later copies will not overwrite earlier copies. -.It Fl -keep-newer-files ( Fl W Cm keep-newer-files ) +.It Fl -keep-newer-files (x mode only) Do not overwrite existing files that are newer than the versions appearing in the archive being extracted. @@ -245,28 +245,28 @@ By default, the modification time is set to the time stored in the archive. .It Fl n (c, r, u modes only) Do not recursively archive the contents of directories. -.It Fl -newer Ar date ( Fl W Cm newer Ns = Ns Ar date ) +.It Fl -newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. This compares ctime entries. -.It Fl -newer-mtime Ar date ( Fl W Cm newer-mtime Ns = Ns Ar date ) +.It Fl -newer-mtime Ar date (c, r, u modes only) Like .Fl -newer , except it compares mtime entries instead of ctime entries. -.It Fl -newer-than Pa file ( Fl W Cm newer-than Ns = Ns Pa file ) +.It Fl -newer-than Pa file (c, r, u modes only) Only include files and directories newer than the specified file. This compares ctime entries. -.It Fl -newer-mtime-than Pa file ( Fl W Cm newer-mtime-than Ns = Ns Pa file ) +.It Fl -newer-mtime-than Pa file (c, r, u modes only) Like .Fl -newer-than , except it compares mtime entries instead of ctime entries. -.It Fl -nodump ( Fl W Cm nodump ) +.It Fl -nodump (c and r modes only) Honor the nodump file flag by skipping this file. -.It Fl -null ( Fl W Cm null ) +.It Fl -null (use with .Fl I , .Fl T , @@ -302,9 +302,67 @@ the archive will be discarded. (c, r, u mode) A synonym for .Fl -format Ar ustar -.It Fl -one-file-system ( Fl W Cm one-file-system ) +.It Fl -one-file-system (c, r, and u modes) Do not cross mount points. +.It Fl -options Ar options +Select optional behaviors for particular modules. +The argument is a text string containing comma-separated +keywords and values. +These are passed to the modules that handle particular +formats to control how those formats will behave. +Each option has one of the following forms: +.Bl -tag -compact -width indent +.It Ar key=value +The key will be set to the specified value in every module that supports it. +Modules that do not support this key will ignore it. +.It Ar key +The key will be enabled in every module that supports it. +This is equivalent to +.Ar key Ns Cm =1 . +.It Ar !key +The key will be disabled in every module that supports it. +.It Ar module:key=value , Ar module:key , Ar module:!key +As above, but the corresponding key and value will be provided +only to modules whose name matches +.Ar module . +.El +The currently supported modules and keys are: +.Bl -tag -compact -width indent +.It Cm iso9660:joliet +Support Joliet extensions. +This is enabled by default, use +.Cm !joliet +or +.Cm iso9660:!joliet +to disable. +.It Cm gzip:compression-level +A decimal integer from 0 to 9 specifying the gzip compression level. +.It Cm xz:compression-level +A decimal integer from 0 to 9 specifying the xz compression level. +.It Cm mtree: Ns Ar keyword +The mtree writer module allows you to specify which mtree keywords +will be included in the output. +Supported keywords include: +.Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , +.Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , +.Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname . +The default is equivalent to: +.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname . +.It Cm mtree:all +Enables all of the above keywords. +You can also use +.Cm mtree:!all +to disable all keywords. +.It Cm mtree:use-set +Enable generation of +.Cm /set +lines in the output. +.It Cm mtree:indent +XXX need explanation XXX +.El +If a provided option is not supported by any module, that +is a fatal error. .It Fl P Preserve pathnames. By default, absolute pathnames (those that begin with a / @@ -345,8 +403,8 @@ Extract files as sparse files. For every block on disk, check first if it contains only NULL bytes and seek over it otherwise. This works similiar to the conv=sparse option of dd. -.It Fl -strip-components Ar count ( Fl W Cm strip-components Ns = Ns Ar count ) -(x and t mode only) +.It Fl -strip-components Ar count +(x mode only) Remove the specified number of leading path elements. Pathnames with fewer elements will be silently skipped. Note that the pathname is edited after checking inclusion/exclusion patterns @@ -412,16 +470,12 @@ will produce output similar to that of Additional .Fl v options will provide additional detail. -.It Fl W Ar longopt=value -Long options (preceded by -.Fl - ) -are only supported directly on systems that have the -.Xr getopt_long 3 -function. -The -.Fl W -option can be used to access long options on systems that -do not support this function. +.It Fl -version +Print version of +.Nm +and +.Nm libarchive , +and exit. .It Fl w Ask for confirmation for every action. .It Fl X Ar filename @@ -544,6 +598,7 @@ format can be used to create an output archive with arbitrary ownership, permissions, or names that differ from existing data on disk: .Pp .Dl $ cat input.mtree +.Dl #mtree .Dl usr/bin uid=0 gid=0 mode=0755 type=dir .Dl usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls .Dl $ tar -cvf output.tar @input.mtree @@ -558,6 +613,27 @@ switches accept a variety of common date and time specifications, including .Dq 5 minutes ago , and .Dq 19:14 PST May 1 . +.Pp +The +.Fl -options +argument can be used to control various details of archive generation +or reading. +For example, you can generate mtree output which only contains +.Cm type , Cm time , +and +.Cm uid +keywords: +.Dl Nm Fl cf Pa file.tar Fl -format=mtree Fl -options='!all,type,time,uid' Pa dir +or you can set the compression level used by gzip or xz compression: +.Dl Nm Fl czf Pa file.tar Fl -options='compression-level=9' . +For more details, see the explanation of the +.Fn archive_read_set_options +and +.Fn archive_write_set_options +API calls that are described in +.Xr archive_read 3 +and +.Xr archive_write 3 . .Sh COMPATIBILITY The bundled-arguments format is supported for compatibility with historic implementations. @@ -610,8 +686,8 @@ and .Cm w options. .Pp -On systems that support getopt_long(), additional long options -are available to improve compatibility with other tar implementations. +Additional long options are provided to improve compatibility with other +tar implementations. .Sh SECURITY Certain security issues are common to many archiving programs, including .Nm . diff --git a/contrib/libarchive/tar/bsdtar.c b/contrib/libarchive/tar/bsdtar.c index 1c13cc6bfb..81b9b61bcd 100644 --- a/contrib/libarchive/tar/bsdtar.c +++ b/contrib/libarchive/tar/bsdtar.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2008 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.91 2008/05/26 17:10:10 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.93 2008/11/08 04:43:24 kientzle Exp $"); #ifdef HAVE_SYS_PARAM_H #include @@ -38,18 +38,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.91 2008/05/26 17:10:10 kientzle #ifdef HAVE_FCNTL_H #include #endif -#ifdef HAVE_GETOPT_LONG -#include -#else -struct option { - const char *name; - int has_arg; - int *flag; - int val; -}; -#define no_argument 0 -#define required_argument 1 -#endif #ifdef HAVE_LANGINFO_H #include #endif @@ -78,14 +66,6 @@ struct option { #include "bsdtar.h" -#if !HAVE_DECL_OPTARG -extern int optarg; -#endif - -#if !HAVE_DECL_OPTIND -extern int optind; -#endif - /* * Per POSIX.1-1988, tar defaults to reading/writing archives to/from * the default tape device for the system. Pick something reasonable here. @@ -93,141 +73,23 @@ extern int optind; #ifdef __linux #define _PATH_DEFTAPE "/dev/st0" #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#define _PATH_DEFTAPE "\\\\.\\tape0" +#endif #ifndef _PATH_DEFTAPE #define _PATH_DEFTAPE "/dev/tape" #endif /* External function to parse a date/time string (from getdate.y) */ -time_t get_date(const char *); +time_t get_date(time_t, const char *); -static int bsdtar_getopt(struct bsdtar *, const char *optstring, - const struct option **poption); static void long_help(struct bsdtar *); static void only_mode(struct bsdtar *, const char *opt, const char *valid); -static char ** rewrite_argv(struct bsdtar *, - int *argc, char ** src_argv, - const char *optstring); static void set_mode(struct bsdtar *, char opt); static void version(void); -/* - * The leading '+' here forces the GNU version of getopt() (as well as - * both the GNU and BSD versions of getopt_long) to stop at the first - * non-option. Otherwise, GNU getopt() permutes the arguments and - * screws up -C processing. - */ -static const char *tar_opts = "+Bb:C:cf:HhI:jkLlmnOoPprts:ST:UuvW:wX:xyZz"; - -/* - * Most of these long options are deliberately not documented. They - * are provided only to make life easier for people who also use GNU tar. - * The only long options documented in the manual page are the ones - * with no corresponding short option, such as --exclude, --nodump, - * and --fast-read. - * - * On systems that lack getopt_long, long options can be specified - * using -W longopt and -W longopt=value, e.g. "-W nodump" is the same - * as "--nodump" and "-W exclude=pattern" is the same as "--exclude - * pattern". This does not rely the GNU getopt() "W;" extension, so - * should work correctly on any system with a POSIX-compliant getopt(). - */ - -/* Fake short equivalents for long options that otherwise lack them. */ -enum { - OPTION_CHECK_LINKS = 1, - OPTION_CHROOT, - OPTION_EXCLUDE, - OPTION_FORMAT, - OPTION_HELP, - OPTION_INCLUDE, - OPTION_KEEP_NEWER_FILES, - OPTION_NEWER_CTIME, - OPTION_NEWER_CTIME_THAN, - OPTION_NEWER_MTIME, - OPTION_NEWER_MTIME_THAN, - OPTION_NODUMP, - OPTION_NO_SAME_OWNER, - OPTION_NO_SAME_PERMISSIONS, - OPTION_NULL, - OPTION_NUMERIC_OWNER, - OPTION_ONE_FILE_SYSTEM, - OPTION_POSIX, - OPTION_STRIP_COMPONENTS, - OPTION_TOTALS, - OPTION_USE_COMPRESS_PROGRAM, - OPTION_VERSION -}; - -/* - * If you add anything, be very careful to keep this list properly - * sorted, as the -W logic relies on it. - */ -static const struct option tar_longopts[] = { - { "absolute-paths", no_argument, NULL, 'P' }, - { "append", no_argument, NULL, 'r' }, - { "block-size", required_argument, NULL, 'b' }, - { "bunzip2", no_argument, NULL, 'j' }, - { "bzip", no_argument, NULL, 'j' }, - { "bzip2", no_argument, NULL, 'j' }, - { "cd", required_argument, NULL, 'C' }, - { "check-links", no_argument, NULL, OPTION_CHECK_LINKS }, - { "chroot", no_argument, NULL, OPTION_CHROOT }, - { "compress", no_argument, NULL, 'Z' }, - { "confirmation", no_argument, NULL, 'w' }, - { "create", no_argument, NULL, 'c' }, - { "dereference", no_argument, NULL, 'L' }, - { "directory", required_argument, NULL, 'C' }, - { "exclude", required_argument, NULL, OPTION_EXCLUDE }, - { "exclude-from", required_argument, NULL, 'X' }, - { "extract", no_argument, NULL, 'x' }, - { "fast-read", no_argument, NULL, 'q' }, - { "file", required_argument, NULL, 'f' }, - { "files-from", required_argument, NULL, 'T' }, - { "format", required_argument, NULL, OPTION_FORMAT }, - { "gunzip", no_argument, NULL, 'z' }, - { "gzip", no_argument, NULL, 'z' }, - { "help", no_argument, NULL, OPTION_HELP }, - { "include", required_argument, NULL, OPTION_INCLUDE }, - { "interactive", no_argument, NULL, 'w' }, - { "insecure", no_argument, NULL, 'P' }, - { "keep-newer-files", no_argument, NULL, OPTION_KEEP_NEWER_FILES }, - { "keep-old-files", no_argument, NULL, 'k' }, - { "list", no_argument, NULL, 't' }, - { "modification-time", no_argument, NULL, 'm' }, - { "newer", required_argument, NULL, OPTION_NEWER_CTIME }, - { "newer-ctime", required_argument, NULL, OPTION_NEWER_CTIME }, - { "newer-ctime-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, - { "newer-mtime", required_argument, NULL, OPTION_NEWER_MTIME }, - { "newer-mtime-than", required_argument, NULL, OPTION_NEWER_MTIME_THAN }, - { "newer-than", required_argument, NULL, OPTION_NEWER_CTIME_THAN }, - { "nodump", no_argument, NULL, OPTION_NODUMP }, - { "norecurse", no_argument, NULL, 'n' }, - { "no-recursion", no_argument, NULL, 'n' }, - { "no-same-owner", no_argument, NULL, OPTION_NO_SAME_OWNER }, - { "no-same-permissions",no_argument, NULL, OPTION_NO_SAME_PERMISSIONS }, - { "null", no_argument, NULL, OPTION_NULL }, - { "numeric-owner", no_argument, NULL, OPTION_NUMERIC_OWNER }, - { "one-file-system", no_argument, NULL, OPTION_ONE_FILE_SYSTEM }, - { "posix", no_argument, NULL, OPTION_POSIX }, - { "preserve-permissions", no_argument, NULL, 'p' }, - { "read-full-blocks", no_argument, NULL, 'B' }, - { "same-permissions", no_argument, NULL, 'p' }, - { "strip-components", required_argument, NULL, OPTION_STRIP_COMPONENTS }, - { "to-stdout", no_argument, NULL, 'O' }, - { "totals", no_argument, NULL, OPTION_TOTALS }, - { "uncompress", no_argument, NULL, 'Z' }, - { "unlink", no_argument, NULL, 'U' }, - { "unlink-first", no_argument, NULL, 'U' }, - { "update", no_argument, NULL, 'u' }, - { "use-compress-program", - required_argument, NULL, OPTION_USE_COMPRESS_PROGRAM }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, OPTION_VERSION }, - { NULL, 0, NULL, 0 } -}; - /* A basic set of security flags to request from libarchive. */ #define SECURITY \ (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ @@ -237,11 +99,11 @@ int main(int argc, char **argv) { struct bsdtar *bsdtar, bsdtar_storage; - const struct option *option; int opt, t; char option_o; char possible_help_request; char buff[16]; + time_t now; /* * Use a pointer for consistency, but stack-allocated storage @@ -251,18 +113,30 @@ main(int argc, char **argv) memset(bsdtar, 0, sizeof(*bsdtar)); bsdtar->fd = -1; /* Mark as "unused" */ option_o = 0; +#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 bsdtar->progname before calling bsdtar_warnc. */ if (*argv == NULL) bsdtar->progname = "bsdtar"; else { +#if defined(_WIN32) && !defined(__CYGWIN__) + bsdtar->progname = strrchr(*argv, '\\'); +#else bsdtar->progname = strrchr(*argv, '/'); +#endif if (bsdtar->progname != NULL) bsdtar->progname++; else bsdtar->progname = *argv; } + time(&now); + if (setlocale(LC_ALL, "") == NULL) bsdtar_warnc(bsdtar, 0, "Failed to set default locale"); #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) @@ -285,7 +159,7 @@ main(int argc, char **argv) bsdtar->extract_flags |= SECURITY; /* Defaults for root user: */ - if (bsdtar->user_uid == 0) { + if (bsdtar_is_privileged(bsdtar)) { /* --same-owner */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; /* -p */ @@ -295,33 +169,29 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; } - /* Rewrite traditional-style tar arguments, if used. */ - argv = rewrite_argv(bsdtar, &argc, argv, tar_opts); - bsdtar->argv = argv; bsdtar->argc = argc; - /* Process all remaining arguments now. */ /* * Comments following each option indicate where that option * originated: SUSv2, POSIX, GNU tar, star, etc. If there's * no such comment, then I don't know of anyone else who * implements that option. */ - while ((opt = bsdtar_getopt(bsdtar, tar_opts, &option)) != -1) { + while ((opt = bsdtar_getopt(bsdtar)) != -1) { switch (opt) { case 'B': /* GNU tar */ /* libarchive doesn't need this; just ignore it. */ break; case 'b': /* SUSv2 */ - t = atoi(optarg); + t = atoi(bsdtar->optarg); if (t <= 0 || t > 1024) bsdtar_errc(bsdtar, 1, 0, "Argument to -b is out of range (1..1024)"); bsdtar->bytes_per_block = 512 * t; break; case 'C': /* GNU tar */ - set_chdir(bsdtar, optarg); + set_chdir(bsdtar, bsdtar->optarg); break; case 'c': /* SUSv2 */ set_mode(bsdtar, opt); @@ -333,15 +203,18 @@ main(int argc, char **argv) bsdtar->option_chroot = 1; break; case OPTION_EXCLUDE: /* GNU tar */ - if (exclude(bsdtar, optarg)) + if (exclude(bsdtar, bsdtar->optarg)) bsdtar_errc(bsdtar, 1, 0, - "Couldn't exclude %s\n", optarg); + "Couldn't exclude %s\n", bsdtar->optarg); break; case OPTION_FORMAT: /* GNU tar, others */ - bsdtar->create_format = optarg; + bsdtar->create_format = bsdtar->optarg; + break; + case OPTION_OPTIONS: + bsdtar->option_options = bsdtar->optarg; break; case 'f': /* SUSv2 */ - bsdtar->filename = optarg; + bsdtar->filename = bsdtar->optarg; if (strcmp(bsdtar->filename, "-") == 0) bsdtar->filename = NULL; break; @@ -368,7 +241,7 @@ main(int argc, char **argv) * permissions without having to create those * permissions on disk. */ - bsdtar->names_from_file = optarg; + bsdtar->names_from_file = bsdtar->optarg; break; case OPTION_INCLUDE: /* @@ -376,10 +249,10 @@ main(int argc, char **argv) * noone else needs this to filter entries * when transforming archives. */ - if (include(bsdtar, optarg)) + if (include(bsdtar, bsdtar->optarg)) bsdtar_errc(bsdtar, 1, 0, "Failed to add %s to inclusion list", - optarg); + bsdtar->optarg); break; case 'j': /* GNU tar */ #if HAVE_LIBBZ2 @@ -389,7 +262,21 @@ main(int argc, char **argv) bsdtar->create_compression); bsdtar->create_compression = opt; #else - bsdtar_warnc(bsdtar, 0, "-j compression not supported by this version of bsdtar"); + bsdtar_warnc(bsdtar, 0, + "bzip2 compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; + case 'J': /* GNU tar 1.21 and later */ +#if HAVE_LIBLZMA + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, + "xz compression not supported by this version of bsdtar"); usage(bsdtar); #endif break; @@ -406,6 +293,19 @@ main(int argc, char **argv) /* GNU tar 1.13 used -l for --one-file-system */ bsdtar->option_warn_links = 1; break; + case OPTION_LZMA: +#if HAVE_LIBLZMA + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, + "lzma compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; case 'm': /* SUSv2 */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; break; @@ -420,28 +320,28 @@ main(int argc, char **argv) * TODO: Add corresponding "older" options to reverse these. */ case OPTION_NEWER_CTIME: /* GNU tar */ - bsdtar->newer_ctime_sec = get_date(optarg); + bsdtar->newer_ctime_sec = get_date(now, bsdtar->optarg); break; case OPTION_NEWER_CTIME_THAN: { struct stat st; - if (stat(optarg, &st) != 0) + if (stat(bsdtar->optarg, &st) != 0) bsdtar_errc(bsdtar, 1, 0, - "Can't open file %s", optarg); + "Can't open file %s", bsdtar->optarg); bsdtar->newer_ctime_sec = st.st_ctime; bsdtar->newer_ctime_nsec = ARCHIVE_STAT_CTIME_NANOS(&st); } break; case OPTION_NEWER_MTIME: /* GNU tar */ - bsdtar->newer_mtime_sec = get_date(optarg); + bsdtar->newer_mtime_sec = get_date(now, bsdtar->optarg); break; case OPTION_NEWER_MTIME_THAN: { struct stat st; - if (stat(optarg, &st) != 0) + if (stat(bsdtar->optarg, &st) != 0) bsdtar_errc(bsdtar, 1, 0, - "Can't open file %s", optarg); + "Can't open file %s", bsdtar->optarg); bsdtar->newer_mtime_sec = st.st_mtime; bsdtar->newer_mtime_nsec = ARCHIVE_STAT_MTIME_NANOS(&st); @@ -509,17 +409,21 @@ main(int argc, char **argv) break; case 's': /* NetBSD pax-as-tar */ #if HAVE_REGEX_H - add_substitution(bsdtar, optarg); + add_substitution(bsdtar, bsdtar->optarg); #else - bsdtar_warnc(bsdtar, 0, "-s is not supported by this version of bsdtar"); + bsdtar_warnc(bsdtar, 0, + "-s is not supported by this version of bsdtar"); usage(bsdtar); #endif break; + case OPTION_SAME_OWNER: /* GNU tar */ + bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; + break; case OPTION_STRIP_COMPONENTS: /* GNU tar 1.15 */ - bsdtar->strip_components = atoi(optarg); + bsdtar->strip_components = atoi(bsdtar->optarg); break; case 'T': /* GNU tar */ - bsdtar->names_from_file = optarg; + bsdtar->names_from_file = bsdtar->optarg; break; case 't': /* SUSv2 */ set_mode(bsdtar, opt); @@ -544,19 +448,19 @@ main(int argc, char **argv) #if 0 /* * The -W longopt feature is handled inside of - * bsdtar_getop(), so -W is not available here. + * bsdtar_getopt(), so -W is not available here. */ - case 'W': /* Obscure, but useful GNU convention. */ + case 'W': /* Obscure GNU convention. */ break; #endif case 'w': /* SUSv2 */ bsdtar->option_interactive = 1; break; case 'X': /* GNU tar */ - if (exclude_from_file(bsdtar, optarg)) + if (exclude_from_file(bsdtar, bsdtar->optarg)) bsdtar_errc(bsdtar, 1, 0, "failed to process exclusions from file %s", - optarg); + bsdtar->optarg); break; case 'x': /* SUSv2 */ set_mode(bsdtar, opt); @@ -569,7 +473,8 @@ main(int argc, char **argv) bsdtar->create_compression); bsdtar->create_compression = opt; #else - bsdtar_warnc(bsdtar, 0, "-y compression not supported by this version of bsdtar"); + bsdtar_warnc(bsdtar, 0, + "bzip2 compression not supported by this version of bsdtar"); usage(bsdtar); #endif break; @@ -588,12 +493,13 @@ main(int argc, char **argv) bsdtar->create_compression); bsdtar->create_compression = opt; #else - bsdtar_warnc(bsdtar, 0, "-z compression not supported by this version of bsdtar"); + bsdtar_warnc(bsdtar, 0, + "gzip compression not supported by this version of bsdtar"); usage(bsdtar); #endif break; case OPTION_USE_COMPRESS_PROGRAM: - bsdtar->compress_program = optarg; + bsdtar->compress_program = bsdtar->optarg; break; default: usage(bsdtar); @@ -668,9 +574,6 @@ main(int argc, char **argv) if (bsdtar->strip_components != 0) only_mode(bsdtar, "--strip-components", "xt"); - bsdtar->argc -= optind; - bsdtar->argv += optind; - switch(bsdtar->mode) { case 'c': tar_mode_c(bsdtar); @@ -722,72 +625,6 @@ only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) } -/*- - * Convert traditional tar arguments into new-style. - * For example, - * tar tvfb file.tar 32 --exclude FOO - * will be converted to - * tar -t -v -f file.tar -b 32 --exclude FOO - * - * This requires building a new argv array. The initial bundled word - * gets expanded into a new string that looks like "-t\0-v\0-f\0-b\0". - * The new argv array has pointers into this string intermingled with - * pointers to the existing arguments. Arguments are moved to - * immediately follow their options. - * - * The optstring argument here is the same one passed to getopt(3). - * It is used to determine which option letters have trailing arguments. - */ -char ** -rewrite_argv(struct bsdtar *bsdtar, int *argc, char **src_argv, - const char *optstring) -{ - char **new_argv, **dest_argv; - const char *p; - char *src, *dest; - - if (src_argv[0] == NULL || src_argv[1] == NULL || - src_argv[1][0] == '-' || src_argv[1][0] == '\0') - return (src_argv); - - *argc += strlen(src_argv[1]) - 1; - new_argv = malloc((*argc + 1) * sizeof(new_argv[0])); - if (new_argv == NULL) - bsdtar_errc(bsdtar, 1, errno, "No Memory"); - - dest_argv = new_argv; - *dest_argv++ = *src_argv++; - - dest = malloc(strlen(*src_argv) * 3); - if (dest == NULL) - bsdtar_errc(bsdtar, 1, errno, "No memory"); - for (src = *src_argv++; *src != '\0'; src++) { - *dest_argv++ = dest; - *dest++ = '-'; - *dest++ = *src; - *dest++ = '\0'; - /* If option takes an argument, insert that into the list. */ - for (p = optstring; p != NULL && *p != '\0'; p++) { - if (*p != *src) - continue; - if (p[1] != ':') /* No arg required, done. */ - break; - if (*src_argv == NULL) /* No arg available? Error. */ - bsdtar_errc(bsdtar, 1, 0, - "Option %c requires an argument", - *src); - *dest_argv++ = *src_argv++; - break; - } - } - - /* Copy remaining arguments, including trailing NULL. */ - while ((*dest_argv++ = *src_argv++) != NULL) - ; - - return (new_argv); -} - void usage(struct bsdtar *bsdtar) { @@ -799,11 +636,7 @@ usage(struct bsdtar *bsdtar) fprintf(stderr, " List: %s -tf \n", p); fprintf(stderr, " Extract: %s -xf \n", p); fprintf(stderr, " Create: %s -cf [filenames...]\n", p); -#ifdef HAVE_GETOPT_LONG fprintf(stderr, " Help: %s --help\n", p); -#else - fprintf(stderr, " Help: %s -h\n", p); -#endif exit(1); } @@ -826,13 +659,9 @@ static const char *long_help_msg = " -w Interactive\n" "Create: %p -c [options] [ | | @ | -C ]\n" " , add these items to archive\n" - " -z, -j Compress archive with gzip/bzip2\n" + " -z, -j, -J, --lzma Compress archive with gzip/bzip2/xz/lzma\n" " --format {ustar|pax|cpio|shar} Select archive format\n" -#ifdef HAVE_GETOPT_LONG " --exclude Skip files that match pattern\n" -#else - " -W exclude= Skip files that match pattern\n" -#endif " -C Change to before processing remaining files\n" " @ Add entries from to output\n" "List: %p -t [options] []\n" @@ -880,80 +709,3 @@ long_help(struct bsdtar *bsdtar) } version(); } - -static int -bsdtar_getopt(struct bsdtar *bsdtar, const char *optstring, - const struct option **poption) -{ - char *p, *q; - const struct option *option; - int opt; - int option_index; - size_t option_length; - - option_index = -1; - *poption = NULL; - -#ifdef HAVE_GETOPT_LONG - opt = getopt_long(bsdtar->argc, bsdtar->argv, optstring, - tar_longopts, &option_index); - if (option_index > -1) - *poption = tar_longopts + option_index; -#else - opt = getopt(bsdtar->argc, bsdtar->argv, optstring); -#endif - - /* Support long options through -W longopt=value */ - if (opt == 'W') { - p = optarg; - q = strchr(optarg, '='); - if (q != NULL) { - option_length = (size_t)(q - p); - optarg = q + 1; - } else { - option_length = strlen(p); - optarg = NULL; - } - option = tar_longopts; - while (option->name != NULL && - (strlen(option->name) < option_length || - strncmp(p, option->name, option_length) != 0 )) { - option++; - } - - if (option->name != NULL) { - *poption = option; - opt = option->val; - - /* If the first match was exact, we're done. */ - if (strncmp(p, option->name, strlen(option->name)) == 0) { - while (option->name != NULL) - option++; - } else { - /* Check if there's another match. */ - option++; - while (option->name != NULL && - (strlen(option->name) < option_length || - strncmp(p, option->name, option_length) != 0)) { - option++; - } - } - if (option->name != NULL) - bsdtar_errc(bsdtar, 1, 0, - "Ambiguous option %s " - "(matches both %s and %s)", - p, (*poption)->name, option->name); - - if ((*poption)->has_arg == required_argument - && optarg == NULL) - bsdtar_errc(bsdtar, 1, 0, - "Option \"%s\" requires argument", p); - } else { - opt = '?'; - /* TODO: Set up a fake 'struct option' for - * error reporting... ? ? ? */ - } - } - - return (opt); -} diff --git a/contrib/libarchive/tar/bsdtar.h b/contrib/libarchive/tar/bsdtar.h index 4153a44f94..26fe0d3f13 100644 --- a/contrib/libarchive/tar/bsdtar.h +++ b/contrib/libarchive/tar/bsdtar.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.33 2008/05/26 17:10:10 kientzle Exp $ + * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.37 2008/12/06 07:37:14 kientzle Exp $ */ #include "bsdtar_platform.h" @@ -60,6 +60,7 @@ struct bsdtar { char option_chroot; /* --chroot */ char option_dont_traverse_mounts; /* --one-file-system */ char option_fast_read; /* --fast-read */ + const char *option_options; /* --options */ char option_honor_nodump; /* --nodump */ char option_interactive; /* -w */ char option_no_owner; /* -o */ @@ -80,6 +81,7 @@ struct bsdtar { const char *progname; int argc; char **argv; + const char *optarg; size_t gs_width; /* For 'list_item' in read.c */ size_t u_width; /* for 'list_item' in read.c */ uid_t user_uid; /* UID running this program */ @@ -91,9 +93,11 @@ struct bsdtar { * Data for various subsystems. Full definitions are located in * the file where they are used. */ - struct archive_entry_linkresolver *resolver; + struct archive *diskreader; /* for write.c */ + struct archive_entry_linkresolver *resolver; /* for write.c */ struct archive_dir *archive_dir; /* for write.c */ struct name_cache *gname_cache; /* for write.c */ + char *buff; /* for write.c */ struct matching *matching; /* for matching.c */ struct security *security; /* for read.c */ struct name_cache *uname_cache; /* for write.c */ @@ -101,8 +105,39 @@ struct bsdtar { struct substitution *substitution; /* for subst.c */ }; +/* Fake short equivalents for long options that otherwise lack them. */ +enum { + OPTION_CHECK_LINKS = 1, + OPTION_CHROOT, + OPTION_EXCLUDE, + OPTION_FORMAT, + OPTION_OPTIONS, + OPTION_HELP, + OPTION_INCLUDE, + OPTION_KEEP_NEWER_FILES, + OPTION_LZMA, + OPTION_NEWER_CTIME, + OPTION_NEWER_CTIME_THAN, + OPTION_NEWER_MTIME, + OPTION_NEWER_MTIME_THAN, + OPTION_NODUMP, + OPTION_NO_SAME_OWNER, + OPTION_NO_SAME_PERMISSIONS, + OPTION_NULL, + OPTION_NUMERIC_OWNER, + OPTION_ONE_FILE_SYSTEM, + OPTION_POSIX, + OPTION_SAME_OWNER, + OPTION_STRIP_COMPONENTS, + OPTION_TOTALS, + OPTION_USE_COMPRESS_PROGRAM, + OPTION_VERSION +}; + + void bsdtar_errc(struct bsdtar *, int _eval, int _code, - const char *fmt, ...); + const char *fmt, ...) __LA_DEAD; +int bsdtar_getopt(struct bsdtar *); void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...); void cleanup_exclusions(struct bsdtar *); void do_chdir(struct bsdtar *); diff --git a/contrib/libarchive/tar/bsdtar_platform.h b/contrib/libarchive/tar/bsdtar_platform.h index ccb9d3c025..86f344260b 100644 --- a/contrib/libarchive/tar/bsdtar_platform.h +++ b/contrib/libarchive/tar/bsdtar_platform.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.25 2008/01/02 00:23:00 kientzle Exp $ + * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.26 2008/12/06 07:37:14 kientzle Exp $ */ /* @@ -39,7 +39,7 @@ #include PLATFORM_CONFIG_H #elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ -#include "../config.h" +#include "config.h" #else /* Warn if bsdtar hasn't been (automatically or manually) configured. */ #error Oops: No config.h and no built-in configuration in bsdtar_platform.h. @@ -137,14 +137,39 @@ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC #define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec #define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec -#else -#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC #define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctim.tv_nsec #define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtim.tv_nsec +#elif HAVE_STRUCT_STAT_ST_MTIME_N +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctime_n +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtime_n +#elif HAVE_STRUCT_STAT_ST_UMTIME +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_uctime * 1000 +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_umtime * 1000 +#elif HAVE_STRUCT_STAT_ST_MTIME_USEC +#define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctime_usec * 1000 +#define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtime_usec * 1000 #else #define ARCHIVE_STAT_CTIME_NANOS(st) (0) #define ARCHIVE_STAT_MTIME_NANOS(st) (0) #endif + +/* How to mark functions that don't return. */ +/* This facilitates use of some newer static code analysis tools. */ +#undef __LA_DEAD +#if defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_DEAD __attribute__((__noreturn__)) +#else +#define __LA_DEAD +#endif + +#if defined(__CYGWIN__) +#include "bsdtar_cygwin.h" +#elif defined(_WIN32) /* && !__CYGWIN__ */ +#include "bsdtar_windows.h" +#else +#define bsdtar_is_privileged(bsdtar) (bsdtar->user_uid == 0) #endif #endif /* !BSDTAR_PLATFORM_H_INCLUDED */ diff --git a/contrib/libarchive/tar/cmdline.c b/contrib/libarchive/tar/cmdline.c new file mode 100644 index 0000000000..efaeafdcb2 --- /dev/null +++ b/contrib/libarchive/tar/cmdline.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 2003-2008 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. + * 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. + */ + +/* + * Command line parser for tar. + */ + +#include "bsdtar_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "bsdtar.h" + +/* + * Short options for tar. Please keep this sorted. + */ +static const char *short_options + = "Bb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz"; + +/* + * Long options for tar. Please keep this list sorted. + * + * The symbolic names for options that lack a short equivalent are + * defined in bsdtar.h. Also note that so far I've found no need + * to support optional arguments to long options. That would be + * a small change to the code below. + */ + +static struct option { + const char *name; + int required; /* 1 if this option requires an argument. */ + int equivalent; /* Equivalent short option. */ +} tar_longopts[] = { + { "absolute-paths", 0, 'P' }, + { "append", 0, 'r' }, + { "block-size", 1, 'b' }, + { "bunzip2", 0, 'j' }, + { "bzip", 0, 'j' }, + { "bzip2", 0, 'j' }, + { "cd", 1, 'C' }, + { "check-links", 0, OPTION_CHECK_LINKS }, + { "chroot", 0, OPTION_CHROOT }, + { "compress", 0, 'Z' }, + { "confirmation", 0, 'w' }, + { "create", 0, 'c' }, + { "dereference", 0, 'L' }, + { "directory", 1, 'C' }, + { "exclude", 1, OPTION_EXCLUDE }, + { "exclude-from", 1, 'X' }, + { "extract", 0, 'x' }, + { "fast-read", 0, 'q' }, + { "file", 1, 'f' }, + { "files-from", 1, 'T' }, + { "format", 1, OPTION_FORMAT }, + { "options", 1, OPTION_OPTIONS }, + { "gunzip", 0, 'z' }, + { "gzip", 0, 'z' }, + { "help", 0, OPTION_HELP }, + { "include", 1, OPTION_INCLUDE }, + { "interactive", 0, 'w' }, + { "insecure", 0, 'P' }, + { "keep-newer-files", 0, OPTION_KEEP_NEWER_FILES }, + { "keep-old-files", 0, 'k' }, + { "list", 0, 't' }, + { "lzma", 0, OPTION_LZMA }, + { "modification-time", 0, 'm' }, + { "newer", 1, OPTION_NEWER_CTIME }, + { "newer-ctime", 1, OPTION_NEWER_CTIME }, + { "newer-ctime-than", 1, OPTION_NEWER_CTIME_THAN }, + { "newer-mtime", 1, OPTION_NEWER_MTIME }, + { "newer-mtime-than", 1, OPTION_NEWER_MTIME_THAN }, + { "newer-than", 1, OPTION_NEWER_CTIME_THAN }, + { "nodump", 0, OPTION_NODUMP }, + { "norecurse", 0, 'n' }, + { "no-recursion", 0, 'n' }, + { "no-same-owner", 0, OPTION_NO_SAME_OWNER }, + { "no-same-permissions", 0, OPTION_NO_SAME_PERMISSIONS }, + { "null", 0, OPTION_NULL }, + { "numeric-owner", 0, OPTION_NUMERIC_OWNER }, + { "one-file-system", 0, OPTION_ONE_FILE_SYSTEM }, + { "posix", 0, OPTION_POSIX }, + { "preserve-permissions", 0, 'p' }, + { "read-full-blocks", 0, 'B' }, + { "same-owner", 0, OPTION_SAME_OWNER }, + { "same-permissions", 0, 'p' }, + { "strip-components", 1, OPTION_STRIP_COMPONENTS }, + { "to-stdout", 0, 'O' }, + { "totals", 0, OPTION_TOTALS }, + { "uncompress", 0, 'Z' }, + { "unlink", 0, 'U' }, + { "unlink-first", 0, 'U' }, + { "update", 0, 'u' }, + { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM }, + { "verbose", 0, 'v' }, + { "version", 0, OPTION_VERSION }, + { "xz", 0, 'J' }, + { NULL, 0, 0 } +}; + +/* + * This getopt implementation has two key features that common + * getopt_long() implementations lack. Apart from those, it's a + * straightforward option parser, considerably simplified by not + * needing to support the wealth of exotic getopt_long() features. It + * has, of course, been shamelessly tailored for bsdtar. (If you're + * looking for a generic getopt_long() implementation for your + * project, I recommend Gregory Pietsch's public domain getopt_long() + * implementation.) The two additional features are: + * + * Old-style tar arguments: The original tar implementation treated + * the first argument word as a list of single-character option + * letters. All arguments follow as separate words. For example, + * tar xbf 32 /dev/tape + * Here, the "xbf" is three option letters, "32" is the argument for + * "b" and "/dev/tape" is the argument for "f". We support this usage + * if the first command-line argument does not begin with '-'. We + * also allow regular short and long options to follow, e.g., + * tar xbf 32 /dev/tape -P --format=pax + * + * -W long options: There's an obscure GNU convention (only rarely + * supported even there) that allows "-W option=argument" as an + * alternative way to support long options. This was supported in + * early bsdtar as a way to access long options on platforms that did + * not support getopt_long() and is preserved here for backwards + * compatibility. (Of course, if I'd started with a custom + * command-line parser from the beginning, I would have had normal + * long option support on every platform so that hack wouldn't have + * been necessary. Oh, well. Some mistakes you just have to live + * with.) + * + * TODO: We should be able to use this to pull files and intermingled + * options (such as -C) from the command line in write mode. That + * will require a little rethinking of the argument handling in + * bsdtar.c. + * + * TODO: If we want to support arbitrary command-line options from -T + * input (as GNU tar does), we may need to extend this to handle option + * words from sources other than argv/arc. I'm not really sure if I + * like that feature of GNU tar, so it's certainly not a priority. + */ + +int +bsdtar_getopt(struct bsdtar *bsdtar) +{ + enum { state_start = 0, state_old_tar, state_next_word, + state_short, state_long }; + static int state = state_start; + static char *opt_word; + + const struct option *popt, *match = NULL, *match2 = NULL; + const char *p, *long_prefix = "--"; + size_t optlength; + int opt = '?'; + int required = 0; + + bsdtar->optarg = NULL; + + /* First time through, initialize everything. */ + if (state == state_start) { + /* Skip program name. */ + ++bsdtar->argv; + --bsdtar->argc; + if (*bsdtar->argv == NULL) + return (-1); + /* Decide between "new style" and "old style" arguments. */ + if (bsdtar->argv[0][0] == '-') { + state = state_next_word; + } else { + state = state_old_tar; + opt_word = *bsdtar->argv++; + --bsdtar->argc; + } + } + + /* + * We're parsing old-style tar arguments + */ + if (state == state_old_tar) { + /* Get the next option character. */ + opt = *opt_word++; + if (opt == '\0') { + /* New-style args can follow old-style. */ + state = state_next_word; + } else { + /* See if it takes an argument. */ + p = strchr(short_options, opt); + if (p == NULL) + return ('?'); + if (p[1] == ':') { + bsdtar->optarg = *bsdtar->argv; + if (bsdtar->optarg == NULL) { + bsdtar_warnc(bsdtar, 0, + "Option %c requires an argument", + opt); + return ('?'); + } + ++bsdtar->argv; + --bsdtar->argc; + } + } + } + + /* + * We're ready to look at the next word in argv. + */ + if (state == state_next_word) { + /* No more arguments, so no more options. */ + if (bsdtar->argv[0] == NULL) + return (-1); + /* Doesn't start with '-', so no more options. */ + if (bsdtar->argv[0][0] != '-') + return (-1); + /* "--" marks end of options; consume it and return. */ + if (strcmp(bsdtar->argv[0], "--") == 0) { + ++bsdtar->argv; + --bsdtar->argc; + return (-1); + } + /* Get next word for parsing. */ + opt_word = *bsdtar->argv++; + --bsdtar->argc; + if (opt_word[1] == '-') { + /* Set up long option parser. */ + state = state_long; + opt_word += 2; /* Skip leading '--' */ + } else { + /* Set up short option parser. */ + state = state_short; + ++opt_word; /* Skip leading '-' */ + } + } + + /* + * We're parsing a group of POSIX-style single-character options. + */ + if (state == state_short) { + /* Peel next option off of a group of short options. */ + opt = *opt_word++; + if (opt == '\0') { + /* End of this group; recurse to get next option. */ + state = state_next_word; + return bsdtar_getopt(bsdtar); + } + + /* Does this option take an argument? */ + p = strchr(short_options, opt); + if (p == NULL) + return ('?'); + if (p[1] == ':') + required = 1; + + /* If it takes an argument, parse that. */ + if (required) { + /* If arg is run-in, opt_word already points to it. */ + if (opt_word[0] == '\0') { + /* Otherwise, pick up the next word. */ + opt_word = *bsdtar->argv; + if (opt_word == NULL) { + bsdtar_warnc(bsdtar, 0, + "Option -%c requires an argument", + opt); + return ('?'); + } + ++bsdtar->argv; + --bsdtar->argc; + } + if (opt == 'W') { + state = state_long; + long_prefix = "-W "; /* For clearer errors. */ + } else { + state = state_next_word; + bsdtar->optarg = opt_word; + } + } + } + + /* We're reading a long option, including -W long=arg convention. */ + if (state == state_long) { + /* After this long option, we'll be starting a new word. */ + state = state_next_word; + + /* Option name ends at '=' if there is one. */ + p = strchr(opt_word, '='); + if (p != NULL) { + optlength = (size_t)(p - opt_word); + bsdtar->optarg = (char *)(uintptr_t)(p + 1); + } else { + optlength = strlen(opt_word); + } + + /* Search the table for an unambiguous match. */ + for (popt = tar_longopts; popt->name != NULL; popt++) { + /* Short-circuit if first chars don't match. */ + if (popt->name[0] != opt_word[0]) + continue; + /* If option is a prefix of name in table, record it.*/ + if (strncmp(opt_word, popt->name, optlength) == 0) { + match2 = match; /* Record up to two matches. */ + match = popt; + /* If it's an exact match, we're done. */ + if (strlen(popt->name) == optlength) { + match2 = NULL; /* Forget the others. */ + break; + } + } + } + + /* Fail if there wasn't a unique match. */ + if (match == NULL) { + bsdtar_warnc(bsdtar, 0, + "Option %s%s is not supported", + long_prefix, opt_word); + return ('?'); + } + if (match2 != NULL) { + bsdtar_warnc(bsdtar, 0, + "Ambiguous option %s%s (matches --%s and --%s)", + long_prefix, opt_word, match->name, match2->name); + return ('?'); + } + + /* We've found a unique match; does it need an argument? */ + if (match->required) { + /* Argument required: get next word if necessary. */ + if (bsdtar->optarg == NULL) { + bsdtar->optarg = *bsdtar->argv; + if (bsdtar->optarg == NULL) { + bsdtar_warnc(bsdtar, 0, + "Option %s%s requires an argument", + long_prefix, match->name); + return ('?'); + } + ++bsdtar->argv; + --bsdtar->argc; + } + } else { + /* Argument forbidden: fail if there is one. */ + if (bsdtar->optarg != NULL) { + bsdtar_warnc(bsdtar, 0, + "Option %s%s does not allow an argument", + long_prefix, match->name); + return ('?'); + } + } + return (match->equivalent); + } + + return (opt); +} diff --git a/contrib/libarchive/tar/getdate.c b/contrib/libarchive/tar/getdate.c new file mode 100644 index 0000000000..8df1e26fba --- /dev/null +++ b/contrib/libarchive/tar/getdate.c @@ -0,0 +1,1050 @@ +/* + * This code is in the public domain and has no copyright. + * + * This is a plain C recursive-descent translation of an old + * public-domain YACC grammar that has been used for parsing dates in + * very many open-source projects. + * + * Since the original authors were generous enough to donate their + * work to the public domain, I feel compelled to match their + * generosity. + * + * Tim Kientzle, February 2009. + */ + +/* + * Header comment from original getdate.y: + */ + +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ + +#ifdef __FreeBSD__ +#include +__FBSDID("$FreeBSD$"); +#endif + +#include +#include +#include +#include +#include + +/* This file defines a single public function. */ +time_t get_date(time_t now, char *); + +/* Basic time units. */ +#define EPOCH 1970 +#define MINUTE (60L) +#define HOUR (60L * MINUTE) +#define DAY (24L * HOUR) + +/* Daylight-savings mode: on, off, or not yet known. */ +enum DSTMODE { DSTon, DSToff, DSTmaybe }; +/* Meridian: am or pm. */ +enum { tAM, tPM }; +/* Token types returned by nexttoken() */ +enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, + tUNUMBER, tZONE, tDST }; +struct token { int token; time_t value; }; + +/* + * Parser state. + */ +struct gdstate { + struct token *tokenp; /* Pointer to next token. */ + /* HaveXxxx counts how many of this kind of phrase we've seen; + * it's a fatal error to have more than one time, zone, day, + * or date phrase. */ + int HaveYear; + int HaveMonth; + int HaveDay; + int HaveWeekDay; /* Day of week */ + int HaveTime; /* Hour/minute/second */ + int HaveZone; /* timezone and/or DST info */ + int HaveRel; /* time offset; we can have more than one */ + /* Absolute time values. */ + time_t Timezone; /* Seconds offset from GMT */ + time_t Day; + time_t Hour; + time_t Minutes; + time_t Month; + time_t Seconds; + time_t Year; + /* DST selection */ + enum DSTMODE DSTmode; + /* Day of week accounting, e.g., "3rd Tuesday" */ + time_t DayOrdinal; /* "3" in "3rd Tuesday" */ + time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */ + /* Relative time values: hour/day/week offsets are measured in + * seconds, month/year are counted in months. */ + time_t RelMonth; + time_t RelSeconds; +}; + +/* + * A series of functions that recognize certain common time phrases. + * Each function returns 1 if it managed to make sense of some of the + * tokens, zero otherwise. + */ + +/* + * hour:minute or hour:minute:second with optional AM, PM, or numeric + * timezone offset + */ +static int +timephrase(struct gdstate *gds) +{ + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == ':' + && gds->tokenp[2].token == tUNUMBER + && gds->tokenp[3].token == ':' + && gds->tokenp[4].token == tUNUMBER) { + /* "12:14:18" or "22:08:07" */ + ++gds->HaveTime; + gds->Hour = gds->tokenp[0].value; + gds->Minutes = gds->tokenp[2].value; + gds->Seconds = gds->tokenp[4].value; + gds->tokenp += 5; + } + else if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == ':' + && gds->tokenp[2].token == tUNUMBER) { + /* "12:14" or "22:08" */ + ++gds->HaveTime; + gds->Hour = gds->tokenp[0].value; + gds->Minutes = gds->tokenp[2].value; + gds->Seconds = 0; + gds->tokenp += 3; + } + else if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tAMPM) { + /* "7" is a time if it's followed by "am" or "pm" */ + ++gds->HaveTime; + gds->Hour = gds->tokenp[0].value; + gds->Minutes = gds->Seconds = 0; + /* We'll handle the AM/PM below. */ + gds->tokenp += 1; + } else { + /* We can't handle this. */ + return 0; + } + + if (gds->tokenp[0].token == tAMPM) { + /* "7:12pm", "12:20:13am" */ + if (gds->Hour == 12) + gds->Hour = 0; + if (gds->tokenp[0].value == tPM) + gds->Hour += 12; + gds->tokenp += 1; + } + if (gds->tokenp[0].token == '+' + && gds->tokenp[1].token == tUNUMBER) { + /* "7:14+0700" */ + gds->HaveZone++; + gds->DSTmode = DSToff; + gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR + + (gds->tokenp[1].value % 100) * MINUTE); + gds->tokenp += 2; + } + if (gds->tokenp[0].token == '-' + && gds->tokenp[1].token == tUNUMBER) { + /* "19:14:12-0530" */ + gds->HaveZone++; + gds->DSTmode = DSToff; + gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR + + (gds->tokenp[1].value % 100) * MINUTE); + gds->tokenp += 2; + } + return 1; +} + +/* + * Timezone name, possibly including DST. + */ +static int +zonephrase(struct gdstate *gds) +{ + if (gds->tokenp[0].token == tZONE + && gds->tokenp[1].token == tDST) { + gds->HaveZone++; + gds->Timezone = gds->tokenp[0].value; + gds->DSTmode = DSTon; + gds->tokenp += 1; + return 1; + } + + if (gds->tokenp[0].token == tZONE) { + gds->HaveZone++; + gds->Timezone = gds->tokenp[0].value; + gds->DSTmode = DSToff; + gds->tokenp += 1; + return 1; + } + + if (gds->tokenp[0].token == tDAYZONE) { + gds->HaveZone++; + gds->Timezone = gds->tokenp[0].value; + gds->DSTmode = DSTon; + gds->tokenp += 1; + return 1; + } + return 0; +} + +/* + * Year/month/day in various combinations. + */ +static int +datephrase(struct gdstate *gds) +{ + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == '/' + && gds->tokenp[2].token == tUNUMBER + && gds->tokenp[3].token == '/' + && gds->tokenp[4].token == tUNUMBER) { + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + if (gds->tokenp[0].value >= 13) { + /* First number is big: 2004/01/29, 99/02/17 */ + gds->Year = gds->tokenp[0].value; + gds->Month = gds->tokenp[2].value; + gds->Day = gds->tokenp[4].value; + } else if ((gds->tokenp[4].value >= 13) || (gds->tokenp[2].value >= 13)) { + /* Last number is big: 01/07/98 */ + /* Middle number is big: 01/29/04 */ + gds->Month = gds->tokenp[0].value; + gds->Day = gds->tokenp[2].value; + gds->Year = gds->tokenp[4].value; + } else { + /* No significant clues: 02/03/04 */ + gds->Month = gds->tokenp[0].value; + gds->Day = gds->tokenp[2].value; + gds->Year = gds->tokenp[4].value; + } + gds->tokenp += 5; + return 1; + } + + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == '/' + && gds->tokenp[2].token == tUNUMBER) { + /* "1/15" */ + gds->HaveMonth++; + gds->HaveDay++; + gds->Month = gds->tokenp[0].value; + gds->Day = gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == '-' + && gds->tokenp[2].token == tUNUMBER + && gds->tokenp[3].token == '-' + && gds->tokenp[4].token == tUNUMBER) { + /* ISO 8601 format. yyyy-mm-dd. */ + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + gds->Year = gds->tokenp[0].value; + gds->Month = gds->tokenp[2].value; + gds->Day = gds->tokenp[4].value; + gds->tokenp += 5; + return 1; + } + + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == '-' + && gds->tokenp[2].token == tMONTH + && gds->tokenp[3].token == '-' + && gds->tokenp[4].token == tUNUMBER) { + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + if (gds->tokenp[0].value > 31) { + /* e.g. 1992-Jun-17 */ + gds->Year = gds->tokenp[0].value; + gds->Month = gds->tokenp[2].value; + gds->Day = gds->tokenp[4].value; + } else { + /* e.g. 17-JUN-1992. */ + gds->Day = gds->tokenp[0].value; + gds->Month = gds->tokenp[2].value; + gds->Year = gds->tokenp[4].value; + } + gds->tokenp += 5; + return 1; + } + + if (gds->tokenp[0].token == tMONTH + && gds->tokenp[1].token == tUNUMBER + && gds->tokenp[2].token == ',' + && gds->tokenp[3].token == tUNUMBER) { + /* "June 17, 2001" */ + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + gds->Month = gds->tokenp[0].value; + gds->Day = gds->tokenp[1].value; + gds->Year = gds->tokenp[3].value; + gds->tokenp += 4; + return 1; + } + + if (gds->tokenp[0].token == tMONTH + && gds->tokenp[1].token == tUNUMBER) { + /* "May 3" */ + gds->HaveMonth++; + gds->HaveDay++; + gds->Month = gds->tokenp[0].value; + gds->Day = gds->tokenp[1].value; + gds->tokenp += 2; + return 1; + } + + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tMONTH + && gds->tokenp[2].token == tUNUMBER) { + /* "12 Sept 1997" */ + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + gds->Day = gds->tokenp[0].value; + gds->Month = gds->tokenp[1].value; + gds->Year = gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tMONTH) { + /* "12 Sept" */ + gds->HaveMonth++; + gds->HaveDay++; + gds->Day = gds->tokenp[0].value; + gds->Month = gds->tokenp[1].value; + gds->tokenp += 2; + return 1; + } + + return 0; +} + +/* + * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc. + */ +static int +relunitphrase(struct gdstate *gds) +{ + if (gds->tokenp[0].token == '-' + && gds->tokenp[1].token == tUNUMBER + && gds->tokenp[2].token == tSEC_UNIT) { + /* "-3 hours" */ + gds->HaveRel++; + gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + if (gds->tokenp[0].token == '+' + && gds->tokenp[1].token == tUNUMBER + && gds->tokenp[2].token == tSEC_UNIT) { + /* "+1 minute" */ + gds->HaveRel++; + gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tSEC_UNIT) { + /* "1 day" */ + gds->HaveRel++; + gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + if (gds->tokenp[0].token == '-' + && gds->tokenp[1].token == tUNUMBER + && gds->tokenp[2].token == tMONTH_UNIT) { + /* "-3 months" */ + gds->HaveRel++; + gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + if (gds->tokenp[0].token == '+' + && gds->tokenp[1].token == tUNUMBER + && gds->tokenp[2].token == tMONTH_UNIT) { + /* "+5 years" */ + gds->HaveRel++; + gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value; + gds->tokenp += 3; + return 1; + } + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tMONTH_UNIT) { + /* "2 years" */ + gds->HaveRel++; + gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value; + gds->tokenp += 2; + return 1; + } + if (gds->tokenp[0].token == tSEC_UNIT) { + /* "now", "tomorrow" */ + gds->HaveRel++; + gds->RelSeconds += gds->tokenp[0].value; + ++gds->tokenp; + return 1; + } + if (gds->tokenp[0].token == tMONTH_UNIT) { + /* "month" */ + gds->HaveRel++; + gds->RelMonth += gds->tokenp[0].value; + gds->tokenp += 1; + return 1; + } + return 0; +} + +/* + * Day of the week specification. + */ +static int +dayphrase(struct gdstate *gds) +{ + if (gds->tokenp[0].token == tDAY) { + /* "tues", "wednesday," */ + gds->HaveWeekDay++; + gds->DayOrdinal = 1; + gds->DayNumber = gds->tokenp[0].value; + gds->tokenp += 1; + if (gds->tokenp[0].token == ',') + gds->tokenp += 1; + return 1; + } + if (gds->tokenp[0].token == tUNUMBER + && gds->tokenp[1].token == tDAY) { + /* "second tues" "3 wed" */ + gds->HaveWeekDay++; + gds->DayOrdinal = gds->tokenp[0].value; + gds->DayNumber = gds->tokenp[1].value; + gds->tokenp += 2; + return 1; + } + return 0; +} + +/* + * Try to match a phrase using one of the above functions. + * This layer also deals with a couple of generic issues. + */ +static int +phrase(struct gdstate *gds) +{ + if (timephrase(gds)) + return 1; + if (zonephrase(gds)) + return 1; + if (datephrase(gds)) + return 1; + if (dayphrase(gds)) + return 1; + if (relunitphrase(gds)) { + if (gds->tokenp[0].token == tAGO) { + gds->RelSeconds = -gds->RelSeconds; + gds->RelMonth = -gds->RelMonth; + gds->tokenp += 1; + } + return 1; + } + + /* Bare numbers sometimes have meaning. */ + if (gds->tokenp[0].token == tUNUMBER) { + if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) { + gds->HaveYear++; + gds->Year = gds->tokenp[0].value; + gds->tokenp += 1; + return 1; + } + + if(gds->tokenp[0].value > 10000) { + /* "20040301" */ + gds->HaveYear++; + gds->HaveMonth++; + gds->HaveDay++; + gds->Day= (gds->tokenp[0].value)%100; + gds->Month= (gds->tokenp[0].value/100)%100; + gds->Year = gds->tokenp[0].value/10000; + gds->tokenp += 1; + return 1; + } + + if (gds->tokenp[0].value < 24) { + gds->HaveTime++; + gds->Hour = gds->tokenp[0].value; + gds->Minutes = 0; + gds->Seconds = 0; + gds->tokenp += 1; + return 1; + } + + if ((gds->tokenp[0].value / 100 < 24) + && (gds->tokenp[0].value % 100 < 60)) { + /* "513" is same as "5:13" */ + gds->Hour = gds->tokenp[0].value / 100; + gds->Minutes = gds->tokenp[0].value % 100; + gds->Seconds = 0; + gds->tokenp += 1; + return 1; + } + } + + return 0; +} + +/* + * A dictionary of time words. + */ +static struct LEXICON { + size_t abbrev; + const char *name; + int type; + time_t value; +} const TimeWords[] = { + /* am/pm */ + { 0, "am", tAMPM, tAM }, + { 0, "pm", tAMPM, tPM }, + + /* Month names. */ + { 3, "january", tMONTH, 1 }, + { 3, "february", tMONTH, 2 }, + { 3, "march", tMONTH, 3 }, + { 3, "april", tMONTH, 4 }, + { 3, "may", tMONTH, 5 }, + { 3, "june", tMONTH, 6 }, + { 3, "july", tMONTH, 7 }, + { 3, "august", tMONTH, 8 }, + { 3, "september", tMONTH, 9 }, + { 3, "october", tMONTH, 10 }, + { 3, "november", tMONTH, 11 }, + { 3, "december", tMONTH, 12 }, + + /* Days of the week. */ + { 2, "sunday", tDAY, 0 }, + { 3, "monday", tDAY, 1 }, + { 2, "tuesday", tDAY, 2 }, + { 3, "wednesday", tDAY, 3 }, + { 2, "thursday", tDAY, 4 }, + { 2, "friday", tDAY, 5 }, + { 2, "saturday", tDAY, 6 }, + + /* Timezones: Offsets are in seconds. */ + { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */ + { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */ + { 0, "utc", tZONE, 0*HOUR }, + { 0, "wet", tZONE, 0*HOUR }, /* Western European */ + { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */ + { 0, "wat", tZONE, 1*HOUR }, /* West Africa */ + { 0, "at", tZONE, 2*HOUR }, /* Azores */ + /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */ + /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/ + { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */ + { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */ + { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */ + { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */ + { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */ + { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */ + { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */ + { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */ + { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */ + { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */ + { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */ + { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */ + { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */ + { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */ + { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */ + { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */ + { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */ + { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */ + { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */ + { 0, "nt", tZONE, 11*HOUR }, /* Nome */ + { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */ + { 0, "cet", tZONE, -1*HOUR }, /* Central European */ + { 0, "met", tZONE, -1*HOUR }, /* Middle European */ + { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */ + { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */ + { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */ + { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */ + { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */ + { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */ + { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */ + { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */ + { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */ + { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */ + { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */ + { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */ + { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */ + /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */ + /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */ + { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */ + { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */ + { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/ + { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */ + { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */ + { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */ + { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */ + { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */ + { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */ + { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */ + { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */ + { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */ + { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */ + { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */ + + { 0, "dst", tDST, 0 }, + + /* Time units. */ + { 4, "years", tMONTH_UNIT, 12 }, + { 5, "months", tMONTH_UNIT, 1 }, + { 9, "fortnights", tSEC_UNIT, 14 * DAY }, + { 4, "weeks", tSEC_UNIT, 7 * DAY }, + { 3, "days", tSEC_UNIT, DAY }, + { 4, "hours", tSEC_UNIT, HOUR }, + { 3, "minutes", tSEC_UNIT, MINUTE }, + { 3, "seconds", tSEC_UNIT, 1 }, + + /* Relative-time words. */ + { 0, "tomorrow", tSEC_UNIT, DAY }, + { 0, "yesterday", tSEC_UNIT, -DAY }, + { 0, "today", tSEC_UNIT, 0 }, + { 0, "now", tSEC_UNIT, 0 }, + { 0, "last", tUNUMBER, -1 }, + { 0, "this", tSEC_UNIT, 0 }, + { 0, "next", tUNUMBER, 2 }, + { 0, "first", tUNUMBER, 1 }, + { 0, "1st", tUNUMBER, 1 }, +/* { 0, "second", tUNUMBER, 2 }, */ + { 0, "2nd", tUNUMBER, 2 }, + { 0, "third", tUNUMBER, 3 }, + { 0, "3rd", tUNUMBER, 3 }, + { 0, "fourth", tUNUMBER, 4 }, + { 0, "4th", tUNUMBER, 4 }, + { 0, "fifth", tUNUMBER, 5 }, + { 0, "5th", tUNUMBER, 5 }, + { 0, "sixth", tUNUMBER, 6 }, + { 0, "seventh", tUNUMBER, 7 }, + { 0, "eighth", tUNUMBER, 8 }, + { 0, "ninth", tUNUMBER, 9 }, + { 0, "tenth", tUNUMBER, 10 }, + { 0, "eleventh", tUNUMBER, 11 }, + { 0, "twelfth", tUNUMBER, 12 }, + { 0, "ago", tAGO, 1 }, + + /* Military timezones. */ + { 0, "a", tZONE, 1*HOUR }, + { 0, "b", tZONE, 2*HOUR }, + { 0, "c", tZONE, 3*HOUR }, + { 0, "d", tZONE, 4*HOUR }, + { 0, "e", tZONE, 5*HOUR }, + { 0, "f", tZONE, 6*HOUR }, + { 0, "g", tZONE, 7*HOUR }, + { 0, "h", tZONE, 8*HOUR }, + { 0, "i", tZONE, 9*HOUR }, + { 0, "k", tZONE, 10*HOUR }, + { 0, "l", tZONE, 11*HOUR }, + { 0, "m", tZONE, 12*HOUR }, + { 0, "n", tZONE, -1*HOUR }, + { 0, "o", tZONE, -2*HOUR }, + { 0, "p", tZONE, -3*HOUR }, + { 0, "q", tZONE, -4*HOUR }, + { 0, "r", tZONE, -5*HOUR }, + { 0, "s", tZONE, -6*HOUR }, + { 0, "t", tZONE, -7*HOUR }, + { 0, "u", tZONE, -8*HOUR }, + { 0, "v", tZONE, -9*HOUR }, + { 0, "w", tZONE, -10*HOUR }, + { 0, "x", tZONE, -11*HOUR }, + { 0, "y", tZONE, -12*HOUR }, + { 0, "z", tZONE, 0*HOUR }, + + /* End of table. */ + { 0, NULL, 0, 0 } +}; + +/* + * Convert hour/minute/second to count of seconds. + */ +static time_t +ToSeconds(time_t Hours, time_t Minutes, time_t Seconds) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + if (Hours < 0 || Hours > 23) + return -1; + return Hours * HOUR + Minutes * MINUTE + Seconds; +} + + +/* + * Year is either: + * = A number from 0 to 99, which means a year from 1970 to 2069, or + * = The actual year (>=100). + */ +static time_t +Convert(time_t Month, time_t Day, time_t Year, + time_t Hours, time_t Minutes, time_t Seconds, + time_t Timezone, enum DSTMODE DSTmode) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + /* Checking for 2038 bogusly assumes that time_t is 32 bits. But + I'm too lazy to try to check for time_t overflow in another way. */ + if (Year < EPOCH || Year > 2038 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + Julian = Day - 1; + for (i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= DAY; + Julian += Timezone; + if ((tod = ToSeconds(Hours, Minutes, Seconds)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= HOUR; + return Julian; +} + + +static time_t +DSTcorrect(time_t Start, time_t Future) +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * HOUR; +} + + +static time_t +RelativeDate(time_t Start, time_t zone, int dstmode, + time_t DayOrdinal, time_t DayNumber) +{ + struct tm *tm; + time_t t, now; + + t = Start - zone; + tm = gmtime(&t); + now = Start; + now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + if (dstmode == DSTmaybe) + return DSTcorrect(Start, now); + return now - Start; +} + + +static time_t +RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + Timezone, DSTmaybe)); +} + +/* + * Tokenizer. + */ +static int +nexttoken(char **in, time_t *value) +{ + char c; + char buff[64]; + + for ( ; ; ) { + while (isspace((unsigned char)**in)) + ++*in; + + /* Skip parenthesized comments. */ + if (**in == '(') { + int Count = 0; + do { + c = *(*in)++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + continue; + } + + /* Try the next token in the word table first. */ + /* This allows us to match "2nd", for example. */ + { + char *src = *in; + const struct LEXICON *tp; + unsigned i = 0; + + /* Force to lowercase and strip '.' characters. */ + while (*src != '\0' + && (isalnum((unsigned char)*src) || *src == '.') + && i < sizeof(buff)-1) { + if (*src != '.') { + if (isupper((unsigned char)*src)) + buff[i++] = tolower((unsigned char)*src); + else + buff[i++] = *src; + } + src++; + } + buff[i++] = '\0'; + + /* + * Find the first match. If the word can be + * abbreviated, make sure we match at least + * the minimum abbreviation. + */ + for (tp = TimeWords; tp->name; tp++) { + size_t abbrev = tp->abbrev; + if (abbrev == 0) + abbrev = strlen(tp->name); + if (strlen(buff) >= abbrev + && strncmp(tp->name, buff, strlen(buff)) + == 0) { + /* Skip over token. */ + *in = src; + /* Return the match. */ + *value = tp->value; + return tp->type; + } + } + } + + /* + * Not in the word table, maybe it's a number. Note: + * Because '-' and '+' have other special meanings, I + * don't deal with signed numbers here. + */ + if (isdigit((unsigned char)(c = **in))) { + for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) + *value = 10 * *value + c - '0'; + (*in)--; + return (tUNUMBER); + } + + return *(*in)++; + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (long)(ay-by) * 365 + ); + return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR + + (a->tm_min - b->tm_min) * MINUTE + + (a->tm_sec - b->tm_sec)); +} + +/* + * + * The public function. + * + * TODO: tokens[] array should be dynamically sized. + */ +time_t +get_date(time_t now, char *p) +{ + struct token tokens[256]; + struct gdstate _gds; + struct token *lasttoken; + struct gdstate *gds; + struct tm local, *tm; + struct tm gmt, *gmt_ptr; + time_t Start; + time_t tod; + long tzone; + + /* Clear out the parsed token array. */ + memset(tokens, 0, sizeof(tokens)); + /* Initialize the parser state. */ + memset(&_gds, 0, sizeof(_gds)); + gds = &_gds; + + /* Look up the current time. */ + memset(&local, 0, sizeof(local)); + tm = localtime (&now); + if (tm == NULL) + return -1; + local = *tm; + + /* Look up UTC if we can and use that to determine the current + * timezone offset. */ + memset(&gmt, 0, sizeof(gmt)); + gmt_ptr = gmtime (&now); + if (gmt_ptr != NULL) { + /* Copy, in case localtime and gmtime use the same buffer. */ + gmt = *gmt_ptr; + } + if (gmt_ptr != NULL) + tzone = difftm (&gmt, &local); + else + /* This system doesn't understand timezones; fake it. */ + tzone = 0; + if(local.tm_isdst) + tzone += HOUR; + + /* Tokenize the input string. */ + lasttoken = tokens; + while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) { + ++lasttoken; + if (lasttoken > tokens + 255) + return -1; + } + gds->tokenp = tokens; + + /* Match phrases until we run out of input tokens. */ + while (gds->tokenp < lasttoken) { + if (!phrase(gds)) + return -1; + } + + /* Use current local timezone if none was specified. */ + if (!gds->HaveZone) { + gds->Timezone = tzone; + gds->DSTmode = DSTmaybe; + } + + /* If a timezone was specified, use that for generating the default + * time components instead of the local timezone. */ + if (gds->HaveZone && gmt_ptr != NULL) { + now -= gds->Timezone; + gmt_ptr = gmtime (&now); + if (gmt_ptr != NULL) + local = *gmt_ptr; + now += gds->Timezone; + } + + if (!gds->HaveYear) + gds->Year = local.tm_year + 1900; + if (!gds->HaveMonth) + gds->Month = local.tm_mon + 1; + if (!gds->HaveDay) + gds->Day = local.tm_mday; + /* Note: No default for hour/min/sec; a specifier that just + * gives date always refers to 00:00 on that date. */ + + /* If we saw more than one time, timezone, weekday, year, month, + * or day, then give up. */ + if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1 + || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1) + return -1; + + /* Compute an absolute time based on whatever absolute information + * we collected. */ + if (gds->HaveYear || gds->HaveMonth || gds->HaveDay + || gds->HaveTime || gds->HaveWeekDay) { + Start = Convert(gds->Month, gds->Day, gds->Year, + gds->Hour, gds->Minutes, gds->Seconds, + gds->Timezone, gds->DSTmode); + if (Start < 0) + return -1; + } else { + Start = now; + if (!gds->HaveRel) + Start -= local.tm_hour * HOUR + local.tm_min * MINUTE + + local.tm_sec; + } + + /* Add the relative offset. */ + Start += gds->RelSeconds; + Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth); + + /* Adjust for day-of-week offsets. */ + if (gds->HaveWeekDay + && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) { + tod = RelativeDate(Start, gds->Timezone, + gds->DSTmode, gds->DayOrdinal, gds->DayNumber); + Start += tod; + } + + /* -1 is an error indicator, so return 0 instead of -1 if + * that's the actual time. */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int +main(int argc, char **argv) +{ + time_t d; + + while (*++argv != NULL) { + (void)printf("Input: %s\n", *argv); + d = get_date(*argv); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("Output: %s\n", ctime(&d)); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/contrib/libarchive/tar/getdate.y b/contrib/libarchive/tar/getdate.y deleted file mode 100644 index 3253f6d68a..0000000000 --- a/contrib/libarchive/tar/getdate.y +++ /dev/null @@ -1,811 +0,0 @@ -%{ -/* - * March 2005: Further modified and simplified by Tim Kientzle: - * Eliminate minutes-based calculations (just do everything in - * seconds), have lexer only recognize unsigned integers (handle '+' - * and '-' characters in grammar), combine tables into one table with - * explicit abbreviation notes, do am/pm adjustments in the grammar - * (eliminate some state variables and post-processing). Among other - * things, these changes eliminated two shift/reduce conflicts. (Went - * from 10 to 8.) - * All of Tim Kientzle's changes to this file are public domain. - */ - -/* -** Originally written by Steven M. Bellovin while -** at the University of North Carolina at Chapel Hill. Later tweaked by -** a couple of people on Usenet. Completely overhauled by Rich $alz -** and Jim Berets in August, 1990; -** -** This grammar has 10 shift/reduce conflicts. -** -** This code is in the public domain and has no copyright. -*/ -/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ -/* SUPPRESS 288 on yyerrlab *//* Label unused */ - -#ifdef __FreeBSD__ -#include -__FBSDID("$FreeBSD: src/usr.bin/tar/getdate.y,v 1.9 2007/07/20 01:27:50 kientzle Exp $"); -#endif - -#include -#include -#include -#include -#include - -#define yyparse getdate_yyparse -#define yylex getdate_yylex -#define yyerror getdate_yyerror - -static int yyparse(void); -static int yylex(void); -static int yyerror(const char *); - -time_t get_date(char *); - -#define EPOCH 1970 -#define HOUR(x) ((time_t)(x) * 60) -#define SECSPERDAY (24L * 60L * 60L) - -/* -** Daylight-savings mode: on, off, or not yet known. -*/ -typedef enum _DSTMODE { - DSTon, DSToff, DSTmaybe -} DSTMODE; - -/* -** Meridian: am or pm. -*/ -enum { tAM, tPM }; - -/* -** Global variables. We could get rid of most of these by using a good -** union as the yacc stack. (This routine was originally written before -** yacc had the %union construct.) Maybe someday; right now we only use -** the %union very rarely. -*/ -static char *yyInput; - -static DSTMODE yyDSTmode; -static time_t yyDayOrdinal; -static time_t yyDayNumber; -static int yyHaveDate; -static int yyHaveDay; -static int yyHaveRel; -static int yyHaveTime; -static int yyHaveZone; -static time_t yyTimezone; -static time_t yyDay; -static time_t yyHour; -static time_t yyMinutes; -static time_t yyMonth; -static time_t yySeconds; -static time_t yyYear; -static time_t yyRelMonth; -static time_t yyRelSeconds; - -%} - -%union { - time_t Number; -} - -%token tAGO tDAY tDAYZONE tAMPM tMONTH tMONTH_UNIT tSEC_UNIT tUNUMBER -%token tZONE tDST - -%type tDAY tDAYZONE tMONTH tMONTH_UNIT -%type tSEC_UNIT tUNUMBER tZONE tAMPM - -%% - -spec : /* NULL */ - | spec item - ; - -item : time { yyHaveTime++; } - | zone { yyHaveZone++; } - | date { yyHaveDate++; } - | day { yyHaveDay++; } - | rel { yyHaveRel++; } - | number - ; - -time : tUNUMBER tAMPM { - /* "7am" */ - yyHour = $1; - if (yyHour == 12) - yyHour = 0; - yyMinutes = 0; - yySeconds = 0; - if ($2 == tPM) - yyHour += 12; - } - | bare_time { - /* "7:12:18" "19:17" */ - } - | bare_time tAMPM { - /* "7:12pm", "12:20:13am" */ - if (yyHour == 12) - yyHour = 0; - if ($2 == tPM) - yyHour += 12; - } - | bare_time '+' tUNUMBER { - /* "7:14+0700" */ - yyDSTmode = DSToff; - yyTimezone = - ($3 % 100 + ($3 / 100) * 60); - } - | bare_time '-' tUNUMBER { - /* "19:14:12-0530" */ - yyDSTmode = DSToff; - yyTimezone = + ($3 % 100 + ($3 / 100) * 60); - } - ; - -bare_time : tUNUMBER ':' tUNUMBER { - yyHour = $1; - yyMinutes = $3; - yySeconds = 0; - } - | tUNUMBER ':' tUNUMBER ':' tUNUMBER { - yyHour = $1; - yyMinutes = $3; - yySeconds = $5; - } - ; - -zone : tZONE { - yyTimezone = $1; - yyDSTmode = DSToff; - } - | tDAYZONE { - yyTimezone = $1; - yyDSTmode = DSTon; - } - | tZONE tDST { - yyTimezone = $1; - yyDSTmode = DSTon; - } - ; - -day : tDAY { - yyDayOrdinal = 1; - yyDayNumber = $1; - } - | tDAY ',' { - /* "tue," "wednesday," */ - yyDayOrdinal = 1; - yyDayNumber = $1; - } - | tUNUMBER tDAY { - /* "second tues" "3 wed" */ - yyDayOrdinal = $1; - yyDayNumber = $2; - } - ; - -date : tUNUMBER '/' tUNUMBER { - /* "1/15" */ - yyMonth = $1; - yyDay = $3; - } - | tUNUMBER '/' tUNUMBER '/' tUNUMBER { - if ($1 >= 13) { - /* First number is big: 2004/01/29, 99/02/17 */ - yyYear = $1; - yyMonth = $3; - yyDay = $5; - } else if (($5 >= 13) || ($3 >= 13)) { - /* Last number is big: 01/07/98 */ - /* Middle number is big: 01/29/04 */ - yyMonth = $1; - yyDay = $3; - yyYear = $5; - } else { - /* No significant clues: 02/03/04 */ - yyMonth = $1; - yyDay = $3; - yyYear = $5; - } - } - | tUNUMBER '-' tUNUMBER '-' tUNUMBER { - /* ISO 8601 format. yyyy-mm-dd. */ - yyYear = $1; - yyMonth = $3; - yyDay = $5; - } - | tUNUMBER '-' tMONTH '-' tUNUMBER { - if ($1 > 31) { - /* e.g. 1992-Jun-17 */ - yyYear = $1; - yyMonth = $3; - yyDay = $5; - } else { - /* e.g. 17-JUN-1992. */ - yyDay = $1; - yyMonth = $3; - yyYear = $5; - } - } - | tMONTH tUNUMBER { - /* "May 3" */ - yyMonth = $1; - yyDay = $2; - } - | tMONTH tUNUMBER ',' tUNUMBER { - /* "June 17, 2001" */ - yyMonth = $1; - yyDay = $2; - yyYear = $4; - } - | tUNUMBER tMONTH { - /* "12 Sept" */ - yyDay = $1; - yyMonth = $2; - } - | tUNUMBER tMONTH tUNUMBER { - /* "12 Sept 1997" */ - yyDay = $1; - yyMonth = $2; - yyYear = $3; - } - ; - -rel : relunit tAGO { - yyRelSeconds = -yyRelSeconds; - yyRelMonth = -yyRelMonth; - } - | relunit - ; - -relunit : '-' tUNUMBER tSEC_UNIT { - /* "-3 hours" */ - yyRelSeconds -= $2 * $3; - } - | '+' tUNUMBER tSEC_UNIT { - /* "+1 minute" */ - yyRelSeconds += $2 * $3; - } - | tUNUMBER tSEC_UNIT { - /* "1 day" */ - yyRelSeconds += $1 * $2; - } - | tSEC_UNIT { - /* "hour" */ - yyRelSeconds += $1; - } - | '-' tUNUMBER tMONTH_UNIT { - /* "-3 months" */ - yyRelMonth -= $2 * $3; - } - | '+' tUNUMBER tMONTH_UNIT { - /* "+5 years" */ - yyRelMonth += $2 * $3; - } - | tUNUMBER tMONTH_UNIT { - /* "2 years" */ - yyRelMonth += $1 * $2; - } - | tMONTH_UNIT { - /* "6 months" */ - yyRelMonth += $1; - } - ; - -number : tUNUMBER { - if (yyHaveTime && yyHaveDate && !yyHaveRel) - yyYear = $1; - else { - if($1>10000) { - /* "20040301" */ - yyHaveDate++; - yyDay= ($1)%100; - yyMonth= ($1/100)%100; - yyYear = $1/10000; - } - else { - /* "513" is same as "5:13" */ - yyHaveTime++; - if ($1 < 100) { - yyHour = $1; - yyMinutes = 0; - } - else { - yyHour = $1 / 100; - yyMinutes = $1 % 100; - } - yySeconds = 0; - } - } - } - ; - - -%% - -static struct TABLE { - size_t abbrev; - const char *name; - int type; - time_t value; -} const TimeWords[] = { - /* am/pm */ - { 0, "am", tAMPM, tAM }, - { 0, "pm", tAMPM, tPM }, - - /* Month names. */ - { 3, "january", tMONTH, 1 }, - { 3, "february", tMONTH, 2 }, - { 3, "march", tMONTH, 3 }, - { 3, "april", tMONTH, 4 }, - { 3, "may", tMONTH, 5 }, - { 3, "june", tMONTH, 6 }, - { 3, "july", tMONTH, 7 }, - { 3, "august", tMONTH, 8 }, - { 3, "september", tMONTH, 9 }, - { 3, "october", tMONTH, 10 }, - { 3, "november", tMONTH, 11 }, - { 3, "december", tMONTH, 12 }, - - /* Days of the week. */ - { 2, "sunday", tDAY, 0 }, - { 3, "monday", tDAY, 1 }, - { 2, "tuesday", tDAY, 2 }, - { 3, "wednesday", tDAY, 3 }, - { 2, "thursday", tDAY, 4 }, - { 2, "friday", tDAY, 5 }, - { 2, "saturday", tDAY, 6 }, - - /* Timezones: Offsets are in minutes. */ - { 0, "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ - { 0, "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ - { 0, "utc", tZONE, HOUR( 0) }, - { 0, "wet", tZONE, HOUR( 0) }, /* Western European */ - { 0, "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ - { 0, "wat", tZONE, HOUR( 1) }, /* West Africa */ - { 0, "at", tZONE, HOUR( 2) }, /* Azores */ - /* { 0, "bst", tZONE, HOUR( 3) }, */ /* Brazil Standard: Conflict */ - /* { 0, "gst", tZONE, HOUR( 3) }, */ /* Greenland Standard: Conflict*/ - { 0, "nft", tZONE, HOUR(3)+30 }, /* Newfoundland */ - { 0, "nst", tZONE, HOUR(3)+30 }, /* Newfoundland Standard */ - { 0, "ndt", tDAYZONE, HOUR(3)+30 }, /* Newfoundland Daylight */ - { 0, "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ - { 0, "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ - { 0, "est", tZONE, HOUR( 5) }, /* Eastern Standard */ - { 0, "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ - { 0, "cst", tZONE, HOUR( 6) }, /* Central Standard */ - { 0, "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ - { 0, "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ - { 0, "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ - { 0, "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ - { 0, "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ - { 0, "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ - { 0, "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ - { 0, "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ - { 0, "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ - { 0, "cat", tZONE, HOUR(10) }, /* Central Alaska */ - { 0, "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ - { 0, "nt", tZONE, HOUR(11) }, /* Nome */ - { 0, "idlw", tZONE, HOUR(12) }, /* Intl Date Line West */ - { 0, "cet", tZONE, -HOUR(1) }, /* Central European */ - { 0, "met", tZONE, -HOUR(1) }, /* Middle European */ - { 0, "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ - { 0, "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ - { 0, "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ - { 0, "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ - { 0, "fwt", tZONE, -HOUR(1) }, /* French Winter */ - { 0, "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ - { 0, "eet", tZONE, -HOUR(2) }, /* Eastern Eur, USSR Zone 1 */ - { 0, "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ - { 0, "it", tZONE, -HOUR(3)-30 },/* Iran */ - { 0, "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ - { 0, "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ - { 0, "ist", tZONE, -HOUR(5)-30 },/* Indian Standard */ - { 0, "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ - /* { 0, "nst", tZONE, -HOUR(6.5) }, */ /* North Sumatra: Conflict */ - /* { 0, "sst", tZONE, -HOUR(7) }, */ /* So Sumatra, USSR 6: Conflict */ - { 0, "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ - { 0, "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ - { 0, "jt", tZONE, -HOUR(7)-30 },/* Java (3pm in Cronusland!)*/ - { 0, "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ - { 0, "jst", tZONE, -HOUR(9) }, /* Japan Std, USSR Zone 8 */ - { 0, "cast", tZONE, -HOUR(9)-30 },/* Central Australian Std */ - { 0, "cadt", tDAYZONE, -HOUR(9)-30 },/* Central Australian Daylt */ - { 0, "east", tZONE, -HOUR(10) }, /* Eastern Australian Std */ - { 0, "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylt */ - { 0, "gst", tZONE, -HOUR(10) }, /* Guam Std, USSR Zone 9 */ - { 0, "nzt", tZONE, -HOUR(12) }, /* New Zealand */ - { 0, "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ - { 0, "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ - { 0, "idle", tZONE, -HOUR(12) }, /* Intl Date Line East */ - - { 0, "dst", tDST, 0 }, - - /* Time units. */ - { 4, "years", tMONTH_UNIT, 12 }, - { 5, "months", tMONTH_UNIT, 1 }, - { 9, "fortnights", tSEC_UNIT, 14 * 24 * 60 * 60 }, - { 4, "weeks", tSEC_UNIT, 7 * 24 * 60 * 60 }, - { 3, "days", tSEC_UNIT, 1 * 24 * 60 * 60 }, - { 4, "hours", tSEC_UNIT, 60 * 60 }, - { 3, "minutes", tSEC_UNIT, 60 }, - { 3, "seconds", tSEC_UNIT, 1 }, - - /* Relative-time words. */ - { 0, "tomorrow", tSEC_UNIT, 1 * 24 * 60 * 60 }, - { 0, "yesterday", tSEC_UNIT, -1 * 24 * 60 * 60 }, - { 0, "today", tSEC_UNIT, 0 }, - { 0, "now", tSEC_UNIT, 0 }, - { 0, "last", tUNUMBER, -1 }, - { 0, "this", tSEC_UNIT, 0 }, - { 0, "next", tUNUMBER, 2 }, - { 0, "first", tUNUMBER, 1 }, - { 0, "1st", tUNUMBER, 1 }, -/* { 0, "second", tUNUMBER, 2 }, */ - { 0, "2nd", tUNUMBER, 2 }, - { 0, "third", tUNUMBER, 3 }, - { 0, "3rd", tUNUMBER, 3 }, - { 0, "fourth", tUNUMBER, 4 }, - { 0, "4th", tUNUMBER, 4 }, - { 0, "fifth", tUNUMBER, 5 }, - { 0, "5th", tUNUMBER, 5 }, - { 0, "sixth", tUNUMBER, 6 }, - { 0, "seventh", tUNUMBER, 7 }, - { 0, "eighth", tUNUMBER, 8 }, - { 0, "ninth", tUNUMBER, 9 }, - { 0, "tenth", tUNUMBER, 10 }, - { 0, "eleventh", tUNUMBER, 11 }, - { 0, "twelfth", tUNUMBER, 12 }, - { 0, "ago", tAGO, 1 }, - - /* Military timezones. */ - { 0, "a", tZONE, HOUR( 1) }, - { 0, "b", tZONE, HOUR( 2) }, - { 0, "c", tZONE, HOUR( 3) }, - { 0, "d", tZONE, HOUR( 4) }, - { 0, "e", tZONE, HOUR( 5) }, - { 0, "f", tZONE, HOUR( 6) }, - { 0, "g", tZONE, HOUR( 7) }, - { 0, "h", tZONE, HOUR( 8) }, - { 0, "i", tZONE, HOUR( 9) }, - { 0, "k", tZONE, HOUR( 10) }, - { 0, "l", tZONE, HOUR( 11) }, - { 0, "m", tZONE, HOUR( 12) }, - { 0, "n", tZONE, HOUR(- 1) }, - { 0, "o", tZONE, HOUR(- 2) }, - { 0, "p", tZONE, HOUR(- 3) }, - { 0, "q", tZONE, HOUR(- 4) }, - { 0, "r", tZONE, HOUR(- 5) }, - { 0, "s", tZONE, HOUR(- 6) }, - { 0, "t", tZONE, HOUR(- 7) }, - { 0, "u", tZONE, HOUR(- 8) }, - { 0, "v", tZONE, HOUR(- 9) }, - { 0, "w", tZONE, HOUR(-10) }, - { 0, "x", tZONE, HOUR(-11) }, - { 0, "y", tZONE, HOUR(-12) }, - { 0, "z", tZONE, HOUR( 0) }, - - /* End of table. */ - { 0, NULL, 0, 0 } -}; - - - - -/* ARGSUSED */ -static int -yyerror(const char *s) -{ - (void)s; - return 0; -} - -static time_t -ToSeconds(time_t Hours, time_t Minutes, time_t Seconds) -{ - if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) - return -1; - if (Hours < 0 || Hours > 23) - return -1; - return (Hours * 60L + Minutes) * 60L + Seconds; -} - - -/* Year is either - * A number from 0 to 99, which means a year from 1970 to 2069, or - * The actual year (>=100). */ -static time_t -Convert(time_t Month, time_t Day, time_t Year, - time_t Hours, time_t Minutes, time_t Seconds, DSTMODE DSTmode) -{ - static int DaysInMonth[12] = { - 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - time_t tod; - time_t Julian; - int i; - - if (Year < 69) - Year += 2000; - else if (Year < 100) - Year += 1900; - DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) - ? 29 : 28; - /* Checking for 2038 bogusly assumes that time_t is 32 bits. But - I'm too lazy to try to check for time_t overflow in another way. */ - if (Year < EPOCH || Year > 2038 - || Month < 1 || Month > 12 - /* Lint fluff: "conversion from long may lose accuracy" */ - || Day < 1 || Day > DaysInMonth[(int)--Month]) - return -1; - - Julian = Day - 1; - for (i = 0; i < Month; i++) - Julian += DaysInMonth[i]; - for (i = EPOCH; i < Year; i++) - Julian += 365 + (i % 4 == 0); - Julian *= SECSPERDAY; - Julian += yyTimezone * 60L; - if ((tod = ToSeconds(Hours, Minutes, Seconds)) < 0) - return -1; - Julian += tod; - if (DSTmode == DSTon - || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) - Julian -= 60 * 60; - return Julian; -} - - -static time_t -DSTcorrect(time_t Start, time_t Future) -{ - time_t StartDay; - time_t FutureDay; - - StartDay = (localtime(&Start)->tm_hour + 1) % 24; - FutureDay = (localtime(&Future)->tm_hour + 1) % 24; - return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; -} - - -static time_t -RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber) -{ - struct tm *tm; - time_t now; - - now = Start; - tm = localtime(&now); - now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); - now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); - return DSTcorrect(Start, now); -} - - -static time_t -RelativeMonth(time_t Start, time_t RelMonth) -{ - struct tm *tm; - time_t Month; - time_t Year; - - if (RelMonth == 0) - return 0; - tm = localtime(&Start); - Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; - Year = Month / 12; - Month = Month % 12 + 1; - return DSTcorrect(Start, - Convert(Month, (time_t)tm->tm_mday, Year, - (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, - DSTmaybe)); -} - -static int -yylex(void) -{ - char c; - char buff[64]; - - for ( ; ; ) { - while (isspace((unsigned char)*yyInput)) - yyInput++; - - /* Skip parenthesized comments. */ - if (*yyInput == '(') { - int Count = 0; - do { - c = *yyInput++; - if (c == '\0') - return c; - if (c == '(') - Count++; - else if (c == ')') - Count--; - } while (Count > 0); - continue; - } - - /* Try the next token in the word table first. */ - /* This allows us to match "2nd", for example. */ - { - char *src = yyInput; - const struct TABLE *tp; - unsigned i = 0; - - /* Force to lowercase and strip '.' characters. */ - while (*src != '\0' - && (isalnum((unsigned char)*src) || *src == '.') - && i < sizeof(buff)-1) { - if (*src != '.') { - if (isupper((unsigned char)*src)) - buff[i++] = tolower((unsigned char)*src); - else - buff[i++] = *src; - } - src++; - } - buff[i++] = '\0'; - - /* - * Find the first match. If the word can be - * abbreviated, make sure we match at least - * the minimum abbreviation. - */ - for (tp = TimeWords; tp->name; tp++) { - size_t abbrev = tp->abbrev; - if (abbrev == 0) - abbrev = strlen(tp->name); - if (strlen(buff) >= abbrev - && strncmp(tp->name, buff, strlen(buff)) - == 0) { - /* Skip over token. */ - yyInput = src; - /* Return the match. */ - yylval.Number = tp->value; - return tp->type; - } - } - } - - /* - * Not in the word table, maybe it's a number. Note: - * Because '-' and '+' have other special meanings, I - * don't deal with signed numbers here. - */ - if (isdigit((unsigned char)(c = *yyInput))) { - for (yylval.Number = 0; isdigit((unsigned char)(c = *yyInput++)); ) - yylval.Number = 10 * yylval.Number + c - '0'; - yyInput--; - return (tUNUMBER); - } - - return (*yyInput++); - } -} - -#define TM_YEAR_ORIGIN 1900 - -/* Yield A - B, measured in seconds. */ -static long -difftm (struct tm *a, struct tm *b) -{ - int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); - int by = b->tm_year + (TM_YEAR_ORIGIN - 1); - int days = ( - /* difference in day of year */ - a->tm_yday - b->tm_yday - /* + intervening leap days */ - + ((ay >> 2) - (by >> 2)) - - (ay/100 - by/100) - + ((ay/100 >> 2) - (by/100 >> 2)) - /* + difference in years * 365 */ - + (long)(ay-by) * 365 - ); - return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) - + (a->tm_min - b->tm_min)) - + (a->tm_sec - b->tm_sec)); -} - -time_t -get_date(char *p) -{ - struct tm *tm; - struct tm gmt, *gmt_ptr; - time_t Start; - time_t tod; - time_t nowtime; - long tzone; - - memset(&gmt, 0, sizeof(gmt)); - yyInput = p; - - (void)time (&nowtime); - - gmt_ptr = gmtime (&nowtime); - if (gmt_ptr != NULL) { - /* Copy, in case localtime and gmtime use the same buffer. */ - gmt = *gmt_ptr; - } - - if (! (tm = localtime (&nowtime))) - return -1; - - if (gmt_ptr != NULL) - tzone = difftm (&gmt, tm) / 60; - else - /* This system doesn't understand timezones; fake it. */ - tzone = 0; - if(tm->tm_isdst) - tzone += 60; - - yyYear = tm->tm_year + 1900; - yyMonth = tm->tm_mon + 1; - yyDay = tm->tm_mday; - yyTimezone = tzone; - yyDSTmode = DSTmaybe; - yyHour = 0; - yyMinutes = 0; - yySeconds = 0; - yyRelSeconds = 0; - yyRelMonth = 0; - yyHaveDate = 0; - yyHaveDay = 0; - yyHaveRel = 0; - yyHaveTime = 0; - yyHaveZone = 0; - - if (yyparse() - || yyHaveTime > 1 || yyHaveZone > 1 - || yyHaveDate > 1 || yyHaveDay > 1) - return -1; - - if (yyHaveDate || yyHaveTime || yyHaveDay) { - Start = Convert(yyMonth, yyDay, yyYear, - yyHour, yyMinutes, yySeconds, yyDSTmode); - if (Start < 0) - return -1; - } else { - Start = nowtime; - if (!yyHaveRel) - Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; - } - - Start += yyRelSeconds; - Start += RelativeMonth(Start, yyRelMonth); - - if (yyHaveDay && !yyHaveDate) { - tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); - Start += tod; - } - - /* Have to do *something* with a legitimate -1 so it's - * distinguishable from the error return value. (Alternately - * could set errno on error.) */ - return Start == -1 ? 0 : Start; -} - - -#if defined(TEST) - -/* ARGSUSED */ -int -main(int argc, char **argv) -{ - time_t d; - - while (*++argv != NULL) { - (void)printf("Input: %s\n", *argv); - d = get_date(*argv); - if (d == -1) - (void)printf("Bad format - couldn't convert.\n"); - else - (void)printf("Output: %s\n", ctime(&d)); - } - exit(0); - /* NOTREACHED */ -} -#endif /* defined(TEST) */ diff --git a/contrib/libarchive/tar/matching.c b/contrib/libarchive/tar/matching.c index 952055fe8a..acc60dbaab 100644 --- a/contrib/libarchive/tar/matching.c +++ b/contrib/libarchive/tar/matching.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/matching.c,v 1.13 2008/05/26 17:10:10 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/matching.c,v 1.16 2008/08/18 18:13:40 kientzle Exp $"); #ifdef HAVE_ERRNO_H #include @@ -59,6 +59,7 @@ static int bsdtar_fnmatch(const char *p, const char *s); static void initialize_matching(struct bsdtar *); static int match_exclusion(struct match *, const char *pathname); static int match_inclusion(struct match *, const char *pathname); +static int pathmatch(const char *p, const char *s); /* * The matching logic here needs to be re-thought. I started out to @@ -118,8 +119,6 @@ add_pattern(struct bsdtar *bsdtar, struct match **list, const char *pattern) match = malloc(sizeof(*match) + strlen(pattern) + 1); if (match == NULL) bsdtar_errc(bsdtar, 1, errno, "Out of memory"); - if (pattern[0] == '/') - pattern++; strcpy(match->pattern, pattern); /* Both "foo/" and "foo" should match "foo/bar". */ if (match->pattern[strlen(match->pattern)-1] == '/') @@ -189,18 +188,18 @@ excluded(struct bsdtar *bsdtar, const char *pathname) * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar' * */ -int +static int match_exclusion(struct match *match, const char *pathname) { const char *p; if (*match->pattern == '*' || *match->pattern == '/') - return (bsdtar_fnmatch(match->pattern, pathname) == 0); + return (pathmatch(match->pattern, pathname) == 0); for (p = pathname; p != NULL; p = strchr(p, '/')) { if (*p == '/') p++; - if (bsdtar_fnmatch(match->pattern, p) == 0) + if (pathmatch(match->pattern, p) == 0) return (1); } return (0); @@ -213,7 +212,7 @@ match_exclusion(struct match *match, const char *pathname) int match_inclusion(struct match *match, const char *pathname) { - return (bsdtar_fnmatch(match->pattern, pathname) == 0); + return (pathmatch(match->pattern, pathname) == 0); } void @@ -281,6 +280,41 @@ unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg) return (matching->inclusions_unmatched_count); } +/* + * TODO: Extend this so that the following matches work: + * "foo//bar" == "foo/bar" + * "foo/./bar" == "foo/bar" + * "./foo" == "foo" + * + * The POSIX fnmatch() function doesn't handle any of these, but + * all are common situations that arise when paths are generated within + * large scripts. E.g., the following is quite common: + * MYPATH=foo/ TARGET=$MYPATH/bar + * It may be worthwhile to edit such paths at write time as well, + * especially when such editing may avoid the need for long pathname + * extensions. + */ +static int +pathmatch(const char *pattern, const char *string) +{ + /* + * Strip leading "./" or ".//" so that, e.g., + * "foo" matches "./foo". In particular, this + * opens up an optimization for the writer to + * elide leading "./". + */ + if (pattern[0] == '.' && pattern[1] == '/') { + pattern += 2; + while (pattern[0] == '/') + ++pattern; + } + if (string[0] == '.' && string[1] == '/') { + string += 2; + while (string[0] == '/') + ++string; + } + return (bsdtar_fnmatch(pattern, string)); +} #if defined(HAVE_FNMATCH) && defined(HAVE_FNM_LEADING_DIR) diff --git a/contrib/libarchive/tar/read.c b/contrib/libarchive/tar/read.c index fbb82533a7..642c548b42 100644 --- a/contrib/libarchive/tar/read.c +++ b/contrib/libarchive/tar/read.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.38 2008/05/26 17:10:10 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -127,6 +127,8 @@ read_archive(struct bsdtar *bsdtar, char mode) else archive_read_support_compression_all(a); archive_read_support_format_all(a); + if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK)) @@ -210,22 +212,17 @@ read_archive(struct bsdtar *bsdtar, char mode) if (excluded(bsdtar, archive_entry_pathname(entry))) continue; /* Excluded by a pattern test. */ - /* - * Modify the pathname as requested by the user. We - * do this for -t as well to give users a way to - * preview the effects of their rewrites. We also do - * this before extraction security checks (including - * leading '/' removal). Note that some rewrite - * failures prevent extraction. - */ - if (edit_pathname(bsdtar, entry)) - continue; /* Excluded by a rewrite failure. */ - if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; + /* + * TODO: Provide some reasonable way to + * preview rewrites. gtar always displays + * the unedited path in -t output, which means + * you cannot easily preview rewrites. + */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); @@ -252,6 +249,10 @@ read_archive(struct bsdtar *bsdtar, char mode) } fprintf(out, "\n"); } else { + /* Note: some rewrite failures prevent extraction. */ + if (edit_pathname(bsdtar, entry)) + continue; /* Excluded by a rewrite failure. */ + if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; @@ -293,6 +294,13 @@ read_archive(struct bsdtar *bsdtar, char mode) } } + + r = archive_read_close(a); + if (r != ARCHIVE_OK) + bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + if (r <= ARCHIVE_WARN) + bsdtar->return_value = 1; + if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_compression_name(a)); @@ -385,10 +393,18 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) /* Format the time using 'ls -l' conventions. */ tim = (time_t)st->st_mtime; +#if defined(_WIN32) && !defined(__CYGWIN__) + /* Windows' strftime function does not support %e format. */ + if (abs(tim - now) > (365/2)*86400) + fmt = bsdtar->day_first ? "%d %b %Y" : "%b %d %Y"; + else + fmt = bsdtar->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; +#else if (abs(tim - now) > (365/2)*86400) fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = bsdtar->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; +#endif strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); diff --git a/contrib/libarchive/tar/siginfo.c b/contrib/libarchive/tar/siginfo.c index 249ee34a93..5f28e23003 100644 --- a/contrib/libarchive/tar/siginfo.c +++ b/contrib/libarchive/tar/siginfo.c @@ -82,8 +82,10 @@ siginfo_init(struct bsdtar *bsdtar) /* We want to catch SIGINFO, if it exists. */ bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler); #endif +#ifdef SIGUSR1 /* ... and treat SIGUSR1 the same way as SIGINFO. */ bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler); +#endif } void @@ -135,8 +137,10 @@ siginfo_done(struct bsdtar *bsdtar) /* Restore old SIGINFO handler. */ signal(SIGINFO, bsdtar->siginfo->siginfo_old); #endif +#ifdef SIGUSR1 /* And the old SIGUSR1 handler, too. */ signal(SIGUSR1, bsdtar->siginfo->sigusr1_old); +#endif /* Free strings. */ free(bsdtar->siginfo->path); diff --git a/contrib/libarchive/tar/subst.c b/contrib/libarchive/tar/subst.c index 1c32fb0752..9e864984b4 100644 --- a/contrib/libarchive/tar/subst.c +++ b/contrib/libarchive/tar/subst.c @@ -202,7 +202,7 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s if (symlink_only && !rule->symlink) continue; if (regexec(&rule->re, name, 10, matches, 0)) - break; + continue; got_match = 1; print_match |= rule->print; diff --git a/contrib/libarchive/tar/tree.c b/contrib/libarchive/tar/tree.c index 23e64e3da0..bebfb7977f 100644 --- a/contrib/libarchive/tar/tree.c +++ b/contrib/libarchive/tar/tree.c @@ -43,7 +43,7 @@ * regular dir or via fchdir(2) for a symlink). */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.8 2007/03/11 10:36:42 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.9 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -82,7 +82,13 @@ struct tree_entry { size_t dirname_length; dev_t dev; ino_t ino; +#ifdef HAVE_FCHDIR int fd; +#elif defined(_WIN32) && !defined(__CYGWIN__) + char *fullpath; +#else +#error fchdir function required. +#endif int flags; }; @@ -99,7 +105,11 @@ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; +#ifdef HAVE_FCHDIR int initialDirFd; +#elif defined(_WIN32) && !defined(__CYGWIN__) + char *initialDir; +#endif int flags; int visit_type; int tree_errno; /* Error code from last failed operation. */ @@ -163,7 +173,11 @@ tree_push(struct tree *t, const char *path) memset(te, 0, sizeof(*te)); te->next = t->stack; t->stack = te; +#ifdef HAVE_FCHDIR te->fd = -1; +#elif defined(_WIN32) && !defined(__CYGWIN__) + te->fullpath = NULL; +#endif te->name = strdup(path); te->flags = needsPreVisit | needsPostVisit; te->dirname_length = t->dirname_length; @@ -213,7 +227,11 @@ tree_open(const char *path) t = malloc(sizeof(*t)); memset(t, 0, sizeof(*t)); tree_append(t, path, strlen(path)); +#ifdef HAVE_FCHDIR t->initialDirFd = open(".", O_RDONLY); +#elif defined(_WIN32) && !defined(__CYGWIN__) + t->initialDir = getcwd(NULL, 0); +#endif /* * During most of the traversal, items are set up and then * returned immediately from tree_next(). That doesn't work @@ -227,20 +245,37 @@ tree_open(const char *path) /* * We've finished a directory; ascend back to the parent. */ -static void +static int tree_ascend(struct tree *t) { struct tree_entry *te; + int r = 0; te = t->stack; t->depth--; if (te->flags & isDirLink) { - fchdir(te->fd); +#ifdef HAVE_FCHDIR + if (fchdir(te->fd) != 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; + } close(te->fd); +#elif defined(_WIN32) && !defined(__CYGWIN__) + if (chdir(te->fullpath) != 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; + } + free(te->fullpath); + te->fullpath = NULL; +#endif t->openCount--; } else { - chdir(".."); + if (chdir("..") != 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; + } } + return (r); } /* @@ -272,6 +307,17 @@ int tree_next(struct tree *t) { struct dirent *de = NULL; + int r; + + /* If we're called again after a fatal error, that's an API + * violation. Just crash now. */ + if (t->visit_type == TREE_ERROR_FATAL) { + const char *msg = "Unable to continue traversing" + " directory heirarchy after a fatal error."; + write(2, msg, strlen(msg)); + *(int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */ + exit(1); /* In case the SEGV didn't work. */ + } /* Handle the startup case by returning the initial entry. */ if (t->flags & needsReturn) { @@ -312,7 +358,11 @@ tree_next(struct tree *t) t->stack->flags &= ~needsPreVisit; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { +#ifdef HAVE_FCHDIR t->stack->fd = open(".", O_RDONLY); +#elif defined(_WIN32) && !defined(__CYGWIN__) + t->stack->fullpath = getcwd(NULL, 0); +#endif t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; @@ -327,10 +377,11 @@ tree_next(struct tree *t) t->depth++; t->d = opendir("."); if (t->d == NULL) { - tree_ascend(t); /* Undo "chdir" */ + r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; - return (t->visit_type = TREE_ERROR_DIR); + t->visit_type = r != 0 ? r : TREE_ERROR_DIR; + return (t->visit_type); } t->flags &= ~hasLstat; t->flags &= ~hasStat; @@ -340,11 +391,12 @@ tree_next(struct tree *t) /* We've done everything necessary for the top stack entry. */ if (t->stack->flags & needsPostVisit) { - tree_ascend(t); + r = tree_ascend(t); tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; - return (t->visit_type = TREE_POSTASCENT); + t->visit_type = r != 0 ? r : TREE_POSTASCENT; + return (t->visit_type); } } return (t->visit_type = 0); @@ -533,10 +585,18 @@ tree_close(struct tree *t) if (t->buff) free(t->buff); /* chdir() back to where we started. */ +#ifdef HAVE_FCHDIR if (t->initialDirFd >= 0) { fchdir(t->initialDirFd); close(t->initialDirFd); t->initialDirFd = -1; } +#elif defined(_WIN32) && !defined(__CYGWIN__) + if (t->initialDir != NULL) { + chdir(t->initialDir); + free(t->initialDir); + t->initialDir = NULL; + } +#endif free(t); } diff --git a/contrib/libarchive/tar/tree.h b/contrib/libarchive/tar/tree.h index a32a15f8f3..30d96fe3b0 100644 --- a/contrib/libarchive/tar/tree.h +++ b/contrib/libarchive/tar/tree.h @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/usr.bin/tar/tree.h,v 1.3 2007/01/09 08:12:17 kientzle Exp $ + * $FreeBSD: src/usr.bin/tar/tree.h,v 1.4 2008/11/27 05:49:52 kientzle Exp $ */ /*- @@ -36,8 +36,7 @@ * * Supports very deep logical traversals. The fts package * uses "non-chdir" approach for logical traversals. This * package does use a chdir approach for logical traversals - * and can therefore handle pathnames much longer than - * PATH_MAX. + * and can therefore handle pathnames much longer than PATH_MAX. * * Supports deep physical traversals "out of the box." * Due to the memory optimizations above, there's no need to * limit dir names to 32k. @@ -53,23 +52,31 @@ struct tree *tree_open(const char * /* pathname */); void tree_close(struct tree *); /* - * tree_next() returns Zero if there is no next entry, non-zero if there is. - * Note that directories are potentially visited three times. The first - * time as "regular" file. If tree_descend() is invoked at that time, - * the directory is added to a work list and will be visited two more - * times: once just after descending into the directory and again - * just after ascending back to the parent. + * tree_next() returns Zero if there is no next entry, non-zero if + * there is. Note that directories are potentially visited three + * times. Directories are always visited first as part of enumerating + * their parent. If tree_descend() is invoked at that time, the + * directory is added to a work list and will subsequently be visited + * two more times: once just after descending into the directory and + * again just after ascending back to the parent. * - * TREE_ERROR is returned if the descent failed (because the + * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned - * instead of TREE_PREVISIT/TREE_POSTVISIT. + * instead of TREE_PREVISIT/TREE_POSTVISIT. TREE_ERROR_DIR is not a + * fatal error, but it does imply that the relevant subtree won't be + * visited. TREE_ERROR_FATAL is returned for an error that left the + * traversal completely hosed. Right now, this is only returned for + * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 +#define TREE_ERROR_FATAL -2 + int tree_next(struct tree *); +/* Errno value associated with the last traversal error. */ int tree_errno(struct tree *); /* @@ -85,7 +92,9 @@ void tree_descend(struct tree *); * Return information about the current entry. */ +/* Current depth in the traversal. */ int tree_current_depth(struct tree *); + /* * The current full pathname, length of the full pathname, * and a name that can be used to access the file. @@ -95,6 +104,7 @@ int tree_current_depth(struct tree *); const char *tree_current_path(struct tree *); size_t tree_current_pathlen(struct tree *); const char *tree_current_access_path(struct tree *); + /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the @@ -103,7 +113,9 @@ const char *tree_current_access_path(struct tree *); */ const struct stat *tree_current_stat(struct tree *); const struct stat *tree_current_lstat(struct tree *); -/* The following tests may use mechanisms much faster than stat()/lstat(). */ + +/* The following functions use tricks to avoid a certain number of + * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ int tree_current_is_physical_dir(struct tree *); /* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */ diff --git a/contrib/libarchive/tar/util.c b/contrib/libarchive/tar/util.c index ee1e40dbf2..274ff03ccc 100644 --- a/contrib/libarchive/tar/util.c +++ b/contrib/libarchive/tar/util.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.20 2008/06/09 14:03:55 cperciva Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.23 2008/12/15 06:00:25 kientzle Exp $"); #ifdef HAVE_SYS_STAT_H #include @@ -46,91 +46,161 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.20 2008/06/09 14:03:55 cperciva E #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_WCTYPE_H +#include +#else +/* If we don't have wctype, we need to hack up some version of iswprint(). */ +#define iswprint isprint +#endif #include "bsdtar.h" static void bsdtar_vwarnc(struct bsdtar *, int code, const char *fmt, va_list ap); +static size_t bsdtar_expand_char(char *, size_t, char); +static const char *strip_components(const char *path, int elements); + +/* TODO: Hack up a version of mbtowc for platforms with no wide + * character support at all. I think the following might suffice, + * but it needs careful testing. + * #if !HAVE_MBTOWC + * #define mbtowc(wcp, p, n) ((*wcp = *p), 1) + * #endif + */ /* * Print a string, taking care with any non-printable characters. + * + * Note that we use a stack-allocated buffer to receive the formatted + * string if we can. This is partly performance (avoiding a call to + * malloc()), partly out of expedience (we have to call vsnprintf() + * before malloc() anyway to find out how big a buffer we need; we may + * as well point that first call at a small local buffer in case it + * works), but mostly for safety (so we can use this to print messages + * about out-of-memory conditions). */ void safe_fprintf(FILE *f, const char *fmt, ...) { - char *buff; - char *buff_heap; - int buff_length; + char fmtbuff_stack[256]; /* Place to format the printf() string. */ + char outbuff[256]; /* Buffer for outgoing characters. */ + char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ + char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ + int fmtbuff_length; int length; va_list ap; - char *p; + const char *p; unsigned i; - char buff_stack[256]; - char copy_buff[256]; + wchar_t wc; + char try_wc; /* Use a stack-allocated buffer if we can, for speed and safety. */ - buff_heap = NULL; - buff_length = sizeof(buff_stack); - buff = buff_stack; + fmtbuff_heap = NULL; + fmtbuff_length = sizeof(fmtbuff_stack); + fmtbuff = fmtbuff_stack; + /* Try formatting into the stack buffer. */ va_start(ap, fmt); - length = vsnprintf(buff, buff_length, fmt, ap); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); - /* If the result is too large, allocate a buffer on the heap. */ - if (length >= buff_length) { - buff_length = length+1; - buff_heap = malloc(buff_length); - /* Failsafe: use the truncated string if malloc fails. */ - if (buff_heap != NULL) { - buff = buff_heap; + + /* If the result was too large, allocate a buffer on the heap. */ + if (length >= fmtbuff_length) { + fmtbuff_length = length+1; + fmtbuff_heap = malloc(fmtbuff_length); + + /* Reformat the result into the heap buffer if we can. */ + if (fmtbuff_heap != NULL) { + fmtbuff = fmtbuff_heap; va_start(ap, fmt); - length = vsnprintf(buff, buff_length, fmt, ap); + length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); va_end(ap); + } else { + /* Leave fmtbuff pointing to the truncated + * string in fmtbuff_stack. */ + length = sizeof(fmtbuff_stack) - 1; } } + /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit + * more portable, so we use that here instead. */ + mbtowc(NULL, NULL, 0); /* Reset the shift state. */ + /* Write data, expanding unprintable characters. */ - p = buff; + p = fmtbuff; i = 0; + try_wc = 1; while (*p != '\0') { - unsigned char c = *p++; - - if (isprint(c) && c != '\\') - copy_buff[i++] = c; - else { - copy_buff[i++] = '\\'; - switch (c) { - case '\a': copy_buff[i++] = 'a'; break; - case '\b': copy_buff[i++] = 'b'; break; - case '\f': copy_buff[i++] = 'f'; break; - case '\n': copy_buff[i++] = 'n'; break; -#if '\r' != '\n' - /* On some platforms, \n and \r are the same. */ - case '\r': copy_buff[i++] = 'r'; break; -#endif - case '\t': copy_buff[i++] = 't'; break; - case '\v': copy_buff[i++] = 'v'; break; - case '\\': copy_buff[i++] = '\\'; break; - default: - sprintf(copy_buff + i, "%03o", c); - i += 3; + int n; + + /* Convert to wide char, test if the wide + * char is printable in the current locale. */ + if (try_wc && (n = mbtowc(&wc, p, length)) != -1) { + length -= n; + if (iswprint(wc) && wc != L'\\') { + /* Printable, copy the bytes through. */ + while (n-- > 0) + outbuff[i++] = *p++; + } else { + /* Not printable, format the bytes. */ + while (n-- > 0) + i += bsdtar_expand_char( + outbuff, i, *p++); } + } else { + /* After any conversion failure, don't bother + * trying to convert the rest. */ + i += bsdtar_expand_char(outbuff, i, *p++); + try_wc = 0; } - /* If our temp buffer is full, dump it and keep going. */ - if (i > (sizeof(copy_buff) - 8)) { - copy_buff[i++] = '\0'; - fprintf(f, "%s", copy_buff); + /* If our output buffer is full, dump it and keep going. */ + if (i > (sizeof(outbuff) - 20)) { + outbuff[i++] = '\0'; + fprintf(f, "%s", outbuff); i = 0; } } - copy_buff[i++] = '\0'; - fprintf(f, "%s", copy_buff); + outbuff[i++] = '\0'; + fprintf(f, "%s", outbuff); - /* If we allocated a heap-based buffer, free it now. */ - if (buff_heap != NULL) - free(buff_heap); + /* If we allocated a heap-based formatting buffer, free it now. */ + if (fmtbuff_heap != NULL) + free(fmtbuff_heap); +} + +/* + * Render an arbitrary sequence of bytes into printable ASCII characters. + */ +static size_t +bsdtar_expand_char(char *buff, size_t offset, char c) +{ + size_t i = offset; + + if (isprint((unsigned char)c) && c != '\\') + buff[i++] = c; + else { + buff[i++] = '\\'; + switch (c) { + case '\a': buff[i++] = 'a'; break; + case '\b': buff[i++] = 'b'; break; + case '\f': buff[i++] = 'f'; break; + case '\n': buff[i++] = 'n'; break; +#if '\r' != '\n' + /* On some platforms, \n and \r are the same. */ + case '\r': buff[i++] = 'r'; break; +#endif + case '\t': buff[i++] = 't'; break; + case '\v': buff[i++] = 'v'; break; + case '\\': buff[i++] = '\\'; break; + default: + sprintf(buff + i, "%03o", 0xFF & (int)c); + i += 3; + } + } + + return (i - offset); } static void @@ -184,7 +254,7 @@ yes(const char *fmt, ...) buff[l] = 0; for (p = buff; *p != '\0'; p++) { - if (isspace(0xff & (int)*p)) + if (isspace((unsigned char)*p)) continue; switch(*p) { case 'y': case 'Y': @@ -346,6 +416,31 @@ do_chdir(struct bsdtar *bsdtar) bsdtar->pending_chdir = NULL; } +const char * +strip_components(const char *path, int elements) +{ + const char *p = path; + + while (elements > 0) { + switch (*p++) { + case '/': + elements--; + path = p; + break; + case '\0': + /* Path is too short, skip it. */ + return (NULL); + } + } + + while (*path == '/') + ++path; + if (*path == '\0') + return (NULL); + + return (path); +} + /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. @@ -358,13 +453,13 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) const char *name = archive_entry_pathname(entry); #if HAVE_REGEX_H char *subst_name; -#endif int r; +#endif #if HAVE_REGEX_H r = apply_substitution(bsdtar, name, &subst_name, 0); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry"); + bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -380,7 +475,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_hardlink(entry)) { r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry"); + bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -391,7 +486,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substituion, skipping entry"); + bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -402,42 +497,88 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) #endif /* Strip leading dir names as per --strip-components option. */ - if ((r = bsdtar->strip_components) > 0) { - const char *p = name; - - while (r > 0) { - switch (*p++) { - case '/': - r--; - name = p; - break; - case '\0': - /* Path is too short, skip it. */ + if (bsdtar->strip_components > 0) { + const char *linkname = archive_entry_hardlink(entry); + + name = strip_components(name, bsdtar->strip_components); + if (name == NULL) + return (1); + + if (linkname != NULL) { + linkname = strip_components(linkname, + bsdtar->strip_components); + if (linkname == NULL) return (1); - } + archive_entry_copy_hardlink(entry, linkname); } - while (*name == '/') - ++name; - if (*name == '\0') - return (1); } - /* Strip redundant leading '/' characters. */ - while (name[0] == '/' && name[1] == '/') - name++; - - /* Strip leading '/' unless user has asked us not to. */ - if (name[0] == '/' && !bsdtar->option_absolute_paths) { - /* Generate a warning the first time this happens. */ - if (!bsdtar->warned_lead_slash) { - bsdtar_warnc(bsdtar, 0, - "Removing leading '/' from member names"); + /* By default, don't write or restore absolute pathnames. */ + if (!bsdtar->option_absolute_paths) { + const char *rp, *p = name; + int slashonly = 1; + + /* 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; + slashonly = 0; + } + 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; + slashonly = 0; + } + /* Remove leading "/../", "//", etc. */ + while (p[0] == '/' || p[0] == '\\') { + if (p[1] == '.' && p[2] == '.' && + (p[3] == '/' || p[3] == '\\')) { + p += 3; /* Remove "/..", leave "/" + * for next pass. */ + slashonly = 0; + } else + p += 1; /* Remove "/". */ + } + } while (rp != p); + + if (p != name && !bsdtar->warned_lead_slash) { + /* Generate a warning the first time this happens. */ + if (slashonly) + bsdtar_warnc(bsdtar, 0, + "Removing leading '%c' from member names", + name[0]); + else + bsdtar_warnc(bsdtar, 0, + "Removing leading drive letter from " + "member names"); bsdtar->warned_lead_slash = 1; } - name++; - /* Special case: Stripping leading '/' from "/" yields ".". */ - if (*name == '\0') + + /* Special case: Stripping everything yields ".". */ + if (*p == '\0') name = "."; + else + name = p; + } else { + /* Strip redundant leading '/' characters. */ + while (name[0] == '/' && name[1] == '/') + name++; } /* Safely replace name in archive_entry. */ diff --git a/contrib/libarchive/tar/write.c b/contrib/libarchive/tar/write.c index 1a6e9289ab..f328fe48a6 100644 --- a/contrib/libarchive/tar/write.c +++ b/contrib/libarchive/tar/write.c @@ -24,7 +24,7 @@ */ #include "bsdtar_platform.h" -__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.70 2008/05/26 17:10:10 kientzle Exp $"); +__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.79 2008/11/27 05:49:52 kientzle Exp $"); #ifdef HAVE_SYS_TYPES_H #include @@ -44,9 +44,6 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.70 2008/05/26 17:10:10 kientzle #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_EXT2FS_EXT2_FS_H -#include -#endif #ifdef HAVE_FCNTL_H #include #endif @@ -62,6 +59,17 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.70 2008/05/26 17:10:10 kientzle #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif +/* + * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. + * As the include guards don't agree, the order of include is important. + */ +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) +/* This header exists but is broken on Cygwin. */ +#include +#endif #ifdef HAVE_PWD_H #include #endif @@ -79,6 +87,9 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.70 2008/05/26 17:10:10 kientzle #include "bsdtar.h" #include "tree.h" +/* Size of buffer for holding file data prior to writing. */ +#define FILEDATABUFLEN 65536 + /* Fixed size of uname/gname caches. */ #define name_cache_size 101 @@ -117,29 +128,14 @@ static int archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line); static int copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina); -static void create_cleanup(struct bsdtar *); -static void free_cache(struct name_cache *cache); -static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid); -static int lookup_gname_helper(struct bsdtar *bsdtar, - const char **name, id_t gid); -static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid); -static int lookup_uname_helper(struct bsdtar *bsdtar, - const char **name, id_t uid); static int new_enough(struct bsdtar *, const char *path, const struct stat *); -static void setup_acls(struct bsdtar *, struct archive_entry *, - const char *path); -static void setup_xattrs(struct bsdtar *, struct archive_entry *, - const char *path); static void test_for_append(struct bsdtar *); static void write_archive(struct archive *, struct bsdtar *); -static void write_entry(struct bsdtar *, struct archive *, - const struct stat *, const char *pathname, - const char *accpath); static void write_entry_backend(struct bsdtar *, struct archive *, - struct archive_entry *, int); + struct archive_entry *); static int write_file_data(struct bsdtar *, struct archive *, - int fd); + struct archive_entry *, int fd); static void write_hierarchy(struct bsdtar *, struct archive *, const char *); @@ -152,9 +148,6 @@ tar_mode_c(struct bsdtar *bsdtar) if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) bsdtar_errc(bsdtar, 1, 0, "no files or directories specified"); - /* We want to catch SIGINFO and SIGUSR1. */ - siginfo_init(bsdtar); - a = archive_write_new(); /* Support any format that the library supports. */ @@ -196,6 +189,14 @@ tar_mode_c(struct bsdtar *bsdtar) archive_write_set_compression_bzip2(a); break; #endif +#ifdef HAVE_LIBLZMA + case 'J': + archive_write_set_compression_xz(a); + break; + case OPTION_LZMA: + archive_write_set_compression_lzma(a); + break; +#endif #ifdef HAVE_LIBZ case 'z': archive_write_set_compression_gzip(a); @@ -211,21 +212,11 @@ tar_mode_c(struct bsdtar *bsdtar) } } - r = archive_write_open_file(a, bsdtar->filename); - if (r != ARCHIVE_OK) + if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + if (ARCHIVE_OK != archive_write_open_file(a, bsdtar->filename)) bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); - write_archive(a, bsdtar); - - if (bsdtar->option_totals) { - fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", - (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); - } - - archive_write_finish(a); - - /* Restore old SIGINFO + SIGUSR1 handlers. */ - siginfo_done(bsdtar); } /* @@ -244,9 +235,6 @@ tar_mode_r(struct bsdtar *bsdtar) /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); - /* We want to catch SIGINFO and SIGUSR1. */ - siginfo_init(bsdtar); - format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT, 0666); @@ -313,16 +301,13 @@ tar_mode_r(struct bsdtar *bsdtar) archive_write_set_format(a, format); } lseek(bsdtar->fd, end_offset, SEEK_SET); /* XXX check return val XXX */ - archive_write_open_fd(a, bsdtar->fd); /* XXX check return val XXX */ + if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); write_archive(a, bsdtar); /* XXX check return val XXX */ - if (bsdtar->option_totals) { - fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", - (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); - } - - archive_write_finish(a); close(bsdtar->fd); bsdtar->fd = -1; } @@ -345,9 +330,6 @@ tar_mode_u(struct bsdtar *bsdtar) /* Sanity-test some arguments and the file. */ test_for_append(bsdtar); - /* We want to catch SIGINFO and SIGUSR1. */ - siginfo_init(bsdtar); - bsdtar->fd = open(bsdtar->filename, O_RDWR); if (bsdtar->fd < 0) bsdtar_errc(bsdtar, 1, errno, @@ -402,16 +384,13 @@ tar_mode_u(struct bsdtar *bsdtar) archive_write_set_bytes_per_block(a, DEFAULT_BYTES_PER_BLOCK); lseek(bsdtar->fd, end_offset, SEEK_SET); ftruncate(bsdtar->fd, end_offset); - archive_write_open_fd(a, bsdtar->fd); + if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) + bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); write_archive(a, bsdtar); - if (bsdtar->option_totals) { - fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", - (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); - } - - archive_write_finish(a); close(bsdtar->fd); bsdtar->fd = -1; @@ -434,10 +413,20 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) const char *arg; struct archive_entry *entry, *sparse_entry; + /* We want to catch SIGINFO and SIGUSR1. */ + siginfo_init(bsdtar); + + /* Allocate a buffer for file data. */ + if ((bsdtar->buff = malloc(FILEDATABUFLEN)) == NULL) + bsdtar_errc(bsdtar, 1, 0, "cannot allocate memory"); + if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) bsdtar_errc(bsdtar, 1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); + if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) + bsdtar_errc(bsdtar, 1, 0, "Cannot create read_disk object"); + archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) archive_names_from_file(bsdtar, a); @@ -453,7 +442,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) bsdtar_warnc(bsdtar, 1, 0, "Missing argument for -C"); bsdtar->return_value = 1; - return; + goto cleanup; } } set_chdir(bsdtar, arg); @@ -465,7 +454,12 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) arg + 1) != 0) break; } else +#if defined(_WIN32) && !defined(__CYGWIN__) + write_hierarchy_win(bsdtar, a, arg, + write_hierarchy); +#else write_hierarchy(bsdtar, a, arg); +#endif } bsdtar->argv++; } @@ -473,18 +467,34 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); while (entry != NULL) { - int fd = -1; - write_entry_backend(bsdtar, a, entry, fd); + write_entry_backend(bsdtar, a, entry); archive_entry_free(entry); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); } - create_cleanup(bsdtar); if (archive_write_close(a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } + +cleanup: + /* Free file data buffer. */ + free(bsdtar->buff); + archive_entry_linkresolver_free(bsdtar->resolver); + bsdtar->resolver = NULL; + archive_read_finish(bsdtar->diskreader); + bsdtar->diskreader = NULL; + + if (bsdtar->option_totals) { + fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", + (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); + } + + archive_write_finish(a); + + /* Restore old SIGINFO + SIGUSR1 handlers. */ + siginfo_done(bsdtar); } /* @@ -616,22 +626,23 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) static int copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { - char buff[64*1024]; ssize_t bytes_read; ssize_t bytes_written; off_t progress = 0; - bytes_read = archive_read_data(ina, buff, sizeof(buff)); + bytes_read = archive_read_data(ina, bsdtar->buff, FILEDATABUFLEN); while (bytes_read > 0) { siginfo_printinfo(bsdtar, progress); - bytes_written = archive_write_data(a, buff, bytes_read); + bytes_written = archive_write_data(a, bsdtar->buff, + bytes_read); if (bytes_written < bytes_read) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); return (-1); } progress += bytes_written; - bytes_read = archive_read_data(ina, buff, sizeof(buff)); + bytes_read = archive_read_data(ina, bsdtar->buff, + FILEDATABUFLEN); } return (0); @@ -643,15 +654,12 @@ copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { + struct archive_entry *entry = NULL, *spare_entry = NULL; struct tree *tree; char symlink_mode = bsdtar->symlink_mode; dev_t first_dev = 0; int dev_recorded = 0; int tree_ret; -#ifdef __linux - int fd, r; - unsigned long fflags; -#endif tree = tree_open(path); @@ -662,39 +670,73 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) } while ((tree_ret = tree_next(tree))) { + int r; const char *name = tree_current_path(tree); - const struct stat *st = NULL, *lst = NULL; + const struct stat *st = NULL; /* info to use for this entry */ + const struct stat *lst = NULL; /* lstat() information */ int descend; - if (tree_ret == TREE_ERROR_DIR) - bsdtar_warnc(bsdtar, errno, "%s: Couldn't visit directory", name); + if (tree_ret == TREE_ERROR_FATAL) + bsdtar_errc(bsdtar, 1, tree_errno(tree), + "%s: Unable to continue traversing directory tree", + name); + if (tree_ret == TREE_ERROR_DIR) { + bsdtar_warnc(bsdtar, errno, + "%s: Couldn't visit directory", name); + bsdtar->return_value = 1; + } if (tree_ret != TREE_REGULAR) continue; + + /* + * If this file/dir is excluded by a filename + * pattern, skip it. + */ + if (excluded(bsdtar, name)) + continue; + + /* + * Get lstat() info from the tree library. + */ lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name); - - /* - * Report an error via the exit code if the failed - * path is a prefix of what the user provided via - * the command line. (Testing for string equality - * here won't work due to trailing '/' characters.) - */ - if (memcmp(name, path, strlen(name)) == 0) - bsdtar->return_value = 1; - + /* Return error if files disappear during traverse. */ + bsdtar->return_value = 1; continue; } - if (S_ISLNK(lst->st_mode)) + + /* + * Distinguish 'L'/'P'/'H' symlink following. + */ + switch(symlink_mode) { + case 'H': + /* 'H': After the first item, rest like 'P'. */ + symlink_mode = 'P'; + /* 'H': First item (from command line) like 'L'. */ + /* FALLTHROUGH */ + case 'L': + /* 'L': Do descend through a symlink to dir. */ + descend = tree_current_is_dir(tree); + /* 'L': Follow symlinks to files. */ + archive_read_disk_set_symlink_logical(bsdtar->diskreader); + /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(tree); - /* Default: descend into any dir or symlink to dir. */ - /* We'll adjust this later on. */ - descend = 0; - if ((st != NULL) && S_ISDIR(st->st_mode)) - descend = 1; - if ((lst != NULL) && S_ISDIR(lst->st_mode)) - descend = 1; + if (st != NULL) + break; + /* If stat fails, we have a broken symlink; + * in that case, don't follow the link. */ + /* FALLTHROUGH */ + default: + /* 'P': Don't descend through a symlink to dir. */ + descend = tree_current_is_physical_dir(tree); + /* 'P': Don't follow symlinks to files. */ + archive_read_disk_set_symlink_physical(bsdtar->diskreader); + /* 'P': Archive symlinks as symlinks. */ + st = lst; + break; + } /* * If user has asked us not to cross mount points, @@ -706,90 +748,115 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) dev_recorded = 1; } if (bsdtar->option_dont_traverse_mounts) { - if (lst != NULL && lst->st_dev != first_dev) + if (lst->st_dev != first_dev) descend = 0; } /* - * If this file/dir is flagged "nodump" and we're - * honoring such flags, skip this file/dir. + * In -u mode, check that the file is newer than what's + * already in the archive; in all modes, obey --newerXXX flags. */ -#ifdef HAVE_CHFLAGS - if (bsdtar->option_honor_nodump && - (lst->st_flags & UF_NODUMP)) + if (!new_enough(bsdtar, name, st)) continue; -#endif -#ifdef __linux + archive_entry_free(entry); + entry = archive_entry_new(); + + archive_entry_set_pathname(entry, name); + archive_entry_copy_sourcepath(entry, + tree_current_access_path(tree)); + + /* Populate the archive_entry with metadata from the disk. */ + /* XXX TODO: Arrange to open a regular file before + * calling this so we can pass in an fd and shorten + * the race to query metadata. The linkify dance + * makes this more complex than it might sound. */ + r = archive_read_disk_entry_from_file(bsdtar->diskreader, + entry, -1, st); + if (r != ARCHIVE_OK) + bsdtar_warnc(bsdtar, archive_errno(bsdtar->diskreader), + archive_error_string(bsdtar->diskreader)); + if (r < ARCHIVE_WARN) + continue; + + /* XXX TODO: Just use flag data from entry; avoid the + * duplicate check here. */ + /* - * Linux has a nodump flag too but to read it - * we have to open() the file/dir and do an ioctl on it... + * If this file/dir is flagged "nodump" and we're + * honoring such flags, skip this file/dir. */ +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + /* BSD systems store flags in struct stat */ if (bsdtar->option_honor_nodump && - ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0) && - ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags)), - close(fd), r) >= 0 && - (fflags & EXT2_NODUMP_FL)) + (lst->st_flags & UF_NODUMP)) continue; #endif - /* - * If this file/dir is excluded by a filename - * pattern, skip it. - */ - if (excluded(bsdtar, name)) - continue; +#if defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) + /* Linux uses ioctl to read flags. */ + if (bsdtar->option_honor_nodump) { + int fd = open(name, O_RDONLY | O_NONBLOCK); + if (fd >= 0) { + unsigned long fflags; + int r = ioctl(fd, EXT2_IOC_GETFLAGS, &fflags); + close(fd); + if (r >= 0 && (fflags & EXT2_NODUMP_FL)) + continue; + } + } +#endif /* * If the user vetoes this file/directory, skip it. + * We want this to be fairly late; if some other + * check would veto this file, we shouldn't bother + * the user with it. */ if (bsdtar->option_interactive && !yes("add '%s'", name)) continue; - /* - * If this is a dir, decide whether or not to recurse. - */ - if (bsdtar->option_no_subdirs) - descend = 0; + /* Note: if user vetoes, we won't descend. */ + if (descend && !bsdtar->option_no_subdirs) + tree_descend(tree); /* - * Distinguish 'L'/'P'/'H' symlink following. + * Rewrite the pathname to be archived. If rewrite + * fails, skip the entry. */ - switch(symlink_mode) { - case 'H': - /* 'H': After the first item, rest like 'P'. */ - symlink_mode = 'P'; - /* 'H': First item (from command line) like 'L'. */ - /* FALLTHROUGH */ - case 'L': - /* 'L': Do descend through a symlink to dir. */ - /* 'L': Archive symlink to file as file. */ - lst = tree_current_stat(tree); - /* If stat fails, we have a broken symlink; - * in that case, archive the link as such. */ - if (lst == NULL) - lst = tree_current_lstat(tree); - break; - default: - /* 'P': Don't descend through a symlink to dir. */ - if (!S_ISDIR(lst->st_mode)) - descend = 0; - /* 'P': Archive symlink to file as symlink. */ - /* lst = tree_current_lstat(tree); */ - break; - } + if (edit_pathname(bsdtar, entry)) + continue; - if (descend) - tree_descend(tree); + /* Display entry as we process it. + * This format is required by SUSv2. */ + if (bsdtar->verbose) + safe_fprintf(stderr, "a %s", + archive_entry_pathname(entry)); - /* - * Write the entry. Note that write_entry() handles - * pathname editing and newness testing. - */ - write_entry(bsdtar, a, lst, name, - tree_current_access_path(tree)); + /* Non-regular files get archived with zero size. */ + if (!S_ISREG(st->st_mode)) + archive_entry_set_size(entry, 0); + + /* Record what we're doing, for SIGINFO / SIGUSR1. */ + siginfo_setinfo(bsdtar, "adding", + archive_entry_pathname(entry), archive_entry_size(entry)); + archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); + + /* Handle SIGINFO / SIGUSR1 request if one was made. */ + siginfo_printinfo(bsdtar, 0); + + while (entry != NULL) { + write_entry_backend(bsdtar, a, entry); + archive_entry_free(entry); + entry = spare_entry; + spare_entry = NULL; + } + + if (bsdtar->verbose) + fprintf(stderr, "\n"); } + archive_entry_free(entry); tree_close(tree); } @@ -798,11 +865,12 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) */ static void write_entry_backend(struct bsdtar *bsdtar, struct archive *a, - struct archive_entry *entry, int fd) + struct archive_entry *entry) { + int fd = -1; int e; - if (fd == -1 && archive_entry_size(entry) > 0) { + if (archive_entry_size(entry) > 0) { const char *pathname = archive_entry_sourcepath(entry); fd = open(pathname, O_RDONLY); if (fd == -1) { @@ -835,142 +903,34 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, * that case, just skip the write. */ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0) { - if (write_file_data(bsdtar, a, fd)) + if (write_file_data(bsdtar, a, entry, fd)) exit(1); - close(fd); } -} - -/* - * Add a single filesystem object to the archive. - */ -static void -write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st, - const char *pathname, const char *accpath) -{ - struct archive_entry *entry, *sparse_entry; - int fd; -#ifdef __linux - int r; - unsigned long stflags; -#endif - static char linkbuffer[PATH_MAX+1]; - - fd = -1; - entry = archive_entry_new(); - - archive_entry_set_pathname(entry, pathname); - archive_entry_copy_sourcepath(entry, accpath); /* - * Rewrite the pathname to be archived. If rewrite - * fails, skip the entry. + * If we opened a file, close it now even if there was an error + * which made us decide not to write the archive body. */ - if (edit_pathname(bsdtar, entry)) - goto abort; - - /* - * In -u mode, check that the file is newer than what's - * already in the archive; in all modes, obey --newerXXX flags. - */ - if (!new_enough(bsdtar, archive_entry_pathname(entry), st)) - goto abort; - - /* Display entry as we process it. This format is required by SUSv2. */ - if (bsdtar->verbose) - safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); - - /* Read symbolic link information. */ - if ((st->st_mode & S_IFMT) == S_IFLNK) { - int lnklen; - - lnklen = readlink(accpath, linkbuffer, PATH_MAX); - if (lnklen < 0) { - if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, errno, - "%s: Couldn't read symbolic link", - pathname); - else - safe_fprintf(stderr, - ": Couldn't read symbolic link: %s", - strerror(errno)); - goto cleanup; - } - linkbuffer[lnklen] = 0; - archive_entry_set_symlink(entry, linkbuffer); - } - - /* Look up username and group name. */ - archive_entry_set_uname(entry, lookup_uname(bsdtar, st->st_uid)); - archive_entry_set_gname(entry, lookup_gname(bsdtar, st->st_gid)); - -#ifdef HAVE_CHFLAGS - if (st->st_flags != 0) - archive_entry_set_fflags(entry, st->st_flags, 0); -#endif - -#ifdef __linux - if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) && - ((fd = open(accpath, O_RDONLY|O_NONBLOCK)) >= 0) && - ((r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags)), close(fd), (fd = -1), r) >= 0 && - stflags) { - archive_entry_set_fflags(entry, stflags, 0); - } -#endif - - archive_entry_copy_stat(entry, st); - setup_acls(bsdtar, entry, accpath); - setup_xattrs(bsdtar, entry, accpath); - - /* Non-regular files get archived with zero size. */ - if (!S_ISREG(st->st_mode)) - archive_entry_set_size(entry, 0); - - /* Record what we're doing, for the benefit of SIGINFO / SIGUSR1. */ - siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry), - archive_entry_size(entry)); - archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); - - /* Handle SIGINFO / SIGUSR1 request if one was made. */ - siginfo_printinfo(bsdtar, 0); - - while (entry != NULL) { - write_entry_backend(bsdtar, a, entry, fd); - fd = -1; - archive_entry_free(entry); - entry = sparse_entry; - sparse_entry = NULL; - } - -cleanup: - if (bsdtar->verbose) - fprintf(stderr, "\n"); - -abort: if (fd >= 0) close(fd); - - archive_entry_free(entry); } -/* Helper function to copy file to archive, with stack-allocated buffer. */ +/* Helper function to copy file to archive. */ static int -write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd) +write_file_data(struct bsdtar *bsdtar, struct archive *a, + struct archive_entry *entry, int fd) { - char buff[64*1024]; ssize_t bytes_read; ssize_t bytes_written; off_t progress = 0; - /* XXX TODO: Allocate buffer on heap and store pointer to - * it in bsdtar structure; arrange cleanup as well. XXX */ - - bytes_read = read(fd, buff, sizeof(buff)); + bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN); while (bytes_read > 0) { siginfo_printinfo(bsdtar, progress); - bytes_written = archive_write_data(a, buff, bytes_read); + bytes_written = archive_write_data(a, bsdtar->buff, + bytes_read); if (bytes_written < 0) { /* Write failed; this is bad */ bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); @@ -979,351 +939,16 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd) if (bytes_written < bytes_read) { /* Write was truncated; warn but continue. */ bsdtar_warnc(bsdtar, 0, - "Truncated write; file may have grown while being archived."); + "%s: Truncated write; file may have grown while being archived.", + archive_entry_pathname(entry)); return (0); } progress += bytes_written; - bytes_read = read(fd, buff, sizeof(buff)); + bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN); } return 0; } - -static void -create_cleanup(struct bsdtar *bsdtar) -{ - free_cache(bsdtar->uname_cache); - bsdtar->uname_cache = NULL; - free_cache(bsdtar->gname_cache); - bsdtar->gname_cache = NULL; -} - -#ifdef HAVE_POSIX_ACL -static void setup_acl(struct bsdtar *bsdtar, - struct archive_entry *entry, const char *accpath, - int acl_type, int archive_entry_acl_type); - -static void -setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath) -{ - archive_entry_acl_clear(entry); - - setup_acl(bsdtar, entry, accpath, - ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); - /* Only directories can have default ACLs. */ - if (S_ISDIR(archive_entry_mode(entry))) - setup_acl(bsdtar, entry, accpath, - ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); -} - -static void -setup_acl(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath, int acl_type, int archive_entry_acl_type) -{ - acl_t acl; - acl_tag_t acl_tag; - acl_entry_t acl_entry; - acl_permset_t acl_permset; - int s, ae_id, ae_tag, ae_perm; - const char *ae_name; - - /* Retrieve access ACL from file. */ - acl = acl_get_file(accpath, acl_type); - if (acl != NULL) { - s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); - while (s == 1) { - ae_id = -1; - ae_name = NULL; - - acl_get_tag_type(acl_entry, &acl_tag); - if (acl_tag == ACL_USER) { - ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); - ae_name = lookup_uname(bsdtar, ae_id); - ae_tag = ARCHIVE_ENTRY_ACL_USER; - } else if (acl_tag == ACL_GROUP) { - ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); - ae_name = lookup_gname(bsdtar, ae_id); - ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - } else if (acl_tag == ACL_MASK) { - ae_tag = ARCHIVE_ENTRY_ACL_MASK; - } else if (acl_tag == ACL_USER_OBJ) { - ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (acl_tag == ACL_GROUP_OBJ) { - ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (acl_tag == ACL_OTHER) { - ae_tag = ARCHIVE_ENTRY_ACL_OTHER; - } else { - /* Skip types that libarchive can't support. */ - continue; - } - - acl_get_permset(acl_entry, &acl_permset); - ae_perm = 0; - /* - * acl_get_perm() is spelled differently on different - * platforms; see bsdtar_platform.h for details. - */ - if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) - ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; - if (ACL_GET_PERM(acl_permset, ACL_READ)) - ae_perm |= ARCHIVE_ENTRY_ACL_READ; - if (ACL_GET_PERM(acl_permset, ACL_WRITE)) - ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; - - archive_entry_acl_add_entry(entry, - archive_entry_acl_type, ae_perm, ae_tag, - ae_id, ae_name); - - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); - } - acl_free(acl); - } -} -#else -static void -setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath) -{ - (void)bsdtar; - (void)entry; - (void)accpath; -} -#endif - -#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR - -static void -setup_xattr(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath, const char *name) -{ - size_t size; - void *value = NULL; - char symlink_mode = bsdtar->symlink_mode; - - if (symlink_mode == 'H') - size = getxattr(accpath, name, NULL, 0); - else - size = lgetxattr(accpath, name, NULL, 0); - - if (size == -1) { - bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute"); - return; - } - - if (size > 0 && (value = malloc(size)) == NULL) { - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); - return; - } - - if (symlink_mode == 'H') - size = getxattr(accpath, name, value, size); - else - size = lgetxattr(accpath, name, value, size); - - if (size == -1) { - bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute"); - return; - } - - archive_entry_xattr_add_entry(entry, name, value, size); - - free(value); -} - -/* - * Linux extended attribute support - */ -static void -setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath) -{ - char *list, *p; - size_t list_size; - char symlink_mode = bsdtar->symlink_mode; - - if (symlink_mode == 'H') - list_size = listxattr(accpath, NULL, 0); - else - list_size = llistxattr(accpath, NULL, 0); - - if (list_size == -1) { - bsdtar_warnc(bsdtar, errno, - "Couldn't list extended attributes"); - return; - } else if (list_size == 0) - return; - - if ((list = malloc(list_size)) == NULL) { - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); - return; - } - - if (symlink_mode == 'H') - list_size = listxattr(accpath, list, list_size); - else - list_size = llistxattr(accpath, list, list_size); - - if (list_size == -1) { - bsdtar_warnc(bsdtar, errno, - "Couldn't list extended attributes"); - free(list); - return; - } - - for (p = list; (p - list) < list_size; p += strlen(p) + 1) { - if (strncmp(p, "system.", 7) == 0 || - strncmp(p, "xfsroot.", 8) == 0) - continue; - - setup_xattr(bsdtar, entry, accpath, p); - } - - free(list); -} - -#else - -/* - * Generic (stub) extended attribute support. - */ -static void -setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry, - const char *accpath) -{ - (void)bsdtar; /* UNUSED */ - (void)entry; /* UNUSED */ - (void)accpath; /* UNUSED */ -} - -#endif - -static void -free_cache(struct name_cache *cache) -{ - size_t i; - - if (cache != NULL) { - for (i = 0; i < cache->size; i++) { - if (cache->cache[i].name != NULL && - cache->cache[i].name != NO_NAME) - free((void *)(uintptr_t)cache->cache[i].name); - } - free(cache); - } -} - -/* - * Lookup uid/gid from uname/gname, return NULL if no match. - */ -static const char * -lookup_name(struct bsdtar *bsdtar, struct name_cache **name_cache_variable, - int (*lookup_fn)(struct bsdtar *, const char **, id_t), id_t id) -{ - 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) - bsdtar_errc(bsdtar, 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++; - if (cache->cache[slot].name == NO_NAME) - return (NULL); - return (cache->cache[slot].name); - } - if (cache->cache[slot].name != NO_NAME) - free((void *)(uintptr_t)cache->cache[slot].name); - cache->cache[slot].name = NULL; - } - - if (lookup_fn(bsdtar, &name, id) == 0) { - if (name == NULL || name[0] == '\0') { - /* Cache the negative response. */ - cache->cache[slot].name = NO_NAME; - cache->cache[slot].id = id; - } else { - 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 bsdtar *bsdtar, uid_t uid) -{ - return (lookup_name(bsdtar, &bsdtar->uname_cache, - &lookup_uname_helper, (id_t)uid)); -} - -static int -lookup_uname_helper(struct bsdtar *bsdtar, const char **name, id_t id) -{ - struct passwd *pwent; - - (void)bsdtar; /* UNUSED */ - - errno = 0; - pwent = getpwuid((uid_t)id); - if (pwent == NULL) { - *name = NULL; - if (errno != 0) - bsdtar_warnc(bsdtar, errno, "getpwuid(%d) failed", id); - return (errno); - } - - *name = pwent->pw_name; - return (0); -} - -static const char * -lookup_gname(struct bsdtar *bsdtar, gid_t gid) -{ - return (lookup_name(bsdtar, &bsdtar->gname_cache, - &lookup_gname_helper, (id_t)gid)); -} - -static int -lookup_gname_helper(struct bsdtar *bsdtar, const char **name, id_t id) -{ - struct group *grent; - - (void)bsdtar; /* UNUSED */ - - errno = 0; - grent = getgrgid((gid_t)id); - if (grent == NULL) { - *name = NULL; - if (errno != 0) - bsdtar_warnc(bsdtar, errno, "getgrgid(%d) failed", id); - return (errno); - } - - *name = grent->gr_name; - return (0); -} - /* * Test if the specified file is new enough to include in the archive. */ diff --git a/contrib/libarchive/version b/contrib/libarchive/version deleted file mode 100644 index b50fb110a5..0000000000 --- a/contrib/libarchive/version +++ /dev/null @@ -1 +0,0 @@ -2005005 -- 2.41.0