Upgrade to libarchive-2.7.0. vendor/LIBARCHIVE
authorPeter Avalos <pavalos@theshell.com>
Wed, 24 Jun 2009 21:29:09 +0000 (11:29 -1000)
committerPeter Avalos <pavalos@theshell.com>
Sat, 27 Jun 2009 17:38:43 +0000 (07:38 -1000)
93 files changed:
contrib/libarchive/NEWS
contrib/libarchive/README
contrib/libarchive/README.DELETED
contrib/libarchive/build/version [new file with mode: 0644]
contrib/libarchive/cpio/bsdcpio.1
contrib/libarchive/cpio/cmdline.c
contrib/libarchive/cpio/cpio.c
contrib/libarchive/cpio/cpio.h
contrib/libarchive/cpio/cpio_platform.h
contrib/libarchive/cpio/pathmatch.c
contrib/libarchive/cpio/pathmatch.h
contrib/libarchive/libarchive/COPYING [new file with mode: 0644]
contrib/libarchive/libarchive/README [copied from contrib/libarchive/README with 56% similarity]
contrib/libarchive/libarchive/archive.h
contrib/libarchive/libarchive/archive.h.in [deleted file]
contrib/libarchive/libarchive/archive_check_magic.c
contrib/libarchive/libarchive/archive_endian.h
contrib/libarchive/libarchive/archive_entry.c
contrib/libarchive/libarchive/archive_entry.h
contrib/libarchive/libarchive/archive_entry_copy_stat.c
contrib/libarchive/libarchive/archive_entry_link_resolver.c
contrib/libarchive/libarchive/archive_entry_private.h
contrib/libarchive/libarchive/archive_entry_stat.c
contrib/libarchive/libarchive/archive_platform.h
contrib/libarchive/libarchive/archive_private.h
contrib/libarchive/libarchive/archive_read.3
contrib/libarchive/libarchive/archive_read.c
contrib/libarchive/libarchive/archive_read_disk.3 [new file with mode: 0644]
contrib/libarchive/libarchive/archive_read_disk.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_read_disk_private.h [copied from contrib/libarchive/cpio/pathmatch.h with 59% similarity]
contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_read_open_fd.c
contrib/libarchive/libarchive/archive_read_open_file.c
contrib/libarchive/libarchive/archive_read_open_filename.c
contrib/libarchive/libarchive/archive_read_private.h
contrib/libarchive/libarchive/archive_read_support_compression_all.c
contrib/libarchive/libarchive/archive_read_support_compression_bzip2.c
contrib/libarchive/libarchive/archive_read_support_compression_compress.c
contrib/libarchive/libarchive/archive_read_support_compression_gzip.c
contrib/libarchive/libarchive/archive_read_support_compression_none.c
contrib/libarchive/libarchive/archive_read_support_compression_program.c
contrib/libarchive/libarchive/archive_read_support_compression_xz.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_read_support_format_ar.c
contrib/libarchive/libarchive/archive_read_support_format_cpio.c
contrib/libarchive/libarchive/archive_read_support_format_empty.c
contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
contrib/libarchive/libarchive/archive_read_support_format_mtree.c
contrib/libarchive/libarchive/archive_read_support_format_tar.c
contrib/libarchive/libarchive/archive_read_support_format_zip.c
contrib/libarchive/libarchive/archive_string.c
contrib/libarchive/libarchive/archive_string.h
contrib/libarchive/libarchive/archive_string_sprintf.c
contrib/libarchive/libarchive/archive_util.c
contrib/libarchive/libarchive/archive_virtual.c
contrib/libarchive/libarchive/archive_write.3
contrib/libarchive/libarchive/archive_write.c
contrib/libarchive/libarchive/archive_write_disk.3
contrib/libarchive/libarchive/archive_write_disk.c
contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
contrib/libarchive/libarchive/archive_write_open_filename.c
contrib/libarchive/libarchive/archive_write_private.h
contrib/libarchive/libarchive/archive_write_set_compression_bzip2.c
contrib/libarchive/libarchive/archive_write_set_compression_gzip.c
contrib/libarchive/libarchive/archive_write_set_compression_program.c
contrib/libarchive/libarchive/archive_write_set_compression_xz.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_write_set_format.c
contrib/libarchive/libarchive/archive_write_set_format_ar.c
contrib/libarchive/libarchive/archive_write_set_format_by_name.c
contrib/libarchive/libarchive/archive_write_set_format_cpio.c
contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
contrib/libarchive/libarchive/archive_write_set_format_mtree.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_write_set_format_pax.c
contrib/libarchive/libarchive/archive_write_set_format_shar.c
contrib/libarchive/libarchive/archive_write_set_format_ustar.c
contrib/libarchive/libarchive/filter_fork.c
contrib/libarchive/tar/COPYING [new file with mode: 0644]
contrib/libarchive/tar/bsdtar.1
contrib/libarchive/tar/bsdtar.c
contrib/libarchive/tar/bsdtar.h
contrib/libarchive/tar/bsdtar_platform.h
contrib/libarchive/tar/cmdline.c [new file with mode: 0644]
contrib/libarchive/tar/getdate.c [new file with mode: 0644]
contrib/libarchive/tar/getdate.y [deleted file]
contrib/libarchive/tar/matching.c
contrib/libarchive/tar/read.c
contrib/libarchive/tar/siginfo.c
contrib/libarchive/tar/subst.c
contrib/libarchive/tar/tree.c
contrib/libarchive/tar/tree.h
contrib/libarchive/tar/util.c
contrib/libarchive/tar/write.c
contrib/libarchive/version [deleted file]

index 36005fd..de617c3 100644 (file)
@@ -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.
 
index 50a9d62..07fb359 100644 (file)
@@ -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.)
index fed87a8..409636e 100644 (file)
@@ -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 (file)
index 0000000..3a01fdf
--- /dev/null
@@ -0,0 +1 @@
+2007000
index d4a1857..81b3462 100644 (file)
@@ -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 .
index a602e39..f0033e9 100644 (file)
 
 
 #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 <errno.h>
 #endif
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#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 <grp.h>
 #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);
index 76a91cf..c62ed11 100644 (file)
 
 
 #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 <sys/types.h>
 #include <archive.h>
 #include <archive_entry.h>
 
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
 #ifdef HAVE_STDARG_H
 #include <stdarg.h>
 #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 <unistd.h>
 #endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#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);
+}
index 3afd952..79b2223 100644 (file)
@@ -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
  * 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
 };
index 3145198..8ade731 100644 (file)
@@ -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 <sys/cdefs.h>  /* For __FBSDID */
-#else
+#elif !defined(__FBSDID)
 /* Just leaving this macro replacement empty leads to a dangling semicolon. */
 #define        __FBSDID(a)     struct _undefined_hack
 #endif
 #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 */
index d33bd34..40fa836 100644 (file)
@@ -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);
                }
index 990fa1f..fd2c257 100644 (file)
 #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 (file)
index 0000000..90ef954
--- /dev/null
@@ -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 $
+
similarity index 56%
copy from contrib/libarchive/README
copy to contrib/libarchive/libarchive/README
index 50a9d62..df62c23 100644 (file)
@@ -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.)
index 3f9f6e1..0b3f8e1 100644 (file)
  * platform macros.
  */
 
+#include <sys/stat.h>
 #include <sys/types.h>  /* 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 <inttypes.h> /* int64_t, etc. */
 #endif
 #include <stdio.h> /* 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 <unistd.h>  /* 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. <sigh> */
+__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 (file)
index d76c85e..0000000
+++ /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 <sys/types.h>  /* Linux requires this for off_t */
-@ARCHIVE_H_INCLUDE_INTTYPES_H@
-#include <stdio.h> /* For FILE * */
-#ifndef _WIN32
-#include <unistd.h>  /* 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 */
index 715486d..7a19087 100644 (file)
@@ -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 <sys/types.h>
@@ -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 <unistd.h>
 #endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <winbase.h>
+#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. */
 }
index 550be16..3c62fbf 100644 (file)
@@ -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 <sys/endian.h>
  */
 #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. */
index 69921c3..ffbd92f 100644 (file)
@@ -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 <sys/stat.h>
@@ -39,18 +39,22 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.52 2008/05/26 17:00:2
 #include <sys/sysmacros.h>
 #endif
 #endif
-#ifdef HAVE_EXT2FS_EXT2_FS_H
-#include <ext2fs/ext2_fs.h>    /* for Linux file flags */
-#endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
 #ifdef HAVE_LINUX_FS_H
 #include <linux/fs.h>  /* 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 <linux/ext2_fs.h>     /* for Linux file flags */
 #endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>    /* for Linux file flags */
+#endif
 #include <stddef.h>
 #include <stdio.h>
 #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. <sigh> */
 #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
index ee96c56..52fcc4a 100644 (file)
@@ -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
 
 /* 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 <unistd.h>
+#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.
index 514db02..3f624fc 100644 (file)
@@ -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 <sys/stat.h>
@@ -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);
index 4deee26..9300fe3 100644 (file)
@@ -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 <sys/stat.h>
@@ -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
index 0289290..a4317de 100644 (file)
@@ -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. */
index 6ef5b37..315d5cf 100644 (file)
@@ -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 <sys/stat.h>
@@ -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
 
        /*
index 99dfacc..763c296 100644 (file)
@@ -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 $
  */
 
 /*
 /* 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 <inttypes.h>
-#elif HAVE_STDINT_H
+#endif
+#if HAVE_STDINT_H
 #include <stdint.h>
 #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
index 9ca5893..a25dd22 100644 (file)
@@ -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
 #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))
 
index 13063d9..d6d2c74 100644 (file)
@@ -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 ,
 .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"
 .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
index 327969f..d4233c9 100644 (file)
@@ -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 <errno.h>
@@ -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 (file)
index 0000000..fd53095
--- /dev/null
@@ -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 (file)
index 0000000..3c0b815
--- /dev/null
@@ -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 (file)
index 0000000..6e12517
--- /dev/null
@@ -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 <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#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
@@ -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
  * $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 (file)
index 0000000..60054f9
--- /dev/null
@@ -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 <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#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__) */
index 2ebd46d..c7f9d69 100644 (file)
@@ -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);
 }
index 55c431c..bcc13b1 100644 (file)
@@ -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
index 3d6376f..04efb0f 100644 (file)
@@ -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);
 }
index f4d0274..10def72 100644 (file)
@@ -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
 #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
index da2b246..063c288 100644 (file)
  */
 
 #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);
 }
index 824b0cd..082cf49 100644 (file)
@@ -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 <errno.h>
@@ -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. <sigh> */
+               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;