Import of libarchive and bsdtar 1.3.1
authorPeter Avalos <pavalos@dragonflybsd.org>
Mon, 18 Sep 2006 01:05:12 +0000 (01:05 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Mon, 18 Sep 2006 01:05:12 +0000 (01:05 +0000)
64 files changed:
contrib/libarchive-1.3.1/COPYING [new file with mode: 0644]
contrib/libarchive-1.3.1/NEWS [new file with mode: 0644]
contrib/libarchive-1.3.1/README [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive.h.in [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_check_magic.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_entry.3 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_entry.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_entry.h [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_platform.h [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_private.h [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read.3 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_data_into_buffer.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_data_into_fd.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_extract.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_open_fd.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_open_file.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_open_filename.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_open_memory.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_all.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_bzip2.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_compress.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_gzip.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_none.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_format_all.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_format_cpio.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_format_iso9660.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_format_tar.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_read_support_format_zip.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_string.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_string.h [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_string_sprintf.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_util.3 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_util.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write.3 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_open_fd.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_open_file.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_open_filename.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_open_memory.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_compression_bzip2.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_compression_gzip.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_compression_none.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format_by_name.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format_cpio.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format_pax.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format_shar.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/archive_write_set_format_ustar.c [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/libarchive-formats.5 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/libarchive.3 [new file with mode: 0644]
contrib/libarchive-1.3.1/libarchive/tar.5 [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/bsdtar.1 [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/bsdtar.c [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/bsdtar.h [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/bsdtar_platform.h [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/getdate.y [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/matching.c [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/read.c [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/tree.c [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/tree.h [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/util.c [new file with mode: 0644]
contrib/libarchive-1.3.1/tar/write.c [new file with mode: 0644]
contrib/libarchive-1.3.1/version [new file with mode: 0644]

diff --git a/contrib/libarchive-1.3.1/COPYING b/contrib/libarchive-1.3.1/COPYING
new file mode 100644 (file)
index 0000000..6128d17
--- /dev/null
@@ -0,0 +1,26 @@
+All of the C source code and documentation in this package is subject
+to the following:
+
+Copyright (c) 2003-2006 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.
diff --git a/contrib/libarchive-1.3.1/NEWS b/contrib/libarchive-1.3.1/NEWS
new file mode 100644 (file)
index 0000000..f770403
--- /dev/null
@@ -0,0 +1,73 @@
+
+Sep 05, 2006: libarchive 1.3.1 released
+Sep 5, 2006: Bump version to 1.3 for new I/O wrappers.
+Sep 4, 2006: New memory and FILE read/write wrappers.
+Sep 4, 2006: libarchive test harness is now minimally functional;
+    it's located a few minor bugs in error-handling logic
+
+Aug 19, 2006: libarchive 1.2.57 released
+
+Aug 19, 2006: libarchive 1.2.56 released
+Aug 19, 2006: Documentation updates.
+
+Aug 19, 2006: libarchive 1.2.55 released
+
+Aug 17, 2006: libarchive 1.2.54 released
+Aug 17, 2006: Outline ABI changes for libarchive 2.0; these
+   are protected behind #ifdef's until I think I've found everything
+   that needs to change.
+Aug 17, 2006: Fix error-handling in archive_read/write_close()
+   They weren't returning any errors before.
+Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set
+   Fixes a bug adding files when writing archive to pipe or when
+   using archive_write_open() directly.
+Jul 2006: New "skip" handling improves performance extracting
+   single files from large uncompressed archives.
+
+Mar 23, 2006: libarchive 1.2.53 released
+Mar 22, 2006: Fix a stupid error in Linux extended-attribute support
+
+Mar 21, 2006: 1.2.52 released
+Mar 21, 2006: Fix -p on platforms that don't have platform-specific
+   extended attribute code.
+Mar 20, 2006: Add NEWS file; fill in some older history from other
+   files.  I'll try to keep this file up-to-date from now on.
+
+OLDER NEWS SUMMARIES
+
+Mar 19, 2006: libarchive 1.2.51 released
+Mar 18, 2006: Many fixes to extended attribute support, including a redesign
+   of the storage format to simplify debugging.
+Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth
+   spending much time on.
+Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support
+   for extended attributes (Currently Linux-only.).
+Mar 11, 2006: Reorganized distribution package: There is now one tar.gz
+   file that builds both libarchive and bsdtar.
+Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write
+   Pax attribute entry names.
+Nov 7, 2005: Experimental 'tp' format support in libarchive.  Feedback
+   appreciated; this is not enabled by archive_read_support_format_all()
+   yet as I'm not quite content with the format detection heuristics.
+Nov 7, 2005: Some more portability improvements thanks to Darin Broady,
+   minor bugfixes.
+Oct 12, 2005: Use GNU libtool to build shared libraries on many systems.
+Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs.
+Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows:
+   http://gnuwin32.sourceforge.net/
+Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson.  -L/-h
+   fix from Jaakko Heinonen.
+Mar 12, 2005: archive_read_extract can now handle very long
+   pathnames (I've tested with pathnames up to 1MB).
+Mar 12, 2005: Marcus Geiger has written an article about libarchive
+   http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/
+   including examples of using it from Objective-C.  His MoinX
+   http://moinx.antbear.org/ desktop Wiki uses
+   libarchive for archiving and restoring Wiki pages.
+Jan 22, 2005: Preliminary ZIP extraction support,
+    new directory-walking code for bsdtar.
+Jan 16, 2005: ISO9660 extraction code added; manpage corrections.
+May 22, 2004: Many gtar-compatible long options have been added; almost
+    all FreeBSD ports extract correctly with bsdtar.
+May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar,
+    and pdtar archives.
diff --git a/contrib/libarchive-1.3.1/README b/contrib/libarchive-1.3.1/README
new file mode 100644 (file)
index 0000000..872649b
--- /dev/null
@@ -0,0 +1,114 @@
+README for libarchive bundle.
+
+This distribution bundle includes the following components:
+
+   * libarchive: a library for reading and writing streaming archives
+   * tar: the 'bsdtar' program is a full-featured 'tar'
+          replacement built on libarchive
+   * minitar: a compact sample demonstrating use of libarchive
+
+The top-level directory contains the following information files:
+   * AUTHORS - who wrote all of this
+   * COPYING - what you can do with this
+   * ChangeLog - highlights of recent changes
+   * 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
+ * libarchive.3 gives an overview of the library as a whole
+ * archive_read.3 and archive_write.3 provide detailed calling
+   sequences for the read and write APIs
+ * archive_entry.3 details the "struct archive_entry" utility class
+ * libarchive-formats.5 documents the file formats supported by the library
+ * 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.
+
+Currently, the library automatically detects and reads the following:
+  * gzip compression
+  * bzip2 compression
+  * compress/LZW compression
+  * GNU tar format (including GNU long filenames, long link names, and
+    sparse files)
+  * Solaris 9 extended tar format (including ACLs)
+  * Old V7 tar archives
+  * POSIX ustar
+  * POSIX pax interchange format
+  * POSIX octet-oriented cpio
+  * SVR4 ASCII cpio
+  * Binary cpio (big-endian or little-endian)
+  * ISO9660 CD-ROM images (with optional Rockridge extensions)
+  * ZIP archives (with uncompressed or "deflate" compressed entries)
+  * Experimental support for Unix 4th Edition "tp" archives
+
+The library can write:
+  * gzip compression
+  * bzip2 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
+  * shar archives
+
+Notes about the library architecture:
+
+ * 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
+   sacrificing a lot of other features, so don't bother asking.
+
+ * The library is designed to be extended with new compression and
+   archive formats.  The only requirement is that the format be
+   readable or writable as a stream and that each archive entry be
+   independent.
+
+ * On read, compression and format are always detected automatically.
+
+ * I've attempted to minimize static link pollution.  If you don't
+   explicitly invoke a particular feature (such as support for a
+   particular compression or format), it won't get pulled in.
+   In particular, if you don't explicitly enable a particular
+   compression or decompression support, you won't need to link
+   against the corresponding compression or decompression libraries.
+   This also reduces the size of statically-linked binaries in
+   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
+   or mmap the entire archive and give it to the library at once.
+   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.
+
+ * The archive itself is read/written using callback functions.
+   You can read an archive directly from an in-memory buffer or
+   write it to a socket, if you wish.  There are some utility
+   functions to provide easy-to-use "open file," etc, capabilities.
+
+ * The read/write APIs are designed to allow individual entries
+   to be read or written to any data source:  You can create
+   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.
+
+ * Note: "pax interchange format" is really an extended tar format,
+   despite what the name says.
diff --git a/contrib/libarchive-1.3.1/libarchive/archive.h.in b/contrib/libarchive-1.3.1/libarchive/archive.h.in
new file mode 100644 (file)
index 0000000..081e789
--- /dev/null
@@ -0,0 +1,402 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.31 2006/09/05 05:59:45 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define        ARCHIVE_H_INCLUDED
+
+/*
+ * This header file corresponds to:
+ *   Library version @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 * */
+#include <unistd.h>  /* For ssize_t and size_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * 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.
+ *
+ * ARCHIVE_API_FEATURE is incremented with each significant feature
+ * addition, so you can test (at compile or run time) if a particular
+ * feature is implemented.  It's no big deal if ARCHIVE_API_FEATURE !=
+ * archive_api_feature(), as long as both are high enough to include
+ * the features you're relying on.  Specific values of FEATURE are
+ * documented here:
+ *
+ *    1 - Version tests are available.
+ *    2 - archive_{read,write}_close available separately from _finish.
+ *    3 - open_memory, open_memory2, open_FILE, open_fd available
+ */
+#define        ARCHIVE_API_VERSION     @ARCHIVE_API_MAJOR@
+int            archive_api_version(void);
+#define        ARCHIVE_API_FEATURE     @ARCHIVE_API_MINOR@
+int            archive_api_feature(void);
+/* Textual name/version of the library. */
+#define        ARCHIVE_LIBRARY_VERSION "libarchive @VERSION@"
+const char *   archive_version(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 sucess. */
+#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 */
+typedef ssize_t        archive_skip_callback(struct archive *, void *_client_data,
+                   size_t request);
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef ssize_t        archive_write_callback(struct archive *, void *_client_data,
+                   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
+
+/*
+ * 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.
+ */
+#define        ARCHIVE_FORMAT_BASE_MASK                0xff0000U
+#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
+
+/*-
+ * 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_format_all(struct archive *);
+int             archive_read_support_format_cpio(struct archive *);
+int             archive_read_support_format_gnutar(struct archive *);
+int             archive_read_support_format_iso9660(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 gaurantee 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. */
+/* TODO: The 'Default' comments here are not quite correct; clean this up. */
+#define        ARCHIVE_EXTRACT_OWNER   (1) /* Default: owner/group not restored */
+#define        ARCHIVE_EXTRACT_PERM    (2) /* Default: restore perm only for reg file*/
+#define        ARCHIVE_EXTRACT_TIME    (4) /* Default: mod time not restored */
+#define        ARCHIVE_EXTRACT_NO_OVERWRITE (8) /* Default: Replace files on disk */
+#define        ARCHIVE_EXTRACT_UNLINK  (16) /* Default: don't unlink existing files */
+#define        ARCHIVE_EXTRACT_ACL     (32) /* Default: don't restore ACLs */
+#define        ARCHIVE_EXTRACT_FFLAGS  (64) /* Default: don't restore fflags */
+#define        ARCHIVE_EXTRACT_XATTR   (128) /* Default: don't restore xattrs */
+
+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 *);
+/* 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_cpio(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
+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
+
+/*
+ * 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_set_error(struct archive *, int _err, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_check_magic.c b/contrib/libarchive-1.3.1/libarchive/archive_check_magic.c
new file mode 100644 (file)
index 0000000..63932a0
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_check_magic.c,v 1.5 2004/10/18 04:34:30 kientzle Exp $");
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive_private.h"
+
+static void
+errmsg(const char *m)
+{
+       write(STDERR_FILENO, m, strlen(m));
+}
+
+static void
+diediedie(void)
+{
+       *(char *)0 = 1; /* Deliberately segfault and force a coredump. */
+       _exit(1);       /* If that didn't work, just exit with an error. */
+}
+
+static const char *
+state_name(unsigned s)
+{
+       switch (s) {
+       case ARCHIVE_STATE_NEW:         return ("new");
+       case ARCHIVE_STATE_HEADER:      return ("header");
+       case ARCHIVE_STATE_DATA:        return ("data");
+       case ARCHIVE_STATE_EOF:         return ("eof");
+       case ARCHIVE_STATE_CLOSED:      return ("closed");
+       case ARCHIVE_STATE_FATAL:       return ("fatal");
+       default:                        return ("??");
+       }
+}
+
+
+static void
+write_all_states(int states)
+{
+       unsigned lowbit;
+
+       /* A trick for computing the lowest set bit. */
+       while ((lowbit = states & (-states)) != 0) {
+               states &= ~lowbit;              /* Clear the low bit. */
+               errmsg(state_name(lowbit));
+               if (states != 0)
+                       errmsg("/");
+       }
+}
+
+/*
+ * Check magic value and current state; bail if it isn't valid.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+void
+__archive_check_magic(struct archive *a, unsigned magic, unsigned state,
+    const char *function)
+{
+       if (a->magic != magic) {
+               errmsg("INTERNAL ERROR: Function ");
+               errmsg(function);
+               errmsg(" invoked with invalid struct archive structure.\n");
+               diediedie();
+       }
+
+       if (state == ARCHIVE_STATE_ANY)
+               return;
+
+       if ((a->state & state) == 0) {
+               errmsg("INTERNAL ERROR: Function '");
+               errmsg(function);
+               errmsg("' invoked with archive structure in state '");
+               write_all_states(a->state);
+               errmsg("', should be in state '");
+               write_all_states(state);
+               errmsg("'\n");
+               diediedie();
+       }
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_entry.3 b/contrib/libarchive-1.3.1/libarchive/archive_entry.3
new file mode 100644 (file)
index 0000000..f5ed30d
--- /dev/null
@@ -0,0 +1,341 @@
+.\" Copyright (c) 2003-2004 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: src/lib/libarchive/archive_entry.3,v 1.11 2005/11/24 10:06:05 ru Exp $
+.\"
+.Dd December 15, 2003
+.Dt archive_entry 3
+.Os
+.Sh NAME
+.Nm archive_entry_acl_add_entry ,
+.Nm archive_entry_acl_add_entry_w ,
+.Nm archive_entry_acl_clear ,
+.Nm archive_entry_acl_count ,
+.Nm archive_entry_acl_next ,
+.Nm archive_entry_acl_next_w ,
+.Nm archive_entry_acl_reset ,
+.Nm archive_entry_acl_text_w ,
+.Nm archive_entry_atime ,
+.Nm archive_entry_atime_nsec ,
+.Nm archive_entry_clear ,
+.Nm archive_entry_clone ,
+.Nm archive_entry_copy_fflags_text_w ,
+.Nm archive_entry_copy_gname_w ,
+.Nm archive_entry_copy_hardlink ,
+.Nm archive_entry_copy_hardlink_w ,
+.Nm archive_entry_copy_pathname_w ,
+.Nm archive_entry_copy_stat ,
+.Nm archive_entry_copy_symlink_w ,
+.Nm archive_entry_copy_uname_w ,
+.Nm archive_entry_dev ,
+.Nm archive_entry_fflags ,
+.Nm archive_entry_fflags_text ,
+.Nm archive_entry_free ,
+.Nm archive_entry_gid ,
+.Nm archive_entry_gname ,
+.Nm archive_entry_hardlink ,
+.Nm archive_entry_ino ,
+.Nm archive_entry_mode ,
+.Nm archive_entry_mtime ,
+.Nm archive_entry_mtime_nsec ,
+.Nm archive_entry_new ,
+.Nm archive_entry_pathname ,
+.Nm archive_entry_pathname_w ,
+.Nm archive_entry_rdev ,
+.Nm archive_entry_rdevmajor ,
+.Nm archive_entry_rdevminor ,
+.Nm archive_entry_set_fflags ,
+.Nm archive_entry_set_gid ,
+.Nm archive_entry_set_gname ,
+.Nm archive_entry_set_hardlink ,
+.Nm archive_entry_set_link ,
+.Nm archive_entry_set_mode ,
+.Nm archive_entry_set_mtime ,
+.Nm archive_entry_set_pathname ,
+.Nm archive_entry_set_rdevmajor ,
+.Nm archive_entry_set_rdevminor ,
+.Nm archive_entry_set_size ,
+.Nm archive_entry_set_symlink ,
+.Nm archive_entry_set_uid ,
+.Nm archive_entry_set_uname ,
+.Nm archive_entry_size ,
+.Nm archive_entry_stat ,
+.Nm archive_entry_symlink ,
+.Nm archive_entry_uid ,
+.Nm archive_entry_uname
+.Nd functions for manipulating archive entry descriptions
+.Sh SYNOPSIS
+.In archive_entry.h
+.Ft void
+.Fn archive_entry_acl_add_entry "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const char *name"
+.Ft void
+.Fn archive_entry_acl_add_entry_w "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const wchar_t *name"
+.Ft void
+.Fn archive_entry_acl_clear "struct archive_entry *"
+.Ft int
+.Fn archive_entry_acl_count "struct archive_entry *" "int type"
+.Ft int
+.Fn archive_entry_acl_next "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const char **name"
+.Ft int
+.Fn archive_entry_acl_next_w "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const wchar_t **name"
+.Ft int
+.Fn archive_entry_acl_reset "struct archive_entry *" "int want_type"
+.Ft const wchar_t *
+.Fn archive_entry_acl_text_w "struct archive_entry *" "int flags"
+.Ft time_t
+.Fn archive_entry_atime "struct archive_entry *"
+.Ft long
+.Fn archive_entry_atime_nsec "struct archive_entry *"
+.Ft "struct archive_entry *"
+.Fn archive_entry_clear "struct archive_entry *"
+.Ft struct archive_entry *
+.Fn archive_entry_clone "struct archive_entry *"
+.Ft const wchar_t *
+.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
+.Ft void
+.Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
+.Ft dev_t
+.Fn archive_entry_dev "struct archive_entry *"
+.Ft void
+.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear"
+.Ft const char *
+.Fn archive_entry_fflags_text "struct archive_entry *"
+.Ft void
+.Fn archive_entry_free "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_gname "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_hardlink "struct archive_entry *"
+.Ft ino_t
+.Fn archive_entry_ino "struct archive_entry *"
+.Ft mode_t
+.Fn archive_entry_mode "struct archive_entry *"
+.Ft time_t
+.Fn archive_entry_mtime "struct archive_entry *"
+.Ft long
+.Fn archive_entry_mtime_nsec "struct archive_entry *"
+.Ft struct archive_entry *
+.Fn archive_entry_new "void"
+.Ft const char *
+.Fn archive_entry_pathname "struct archive_entry *"
+.Ft const wchar_t *
+.Fn archive_entry_pathname_w "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdev "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdevmajor "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdevminor "struct archive_entry *"
+.Ft void
+.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear"
+.Ft void
+.Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
+.Ft void
+.Fn archive_entry_set_gname "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_link "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
+.Ft void
+.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
+.Ft void
+.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_size "struct archive_entry *" "int64_t"
+.Ft void
+.Fn archive_entry_set_symlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_uid "struct archive_entry *" "uid_t"
+.Ft void
+.Fn archive_entry_set_uname "struct archive_entry *" "const char *"
+.Ft int64_t
+.Fn archive_entry_size "struct archive_entry *"
+.Ft const struct stat *
+.Fn archive_entry_stat "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_symlink "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_uname "struct archive_entry *"
+.Sh DESCRIPTION
+These functions create and manipulate data objects that
+represent entries within an archive.
+You can think of a
+.Tn struct archive_entry
+as a heavy-duty version of
+.Tn struct stat :
+it includes everything from
+.Tn struct stat
+plus associated pathname, textual group and user names, etc.
+These objects are used by
+.Xr libarchive 3
+to represent the metadata associated with a particular
+entry in an archive.
+.Ss Create and Destroy
+There are functions to allocate, destroy, clear, and copy
+.Va archive_entry
+objects:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_clear
+Erases the object, resetting all internal fields to the
+same state as a newly-created object.
+This is provided to allow you to quickly recycle objects
+without thrashing the heap.
+.It Fn archive_entry_clone
+A deep copy operation; all text fields are duplicated.
+.It Fn archive_entry_free
+Releases the
+.Tn struct archive_entry
+object.
+.It Fn archive_entry_new
+Allocate and return a blank
+.Tn struct archive_entry
+object.
+.El
+.Ss Set and Get Functions
+Most of the functions here set or read entries in an object.
+Such functions have one of the following forms:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_set_XXXX
+Stores the provided data in the object.
+In particular, for strings, the pointer is stored,
+not the referenced string.
+.It Fn archive_entry_copy_XXXX
+As above, except that the referenced data is copied
+into the object.
+.It Fn archive_entry_XXXX
+Returns the specified data.
+In the case of strings, a const-qualified pointer to
+the string is returned.
+.El
+String data can be set or accessed as wide character strings
+or normal
+.Va char
+strings.
+The functions that use wide character strings are suffixed with
+.Cm _w .
+Note that these are different representations of the same data:
+For example, if you store a narrow string and read the corresponding
+wide string, the object will transparently convert formats
+using the current locale.
+Similarly, if you store a wide string and then store a
+narrow string for the same data, the previously-set wide string will
+be discarded in favor of the new data.
+.Pp
+There are a few set/get functions that merit additional description:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_set_link
+This function sets the symlink field if it is already set.
+Otherwise, it sets the hardlink field.
+.El
+.Ss File Flags
+File flags are transparently converted between a bitmap
+representation and a textual format.
+For example, if you set the bitmap and ask for text, the library
+will build a canonical text format.
+However, if you set a text format and request a text format,
+you will get back the same text, even if it is ill-formed.
+If you need to canonicalize a textual flags string, you should first set the
+text form, then request the bitmap form, then use that to set the bitmap form.
+Setting the bitmap format will clear the internal text representation
+and force it to be reconstructed when you next request the text form.
+.Pp
+The bitmap format consists of two integers, one containing bits
+that should be set, the other specifying bits that should be
+cleared.
+Bits not mentioned in either bitmap will be ignored.
+Usually, the bitmap of bits to be cleared will be set to zero.
+In unusual circumstances, you can force a fully-specified set
+of file flags by setting the bitmap of flags to clear to the complement
+of the bitmap of flags to set.
+(This differs from
+.Xr fflagstostr 3 ,
+which only includes names for set bits.)
+Converting a bitmap to a textual string is a platform-specific
+operation; bits that are not meaningful on the current platform
+will be ignored.
+.Pp
+The canonical text format is a comma-separated list of flag names.
+The
+.Fn archive_entry_copy_fflags_text_w
+function parses the provided text and sets the internal bitmap values.
+This is a platform-specific operation; names that are not meaningful
+on the current platform will be ignored.
+The function returns a pointer to the start of the first name that was not
+recognized, or NULL if every name was recognized.
+Note that every name--including names that follow an unrecognized name--will
+be evaluated, and the bitmaps will be set to reflect every name that is
+recognized.
+(In particular, this differs from
+.Xr strtofflags 3 ,
+which stops parsing at the first unrecognized name.)
+.Ss ACL Handling
+XXX This needs serious help.
+XXX
+.Pp
+An
+.Dq Access Control List
+(ACL) is a list of permissions that grant access to particular users or
+groups beyond what would normally be provided by standard POSIX mode bits.
+The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL
+specification.
+In particular, POSIX.1e draft 17 specifies several different formats, but
+none of those formats include both textual user/group names and numeric
+UIDs/GIDs.
+.Pp
+XXX explain ACL stuff XXX
+.\" .Sh EXAMPLE
+.\" .Sh RETURN VALUES
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr archive 3
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle@acm.org .
+.\" .Sh BUGS
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_entry.c b/contrib/libarchive-1.3.1/libarchive/archive_entry.c
new file mode 100644 (file)
index 0000000..2d9f4a3
--- /dev/null
@@ -0,0 +1,1742 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_entry.c,v 1.35 2006/05/01 00:55:44 kientzle Exp $");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#else
+#ifdef MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+#endif
+#ifdef HAVE_EXT2FS_EXT2_FS_H
+#include <ext2fs/ext2_fs.h>    /* for Linux file flags */
+#endif
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Obtain suitable wide-character manipulation functions. */
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#else
+static size_t wcslen(const wchar_t *s)
+{
+       const wchar_t *p = s;
+       while (*p != L'\0')
+               ++p;
+       return p - s;
+}
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+       wchar_t *dest = s1;
+       while ((*s1 = *s2) != L'\0')
+               ++s1, ++s2;
+       return dest;
+}
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#undef max
+#define        max(a, b)       ((a)>(b)?(a):(b))
+
+/*
+ * Handle wide character (i.e., Unicode) and non-wide character
+ * strings transparently.
+ *
+ */
+
+struct aes {
+       const char *aes_mbs;
+       char *aes_mbs_alloc;
+       const wchar_t *aes_wcs;
+       wchar_t *aes_wcs_alloc;
+};
+
+struct ae_acl {
+       struct ae_acl *next;
+       int     type;                   /* E.g., access or default */
+       int     tag;                    /* E.g., user/group/other/mask */
+       int     permset;                /* r/w/x bits */
+       int     id;                     /* uid/gid for user/group */
+       struct aes name;                /* uname/gname */
+};
+
+struct ae_xattr {
+       struct ae_xattr *next;
+
+       char    *name;
+       void    *value;
+       size_t  size;
+};
+
+static void    aes_clean(struct aes *);
+static void    aes_copy(struct aes *dest, struct aes *src);
+static const char *    aes_get_mbs(struct aes *);
+static const wchar_t * aes_get_wcs(struct aes *);
+static void    aes_set_mbs(struct aes *, const char *mbs);
+static void    aes_copy_mbs(struct aes *, const char *mbs);
+/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */
+static void    aes_copy_wcs(struct aes *, const wchar_t *wcs);
+
+static char *   ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
+static const wchar_t   *ae_wcstofflags(const wchar_t *stringp,
+                   unsigned long *setp, unsigned long *clrp);
+static void    append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+                   const wchar_t *wname, int perm, int id);
+static void    append_id_w(wchar_t **wp, int id);
+
+static int     acl_special(struct archive_entry *entry,
+                   int type, int permset, int tag);
+static struct ae_acl *acl_new_entry(struct archive_entry *entry,
+                   int type, int permset, int tag, int id);
+static void    next_field_w(const wchar_t **wp, const wchar_t **start,
+                   const wchar_t **end, wchar_t *sep);
+static int     prefix_w(const wchar_t *start, const wchar_t *end,
+                   const wchar_t *test);
+
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format.  However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).  There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+       /*
+        * Note that ae_stat.st_mode & S_IFMT  can be  0!
+        *
+        * This occurs when the actual file type of the object is not
+        * in the archive.  For example, 'tar' archives store
+        * hardlinks without marking the type of the underlying
+        * object.
+        */
+       struct stat ae_stat;
+
+       /*
+        * Use aes here so that we get transparent mbs<->wcs conversions.
+        */
+       struct aes ae_fflags_text;      /* Text fflags per fflagstostr(3) */
+       unsigned long ae_fflags_set;            /* Bitmap fflags */
+       unsigned long ae_fflags_clear;
+       struct aes ae_gname;            /* Name of owning group */
+       struct aes ae_hardlink; /* Name of target for hardlink */
+       struct aes ae_pathname; /* Name of entry */
+       struct aes ae_symlink;          /* symlink contents */
+       struct aes ae_uname;            /* Name of owner */
+
+       struct ae_acl   *acl_head;
+       struct ae_acl   *acl_p;
+       int              acl_state;     /* See acl_next for details. */
+       wchar_t         *acl_text_w;
+
+       struct ae_xattr *xattr_head;
+       struct ae_xattr *xattr_p;
+};
+
+static void
+aes_clean(struct aes *aes)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       memset(aes, 0, sizeof(*aes));
+}
+
+static void
+aes_copy(struct aes *dest, struct aes *src)
+{
+       *dest = *src;
+       if (src->aes_mbs != NULL) {
+               dest->aes_mbs_alloc = strdup(src->aes_mbs);
+               dest->aes_mbs = dest->aes_mbs_alloc;
+               if (dest->aes_mbs == NULL)
+                       __archive_errx(1, "No memory for aes_copy()");
+       }
+
+       if (src->aes_wcs != NULL) {
+               dest->aes_wcs_alloc = malloc((wcslen(src->aes_wcs) + 1)
+                   * sizeof(wchar_t));
+               dest->aes_wcs = dest->aes_wcs_alloc;
+               if (dest->aes_wcs == NULL)
+                       __archive_errx(1, "No memory for aes_copy()");
+               wcscpy(dest->aes_wcs_alloc, src->aes_wcs);
+       }
+}
+
+static const char *
+aes_get_mbs(struct aes *aes)
+{
+       if (aes->aes_mbs == NULL && aes->aes_wcs == NULL)
+               return NULL;
+       if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) {
+               /*
+                * XXX Need to estimate the number of byte in the
+                * multi-byte form.  Assume that, on average, wcs
+                * chars encode to no more than 3 bytes.  There must
+                * be a better way... XXX
+                */
+               int mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+               aes->aes_mbs_alloc = malloc(mbs_length);
+               aes->aes_mbs = aes->aes_mbs_alloc;
+               if (aes->aes_mbs == NULL)
+                       __archive_errx(1, "No memory for aes_get_mbs()");
+               wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1);
+               aes->aes_mbs_alloc[mbs_length - 1] = 0;
+       }
+       return (aes->aes_mbs);
+}
+
+static const wchar_t *
+aes_get_wcs(struct aes *aes)
+{
+       if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
+               return NULL;
+       if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
+               /*
+                * No single byte will be more than one wide character,
+                * so this length estimate will always be big enough.
+                */
+               int wcs_length = strlen(aes->aes_mbs);
+               aes->aes_wcs_alloc
+                   = malloc((wcs_length + 1) * sizeof(wchar_t));
+               aes->aes_wcs = aes->aes_wcs_alloc;
+               if (aes->aes_wcs == NULL)
+                       __archive_errx(1, "No memory for aes_get_wcs()");
+               mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
+               aes->aes_wcs_alloc[wcs_length] = 0;
+       }
+       return (aes->aes_wcs);
+}
+
+static void
+aes_set_mbs(struct aes *aes, const char *mbs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = mbs;
+       aes->aes_wcs = NULL;
+}
+
+static void
+aes_copy_mbs(struct aes *aes, const char *mbs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs_alloc = malloc((strlen(mbs) + 1) * sizeof(char));
+       if (aes->aes_mbs_alloc == NULL)
+               __archive_errx(1, "No memory for aes_copy_mbs()");
+       strcpy(aes->aes_mbs_alloc, mbs);
+       aes->aes_mbs = aes->aes_mbs_alloc;
+       aes->aes_wcs = NULL;
+}
+
+#if 0
+static void
+aes_set_wcs(struct aes *aes, const wchar_t *wcs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = NULL;
+       aes->aes_wcs = wcs;
+}
+#endif
+
+static void
+aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = NULL;
+       aes->aes_wcs_alloc = malloc((wcslen(wcs) + 1) * sizeof(wchar_t));
+       if (aes->aes_wcs_alloc == NULL)
+               __archive_errx(1, "No memory for aes_copy_wcs()");
+       wcscpy(aes->aes_wcs_alloc, wcs);
+       aes->aes_wcs = aes->aes_wcs_alloc;
+}
+
+struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+       aes_clean(&entry->ae_fflags_text);
+       aes_clean(&entry->ae_gname);
+       aes_clean(&entry->ae_hardlink);
+       aes_clean(&entry->ae_pathname);
+       aes_clean(&entry->ae_symlink);
+       aes_clean(&entry->ae_uname);
+       archive_entry_acl_clear(entry);
+       archive_entry_xattr_clear(entry);
+       memset(entry, 0, sizeof(*entry));
+       return entry;
+}
+
+struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+       struct archive_entry *entry2;
+
+       /* Allocate new structure and copy over all of the fields. */
+       entry2 = malloc(sizeof(*entry2));
+       if (entry2 == NULL)
+               return (NULL);
+       memset(entry2, 0, sizeof(*entry2));
+       entry2->ae_stat = entry->ae_stat;
+       entry2->ae_fflags_set = entry->ae_fflags_set;
+       entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+       aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+       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_symlink, &entry->ae_symlink);
+       aes_copy(&entry2->ae_uname, &entry->ae_uname);
+
+       /* XXX TODO: Copy ACL data over as well. XXX */
+       /* XXX TODO: Copy xattr data over as well. XXX */
+       return (entry2);
+}
+
+void
+archive_entry_free(struct archive_entry *entry)
+{
+       archive_entry_clear(entry);
+       free(entry);
+}
+
+struct archive_entry *
+archive_entry_new(void)
+{
+       struct archive_entry *entry;
+
+       entry = malloc(sizeof(*entry));
+       if (entry == NULL)
+               return (NULL);
+       memset(entry, 0, sizeof(*entry));
+       return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_atime);
+}
+
+long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat));
+}
+
+time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_ctime);
+}
+
+long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat));
+}
+
+dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_dev);
+}
+
+void
+archive_entry_fflags(struct archive_entry *entry,
+    unsigned long *set, unsigned long *clear)
+{
+       *set = entry->ae_fflags_set;
+       *clear = entry->ae_fflags_clear;
+}
+
+/*
+ * Note: if text was provided, this just returns that text.  If you
+ * really need the text to be rebuilt in a canonical form, set the
+ * text, ask for the bitmaps, then set the bitmaps.  (Setting the
+ * bitmaps clears any stored text.)  This design is deliberate: if
+ * we're editing archives, we don't want to discard flags just because
+ * they aren't supported on the current system.  The bitmap<->text
+ * conversions are platform-specific (see below).
+ */
+const char *
+archive_entry_fflags_text(struct archive_entry *entry)
+{
+       const char *f;
+       char *p;
+
+       f = aes_get_mbs(&entry->ae_fflags_text);
+       if (f != NULL)
+               return (f);
+
+       if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
+               return (NULL);
+
+       p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
+       if (p == NULL)
+               return (NULL);
+
+       aes_copy_mbs(&entry->ae_fflags_text, p);
+       free(p);
+       f = aes_get_mbs(&entry->ae_fflags_text);
+       return (f);
+}
+
+gid_t
+archive_entry_gid(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_gid);
+}
+
+const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_gname));
+}
+
+const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_gname));
+}
+
+const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_hardlink));
+}
+
+const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_hardlink));
+}
+
+ino_t
+archive_entry_ino(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_ino);
+}
+
+mode_t
+archive_entry_mode(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_mode);
+}
+
+time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_mtime);
+}
+
+long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat));
+}
+
+const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_pathname));
+}
+
+const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_pathname));
+}
+
+dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_rdev);
+}
+
+dev_t
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+       return (major(entry->ae_stat.st_rdev));
+}
+
+dev_t
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+       return (minor(entry->ae_stat.st_rdev));
+}
+
+int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_size);
+}
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+       return (&entry->ae_stat);
+}
+
+const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_symlink));
+}
+
+const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_symlink));
+}
+
+uid_t
+archive_entry_uid(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_uid);
+}
+
+const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_uname));
+}
+
+const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_uname));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+/*
+ * Note "copy" not "set" here.  The "set" functions that accept a pointer
+ * only store the pointer; they don't copy the underlying object.
+ */
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+       entry->ae_stat = *st;
+}
+
+void
+archive_entry_set_fflags(struct archive_entry *entry,
+    unsigned long set, unsigned long clear)
+{
+       aes_clean(&entry->ae_fflags_text);
+       entry->ae_fflags_set = set;
+       entry->ae_fflags_clear = clear;
+}
+
+const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+    const wchar_t *flags)
+{
+       aes_copy_wcs(&entry->ae_fflags_text, flags);
+       return (ae_wcstofflags(flags,
+                   &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+void
+archive_entry_set_gid(struct archive_entry *entry, gid_t g)
+{
+       entry->ae_stat.st_gid = g;
+}
+
+void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+       aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+       aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+       aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+       entry->ae_stat.st_atime = t;
+       ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns);
+}
+
+void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+       entry->ae_stat.st_ctime = t;
+       ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_set_link(struct archive_entry *entry, const char *target)
+{
+       if (entry->ae_symlink.aes_mbs != NULL ||
+           entry->ae_symlink.aes_wcs != NULL)
+               aes_set_mbs(&entry->ae_symlink, target);
+       else
+               aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+       entry->ae_stat.st_mode = m;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
+{
+       entry->ae_stat.st_mtime = m;
+       ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns);
+}
+
+void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+       aes_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+       dev_t d;
+
+       d = entry->ae_stat.st_rdev;
+       entry->ae_stat.st_rdev = makedev(major(m), minor(d));
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
+{
+       dev_t d;
+
+       d = entry->ae_stat.st_rdev;
+       entry->ae_stat.st_rdev = makedev(major(d), minor(m));
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, int64_t s)
+{
+       entry->ae_stat.st_size = s;
+}
+
+void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+       aes_set_mbs(&entry->ae_symlink, linkname);
+}
+
+void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+       aes_copy_wcs(&entry->ae_symlink, linkname);
+}
+
+void
+archive_entry_set_uid(struct archive_entry *entry, uid_t u)
+{
+       entry->ae_stat.st_uid = u;
+}
+
+void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_uname, name);
+}
+
+/*
+ * ACL management.  The following would, of course, be a lot simpler
+ * if: 1) the last draft of POSIX.1e were a really thorough and
+ * complete standard that addressed the needs of ACL archiving and 2)
+ * everyone followed it faithfully.  Alas, neither is true, so the
+ * following is a lot more complex than might seem necessary to the
+ * uninitiated.
+ */
+
+void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+       struct ae_acl   *ap;
+
+       while (entry->acl_head != NULL) {
+               ap = entry->acl_head->next;
+               aes_clean(&entry->acl_head->name);
+               free(entry->acl_head);
+               entry->acl_head = ap;
+       }
+       if (entry->acl_text_w != NULL) {
+               free(entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+       entry->acl_p = NULL;
+       entry->acl_state = 0; /* Not counting. */
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+void
+archive_entry_acl_add_entry(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const char *name)
+{
+       struct ae_acl *ap;
+
+       if (acl_special(entry, type, permset, tag) == 0)
+               return;
+       ap = acl_new_entry(entry, type, permset, tag, id);
+       if (ap == NULL) {
+               /* XXX Error XXX */
+               return;
+       }
+       if (name != NULL  &&  *name != '\0')
+               aes_copy_mbs(&ap->name, name);
+       else
+               aes_clean(&ap->name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+void
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const wchar_t *name)
+{
+       struct ae_acl *ap;
+
+       if (acl_special(entry, type, permset, tag) == 0)
+               return;
+       ap = acl_new_entry(entry, type, permset, tag, id);
+       if (ap == NULL) {
+               /* XXX Error XXX */
+               return;
+       }
+       if (name != NULL  &&  *name != L'\0')
+               aes_copy_wcs(&ap->name, name);
+       else
+               aes_clean(&ap->name);
+}
+
+/*
+ * If this ACL entry is part of the standard POSIX permissions set,
+ * store the permissions in the stat structure and return zero.
+ */
+static int
+acl_special(struct archive_entry *entry, int type, int permset, int tag)
+{
+       if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+               switch (tag) {
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       entry->ae_stat.st_mode &= ~0700;
+                       entry->ae_stat.st_mode |= (permset & 7) << 6;
+                       return (0);
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       entry->ae_stat.st_mode &= ~0070;
+                       entry->ae_stat.st_mode |= (permset & 7) << 3;
+                       return (0);
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       entry->ae_stat.st_mode &= ~0007;
+                       entry->ae_stat.st_mode |= permset & 7;
+                       return (0);
+               }
+       }
+       return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+static struct ae_acl *
+acl_new_entry(struct archive_entry *entry,
+    int type, int permset, int tag, int id)
+{
+       struct ae_acl *ap;
+
+       if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
+           type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+               return (NULL);
+       if (entry->acl_text_w != NULL) {
+               free(entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+
+       /* XXX TODO: More sanity-checks on the arguments XXX */
+
+       /* If there's a matching entry already in the list, overwrite it. */
+       for (ap = entry->acl_head; ap != NULL; ap = ap->next) {
+               if (ap->type == type && ap->tag == tag && ap->id == id) {
+                       ap->permset = permset;
+                       return (ap);
+               }
+       }
+
+       /* Add a new entry to the list. */
+       ap = malloc(sizeof(*ap));
+       if (ap == NULL)
+               return (NULL);
+       memset(ap, 0, sizeof(*ap));
+       ap->next = entry->acl_head;
+       entry->acl_head = ap;
+       ap->type = type;
+       ap->tag = tag;
+       ap->id = id;
+       ap->permset = permset;
+       return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+       int count;
+       struct ae_acl *ap;
+
+       count = 0;
+       ap = entry->acl_head;
+       while (ap != NULL) {
+               if ((ap->type & want_type) != 0)
+                       count++;
+               ap = ap->next;
+       }
+
+       if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
+               count += 3;
+       return (count);
+}
+
+/*
+ * Prepare for reading entries from the ACL data.  Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_entry_acl_reset(struct archive_entry *entry, int want_type)
+{
+       int count, cutoff;
+
+       count = archive_entry_acl_count(entry, want_type);
+
+       /*
+        * If the only entries are the three standard ones,
+        * then don't return any ACL data.  (In this case,
+        * client can just use chmod(2) to set permissions.)
+        */
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+               cutoff = 3;
+       else
+               cutoff = 0;
+
+       if (count > cutoff)
+               entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+       else
+               entry->acl_state = 0;
+       entry->acl_p = entry->acl_head;
+       return (count);
+}
+
+/*
+ * Return the next ACL entry in the list.  Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+
+int
+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
+    int *permset, int *tag, int *id, const char **name)
+{
+       *name = NULL;
+       *id = -1;
+
+       /*
+        * The acl_state is either zero (no entries available), -1
+        * (reading from list), or an entry type (retrieve that type
+        * from ae_stat.st_mode).
+        */
+       if (entry->acl_state == 0)
+               return (ARCHIVE_WARN);
+
+       /* The first three access entries are special. */
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               switch (entry->acl_state) {
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       *permset = (entry->ae_stat.st_mode >> 6) & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                       entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                       return (ARCHIVE_OK);
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       *permset = (entry->ae_stat.st_mode >> 3) & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                       entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+                       return (ARCHIVE_OK);
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       *permset = entry->ae_stat.st_mode & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_OTHER;
+                       entry->acl_state = -1;
+                       entry->acl_p = entry->acl_head;
+                       return (ARCHIVE_OK);
+               default:
+                       break;
+               }
+       }
+
+       while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
+               entry->acl_p = entry->acl_p->next;
+       if (entry->acl_p == NULL) {
+               entry->acl_state = 0;
+               return (ARCHIVE_WARN);
+       }
+       *type = entry->acl_p->type;
+       *permset = entry->acl_p->permset;
+       *tag = entry->acl_p->tag;
+       *id = entry->acl_p->id;
+       *name = aes_get_mbs(&entry->acl_p->name);
+       entry->acl_p = entry->acl_p->next;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Generate a text version of the ACL.  The flags parameter controls
+ * the style of the generated ACL.
+ */
+const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+       int count;
+       int length;
+       const wchar_t *wname;
+       const wchar_t *prefix;
+       wchar_t separator;
+       struct ae_acl *ap;
+       int id;
+       wchar_t *wp;
+
+       if (entry->acl_text_w != NULL) {
+               free (entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+
+       separator = L',';
+       count = 0;
+       length = 0;
+       ap = entry->acl_head;
+       while (ap != NULL) {
+               if ((ap->type & flags) != 0) {
+                       count++;
+                       if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
+                           (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
+                               length += 8; /* "default:" */
+                       length += 5; /* tag name */
+                       length += 1; /* colon */
+                       wname = aes_get_wcs(&ap->name);
+                       if (wname != NULL)
+                               length += wcslen(wname);
+                       length ++; /* colon */
+                       length += 3; /* rwx */
+                       length += 1; /* colon */
+                       length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
+                       length ++; /* newline */
+               }
+               ap = ap->next;
+       }
+
+       if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
+               length += 10; /* "user::rwx\n" */
+               length += 11; /* "group::rwx\n" */
+               length += 11; /* "other::rwx\n" */
+       }
+
+       if (count == 0)
+               return (NULL);
+
+       /* Now, allocate the string and actually populate it. */
+       wp = entry->acl_text_w = malloc(length * sizeof(wchar_t));
+       if (wp == NULL)
+               __archive_errx(1, "No memory to generate the text version of the ACL");
+       count = 0;
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+                   entry->ae_stat.st_mode & 0700, -1);
+               *wp++ = ',';
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+                   entry->ae_stat.st_mode & 0070, -1);
+               *wp++ = ',';
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+                   entry->ae_stat.st_mode & 0007, -1);
+               count += 3;
+
+               ap = entry->acl_head;
+               while (ap != NULL) {
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+                               wname = aes_get_wcs(&ap->name);
+                               *wp++ = separator;
+                               if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+                                       id = ap->id;
+                               else
+                                       id = -1;
+                               append_entry_w(&wp, NULL, ap->tag, wname,
+                                   ap->permset, id);
+                               count++;
+                       }
+                       ap = ap->next;
+               }
+       }
+
+
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+               if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+                       prefix = L"default:";
+               else
+                       prefix = NULL;
+               ap = entry->acl_head;
+               count = 0;
+               while (ap != NULL) {
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+                               wname = aes_get_wcs(&ap->name);
+                               if (count > 0)
+                                       *wp++ = separator;
+                               if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+                                       id = ap->id;
+                               else
+                                       id = -1;
+                               append_entry_w(&wp, prefix, ap->tag,
+                                   wname, ap->permset, id);
+                               count ++;
+                       }
+                       ap = ap->next;
+               }
+       }
+
+       return (entry->acl_text_w);
+}
+
+static void
+append_id_w(wchar_t **wp, int id)
+{
+       if (id > 9)
+               append_id_w(wp, id / 10);
+       *(*wp)++ = L"0123456789"[id % 10];
+}
+
+static void
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+    const wchar_t *wname, int perm, int id)
+{
+       if (prefix != NULL) {
+               wcscpy(*wp, prefix);
+               *wp += wcslen(*wp);
+       }
+       switch (tag) {
+       case ARCHIVE_ENTRY_ACL_USER_OBJ:
+               wname = NULL;
+               id = -1;
+               /* FALL THROUGH */
+       case ARCHIVE_ENTRY_ACL_USER:
+               wcscpy(*wp, L"user");
+               break;
+       case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+               wname = NULL;
+               id = -1;
+               /* FALL THROUGH */
+       case ARCHIVE_ENTRY_ACL_GROUP:
+               wcscpy(*wp, L"group");
+               break;
+       case ARCHIVE_ENTRY_ACL_MASK:
+               wcscpy(*wp, L"mask");
+               wname = NULL;
+               id = -1;
+               break;
+       case ARCHIVE_ENTRY_ACL_OTHER:
+               wcscpy(*wp, L"other");
+               wname = NULL;
+               id = -1;
+               break;
+       }
+       *wp += wcslen(*wp);
+       *(*wp)++ = L':';
+       if (wname != NULL) {
+               wcscpy(*wp, wname);
+               *wp += wcslen(*wp);
+       }
+       *(*wp)++ = L':';
+       *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+       *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+       *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+       if (id != -1) {
+               *(*wp)++ = L':';
+               append_id_w(wp, id);
+       }
+       **wp = L'\0';
+}
+
+/*
+ * Parse a textual ACL.  This automatically recognizes and supports
+ * extensions described above.  The 'type' argument is used to
+ * indicate the type that should be used for any entries not
+ * explicitly marked as "default:".
+ */
+int
+__archive_entry_acl_parse_w(struct archive_entry *entry,
+    const wchar_t *text, int default_type)
+{
+       int type, tag, permset, id;
+       const wchar_t *start, *end;
+       const wchar_t *name_start, *name_end;
+       wchar_t sep;
+       wchar_t *namebuff;
+       int namebuff_length;
+
+       name_start = name_end = NULL;
+       namebuff = NULL;
+       namebuff_length = 0;
+
+       while (text != NULL  &&  *text != L'\0') {
+               next_field_w(&text, &start, &end, &sep);
+               if (sep != L':')
+                       goto fail;
+
+               /*
+                * Solaris extension:  "defaultuser::rwx" is the
+                * default ACL corresponding to "user::rwx", etc.
+                */
+               if (end-start > 7  && wmemcmp(start, L"default", 7) == 0) {
+                       type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+                       start += 7;
+               } else
+                       type = default_type;
+
+               if (prefix_w(start, end, L"user")) {
+                       next_field_w(&text, &start, &end, &sep);
+                       if (sep != L':')
+                               goto fail;
+                       if (end > start) {
+                               tag = ARCHIVE_ENTRY_ACL_USER;
+                               name_start = start;
+                               name_end = end;
+                       } else
+                               tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+               } else if (prefix_w(start, end, L"group")) {
+                       next_field_w(&text, &start, &end, &sep);
+                       if (sep != L':')
+                               goto fail;
+                       if (end > start) {
+                               tag = ARCHIVE_ENTRY_ACL_GROUP;
+                               name_start = start;
+                               name_end = end;
+                       } else
+                               tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+               } else if (prefix_w(start, end, L"other")) {
+                       next_field_w(&text, &start, &end, &sep);
+                       if (sep != L':')
+                               goto fail;
+                       if (end > start)
+                               goto fail;
+                       tag = ARCHIVE_ENTRY_ACL_OTHER;
+               } else if (prefix_w(start, end, L"mask")) {
+                       next_field_w(&text, &start, &end, &sep);
+                       if (sep != L':')
+                               goto fail;
+                       if (end > start)
+                               goto fail;
+                       tag = ARCHIVE_ENTRY_ACL_MASK;
+               } else
+                       goto fail;
+
+               next_field_w(&text, &start, &end, &sep);
+               permset = 0;
+               while (start < end) {
+                       switch (*start++) {
+                       case 'r': case 'R':
+                               permset |= ARCHIVE_ENTRY_ACL_READ;
+                               break;
+                       case 'w': case 'W':
+                               permset |= ARCHIVE_ENTRY_ACL_WRITE;
+                               break;
+                       case 'x': case 'X':
+                               permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+                               break;
+                       case '-':
+                               break;
+                       default:
+                               goto fail;
+                       }
+               }
+
+               /*
+                * Support star-compatible numeric UID/GID extension.
+                * This extension adds a ":" followed by the numeric
+                * ID so that "group:groupname:rwx", for example,
+                * becomes "group:groupname:rwx:999", where 999 is the
+                * numeric GID.  This extension makes it possible, for
+                * example, to correctly restore ACLs on a system that
+                * might have a damaged passwd file or be disconnected
+                * from a central NIS server.  This extension is compatible
+                * with POSIX.1e draft 17.
+                */
+               if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER ||
+                   tag == ARCHIVE_ENTRY_ACL_GROUP)) {
+                       next_field_w(&text, &start, &end, &sep);
+
+                       id = 0;
+                       while (start < end  && *start >= '0' && *start <= '9') {
+                               if (id > (INT_MAX / 10))
+                                       id = INT_MAX;
+                               else {
+                                       id *= 10;
+                                       id += *start - '0';
+                                       start++;
+                               }
+                       }
+               } else
+                       id = -1; /* No id specified. */
+
+               /* Skip any additional entries. */
+               while (sep == L':') {
+                       next_field_w(&text, &start, &end, &sep);
+               }
+
+               /* Add entry to the internal list. */
+               if (name_end == name_start) {
+                       archive_entry_acl_add_entry_w(entry, type, permset,
+                           tag, id, NULL);
+               } else {
+                       if (namebuff_length <= name_end - name_start) {
+                               if (namebuff != NULL)
+                                       free(namebuff);
+                               namebuff_length = name_end - name_start + 256;
+                               namebuff =
+                                   malloc(namebuff_length * sizeof(wchar_t));
+                               if (namebuff == NULL)
+                                       goto fail;
+                       }
+                       wmemcpy(namebuff, name_start, name_end - name_start);
+                       namebuff[name_end - name_start] = L'\0';
+                       archive_entry_acl_add_entry_w(entry, type,
+                           permset, tag, id, namebuff);
+               }
+       }
+       if (namebuff != NULL)
+               free(namebuff);
+       return (ARCHIVE_OK);
+
+fail:
+       if (namebuff != NULL)
+               free(namebuff);
+       return (ARCHIVE_WARN);
+}
+
+/*
+ * extended attribute handling
+ */
+
+void
+archive_entry_xattr_clear(struct archive_entry *entry)
+{
+       struct ae_xattr *xp;
+
+       while (entry->xattr_head != NULL) {
+               xp = entry->xattr_head->next;
+               free(entry->xattr_head->name);
+               free(entry->xattr_head->value);
+               free(entry->xattr_head);
+               entry->xattr_head = xp;
+       }
+
+       entry->xattr_head = NULL;
+}
+
+void
+archive_entry_xattr_add_entry(struct archive_entry *entry,
+       const char *name, const void *value, size_t size)
+{
+       struct ae_xattr *xp;
+
+       for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+               ;
+
+       if ((xp = malloc(sizeof(struct ae_xattr))) == NULL)
+               /* XXX Error XXX */
+               return;
+
+       xp->name = strdup(name);
+       if ((xp -> value = malloc(size)) != NULL) {
+               memcpy(xp -> value, value, size);
+               xp -> size = size;
+       } else
+               xp -> size = 0;
+
+       xp->next = entry->xattr_head;
+       entry->xattr_head = xp;
+}
+
+
+/*
+ * returns number of the extended attribute entries
+ */
+int
+archive_entry_xattr_count(struct archive_entry *entry)
+{
+       struct ae_xattr *xp;
+       int count = 0;
+
+       for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+               count++;
+
+       return count;
+}
+
+int
+archive_entry_xattr_reset(struct archive_entry * entry)
+{
+       entry->xattr_p = entry->xattr_head;
+
+       return archive_entry_xattr_count(entry);
+}
+
+int
+archive_entry_xattr_next(struct archive_entry * entry,
+       const char **name, const void **value, size_t *size)
+{
+       if (entry->xattr_p) {
+               *name = entry->xattr_p->name;
+               *value = entry->xattr_p->value;
+               *size = entry->xattr_p->size;
+
+               entry->xattr_p = entry->xattr_p->next;
+
+               return (ARCHIVE_OK);
+       } else {
+               *name = NULL;
+               *name = NULL;
+               *size = (size_t)0;
+               return (ARCHIVE_WARN);
+       }
+}
+
+/*
+ * end of xattr handling
+ */
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
+ * to point to just after the separator.  *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier.  In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field_w(const wchar_t **wp, const wchar_t **start,
+    const wchar_t **end, wchar_t *sep)
+{
+       /* Skip leading whitespace to find start of field. */
+       while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
+               (*wp)++;
+       }
+       *start = *wp;
+
+       /* Scan for the separator. */
+       while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
+           **wp != L'\n') {
+               (*wp)++;
+       }
+       *sep = **wp;
+
+       /* Trim trailing whitespace to locate end of field. */
+       *end = *wp - 1;
+       while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+               (*end)--;
+       }
+       (*end)++;
+
+       /* Adjust scanner location. */
+       if (**wp != L'\0')
+               (*wp)++;
+}
+
+static int
+prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
+{
+       if (start == end)
+               return (0);
+
+       if (*start++ != *test++)
+               return (0);
+
+       while (start < end  &&  *start++ == *test++)
+               ;
+
+       if (start < end)
+               return (0);
+
+       return (1);
+}
+
+
+/*
+ * Following code is modified from UC Berkeley sources, and
+ * is subject to the following copyright notice.
+ */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static struct flag {
+       const char      *name;
+       const wchar_t   *wname;
+       unsigned long    set;
+       unsigned long    clear;
+} flags[] = {
+       /* Preferred (shorter) names per flag first, all prefixed by "no" */
+#ifdef SF_APPEND
+       { "nosappnd",   L"nosappnd",            SF_APPEND,      0 },
+       { "nosappend",  L"nosappend",           SF_APPEND,      0 },
+#endif
+#ifdef  EXT2_APPEND_FL                         /* 'a' */
+       { "nosappnd",   L"nosappnd",            EXT2_APPEND_FL, 0 },
+       { "nosappend",  L"nosappend",           EXT2_APPEND_FL, 0 },
+#endif
+#ifdef SF_ARCHIVED
+       { "noarch",     L"noarch",              SF_ARCHIVED,    0 },
+       { "noarchived", L"noarchived",          SF_ARCHIVED,    0 },
+#endif
+#ifdef SF_IMMUTABLE
+       { "noschg",     L"noschg",              SF_IMMUTABLE,   0 },
+       { "noschange",  L"noschange",           SF_IMMUTABLE,   0 },
+       { "nosimmutable",       L"nosimmutable",        SF_IMMUTABLE,   0 },
+#endif
+#ifdef EXT2_IMMUTABLE_FL                       /* 'i' */
+       { "noschg",     L"noschg",              EXT2_IMMUTABLE_FL,      0 },
+       { "noschange",  L"noschange",           EXT2_IMMUTABLE_FL,      0 },
+       { "nosimmutable",       L"nosimmutable",        EXT2_IMMUTABLE_FL,      0 },
+#endif
+#ifdef SF_NOUNLINK
+       { "nosunlnk",   L"nosunlnk",            SF_NOUNLINK,    0 },
+       { "nosunlink",  L"nosunlink",           SF_NOUNLINK,    0 },
+#endif
+#ifdef SF_SNAPSHOT
+       { "nosnapshot", L"nosnapshot",  SF_SNAPSHOT,    0 },
+#endif
+#ifdef UF_APPEND
+       { "nouappnd",   L"nouappnd",            UF_APPEND,      0 },
+       { "nouappend",  L"nouappend",           UF_APPEND,      0 },
+#endif
+#ifdef UF_IMMUTABLE
+       { "nouchg",     L"nouchg",              UF_IMMUTABLE,   0 },
+       { "nouchange",  L"nouchange",           UF_IMMUTABLE,   0 },
+       { "nouimmutable",       L"nouimmutable",        UF_IMMUTABLE,   0 },
+#endif
+#ifdef UF_NODUMP
+       { "nodump",     L"nodump",              0,              UF_NODUMP},
+#endif
+#ifdef EXT2_NODUMP_FL                          /* 'd' */
+       { "nodump",     L"nodump",              0,              EXT2_NODUMP_FL},
+#endif
+#ifdef UF_OPAQUE
+       { "noopaque",   L"noopaque",            UF_OPAQUE,      0 },
+#endif
+#ifdef UF_NOUNLINK
+       { "nouunlnk",   L"nouunlnk",            UF_NOUNLINK,    0 },
+       { "nouunlink",  L"nouunlink",           UF_NOUNLINK,    0 },
+#endif
+#ifdef EXT2_COMPR_FL                           /* 'c' */
+        { "nocompress",        L"nocompress",          EXT2_COMPR_FL,  0 },
+#endif
+
+#ifdef EXT2_NOATIME_FL                         /* 'A' */
+        { "noatime",   L"noatime",             0,              EXT2_NOATIME_FL},
+#endif
+       { NULL,         NULL,                   0,              0 }
+};
+
+/*
+ * fflagstostr --
+ *     Convert file flags to a comma-separated string.  If no flags
+ *     are set, return the empty string.
+ */
+char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+       char *string, *dp;
+       const char *sp;
+       unsigned long bits;
+       struct flag *flag;
+       int     length;
+
+       bits = bitset | bitclear;
+       length = 0;
+       for (flag = flags; flag->name != NULL; flag++)
+               if (bits & (flag->set | flag->clear)) {
+                       length += strlen(flag->name) + 1;
+                       bits &= ~(flag->set | flag->clear);
+               }
+
+       if (length == 0)
+               return (NULL);
+       string = malloc(length);
+       if (string == NULL)
+               return (NULL);
+
+       dp = string;
+       for (flag = flags; flag->name != NULL; flag++) {
+               if (bitset & flag->set || bitclear & flag->clear) {
+                       sp = flag->name + 2;
+               } else if (bitset & flag->clear  ||  bitclear & flag->set) {
+                       sp = flag->name;
+               } else
+                       continue;
+               bitset &= ~(flag->set | flag->clear);
+               bitclear &= ~(flag->set | flag->clear);
+               if (dp > string)
+                       *dp++ = ',';
+               while ((*dp++ = *sp++) != '\0')
+                       ;
+               dp--;
+       }
+
+       *dp = '\0';
+       return (string);
+}
+
+/*
+ * wcstofflags --
+ *     Take string of arguments and return file flags.  This
+ *     version works a little differently than strtofflags(3).
+ *     In particular, it always tests every token, skipping any
+ *     unrecognized tokens.  It returns a pointer to the first
+ *     unrecognized token, or NULL if every token was recognized.
+ *     This version is also const-correct and does not modify the
+ *     provided string.
+ */
+const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+       const wchar_t *start, *end;
+       struct flag *flag;
+       unsigned long set, clear;
+       const wchar_t *failed;
+
+       set = clear = 0;
+       start = s;
+       failed = NULL;
+       /* Find start of first token. */
+       while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+               start++;
+       while (*start != L'\0') {
+               /* Locate end of token. */
+               end = start;
+               while (*end != L'\0'  &&  *end != L'\t'  &&
+                   *end != L' '  &&  *end != L',')
+                       end++;
+               for (flag = flags; flag->wname != NULL; flag++) {
+                       if (wmemcmp(start, flag->wname, end - start) == 0) {
+                               /* Matched "noXXXX", so reverse the sense. */
+                               clear |= flag->set;
+                               set |= flag->clear;
+                               break;
+                       } else if (wmemcmp(start, flag->wname + 2, end - start)
+                           == 0) {
+                               /* Matched "XXXX", so don't reverse. */
+                               set |= flag->set;
+                               clear |= flag->clear;
+                               break;
+                       }
+               }
+               /* Ignore unknown flag names. */
+               if (flag->wname == NULL  &&  failed == NULL)
+                       failed = start;
+
+               /* Find start of next token. */
+               start = end;
+               while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+                       start++;
+
+       }
+
+       if (setp)
+               *setp = set;
+       if (clrp)
+               *clrp = clear;
+
+       /* Return location of first failure. */
+       return (failed);
+}
+
+
+#ifdef TEST
+#include <stdio.h>
+int
+main(int argc, char **argv)
+{
+       struct archive_entry *entry = archive_entry_new();
+       unsigned long set, clear;
+       const wchar_t *remainder;
+
+       remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
+       archive_entry_fflags(entry, &set, &clear);
+
+       wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
+
+       wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
+       return (0);
+}
+#endif
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_entry.h b/contrib/libarchive-1.3.1/libarchive/archive_entry.h
new file mode 100644 (file)
index 0000000..6bf8957
--- /dev/null
@@ -0,0 +1,251 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.19 2006/03/21 16:55:46 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_ENTRY_H_INCLUDED
+#define        ARCHIVE_ENTRY_H_INCLUDED
+
+#include <stddef.h>  /* for wchar_t */
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries that are
+ * supported by "pax interchange" format.  However, GNU, ustar, cpio,
+ * and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).
+ */
+struct archive_entry;
+
+/*
+ * Basic object manipulation
+ */
+
+struct archive_entry   *archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+struct archive_entry   *archive_entry_clone(struct archive_entry *);
+void                    archive_entry_free(struct archive_entry *);
+struct archive_entry   *archive_entry_new(void);
+
+/*
+ * Retrieve fields from an archive_entry.
+ */
+
+time_t                  archive_entry_atime(struct archive_entry *);
+long                    archive_entry_atime_nsec(struct archive_entry *);
+time_t                  archive_entry_ctime(struct archive_entry *);
+long                    archive_entry_ctime_nsec(struct archive_entry *);
+dev_t                   archive_entry_dev(struct archive_entry *);
+void                    archive_entry_fflags(struct archive_entry *,
+                            unsigned long *set, unsigned long *clear);
+const char             *archive_entry_fflags_text(struct archive_entry *);
+gid_t                   archive_entry_gid(struct archive_entry *);
+const char             *archive_entry_gname(struct archive_entry *);
+const wchar_t          *archive_entry_gname_w(struct archive_entry *);
+const char             *archive_entry_hardlink(struct archive_entry *);
+const wchar_t          *archive_entry_hardlink_w(struct archive_entry *);
+ino_t                   archive_entry_ino(struct archive_entry *);
+mode_t                  archive_entry_mode(struct archive_entry *);
+time_t                  archive_entry_mtime(struct archive_entry *);
+long                    archive_entry_mtime_nsec(struct archive_entry *);
+const char             *archive_entry_pathname(struct archive_entry *);
+const wchar_t          *archive_entry_pathname_w(struct archive_entry *);
+dev_t                   archive_entry_rdev(struct archive_entry *);
+dev_t                   archive_entry_rdevmajor(struct archive_entry *);
+dev_t                   archive_entry_rdevminor(struct archive_entry *);
+int64_t                         archive_entry_size(struct archive_entry *);
+const struct stat      *archive_entry_stat(struct archive_entry *);
+const char             *archive_entry_symlink(struct archive_entry *);
+const wchar_t          *archive_entry_symlink_w(struct archive_entry *);
+uid_t                   archive_entry_uid(struct archive_entry *);
+const char             *archive_entry_uname(struct archive_entry *);
+const wchar_t          *archive_entry_uname_w(struct archive_entry *);
+
+/*
+ * Set fields in an 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.
+ */
+
+void   archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+void   archive_entry_set_atime(struct archive_entry *, time_t, long);
+void   archive_entry_set_ctime(struct archive_entry *, time_t, long);
+void   archive_entry_set_fflags(struct archive_entry *,
+           unsigned long set, unsigned long clear);
+/* Returns pointer to start of first invalid token, or NULL if none. */
+/* Note that all recognized tokens are processed, regardless. */
+const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+           const wchar_t *);
+void   archive_entry_set_gid(struct archive_entry *, gid_t);
+void   archive_entry_set_gname(struct archive_entry *, const char *);
+void   archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_hardlink(struct archive_entry *, const char *);
+void   archive_entry_copy_hardlink(struct archive_entry *, const char *);
+void   archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_link(struct archive_entry *, const char *);
+void   archive_entry_set_mode(struct archive_entry *, mode_t);
+void   archive_entry_set_mtime(struct archive_entry *, time_t, long);
+void   archive_entry_set_pathname(struct archive_entry *, const char *);
+void   archive_entry_copy_pathname(struct archive_entry *, const char *);
+void   archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+void   archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+void   archive_entry_set_size(struct archive_entry *, int64_t);
+void   archive_entry_set_symlink(struct archive_entry *, const char *);
+void   archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_uid(struct archive_entry *, uid_t);
+void   archive_entry_set_uname(struct archive_entry *, const char *);
+void   archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+
+/*
+ * ACL routines.  This used to simply store and return text-format ACL
+ * strings, but that proved insufficient for a number of reasons:
+ *   = clients need control over uname/uid and gname/gid mappings
+ *   = there are many different ACL text formats
+ *   = would like to be able to read/convert archives containing ACLs
+ *     on platforms that lack ACL libraries
+ */
+
+/*
+ * Permission bits mimic POSIX.1e.  Note that I've not followed POSIX.1e's
+ * "permset"/"perm" abstract type nonsense.  A permset is just a simple
+ * bitmap, following long-standing Unix tradition.
+ */
+#define        ARCHIVE_ENTRY_ACL_EXECUTE       1
+#define        ARCHIVE_ENTRY_ACL_WRITE         2
+#define        ARCHIVE_ENTRY_ACL_READ          4
+
+/* We need to be able to specify either or both of these. */
+#define        ARCHIVE_ENTRY_ACL_TYPE_ACCESS   256
+#define        ARCHIVE_ENTRY_ACL_TYPE_DEFAULT  512
+
+/* Tag values mimic POSIX.1e */
+#define        ARCHIVE_ENTRY_ACL_USER          10001   /* Specified user. */
+#define        ARCHIVE_ENTRY_ACL_USER_OBJ      10002   /* User who owns the file. */
+#define        ARCHIVE_ENTRY_ACL_GROUP         10003   /* Specified group. */
+#define        ARCHIVE_ENTRY_ACL_GROUP_OBJ     10004   /* Group who owns the file. */
+#define        ARCHIVE_ENTRY_ACL_MASK          10005   /* Modify group access. */
+#define        ARCHIVE_ENTRY_ACL_OTHER         10006   /* Public. */
+
+/*
+ * Set the ACL by clearing it and adding entries one at a time.
+ * Unlike the POSIX.1e ACL routines, you must specify the type
+ * (access/default) for each entry.  Internally, the ACL data is just
+ * a soup of entries.  API calls here allow you to retrieve just the
+ * entries of interest.  This design (which goes against the spirit of
+ * POSIX.1e) is useful for handling archive formats that combine
+ * default and access information in a single ACL list.
+ */
+void    archive_entry_acl_clear(struct archive_entry *);
+void    archive_entry_acl_add_entry(struct archive_entry *,
+            int type, int permset, int tag, int qual, const char *name);
+void    archive_entry_acl_add_entry_w(struct archive_entry *,
+            int type, int permset, int tag, int qual, const wchar_t *name);
+
+/*
+ * To retrieve the ACL, first "reset", then repeatedly ask for the
+ * "next" entry.  The want_type parameter allows you to request only
+ * access entries or only default entries.
+ */
+int     archive_entry_acl_reset(struct archive_entry *, int want_type);
+int     archive_entry_acl_next(struct archive_entry *, int want_type,
+            int *type, int *permset, int *tag, int *qual, const char **name);
+int     archive_entry_acl_next_w(struct archive_entry *, int want_type,
+            int *type, int *permset, int *tag, int *qual,
+            const wchar_t **name);
+
+/*
+ * Construct a text-format ACL.  The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.  (As used by 'star'.)
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ *    default ACL entry.
+ */
+#define        ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID        1024
+#define        ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    2048
+const wchar_t  *archive_entry_acl_text_w(struct archive_entry *, int flags);
+
+/* Return a count of entries matching 'want_type' */
+int     archive_entry_acl_count(struct archive_entry *, int want_type);
+
+/*
+ * Private ACL parser.  This is private because it handles some
+ * very weird formats that clients should not be messing with.
+ * Clients should only deal with their platform-native formats.
+ * Because of the need to support many formats cleanly, new arguments
+ * are likely to get added on a regular basis.  Clients who try to use
+ * this interface are likely to be surprised when it changes.
+ *
+ * You were warned!
+ */
+int             __archive_entry_acl_parse_w(struct archive_entry *,
+                    const wchar_t *, int type);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * extended attributes
+ */
+
+void    archive_entry_xattr_clear(struct archive_entry *);
+void    archive_entry_xattr_add_entry(struct archive_entry *,
+            const char *name, const void *value, size_t size);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+int    archive_entry_xattr_count(struct archive_entry *);
+int    archive_entry_xattr_reset(struct archive_entry *);
+int    archive_entry_xattr_next(struct archive_entry *,
+            const char **name, const void **value, size_t *);
+
+
+#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_platform.h b/contrib/libarchive-1.3.1/libarchive/archive_platform.h
new file mode 100644 (file)
index 0000000..8f86058
--- /dev/null
@@ -0,0 +1,193 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.20 2006/03/08 01:56:06 kientzle Exp $
+ */
+
+/*
+ * This header is the first thing included in any of the libarchive
+ * source files.  As far as possible, platform-specific issues should
+ * be dealt with here and not within individual source files.  I'm
+ * actively trying to minimize #if blocks within the main source,
+ * since they obfuscate the code.
+ */
+
+#ifndef ARCHIVE_PLATFORM_H_INCLUDED
+#define        ARCHIVE_PLATFORM_H_INCLUDED
+
+#if HAVE_CONFIG_H
+#include "../config.h"
+#else
+
+/* A default configuration for FreeBSD, used if there is no config.h. */
+#ifdef __FreeBSD__
+#if __FreeBSD__ > 4
+#define        HAVE_ACL_CREATE_ENTRY 1
+#define        HAVE_ACL_INIT 1
+#define        HAVE_ACL_SET_FD 1
+#define        HAVE_ACL_SET_FD_NP 1
+#define        HAVE_ACL_SET_FILE 1
+#define        HAVE_ACL_USER 1
+#endif
+#define        HAVE_BZLIB_H 1
+#define        HAVE_CHFLAGS 1
+#define        HAVE_DECL_STRERROR_R 1
+#define        HAVE_EFTYPE 1
+#define        HAVE_EILSEQ 1
+#define        HAVE_ERRNO_H 1
+#define        HAVE_FCHDIR 1
+#define        HAVE_FCHFLAGS 1
+#define        HAVE_FCHMOD 1
+#define        HAVE_FCHOWN 1
+#define        HAVE_FCNTL_H 1
+#define        HAVE_FUTIMES 1
+#define        HAVE_INTTYPES_H 1
+#define        HAVE_LCHFLAGS 1
+#define        HAVE_LCHMOD 1
+#define        HAVE_LCHOWN 1
+#define        HAVE_LIMITS_H 1
+#define        HAVE_LUTIMES 1
+#define        HAVE_MALLOC 1
+#define        HAVE_MEMMOVE 1
+#define        HAVE_MEMORY_H 1
+#define        HAVE_MEMSET 1
+#define        HAVE_MKDIR 1
+#define        HAVE_MKFIFO 1
+#define        HAVE_PATHS_H 1
+#define        HAVE_STDINT_H 1
+#define        HAVE_STDLIB_H 1
+#define        HAVE_STRCHR 1
+#define        HAVE_STRDUP 1
+#define        HAVE_STRERROR 1
+#define        HAVE_STRERROR_R 1
+#define        HAVE_STRINGS_H 1
+#define        HAVE_STRING_H 1
+#define        HAVE_STRRCHR 1
+#define        HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define        HAVE_STRUCT_STAT_ST_RDEV 1
+#define        HAVE_SYS_ACL_H 1
+#define        HAVE_SYS_IOCTL_H 1
+#define        HAVE_SYS_STAT_H 1
+#define        HAVE_SYS_TIME_H 1
+#define        HAVE_SYS_TYPES_H 1
+#define        HAVE_SYS_WAIT_H 1
+#define        HAVE_TIMEGM 1
+#define        HAVE_UNISTD_H 1
+#define        HAVE_WCHAR_H 1
+#define        HAVE_ZLIB_H 1
+#define        STDC_HEADERS 1
+#define        TIME_WITH_SYS_TIME 1
+#else /* !__FreeBSD__ */
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no built-in configuration in archive_platform.h.
+#endif /* !__FreeBSD__ */
+
+#endif /* !HAVE_CONFIG_H */
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>  /* For __FBSDID */
+#else
+#define        __FBSDID(a)     /* null */
+#endif
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
+#if defined(__FreeBSD__) && __FreeBSD__ < 5
+#define        intmax_t int64_t
+#define        uintmax_t uint64_t
+#endif
+
+/*
+ * If this platform has <sys/acl.h>, acl_create(), acl_init(),
+ * acl_set_file(), and ACL_USER, we assume it has the rest of the
+ * POSIX.1e draft functions used in archive_read_extract.c.
+ */
+#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER
+#define        HAVE_POSIX_ACL  1
+#endif
+
+/*
+ * If we can't restore metadata using a file descriptor, then
+ * for compatibility's sake, close files before trying to restore metadata.
+ */
+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
+#define        CAN_RESTORE_METADATA_FD
+#endif
+
+/* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define        ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define        ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define        ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define        ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define        ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+/* Select the best way to set/get hi-res timestamps. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+/* FreeBSD uses "timespec" members. */
+#define        ARCHIVE_STAT_ATIME_NANOS(st)    (st)->st_atimespec.tv_nsec
+#define        ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctimespec.tv_nsec
+#define        ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtimespec.tv_nsec
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n)
+#else
+#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+/* Linux uses "tim" members. */
+#define        ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec
+#define        ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec
+#define        ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n)
+#else
+/* If we can't find a better way, just use stubs. */
+#define        ARCHIVE_STAT_ATIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_CTIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_MTIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n)
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n)
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n)
+#endif
+#endif
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_private.h b/contrib/libarchive-1.3.1/libarchive/archive_private.h
new file mode 100644 (file)
index 0000000..e4c5c4f
--- /dev/null
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.23 2006/09/05 05:59:45 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_PRIVATE_H_INCLUDED
+#define        ARCHIVE_PRIVATE_H_INCLUDED
+
+#include "archive.h"
+#include "archive_string.h"
+
+#define        ARCHIVE_WRITE_MAGIC     (0xb0c5c0deU)
+#define        ARCHIVE_READ_MAGIC      (0xdeb0c5U)
+
+struct archive {
+       /*
+        * The magic/state values are used to sanity-check the
+        * client's usage.  If an API function is called at a
+        * rediculous time, or the client passes us an invalid
+        * pointer, these values allow me to catch that.
+        */
+       unsigned          magic;
+       unsigned          state;
+
+       struct archive_entry    *entry;
+       uid_t             user_uid;     /* UID of current user. */
+
+       /* Dev/ino of the archive being read/written. */
+       dev_t             skip_file_dev;
+       ino_t             skip_file_ino;
+
+       /* Utility:  Pointer to a block of nulls. */
+       const unsigned char     *nulls;
+       size_t                   null_length;
+
+       /*
+        * Used by archive_read_data() to track blocks and copy
+        * data to client buffers, filling gaps with zero bytes.
+        */
+       const char       *read_data_block;
+       off_t             read_data_offset;
+       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_write_callback  *client_writer;
+       archive_close_callback  *client_closer;
+       void                    *client_data;
+
+       /*
+        * Blocking information.  Note that bytes_in_last_block is
+        * misleadingly named; I should find a better name.  These
+        * control the final output from all compressors, including
+        * compression_none.
+        */
+       int               bytes_per_block;
+       int               bytes_in_last_block;
+
+       /*
+        * These control whether data within a gzip/bzip2 compressed
+        * stream gets padded or not.  If pad_uncompressed is set,
+        * the data will be padded to a full block before being
+        * compressed.  The pad_uncompressed_byte determines the value
+        * that will be used for padding.  Note that these have no
+        * effect on compression "none."
+        */
+       int               pad_uncompressed;
+       int               pad_uncompressed_byte; /* TODO: Support this. */
+
+       /* Position in UNCOMPRESSED data stream. */
+       off_t             file_position;
+       /* Position in COMPRESSED data stream. */
+       off_t             raw_position;
+       /* File offset of beginning of most recently-read header. */
+       off_t             header_position;
+
+       /*
+        * Detection functions for decompression: bid functions are
+        * given a block of data from the beginning of the stream and
+        * can bid on whether or not they support the data stream.
+        * General guideline: bid the number of bits that you actually
+        * test, e.g., 16 if you test a 2-byte magic value.  The
+        * highest bidder will have their init function invoked, which
+        * can set up pointers to specific handlers.
+        *
+        * On write, the client just invokes an archive_write_set function
+        * which sets up the data here directly.
+        */
+       int       compression_code;     /* Currently active compression. */
+       const char *compression_name;
+       struct {
+               int     (*bid)(const void *buff, size_t);
+               int     (*init)(struct archive *, const void *buff, size_t);
+       }       decompressors[4];
+       /* Read/write data stream (with compression). */
+       void     *compression_data;             /* Data for (de)compressor. */
+       int     (*compression_init)(struct archive *);  /* Initialize. */
+       int     (*compression_finish)(struct archive *);
+       int     (*compression_write)(struct archive *, const void *, size_t);
+       /*
+        * Read uses a peek/consume I/O model: the decompression code
+        * returns a pointer to the requested block and advances the
+        * file position only when requested by a consume call.  This
+        * reduces copying and also simplifies look-ahead for format
+        * detection.
+        */
+       ssize_t (*compression_read_ahead)(struct archive *,
+                   const void **, size_t request);
+       ssize_t (*compression_read_consume)(struct archive *, size_t);
+       ssize_t (*compression_skip)(struct archive *, size_t);
+
+       /*
+        * Format detection is mostly the same as compression
+        * detection, with two significant differences: The bidders
+        * use the read_ahead calls above to examine the stream rather
+        * than having the supervisor hand them a block of data to
+        * examine, and the auction is repeated for every header.
+        * Winning bidders should set the archive_format and
+        * archive_format_name appropriately.  Bid routines should
+        * check archive_format and decline to bid if the format of
+        * the last header was incompatible.
+        *
+        * Again, write support is considerably simpler because there's
+        * no need for an auction.
+        */
+       int               archive_format;
+       const char       *archive_format_name;
+
+       struct archive_format_descriptor {
+               int     (*bid)(struct archive *);
+               int     (*read_header)(struct archive *, struct archive_entry *);
+               int     (*read_data)(struct archive *, const void **, size_t *, off_t *);
+               int     (*read_data_skip)(struct archive *);
+               int     (*cleanup)(struct archive *);
+               void     *format_data;  /* Format-specific data for readers. */
+       }       formats[8];
+       struct archive_format_descriptor        *format; /* Active format. */
+
+       /*
+        * Storage for format-specific data.  Note that there can be
+        * multiple format readers active at one time, so we need to
+        * allow for multiple format readers to have their data
+        * available.  The pformat_data slot here is the solution: on
+        * read, it is gauranteed to always point to a void* variable
+        * that the format can use.
+        */
+       void    **pformat_data;         /* Pointer to current format_data. */
+       void     *format_data;          /* Used by writers. */
+
+       /*
+        * Pointers to format-specific functions for writing.  They're
+        * initialized by archive_write_set_format_XXX() calls.
+        */
+       int     (*format_init)(struct archive *); /* Only used on write. */
+       int     (*format_finish)(struct archive *);
+       int     (*format_finish_entry)(struct archive *);
+       int     (*format_write_header)(struct archive *,
+                   struct archive_entry *);
+       ssize_t (*format_write_data)(struct archive *,
+                   const void *buff, size_t);
+
+       /*
+        * Various information needed by archive_extract.
+        */
+       struct extract           *extract;
+       void                    (*extract_progress)(void *);
+       void                     *extract_progress_user_data;
+       int                     (*cleanup_archive_extract)(struct archive *);
+
+       int               archive_error_number;
+       const char       *error;
+       struct archive_string   error_string;
+};
+
+
+#define        ARCHIVE_STATE_ANY       0xFFFFU
+#define        ARCHIVE_STATE_NEW       1U
+#define        ARCHIVE_STATE_HEADER    2U
+#define        ARCHIVE_STATE_DATA      4U
+#define        ARCHIVE_STATE_EOF       8U
+#define        ARCHIVE_STATE_CLOSED    0x10U
+#define        ARCHIVE_STATE_FATAL     0x8000U
+
+/* Check magic value and state; exit if it isn't valid. */
+void   __archive_check_magic(struct archive *, unsigned magic,
+           unsigned state, const char *func);
+
+
+int    __archive_read_register_format(struct archive *a,
+           void *format_data,
+           int (*bid)(struct archive *),
+           int (*read_header)(struct archive *, struct archive_entry *),
+           int (*read_data)(struct archive *, const void **, size_t *, off_t *),
+           int (*read_data_skip)(struct archive *),
+           int (*cleanup)(struct archive *));
+
+int    __archive_read_register_compression(struct archive *a,
+           int (*bid)(const void *, size_t),
+           int (*init)(struct archive *, const void *, size_t));
+
+void   __archive_errx(int retvalue, const char *msg);
+
+#define        err_combine(a,b)        ((a) < (b) ? (a) : (b))
+
+
+/*
+ * Utility function to format a USTAR header into a buffer.  If
+ * "strict" is set, this tries to create the absolutely most portable
+ * version of a ustar header.  If "strict" is set to 0, then it will
+ * relax certain requirements.
+ *
+ * Generally, format-specific declarations don't belong in this
+ * header; this is a rare example of a function that is shared by
+ * two very similar formats (ustar and pax).
+ */
+int
+__archive_write_format_header_ustar(struct archive *, char buff[512],
+    struct archive_entry *, int tartype, int strict);
+
+#endif
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read.3 b/contrib/libarchive-1.3.1/libarchive/archive_read.3
new file mode 100644 (file)
index 0000000..b210206
--- /dev/null
@@ -0,0 +1,550 @@
+.\" Copyright (c) 2003-2006 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: src/lib/libarchive/archive_read.3,v 1.24 2006/09/05 05:59:45 kientzle Exp $
+.\"
+.Dd August 19, 2006
+.Dt archive_read 3
+.Os
+.Sh NAME
+.Nm archive_read_new ,
+.Nm archive_read_set_bytes_per_block ,
+.Nm archive_read_support_compression_all ,
+.Nm archive_read_support_compression_bzip2 ,
+.Nm archive_read_support_compression_compress ,
+.Nm archive_read_support_compression_gzip ,
+.Nm archive_read_support_compression_none ,
+.Nm archive_read_support_format_all ,
+.Nm archive_read_support_format_cpio ,
+.Nm archive_read_support_format_iso9660 ,
+.Nm archive_read_support_format_tar ,
+.Nm archive_read_support_format_zip ,
+.Nm archive_read_open ,
+.Nm archive_read_open2 ,
+.Nm archive_read_open_fd ,
+.Nm archive_read_open_FILE ,
+.Nm archive_read_open_filename ,
+.Nm archive_read_open_memory ,
+.Nm archive_read_next_header ,
+.Nm archive_read_data ,
+.Nm archive_read_data_block ,
+.Nm archive_read_data_skip ,
+.Nm archive_read_data_into_buffer ,
+.Nm archive_read_data_into_fd ,
+.Nm archive_read_extract ,
+.Nm archive_read_extract_set_progress_callback ,
+.Nm archive_read_close ,
+.Nm archive_read_finish
+.Nd functions for reading streaming archives
+.Sh SYNOPSIS
+.In archive.h
+.Ft struct archive *
+.Fn archive_read_new "void"
+.Ft int
+.Fn archive_read_set_bytes_per_block "struct archive *" "int"
+.Ft int
+.Fn archive_read_support_compression_all "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_bzip2 "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_compress "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_gzip "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_none "struct archive *"
+.Ft int
+.Fn archive_read_support_format_all "struct archive *"
+.Ft int
+.Fn archive_read_support_format_cpio "struct archive *"
+.Ft int
+.Fn archive_read_support_format_iso9660 "struct archive *"
+.Ft int
+.Fn archive_read_support_format_tar "struct archive *"
+.Ft int
+.Fn archive_read_support_format_zip "struct archive *"
+.Ft int
+.Fn archive_read_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_close_callback *"
+.Ft int
+.Fn archive_read_open2 "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_skip_callback *" "archive_close_callback *"
+.Ft int
+.Fn archive_read_open_FILE "struct archive *" "FILE *file"
+.Ft int
+.Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size"
+.Ft int
+.Fn archive_read_open_filename "struct archive *" "const char *filename" "size_t block_size"
+.Ft int
+.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 ssize_t
+.Fn archive_read_data "struct archive *" "void *buff" "size_t len"
+.Ft int
+.Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset"
+.Ft int
+.Fn archive_read_data_skip "struct archive *"
+.Ft int
+.Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len"
+.Ft int
+.Fn archive_read_data_into_fd "struct archive *" "int fd"
+.Ft int
+.Fn archive_read_extract "struct archive *" "struct archive_entry *" "int flags"
+.Ft void
+.Fn archive_read_extract_set_progress_callback "struct archive *" "void (*func)(void *)" "void *user_data"
+.Ft int
+.Fn archive_read_close "struct archive *"
+.Ft int
+.Fn archive_read_finish "struct archive *"
+.Sh DESCRIPTION
+These functions provide a complete API for reading streaming archives.
+The general process is to first create the
+.Tn struct archive
+object, set options, initialize the reader, iterate over the archive
+headers and associated data, then close the archive and release all
+resources.
+The following summary describes the functions in approximately the
+order they would be used:
+.Bl -tag -compact -width indent
+.It Fn archive_read_new
+Allocates and initializes a
+.Tn struct archive
+object suitable for reading from an archive.
+.It Fn archive_read_set_bytes_per_block
+Sets the block size used for reading the archive data.
+This controls the size that will be used when invoking the read
+callback function.
+The default is 20 records or 10240 bytes for tar formats.
+.It Fn archive_read_support_compression_all , Fn archive_read_support_compression_bzip2 , Fn archive_read_support_compression_compress , Fn archive_read_support_compression_gzip , Fn archive_read_support_compression_none
+Enables auto-detection code and decompression support for the
+specified compression.
+Note that
+.Dq none
+is always enabled by default.
+For convenience,
+.Fn archive_read_support_compression_all
+enables all available decompression code.
+.It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip
+Enables support---including auto-detection code---for the
+specified archive format.
+For example,
+.Fn archive_read_support_format_tar
+enables support for a variety of standard tar formats, old-style tar,
+ustar, pax interchange format, and many common variants.
+For convenience,
+.Fn archive_read_support_format_all
+enables support for all available formats.
+Note that there is no default.
+.It Fn archive_read_open
+The same as
+.Fn archive_read_open2 ,
+except that the skip callback is assumed to be
+.Dv NULL .
+.It Fn archive_read_open2
+Freeze the settings, open the archive, and prepare for reading entries.
+This is the most generic version of this call, which accepts
+four callback functions.
+Most clients will want to use
+.Fn archive_read_open_filename ,
+.Fn archive_read_open_FILE ,
+.Fn archive_read_open_fd ,
+or
+.Fn archive_read_open_memory
+instead.
+The library invokes the client-provided functions to obtain
+raw bytes from the archive.
+.It Fn archive_read_open_FILE
+Like
+.Fn archive_read_open ,
+except that it accepts a
+.Ft "FILE *"
+pointer.
+This function should not be used with tape drives or other devices
+that require strict I/O blocking.
+.It Fn archive_read_open_fd
+Like
+.Fn archive_read_open ,
+except that it accepts a file descriptor and block size rather than
+a set of function pointers.
+Note that the file descriptor will not be automatically closed at
+end-of-archive.
+This function is safe for use with tape drives or other blocked devices.
+.It Fn archive_read_open_file
+A deprecated synonym for
+.Fn archive_read_open_filename .
+.It Fn archive_read_open_filename
+Like
+.Fn archive_read_open ,
+except that it accepts a simple filename and a block size.
+A NULL filename represents standard input.
+This function is safe for use with tape drives or other blocked devices.
+.It Fn archive_read_open_memory
+Like
+.Fn archive_read_open ,
+except that it accepts a pointer and size of a block of
+memory containing the archive data.
+.It Fn archive_read_next_header
+Read the header for the next entry and return a pointer to
+a
+.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
+.Fn archive_read_data_block
+and fills any gaps with nulls so that callers see a single
+continuous stream of data.
+.It Fn archive_read_data_block
+Return the next available block of data for this entry.
+Unlike
+.Fn archive_read_data ,
+the
+.Fn archive_read_data_block
+function avoids copying data and allows you to correctly handle
+sparse files, as supported by some archive formats.
+The library guarantees that offsets will increase and that blocks
+will not overlap.
+Note that the blocks returned from this function can be much larger
+than the block size read from disk, due to compression
+and internal buffer optimizations.
+.It Fn archive_read_data_skip
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to skip all of the data for this archive entry.
+.It Fn archive_read_data_into_buffer
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to copy the entire entry into the client-supplied buffer.
+Note that the client is responsible for sizing the buffer appropriately.
+.It Fn archive_read_data_into_fd
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to copy the entire entry to the provided file descriptor.
+.It Fn archive_read_extract_set_skip_file
+This function records the device and inode numbers
+of a file that should not be restored.
+This is a convenience that prevents
+.Fn archive_read_extract
+from restoring a file over the archive itself.
+.It Fn archive_read_extract
+A convenience function that recreates the specified object on
+disk and reads the entry data into that object.
+The filename, permissions, and other critical information
+are taken from the provided
+.Va archive_entry
+object.
+The
+.Va flags
+argument modifies how the object is recreated.
+It consists of a bitwise OR of one or more of the following values:
+.Bl -tag -compact -width "indent"
+.It Cm ARCHIVE_EXTRACT_OWNER
+The user and group IDs should be set on the restored file.
+By default, the user and group IDs are not restored.
+.It Cm ARCHIVE_EXTRACT_PERM
+The permissions (mode bits) should be restored for all objects.
+By default, permissions are only restored for regular files.
+.It Cm ARCHIVE_EXTRACT_TIME
+The timestamps (mtime, ctime, and atime) should be restored.
+By default, they are ignored.
+Note that restoring of atime is not currently supported.
+.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
+Existing files on disk will not be overwritten.
+By default, existing regular files are truncated and overwritten;
+existing directories will have their permissions updated;
+other pre-existing objects are unlinked and recreated from scratch.
+.It Cm ARCHIVE_EXTRACT_UNLINK
+Existing files on disk will be unlinked and recreated from scratch.
+By default, existing files are truncated and rewritten, but
+the file is not recreated.
+In particular, the default behavior does not break existing hard links.
+.It Cm ARCHIVE_EXTRACT_ACL
+Attempt to restore ACLs.
+By default, extended ACLs are ignored.
+.It Cm ARCHIVE_EXTRACT_FFLAGS
+Attempt to restore extended file flags.
+By default, file flags are ignored.
+.El
+Note that not all attributes are set immediately;
+some attributes are cached in memory and written to disk only
+when the archive is closed.
+(For example, read-only directories are initially created
+writable so that files within those directories can be
+restored.
+The final permissions are set when the archive is closed.)
+.It Fn archive_read_extract_set_progress_callback
+Sets a pointer to a user-defined callback that can be used
+for updating progress displays during extraction.
+The progress function will be invoked during the extraction of large
+regular files.
+The progress function will be invoked with the pointer provided to this call.
+Generally, the data pointed to should include a reference to the archive
+object and the archive_entry object so that various statistics
+can be retrieved for the progress display.
+.It Fn archive_read_close
+Complete the archive and invoke the close callback.
+.It Fn archive_read_finish
+Invokes
+.Fn archive_read_close
+if it was not invoked manually, then release all resources.
+Note: In libarchive 1.x, this function was declared to return
+.Ft void ,
+which made it impossible to detect certain errors when
+.Fn archive_read_close
+was invoked implicitly from this function.
+The declaration is corrected beginning with libarchive 2.0.
+.El
+.Pp
+Note that the library determines most of the relevant information about
+the archive by inspection.
+In particular, it automatically detects
+.Xr gzip 1
+or
+.Xr bzip2 1
+compression and transparently performs the appropriate decompression.
+It also automatically detects the archive format.
+.Pp
+A complete description of the
+.Tn struct archive
+and
+.Tn struct archive_entry
+objects can be found in the overview manual page for
+.Xr libarchive 3 .
+.Sh CLIENT CALLBACKS
+The callback functions must match the following prototypes:
+.Bl -item -offset indent
+.It
+.Ft typedef ssize_t
+.Fn archive_read_callback "struct archive *" "void *client_data" "const void **buffer"
+.It
+.Ft typedef int
+.Fn archive_skip_callback "struct archive *" "void *client_data" "size_t request"
+.It
+.Ft typedef int
+.Fn archive_open_callback "struct archive *" "void *client_data"
+.It
+.Ft typedef int
+.Fn archive_close_callback "struct archive *" "void *client_data"
+.El
+.Pp
+The open callback is invoked by
+.Fn archive_open .
+It should return
+.Cm ARCHIVE_OK
+if the underlying file or data source is successfully
+opened.
+If the open fails, it should call
+.Fn archive_set_error
+to register an error code and message and return
+.Cm ARCHIVE_FATAL .
+.Pp
+The read callback is invoked whenever the library
+requires raw bytes from the archive.
+The read callback should read data into a buffer,
+set the
+.Li const void **buffer
+argument to point to the available data, and
+return a count of the number of bytes available.
+The library will invoke the read callback again
+only after it has consumed this data.
+The library imposes no constraints on the size
+of the data blocks returned.
+On end-of-file, the read callback should
+return zero.
+On error, the read callback should invoke
+.Fn archive_set_error
+to register an error code and message and
+return -1.
+.Pp
+The skip callback is invoked when the
+library wants to ignore a block of data.
+The return value is the number of bytes actually
+skipped, which may differ from the request.
+If the callback cannot skip data, it should return
+zero.
+If the skip callback is not provided (the
+function pointer is
+.Dv NULL ),
+the library will invoke the read function
+instead and simply discard the result.
+A skip callback can provide significant
+performance gains when reading uncompressed
+archives from slow disk drives or other media
+that can skip quickly.
+.Pp
+The close callback is invoked by archive_close when
+the archive processing is complete.
+The callback should return
+.Cm ARCHIVE_OK
+on success.
+On failure, the callback should invoke
+.Fn archive_set_error
+to register an error code and message and
+return
+.Cm ARCHIVE_FATAL.
+.Sh EXAMPLE
+The following illustrates basic usage of the library.
+In this example,
+the callback functions are simply wrappers around the standard
+.Xr open 2 ,
+.Xr read 2 ,
+and
+.Xr close 2
+system calls.
+.Bd -literal -offset indent
+void
+list_archive(const char *name)
+{
+  struct mydata *mydata;
+  struct archive *a;
+  struct archive_entry *entry;
+
+  mydata = malloc(sizeof(struct mydata));
+  a = archive_read_new();
+  mydata->name = name;
+  archive_read_support_compression_all(a);
+  archive_read_support_format_all(a);
+  archive_read_open(a, mydata, myopen, myread, myclose);
+  while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+    printf("%s\\n",archive_entry_pathname(entry));
+    archive_read_data_skip(a);
+  }
+  archive_read_finish(a);
+  free(mydata);
+}
+
+ssize_t
+myread(struct archive *a, void *client_data, const void **buff)
+{
+  struct mydata *mydata = client_data;
+
+  *buff = mydata->buff;
+  return (read(mydata->fd, mydata->buff, 10240));
+}
+
+int
+myopen(struct archive *a, void *client_data)
+{
+  struct mydata *mydata = client_data;
+
+  mydata->fd = open(mydata->name, O_RDONLY);
+  return (mydata->fd >= 0 ? ARCHIVE_OK : ARCHIVE_FATAL);
+}
+
+int
+myclose(struct archive *a, void *client_data)
+{
+  struct mydata *mydata = client_data;
+
+  if (mydata->fd > 0)
+    close(mydata->fd);
+  return (ARCHIVE_OK);
+}
+.Ed
+.Sh RETURN VALUES
+Most functions return zero on success, non-zero on error.
+The possible return codes include:
+.Cm ARCHIVE_OK
+(the operation succeeded),
+.Cm ARCHIVE_WARN
+(the operation succeeded but a non-critical error was encountered),
+.Cm ARCHIVE_EOF
+(end-of-archive was encountered),
+.Cm ARCHIVE_RETRY
+(the operation failed but can be retried),
+and
+.Cm ARCHIVE_FATAL
+(there was a fatal error; the archive should be closed immediately).
+Detailed error codes and textual descriptions are available from the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions.
+.Pp
+.Fn archive_read_new
+returns a pointer to a freshly allocated
+.Tn struct archive
+object.
+It returns
+.Dv NULL
+on error.
+.Pp
+.Fn archive_read_data
+returns a count of bytes actually read or zero at the end of the entry.
+On error, a value of
+.Cm ARCHIVE_FATAL ,
+.Cm ARCHIVE_WARN ,
+or
+.Cm ARCHIVE_RETRY
+is returned and an error code and textual description can be retrieved from the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions.
+.Pp
+The library expects the client callbacks to behave similarly.
+If there is an error, you can use
+.Fn archive_set_error
+to set an appropriate error code and description,
+then return one of the non-zero values above.
+(Note that the value eventually returned to the client may
+not be the same; many errors that are not critical at the level
+of basic I/O can prevent the archive from being properly read,
+thus most I/O errors eventually cause
+.Cm ARCHIVE_FATAL
+to be returned.)
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr tar 1 ,
+.Xr archive 3 ,
+.Xr archive_util 3 ,
+.Xr tar 5
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle@acm.org .
+.Sh BUGS
+Directories are actually extracted in two distinct phases.
+Directories are created during
+.Fn archive_read_extract ,
+but final permissions are not set until
+.Fn archive_read_close .
+This separation is necessary to correctly handle borderline
+cases such as a non-writable directory containing
+files, but can cause unexpected results.
+In particular, directory permissions are not fully
+restored until the archive is closed.
+If you use
+.Xr chdir 2
+to change the current directory between calls to
+.Fn archive_read_extract
+or before calling
+.Fn archive_read_close ,
+you may confuse the permission-setting logic with
+the result that directory permissions are restored
+incorrectly.
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read.c b/contrib/libarchive-1.3.1/libarchive/archive_read.c
new file mode 100644 (file)
index 0000000..78475ad
--- /dev/null
@@ -0,0 +1,628 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ */
+
+/*
+ * This file contains the "essential" portions of the read API, that
+ * is, stuff that will probably always be used by any client that
+ * actually needs to read an archive.  Optional pieces have been, as
+ * far as possible, separated out into separate files to avoid
+ * needlessly bloating statically-linked clients.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.22 2006/09/05 05:59:45 kientzle Exp $");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+static int     choose_decompressor(struct archive *, const void*, size_t);
+static int     choose_format(struct archive *);
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+       struct archive  *a;
+       unsigned char   *nulls;
+
+       a = malloc(sizeof(*a));
+       if (a == NULL) {
+               archive_set_error(a, ENOMEM, "Can't allocate archive object");
+               return (NULL);
+       }
+       memset(a, 0, sizeof(*a));
+
+       a->user_uid = geteuid();
+       a->magic = ARCHIVE_READ_MAGIC;
+       a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
+
+       a->null_length = 1024;
+       nulls = malloc(a->null_length);
+       if (nulls == NULL) {
+               archive_set_error(a, ENOMEM, "Can't allocate archive object 'nulls' element");
+               free(a);
+               return (NULL);
+       }
+       memset(nulls, 0, a->null_length);
+       a->nulls = nulls;
+
+       a->state = ARCHIVE_STATE_NEW;
+       a->entry = archive_entry_new();
+
+       /* We always support uncompressed archives. */
+       archive_read_support_compression_none((struct archive*)a);
+
+       return (a);
+}
+
+/*
+ * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
+ */
+void
+archive_read_extract_set_skip_file(struct archive *a, dev_t d, ino_t i)
+{
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file");
+       a->skip_file_dev = d;
+       a->skip_file_ino = i;
+}
+
+
+/*
+ * Open the archive
+ */
+int
+archive_read_open(struct archive *a, void *client_data,
+    archive_open_callback *client_opener, archive_read_callback *client_reader,
+    archive_close_callback *client_closer)
+{
+       /* Old archive_read_open() is just a thin shell around
+        * archive_read_open2. */
+       return archive_read_open2(a, client_data, client_opener,
+           client_reader, NULL, client_closer);
+}
+
+int
+archive_read_open2(struct archive *a, void *client_data,
+    archive_open_callback *client_opener,
+    archive_read_callback *client_reader,
+    archive_skip_callback *client_skipper,
+    archive_close_callback *client_closer)
+{
+       const void *buffer;
+       ssize_t bytes_read;
+       int high_bidder;
+       int e;
+
+       __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, client_data);
+               if (e != 0) {
+                       /* If the open failed, call the closer to clean up. */
+                       if (client_closer)
+                               (client_closer)(a, client_data);
+                       return (e);
+               }
+       }
+
+       /* Read first block now for format detection. */
+       bytes_read = (client_reader)(a, client_data, &buffer);
+
+       if (bytes_read < 0) {
+               /* If the first read fails, close before returning error. */
+               if (client_closer)
+                       (client_closer)(a, client_data);
+               /* client_reader should have already set error information. */
+               return (ARCHIVE_FATAL);
+       }
+
+       /* An empty archive is a serious error. */
+       if (bytes_read == 0) {
+               archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Empty input file");
+               /* Close the empty file. */
+               if (client_closer)
+                       (client_closer)(a, client_data);
+               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;
+
+       /* Select a decompression routine. */
+       high_bidder = choose_decompressor(a, buffer, bytes_read);
+       if (high_bidder < 0)
+               return (ARCHIVE_FATAL);
+
+       /* Initialize decompression routine with the first block of data. */
+       e = (a->decompressors[high_bidder].init)(a, buffer, bytes_read);
+
+       if (e == ARCHIVE_OK)
+               a->state = ARCHIVE_STATE_HEADER;
+
+       return (e);
+}
+
+/*
+ * Allow each registered decompression routine to bid on whether it
+ * wants to handle this stream.  Return index of winning bidder.
+ */
+static int
+choose_decompressor(struct archive *a, const void *buffer, size_t bytes_read)
+{
+       int decompression_slots, i, bid, best_bid, best_bid_slot;
+
+       decompression_slots = sizeof(a->decompressors) /
+           sizeof(a->decompressors[0]);
+
+       best_bid = -1;
+       best_bid_slot = -1;
+
+       for (i = 0; i < decompression_slots; i++) {
+               if (a->decompressors[i].bid) {
+                       bid = (a->decompressors[i].bid)(buffer, bytes_read);
+                       if ((bid > best_bid) || (best_bid_slot < 0)) {
+                               best_bid = bid;
+                               best_bid_slot = i;
+                       }
+               }
+       }
+
+       /*
+        * There were no bidders; this is a serious programmer error
+        * and demands a quick and definitive abort.
+        */
+       if (best_bid_slot < 0)
+               __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_ERRNO_FILE_FORMAT,
+                   "Unrecognized archive format");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (best_bid_slot);
+}
+
+/*
+ * Read header of next entry.
+ */
+int
+archive_read_next_header(struct archive *a, struct archive_entry **entryp)
+{
+       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_string_empty(&a->error_string);
+
+       /*
+        * If client didn't consume entire data, skip any remainder
+        * (This is especially important for GNU incremental directories.)
+        */
+       if (a->state == ARCHIVE_STATE_DATA) {
+               ret = archive_read_data_skip(a);
+               if (ret == ARCHIVE_EOF) {
+                       archive_set_error(a, EIO, "Premature end-of-file.");
+                       a->state = ARCHIVE_STATE_FATAL;
+                       return (ARCHIVE_FATAL);
+               }
+               if (ret != ARCHIVE_OK)
+                       return (ret);
+       }
+
+       /* Record start-of-header. */
+       a->header_position = a->file_position;
+
+       slot = choose_format(a);
+       if (slot < 0) {
+               a->state = ARCHIVE_STATE_FATAL;
+               return (ARCHIVE_FATAL);
+       }
+       a->format = &(a->formats[slot]);
+       a->pformat_data = &(a->format->format_data);
+       ret = (a->format->read_header)(a, entry);
+
+       /*
+        * EOF and FATAL are persistent at this layer.  By
+        * modifying the state, we gaurantee that future calls to
+        * read a header or read data will fail.
+        */
+       switch (ret) {
+       case ARCHIVE_EOF:
+               a->state = ARCHIVE_STATE_EOF;
+               break;
+       case ARCHIVE_OK:
+               a->state = ARCHIVE_STATE_DATA;
+               break;
+       case ARCHIVE_WARN:
+               a->state = ARCHIVE_STATE_DATA;
+               break;
+       case ARCHIVE_RETRY:
+               break;
+       case ARCHIVE_FATAL:
+               a->state = ARCHIVE_STATE_FATAL;
+               break;
+       }
+
+       *entryp = entry;
+       a->read_data_output_offset = 0;
+       a->read_data_remaining = 0;
+       return (ret);
+}
+
+/*
+ * Allow each registered format to bid on whether it wants to handle
+ * the next entry.  Return index of winning bidder.
+ */
+static int
+choose_format(struct archive *a)
+{
+       int slots;
+       int i;
+       int bid, best_bid;
+       int best_bid_slot;
+
+       slots = sizeof(a->formats) / sizeof(a->formats[0]);
+       best_bid = -1;
+       best_bid_slot = -1;
+
+       /* Set up a->format and a->pformat_data for convenience of bidders. */
+       a->format = &(a->formats[0]);
+       for (i = 0; i < slots; i++, a->format++) {
+               if (a->format->bid) {
+                       a->pformat_data = &(a->format->format_data);
+                       bid = (a->format->bid)(a);
+                       if (bid == ARCHIVE_FATAL)
+                               return (ARCHIVE_FATAL);
+                       if ((bid > best_bid) || (best_bid_slot < 0)) {
+                               best_bid = bid;
+                               best_bid_slot = i;
+                       }
+               }
+       }
+
+       /*
+        * There were no bidders; this is a serious programmer error
+        * and demands a quick and definitive abort.
+        */
+       if (best_bid_slot < 0)
+               __archive_errx(1, "No formats were registered; you must "
+                   "invoke at least one archive_read_support_format_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_ERRNO_FILE_FORMAT,
+                   "Unrecognized archive format");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (best_bid_slot);
+}
+
+/*
+ * Return the file offset (within the uncompressed data stream) where
+ * the last header started.
+ */
+int64_t
+archive_read_header_position(struct archive *a)
+{
+       return (a->header_position);
+}
+
+/*
+ * Read data from an archive entry, using a read(2)-style interface.
+ * This is a convenience routine that just calls
+ * archive_read_data_block and copies the results into the client
+ * buffer, filling any gaps with zero bytes.  Clients using this
+ * API can be completely ignorant of sparse-file issues; sparse files
+ * will simply be padded with nulls.
+ *
+ * DO NOT intermingle calls to this function and archive_read_data_block
+ * to read a single entry body.
+ */
+ssize_t
+archive_read_data(struct archive *a, void *buff, size_t s)
+{
+       char    *dest;
+       size_t   bytes_read;
+       size_t   len;
+       int      r;
+
+       bytes_read = 0;
+       dest = buff;
+
+       while (s > 0) {
+               if (a->read_data_remaining <= 0) {
+                       r = archive_read_data_block(a,
+                           (const void **)&a->read_data_block,
+                           &a->read_data_remaining,
+                           &a->read_data_offset);
+                       if (r == ARCHIVE_EOF)
+                               return (bytes_read);
+                       /*
+                        * Error codes are all negative, so the status
+                        * return here cannot be confused with a valid
+                        * byte count.  (ARCHIVE_OK is zero.)
+                        */
+                       if (r < ARCHIVE_OK)
+                               return (r);
+               }
+
+               if (a->read_data_offset < a->read_data_output_offset) {
+                       archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Encountered out-of-order sparse blocks");
+                       return (ARCHIVE_RETRY);
+               } else {
+                       len = a->read_data_remaining;
+                       if (len > s)
+                               len = s;
+                       memcpy(dest, a->read_data_block, len);
+                       s -= len;
+                       a->read_data_block += len;
+                       a->read_data_remaining -= len;
+                       a->read_data_output_offset += len;
+                       a->read_data_offset += len;
+                       dest += len;
+                       bytes_read += len;
+               }
+       }
+       return (bytes_read);
+}
+
+/*
+ * Skip over all remaining data in this entry.
+ */
+int
+archive_read_data_skip(struct archive *a)
+{
+       int r;
+       const void *buff;
+       size_t size;
+       off_t offset;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_skip");
+
+       if (a->format->read_data_skip != NULL)
+               r = (a->format->read_data_skip)(a);
+       else {
+               while ((r = archive_read_data_block(a, &buff, &size, &offset))
+                   == ARCHIVE_OK)
+                       ;
+       }
+
+       if (r == ARCHIVE_EOF)
+               r = ARCHIVE_OK;
+
+       a->state = ARCHIVE_STATE_HEADER;
+       return (r);
+}
+
+/*
+ * Read the next block of entry data from the archive.
+ * This is a zero-copy interface; the client receives a pointer,
+ * size, and file offset of the next available block of data.
+ *
+ * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if
+ * the end of entry is encountered.
+ */
+int
+archive_read_data_block(struct archive *a,
+    const void **buff, size_t *size, off_t *offset)
+{
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block");
+
+       if (a->format->read_data == NULL) {
+               archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+                   "Internal error: "
+                   "No format_read_data_block function registered");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (a->format->read_data)(a, buff, size, offset);
+}
+
+/*
+ * Close the file and release most resources.
+ *
+ * Be careful: client might just call read_new and then read_finish.
+ * Don't assume we actually read anything or performed any non-trivial
+ * initialization.
+ */
+int
+archive_read_close(struct archive *a)
+{
+       int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close");
+       a->state = ARCHIVE_STATE_CLOSED;
+
+       /* Call cleanup functions registered by optional components. */
+       if (a->cleanup_archive_extract != NULL)
+               r = (a->cleanup_archive_extract)(a);
+
+       /* TODO: Finish the format processing. */
+
+       /* Close the input machinery. */
+       if (a->compression_finish != NULL) {
+               r1 = (a->compression_finish)(a);
+               if (r1 < r)
+                       r = r1;
+       }
+
+       return (r);
+}
+
+/*
+ * 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)
+{
+       int i;
+       int slots;
+       int r = ARCHIVE_OK;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_finish");
+       if (a->state != ARCHIVE_STATE_CLOSED)
+               r = archive_read_close(a);
+
+       /* Cleanup format-specific data. */
+       slots = sizeof(a->formats) / sizeof(a->formats[0]);
+       for (i = 0; i < slots; i++) {
+               a->pformat_data = &(a->formats[i].format_data);
+               if (a->formats[i].cleanup)
+                       (a->formats[i].cleanup)(a);
+       }
+
+       /* Casting a pointer to int allows us to remove 'const.' */
+       free((void *)(uintptr_t)(const void *)a->nulls);
+       archive_string_free(&a->error_string);
+       if (a->entry)
+               archive_entry_free(a->entry);
+       a->magic = 0;
+       free(a);
+#if ARCHIVE_API_VERSION > 1
+       return (r);
+#endif
+}
+
+/*
+ * Used internally by read format handlers to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_format(struct archive *a,
+    void *format_data,
+    int (*bid)(struct archive *),
+    int (*read_header)(struct archive *, struct archive_entry *),
+    int (*read_data)(struct archive *, const void **, size_t *, off_t *),
+    int (*read_data_skip)(struct archive *),
+    int (*cleanup)(struct archive *))
+{
+       int i, number_slots;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format");
+
+       number_slots = sizeof(a->formats) / sizeof(a->formats[0]);
+
+       for (i = 0; i < number_slots; i++) {
+               if (a->formats[i].bid == bid)
+                       return (ARCHIVE_WARN); /* We've already installed */
+               if (a->formats[i].bid == NULL) {
+                       a->formats[i].bid = bid;
+                       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].format_data = format_data;
+                       return (ARCHIVE_OK);
+               }
+       }
+
+       __archive_errx(1, "Not enough slots for format registration");
+       return (ARCHIVE_FATAL); /* Never actually called. */
+}
+
+/*
+ * Used internally by decompression routines to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_compression(struct archive *a,
+    int (*bid)(const void *, size_t),
+    int (*init)(struct archive *, const void *, size_t))
+{
+       int i, number_slots;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_compression");
+
+       number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]);
+
+       for (i = 0; i < number_slots; i++) {
+               if (a->decompressors[i].bid == bid)
+                       return (ARCHIVE_OK); /* We've already installed */
+               if (a->decompressors[i].bid == NULL) {
+                       a->decompressors[i].bid = bid;
+                       a->decompressors[i].init = init;
+                       return (ARCHIVE_OK);
+               }
+       }
+
+       __archive_errx(1, "Not enough slots for compression registration");
+       return (ARCHIVE_FATAL); /* Never actually executed. */
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_data_into_buffer.c b/contrib/libarchive-1.3.1/libarchive/archive_read_data_into_buffer.c
new file mode 100644 (file)
index 0000000..48d12ff
--- /dev/null
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_data_into_buffer.c,v 1.4 2004/06/27 01:15:31 kientzle Exp $");
+
+#include <string.h>
+
+#include "archive.h"
+
+int
+archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
+{
+       char *dest;
+       ssize_t bytes_read, total_bytes;
+
+       dest = d;
+       total_bytes = 0;
+       bytes_read = archive_read_data(a, dest, len);
+       while (bytes_read > 0) {
+               total_bytes += bytes_read;
+               bytes_read = archive_read_data(a, dest + total_bytes,
+                   len - total_bytes);
+       }
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_data_into_fd.c b/contrib/libarchive-1.3.1/libarchive/archive_read_data_into_fd.c
new file mode 100644 (file)
index 0000000..98a9021
--- /dev/null
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_data_into_fd.c,v 1.10 2005/09/24 21:15:00 kientzle Exp $");
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* Maximum amount of data to write at one time. */
+#define        MAX_WRITE       (1024 * 1024)
+
+/*
+ * This implementation minimizes copying of data and is sparse-file aware.
+ */
+int
+archive_read_data_into_fd(struct archive *a, int fd)
+{
+       int r;
+       const void *buff;
+       size_t size;
+       ssize_t bytes_to_write, bytes_written, total_written;
+       off_t offset;
+       off_t output_offset;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd");
+
+       total_written = 0;
+       output_offset = 0;
+
+       while ((r = archive_read_data_block(a, &buff, &size, &offset)) ==
+           ARCHIVE_OK) {
+               if (offset > output_offset) {
+                       lseek(fd, offset - output_offset, SEEK_CUR);
+                       output_offset = offset;
+               }
+               while (size > 0) {
+                       bytes_to_write = size;
+                       if (bytes_to_write > MAX_WRITE)
+                               bytes_to_write = MAX_WRITE;
+                       bytes_written = write(fd, buff, bytes_to_write);
+                       if (bytes_written < 0) {
+                               archive_set_error(a, errno, "Write error");
+                               return (-1);
+                       }
+                       output_offset += bytes_written;
+                       total_written += bytes_written;
+                       size -= bytes_written;
+                       if (a->extract_progress != NULL)
+                               (*a->extract_progress)(a->extract_progress_user_data);
+               }
+       }
+
+       if (r != ARCHIVE_EOF)
+               return (r);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_extract.c b/contrib/libarchive-1.3.1/libarchive/archive_read_extract.c
new file mode 100644 (file)
index 0000000..8f398bd
--- /dev/null
@@ -0,0 +1,1605 @@
+/*-
+ * Copyright (c) 2003-2005 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: src/lib/libarchive/archive_read_extract.c,v 1.47 2006/09/05 05:59:45 kientzle Exp $");
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#ifdef HAVE_EXT2FS_EXT2_FS_H
+#include <ext2fs/ext2_fs.h>    /* for Linux file flags */
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>     /* for Linux file flags */
+#endif
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+struct fixup_entry {
+       struct fixup_entry      *next;
+       mode_t                   mode;
+       int64_t                  mtime;
+       int64_t                  atime;
+       unsigned long            mtime_nanos;
+       unsigned long            atime_nanos;
+       unsigned long            fflags_set;
+       int                      fixup; /* bitmask of what needs fixing */
+       char                    *name;
+};
+
+#define        FIXUP_MODE      1
+#define        FIXUP_TIMES     2
+#define        FIXUP_FFLAGS    4
+
+struct bucket {
+       char    *name;
+       int      hash;
+       id_t     id;
+};
+
+struct extract {
+       mode_t                   umask;
+       mode_t                   default_dir_mode;
+       struct archive_string    create_parent_dir;
+       struct fixup_entry      *fixup_list;
+       struct fixup_entry      *current_fixup;
+
+       struct bucket ucache[127];
+       struct bucket gcache[127];
+
+       /*
+        * Cached stat data from disk for the current entry.
+        * If this is valid, pst points to st.  Otherwise,
+        * pst is null.
+        */
+       struct stat              st;
+       struct stat             *pst;
+};
+
+/* Default mode for dirs created automatically (will be modified by umask). */
+#define DEFAULT_DIR_MODE 0777
+/*
+ * Mode to use for newly-created dirs during extraction; the correct
+ * mode will be set at the end of the extraction.
+ */
+#define SECURE_DIR_MODE 0700
+
+static int     archive_extract_cleanup(struct archive *);
+static int     extract_block_device(struct archive *,
+                   struct archive_entry *, int);
+static int     extract_char_device(struct archive *,
+                   struct archive_entry *, int);
+static int     extract_device(struct archive *,
+                   struct archive_entry *, int flags, mode_t mode);
+static int     extract_dir(struct archive *, struct archive_entry *, int);
+static int     extract_fifo(struct archive *, struct archive_entry *, int);
+static int     extract_file(struct archive *, struct archive_entry *, int);
+static int     extract_hard_link(struct archive *, struct archive_entry *, int);
+static int     extract_symlink(struct archive *, struct archive_entry *, int);
+static unsigned int    hash(const char *);
+static gid_t   lookup_gid(struct archive *, const char *uname, gid_t);
+static uid_t   lookup_uid(struct archive *, const char *uname, uid_t);
+static int     create_dir(struct archive *, const char *, int flags);
+static int     create_dir_mutable(struct archive *, char *, int flags);
+static int     create_dir_recursive(struct archive *, char *, int flags);
+static int     create_parent_dir(struct archive *, const char *, int flags);
+static int     create_parent_dir_mutable(struct archive *, char *, int flags);
+static int     restore_metadata(struct archive *, int fd,
+                   struct archive_entry *, int flags);
+#ifdef HAVE_POSIX_ACL
+static int     set_acl(struct archive *, int fd, struct archive_entry *,
+                   acl_type_t, int archive_entry_acl_type, const char *tn);
+#endif
+static int     set_acls(struct archive *, int fd, struct archive_entry *);
+static int     set_xattrs(struct archive *, int fd, struct archive_entry *);
+static int     set_fflags(struct archive *, int fd, const char *name, mode_t,
+                   unsigned long fflags_set, unsigned long fflags_clear);
+static int     set_ownership(struct archive *, int fd, struct archive_entry *,
+                   int flags);
+static int     set_perm(struct archive *, int fd, struct archive_entry *,
+                   int mode, int flags);
+static int     set_time(struct archive *, int fd, struct archive_entry *, int);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks.  According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore.  I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+int
+archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
+{
+       mode_t mode;
+       struct extract *extract;
+       int ret;
+       int restore_pwd;
+       char *original_filename;
+
+       if (a->extract == NULL) {
+               a->extract = malloc(sizeof(*a->extract));
+               if (a->extract == NULL) {
+                       archive_set_error(a, ENOMEM, "Can't extract");
+                       return (ARCHIVE_FATAL);
+               }
+               a->cleanup_archive_extract = archive_extract_cleanup;
+               memset(a->extract, 0, sizeof(*a->extract));
+       }
+       extract = a->extract;
+       umask(extract->umask = umask(0)); /* Read the current umask. */
+       extract->default_dir_mode = DEFAULT_DIR_MODE & ~extract->umask;
+       extract->pst = NULL;
+       extract->current_fixup = NULL;
+       restore_pwd = -1;
+       original_filename = NULL;
+
+       /* The following is not possible without fchdir.  <sigh> */
+#ifdef HAVE_FCHDIR
+       /*
+        * If pathname is longer than PATH_MAX, record starting directory
+        * and chdir to a suitable intermediate dir.
+        */
+       if (strlen(archive_entry_pathname(entry)) > PATH_MAX) {
+               char *intdir, *tail;
+
+               restore_pwd = open(".", O_RDONLY);
+               if (restore_pwd < 0) {
+                               archive_set_error(a, errno,
+                                   "Unable to restore long pathname");
+                               return (ARCHIVE_WARN);
+               }
+
+               /*
+                * Yes, the copy here is necessary because we edit
+                * the pathname in-place to create intermediate dirnames.
+                */
+               original_filename = strdup(archive_entry_pathname(entry));
+
+               /*
+                * "intdir" points to the initial dir section we're going
+                * to remove, "tail" points to the remainder of the path.
+                */
+               intdir = tail = original_filename;
+               while (strlen(tail) > PATH_MAX) {
+                       intdir = tail;
+
+                       /* Locate a dir prefix shorter than PATH_MAX. */
+                       tail = intdir + PATH_MAX - 8;
+                       while (tail > intdir && *tail != '/')
+                               tail--;
+                       if (tail <= intdir) {
+                               archive_set_error(a, EPERM,
+                                   "Path element too long");
+                               ret = ARCHIVE_WARN;
+                               goto cleanup;
+                       }
+
+                       /* Create intdir and chdir to it. */
+                       *tail = '\0'; /* Terminate dir portion */
+                       ret = create_dir(a, intdir, flags);
+                       if (ret == ARCHIVE_OK && chdir(intdir) != 0) {
+                               archive_set_error(a, errno, "Couldn't chdir");
+                               ret = ARCHIVE_WARN;
+                       }
+                       *tail = '/'; /* Restore the / we removed. */
+                       if (ret != ARCHIVE_OK)
+                               goto cleanup;
+                       tail++;
+               }
+               archive_entry_set_pathname(entry, tail);
+       }
+#endif
+
+       if (stat(archive_entry_pathname(entry), &extract->st) == 0)
+               extract->pst = &extract->st;
+
+       if (extract->pst != NULL &&
+           extract->pst->st_dev == a->skip_file_dev &&
+           extract->pst->st_ino == a->skip_file_ino) {
+               archive_set_error(a, 0, "Refusing to overwrite archive");
+               ret = ARCHIVE_WARN;
+       } else if (archive_entry_hardlink(entry) != NULL)
+               ret = extract_hard_link(a, entry, flags);
+       else {
+               mode = archive_entry_mode(entry);
+               switch (mode & S_IFMT) {
+               default:
+                       /* Fall through, as required by POSIX. */
+               case S_IFREG:
+                       ret = extract_file(a, entry, flags);
+                       break;
+               case S_IFLNK:   /* Symlink */
+                       ret = extract_symlink(a, entry, flags);
+                       break;
+               case S_IFCHR:
+                       ret = extract_char_device(a, entry, flags);
+                       break;
+               case S_IFBLK:
+                       ret = extract_block_device(a, entry, flags);
+                       break;
+               case S_IFDIR:
+                       ret = extract_dir(a, entry, flags);
+                       break;
+               case S_IFIFO:
+                       ret = extract_fifo(a, entry, flags);
+                       break;
+               }
+       }
+
+
+cleanup:
+#ifdef HAVE_FCHDIR
+       /* If we changed directory above, restore it here. */
+       if (restore_pwd >= 0 && original_filename != NULL) {
+               fchdir(restore_pwd);
+               close(restore_pwd);
+               archive_entry_copy_pathname(entry, original_filename);
+               free(original_filename);
+       }
+#endif
+
+       return (ret);
+}
+
+/*
+ * Cleanup function for archive_extract.  Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ *   * Dir permissions might prevent us from restoring a file in that
+ *     dir, so we restore the dir 0700 first, then correct the
+ *     mode at the end.
+ *   * Similarly, the act of restoring a file touches the directory
+ *     and changes the timestamp on the dir, so we have to touch-up dir
+ *     timestamps at the end as well.
+ *   * Some file flags can interfere with the restore by, for example,
+ *     preventing the creation of hardlinks to those files.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ *
+ * Registering this function (rather than calling it explicitly by
+ * name from archive_read_finish) reduces static link pollution, since
+ * applications that don't use this API won't get this file linked in.
+ */
+static int
+archive_extract_cleanup(struct archive *a)
+{
+       struct fixup_entry *next, *p;
+       struct extract *extract;
+
+       /* Sort dir list so directories are fixed up in depth-first order. */
+       extract = a->extract;
+       p = sort_dir_list(extract->fixup_list);
+
+       while (p != NULL) {
+               extract->pst = NULL; /* Mark stat cache as out-of-date. */
+               if (p->fixup & FIXUP_TIMES) {
+                       struct timeval times[2];
+                       times[1].tv_sec = p->mtime;
+                       times[1].tv_usec = p->mtime_nanos / 1000;
+                       times[0].tv_sec = p->atime;
+                       times[0].tv_usec = p->atime_nanos / 1000;
+                       utimes(p->name, times);
+               }
+               if (p->fixup & FIXUP_MODE)
+                       chmod(p->name, p->mode);
+
+               if (p->fixup & FIXUP_FFLAGS)
+                       set_fflags(a, -1, p->name, p->mode, p->fflags_set, 0);
+
+               next = p->next;
+               free(p->name);
+               free(p);
+               p = next;
+       }
+       extract->fixup_list = NULL;
+       archive_string_free(&extract->create_parent_dir);
+       free(a->extract);
+       a->extract = NULL;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list.  In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+       struct fixup_entry *a, *b, *t;
+
+       if (p == NULL)
+               return (NULL);
+       /* A one-item list is already sorted. */
+       if (p->next == NULL)
+               return (p);
+
+       /* Step 1: split the list. */
+       t = p;
+       a = p->next->next;
+       while (a != NULL) {
+               /* Step a twice, t once. */
+               a = a->next;
+               if (a != NULL)
+                       a = a->next;
+               t = t->next;
+       }
+       /* Now, t is at the mid-point, so break the list here. */
+       b = t->next;
+       t->next = NULL;
+       a = p;
+
+       /* Step 2: Recursively sort the two sub-lists. */
+       a = sort_dir_list(a);
+       b = sort_dir_list(b);
+
+       /* Step 3: Merge the returned lists. */
+       /* Pick the first element for the merged list. */
+       if (strcmp(a->name, b->name) > 0) {
+               t = p = a;
+               a = a->next;
+       } else {
+               t = p = b;
+               b = b->next;
+       }
+
+       /* Always put the later element on the list first. */
+       while (a != NULL && b != NULL) {
+               if (strcmp(a->name, b->name) > 0) {
+                       t->next = a;
+                       a = a->next;
+               } else {
+                       t->next = b;
+                       b = b->next;
+               }
+               t = t->next;
+       }
+
+       /* Only one list is non-empty, so just splice it on. */
+       if (a != NULL)
+               t->next = a;
+       if (b != NULL)
+               t->next = b;
+
+       return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive *a, const char *pathname)
+{
+       struct extract *extract;
+       struct fixup_entry *fe;
+
+       extract = a->extract;
+       fe = malloc(sizeof(struct fixup_entry));
+       if (fe == NULL)
+               return (NULL);
+       fe->next = extract->fixup_list;
+       extract->fixup_list = fe;
+       fe->fixup = 0;
+       fe->name = strdup(pathname);
+       return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive *a, const char *pathname)
+{
+       struct extract *extract;
+
+       extract = a->extract;
+       if (extract->current_fixup == NULL)
+               extract->current_fixup = new_fixup(a, pathname);
+       return (extract->current_fixup);
+}
+
+static int
+extract_file(struct archive *a, struct archive_entry *entry, int flags)
+{
+       struct extract *extract;
+       const char *name;
+       mode_t mode;
+       int fd, r, r2;
+
+       extract = a->extract;
+       name = archive_entry_pathname(entry);
+       mode = archive_entry_mode(entry) & 0777;
+       r = ARCHIVE_OK;
+
+       /*
+        * If we're not supposed to overwrite pre-existing files,
+        * use O_EXCL.  Otherwise, use O_TRUNC.
+        */
+       if (flags & (ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_NO_OVERWRITE))
+               fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+       else
+               fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
+
+       /* Try removing a pre-existing file. */
+       if (fd < 0 && !(flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+               unlink(name);
+               fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+       }
+
+       /* Might be a non-existent parent dir; try fixing that. */
+       if (fd < 0) {
+               create_parent_dir(a, name, flags);
+               fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+       }
+       if (fd < 0) {
+               archive_set_error(a, errno, "Can't open '%s'", name);
+               return (ARCHIVE_WARN);
+       }
+       r = archive_read_data_into_fd(a, fd);
+       extract->pst = NULL; /* Cached stat data no longer valid. */
+       r2 = restore_metadata(a, fd, entry, flags);
+       close(fd);
+       return (err_combine(r, r2));
+}
+
+static int
+extract_dir(struct archive *a, struct archive_entry *entry, int flags)
+{
+       struct extract *extract;
+       struct fixup_entry *fe;
+       char *path, *p;
+
+       extract = a->extract;
+       extract->pst = NULL; /* Invalidate cached stat data. */
+
+       /* Copy path to mutable storage. */
+       archive_strcpy(&(extract->create_parent_dir),
+           archive_entry_pathname(entry));
+       path = extract->create_parent_dir.s;
+
+       if (*path == '\0') {
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Invalid empty pathname");
+               return (ARCHIVE_WARN);
+       }
+
+       /* Deal with any troublesome trailing path elements. */
+       /* TODO: Someday, generalize this to remove '//' or '/./' from
+        * the middle of paths.  But, it should not compress '..' from
+        * the middle of paths.  It's a feature that restoring
+        * "a/../b" creates both 'a' and 'b' directories. */
+       for (;;) {
+               /* Locate last element. */
+               p = strrchr(path, '/');
+               if (p != NULL)
+                       p++;
+               else
+                       p = path;
+               /* Trim trailing '/' unless that's the entire path. */
+               if (p[0] == '\0' && p - 1 > path) {
+                       p[-1] = '\0';
+                       continue;
+               }
+               /* Trim trailing '.' unless that's the entire path. */
+               if (p > path && p[0] == '.' && p[1] == '\0') {
+                       p[0] = '\0';
+                       continue;
+               }
+               /* Just exit on trailing '..'. */
+               if (p[0] == '.' && p[1] == '.' && p[2] == '\0') {
+                       archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                           "Can't restore directory '..'");
+                       return (ARCHIVE_WARN);
+               }
+               break;
+       }
+
+       if (mkdir(path, SECURE_DIR_MODE) == 0)
+               goto success;
+
+       if (extract->pst == NULL && stat(path, &extract->st) == 0)
+               extract->pst = &extract->st;
+
+       if (extract->pst != NULL) {
+               extract->pst = &extract->st;
+               /* If dir already exists, don't reset permissions. */
+               if (S_ISDIR(extract->pst->st_mode))
+                       return (ARCHIVE_OK);
+               /* It exists but isn't a dir. */
+               if ((flags & ARCHIVE_EXTRACT_UNLINK))
+                       unlink(path);
+       } else {
+               /* Doesn't already exist; try building the parent path. */
+               if (create_parent_dir_mutable(a, path, flags) != ARCHIVE_OK)
+                       return (ARCHIVE_WARN);
+       }
+
+       /* One final attempt to create the dir. */
+       if (mkdir(path, SECURE_DIR_MODE) != 0) {
+               archive_set_error(a, errno, "Can't create directory");
+               return (ARCHIVE_WARN);
+       }
+
+success:
+       /* Add this dir to the fixup list. */
+       fe = current_fixup(a, path);
+       fe->fixup |= FIXUP_MODE;
+       fe->mode = archive_entry_mode(entry);
+       if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
+               fe->mode &= ~extract->umask;
+       if (flags & ARCHIVE_EXTRACT_TIME) {
+               fe->fixup |= FIXUP_TIMES;
+               fe->mtime = archive_entry_mtime(entry);
+               fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+               fe->atime = archive_entry_atime(entry);
+               fe->atime_nanos = archive_entry_atime_nsec(entry);
+       }
+       /* For now, set the mode to SECURE_DIR_MODE. */
+       archive_entry_set_mode(entry, SECURE_DIR_MODE);
+       return (restore_metadata(a, -1, entry, flags));
+}
+
+
+/*
+ * Create the parent of the specified path.  Copy the provided
+ * path into mutable storage first.
+ */
+static int
+create_parent_dir(struct archive *a, const char *path, int flags)
+{
+       int r;
+
+       /* Copy path to mutable storage. */
+       archive_strcpy(&(a->extract->create_parent_dir), path);
+       r = create_parent_dir_mutable(a, a->extract->create_parent_dir.s, flags);
+       return (r);
+}
+
+/*
+ * Like create_parent_dir, but creates the dir actually requested, not
+ * the parent.
+ */
+static int
+create_dir(struct archive *a, const char *path, int flags)
+{
+       int r;
+       /* Copy path to mutable storage. */
+       archive_strcpy(&(a->extract->create_parent_dir), path);
+       r = create_dir_mutable(a, a->extract->create_parent_dir.s, flags);
+       return (r);
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir_mutable(struct archive *a, char *path, int flags)
+{
+       char *slash;
+       int r;
+
+       /* Remove tail element to obtain parent name. */
+       slash = strrchr(path, '/');
+       if (slash == NULL)
+               return (ARCHIVE_OK);
+       *slash = '\0';
+       r = create_dir_mutable(a, path, flags);
+       *slash = '/';
+       return (r);
+}
+
+/*
+ * Create the specified dir, assuming path is already in
+ * mutable storage.
+ */
+static int
+create_dir_mutable(struct archive *a, char *path, int flags)
+{
+       mode_t old_umask;
+       int r;
+
+       old_umask = umask(~SECURE_DIR_MODE);
+       r = create_dir_recursive(a, path, flags);
+       umask(old_umask);
+       return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_WARN.
+ */
+static int
+create_dir_recursive(struct archive *a, char *path, int flags)
+{
+       struct stat st;
+       struct extract *extract;
+       struct fixup_entry *le;
+       char *slash, *base;
+       int r;
+
+       extract = a->extract;
+       r = ARCHIVE_OK;
+
+       /* Check for special names and just skip them. */
+       slash = strrchr(path, '/');
+       base = strrchr(path, '/');
+       if (slash == NULL)
+               base = path;
+       else
+               base = slash + 1;
+
+       if (base[0] == '\0' ||
+           (base[0] == '.' && base[1] == '\0') ||
+           (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
+               /* Don't bother trying to create null path, '.', or '..'. */
+               if (slash != NULL) {
+                       *slash = '\0';
+                       r = create_dir_recursive(a, path, flags);
+                       *slash = '/';
+                       return (r);
+               }
+               return (ARCHIVE_OK);
+       }
+
+       /*
+        * Yes, this should be stat() and not lstat().  Using lstat()
+        * here loses the ability to extract through symlinks.  Also note
+        * that this should not use the extract->st cache.
+        */
+       if (stat(path, &st) == 0) {
+               if (S_ISDIR(st.st_mode))
+                       return (ARCHIVE_OK);
+               if ((flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+                       archive_set_error(a, EEXIST,
+                           "Can't create directory '%s'", path);
+                       return (ARCHIVE_WARN);
+               }
+               if (unlink(path) != 0) {
+                       archive_set_error(a, errno,
+                           "Can't create directory '%s': "
+                           "Conflicting file cannot be removed");
+                       return (ARCHIVE_WARN);
+               }
+       } else if (errno != ENOENT && errno != ENOTDIR) {
+               /* Stat failed? */
+               archive_set_error(a, errno, "Can't test directory '%s'", path);
+               return (ARCHIVE_WARN);
+       } else if (slash != NULL) {
+               *slash = '\0';
+               r = create_dir_recursive(a, path, flags);
+               *slash = '/';
+               if (r != ARCHIVE_OK)
+                       return (r);
+       }
+
+       if (mkdir(path, SECURE_DIR_MODE) == 0) {
+               le = new_fixup(a, path);
+               le->fixup |= FIXUP_MODE;
+               le->mode = extract->default_dir_mode;
+               return (ARCHIVE_OK);
+       }
+
+       /*
+        * Without the following check, a/b/../b/c/d fails at the
+        * second visit to 'b', so 'd' can't be created.  Note that we
+        * don't add it to the fixup list here, as it's already been
+        * added.
+        */
+       if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+               return (ARCHIVE_OK);
+
+       archive_set_error(a, errno, "Failed to create dir '%s'", path);
+       return (ARCHIVE_WARN);
+}
+
+static int
+extract_hard_link(struct archive *a, struct archive_entry *entry, int flags)
+{
+       struct extract *extract;
+       int r;
+       const char *pathname;
+       const char *linkname;
+
+       extract = a->extract;
+       pathname = archive_entry_pathname(entry);
+       linkname = archive_entry_hardlink(entry);
+
+       /* Just remove any pre-existing file with this name. */
+       if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+               unlink(pathname);
+
+       r = link(linkname, pathname);
+       extract->pst = NULL; /* Invalidate cached stat data. */
+
+       if (r != 0) {
+               /* Might be a non-existent parent dir; try fixing that. */
+               create_parent_dir(a, pathname, flags);
+               r = link(linkname, pathname);
+       }
+
+       if (r != 0) {
+               /* XXX Better error message here XXX */
+               archive_set_error(a, errno,
+                   "Can't restore hardlink to '%s'", linkname);
+               return (ARCHIVE_WARN);
+       }
+
+       /* Set ownership, time, permission information. */
+       r = restore_metadata(a, -1, entry, flags);
+       return (r);
+}
+
+static int
+extract_symlink(struct archive *a, struct archive_entry *entry, int flags)
+{
+       struct extract *extract;
+       int r;
+       const char *pathname;
+       const char *linkname;
+
+       extract = a->extract;
+       pathname = archive_entry_pathname(entry);
+       linkname = archive_entry_symlink(entry);
+
+       /* Just remove any pre-existing file with this name. */
+       if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+               unlink(pathname);
+
+       r = symlink(linkname, pathname);
+       extract->pst = NULL; /* Invalidate cached stat data. */
+
+       if (r != 0) {
+               /* Might be a non-existent parent dir; try fixing that. */
+               create_parent_dir(a, pathname, flags);
+               r = symlink(linkname, pathname);
+       }
+
+       if (r != 0) {
+               /* XXX Better error message here XXX */
+               archive_set_error(a, errno,
+                   "Can't restore symlink to '%s'", linkname);
+               return (ARCHIVE_WARN);
+       }
+
+       r = restore_metadata(a, -1, entry, flags);
+       return (r);
+}
+
+static int
+extract_device(struct archive *a, struct archive_entry *entry,
+    int flags, mode_t mode)
+{
+       struct extract *extract;
+       int r;
+
+       extract = a->extract;
+       /* Just remove any pre-existing file with this name. */
+       if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+               unlink(archive_entry_pathname(entry));
+
+       r = mknod(archive_entry_pathname(entry), mode,
+           archive_entry_rdev(entry));
+       extract->pst = NULL; /* Invalidate cached stat data. */
+
+       /* Might be a non-existent parent dir; try fixing that. */
+       if (r != 0 && errno == ENOENT) {
+               create_parent_dir(a, archive_entry_pathname(entry), flags);
+               r = mknod(archive_entry_pathname(entry), mode,
+                   archive_entry_rdev(entry));
+       }
+
+       if (r != 0) {
+               archive_set_error(a, errno, "Can't restore device node");
+               return (ARCHIVE_WARN);
+       }
+
+       r = restore_metadata(a, -1, entry, flags);
+       return (r);
+}
+
+static int
+extract_char_device(struct archive *a, struct archive_entry *entry, int flags)
+{
+       mode_t mode;
+
+       mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFCHR;
+       return (extract_device(a, entry, flags, mode));
+}
+
+static int
+extract_block_device(struct archive *a, struct archive_entry *entry, int flags)
+{
+       mode_t mode;
+
+       mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFBLK;
+       return (extract_device(a, entry, flags, mode));
+}
+
+static int
+extract_fifo(struct archive *a, struct archive_entry *entry, int flags)
+{
+       struct extract *extract;
+       int r;
+
+       extract = a->extract;
+       /* Just remove any pre-existing file with this name. */
+       if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
+               unlink(archive_entry_pathname(entry));
+
+       r = mkfifo(archive_entry_pathname(entry),
+           archive_entry_mode(entry));
+       extract->pst = NULL; /* Invalidate cached stat data. */
+
+       /* Might be a non-existent parent dir; try fixing that. */
+       if (r != 0 && errno == ENOENT) {
+               create_parent_dir(a, archive_entry_pathname(entry), flags);
+               r = mkfifo(archive_entry_pathname(entry),
+                   archive_entry_mode(entry));
+       }
+
+       if (r != 0) {
+               archive_set_error(a, errno, "Can't restore fifo");
+               return (ARCHIVE_WARN);
+       }
+
+       r = restore_metadata(a, -1, entry, flags);
+       return (r);
+}
+
+static int
+restore_metadata(struct archive *a, int fd, struct archive_entry *entry, int flags)
+{
+       int r, r2;
+
+       r = set_ownership(a, fd, entry, flags);
+       r2 = set_time(a, fd, entry, flags);
+       r = err_combine(r, r2);
+       r2 = set_perm(a, fd, entry, archive_entry_mode(entry), flags);
+       return (err_combine(r, r2));
+}
+
+static int
+set_ownership(struct archive *a, int fd,
+    struct archive_entry *entry, int flags)
+{
+       uid_t uid;
+       gid_t gid;
+
+       /* Not changed. */
+       if ((flags & ARCHIVE_EXTRACT_OWNER) == 0)
+               return (ARCHIVE_OK);
+
+       uid = lookup_uid(a, archive_entry_uname(entry),
+           archive_entry_uid(entry));
+       gid = lookup_gid(a, archive_entry_gname(entry),
+           archive_entry_gid(entry));
+
+       /* If we know we can't change it, don't bother trying. */
+       if (a->user_uid != 0  &&  a->user_uid != uid)
+               return (ARCHIVE_OK);
+
+#ifdef HAVE_FCHOWN
+       if (fd >= 0 && fchown(fd, uid, gid) == 0)
+               return (ARCHIVE_OK);
+#endif
+
+#ifdef HAVE_LCHOWN
+       if (lchown(archive_entry_pathname(entry), uid, gid))
+#else
+       if (!S_ISLNK(archive_entry_mode(entry))
+           && chown(archive_entry_pathname(entry), uid, gid) != 0)
+#endif
+       {
+               archive_set_error(a, errno,
+                   "Can't set user=%d/group=%d for %s", uid, gid,
+                   archive_entry_pathname(entry));
+               return (ARCHIVE_WARN);
+       }
+       return (ARCHIVE_OK);
+}
+
+static int
+set_time(struct archive *a, int fd, struct archive_entry *entry, int flags)
+{
+       const struct stat *st;
+       struct timeval times[2];
+
+       (void)a; /* UNUSED */
+       st = archive_entry_stat(entry);
+
+       if ((flags & ARCHIVE_EXTRACT_TIME) == 0)
+               return (ARCHIVE_OK);
+       /* It's a waste of time to mess with dir timestamps here. */
+       if (S_ISDIR(archive_entry_mode(entry)))
+               return (ARCHIVE_OK);
+
+       times[1].tv_sec = st->st_mtime;
+       times[1].tv_usec = ARCHIVE_STAT_MTIME_NANOS(st) / 1000;
+
+       times[0].tv_sec = st->st_atime;
+       times[0].tv_usec = ARCHIVE_STAT_ATIME_NANOS(st) / 1000;
+
+#ifdef HAVE_FUTIMES
+       if (fd >= 0 && futimes(fd, times) == 0)
+               return (ARCHIVE_OK);
+#endif
+
+#ifdef HAVE_LUTIMES
+       if (lutimes(archive_entry_pathname(entry), times) != 0) {
+#else
+       if ((archive_entry_mode(entry) & S_IFMT) != S_IFLNK &&
+           utimes(archive_entry_pathname(entry), times) != 0) {
+#endif
+               archive_set_error(a, errno, "Can't update time for %s",
+                   archive_entry_pathname(entry));
+               return (ARCHIVE_WARN);
+       }
+
+       /*
+        * Note: POSIX does not provide a portable way to restore ctime.
+        * (Apart from resetting the system clock, which is distasteful.)
+        * So, any restoration of ctime will necessarily be OS-specific.
+        */
+
+       /* XXX TODO: Can FreeBSD restore ctime? XXX */
+
+       return (ARCHIVE_OK);
+}
+
+static int
+set_perm(struct archive *a, int fd, struct archive_entry *entry,
+    int mode, int flags)
+{
+       struct extract *extract;
+       struct fixup_entry *le;
+       const char *name;
+       unsigned long    set, clear;
+       int              r;
+       int              critical_flags;
+
+       extract = a->extract;
+
+       /* Obey umask unless ARCHIVE_EXTRACT_PERM. */
+       if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
+               mode &= ~extract->umask; /* Enforce umask. */
+       name = archive_entry_pathname(entry);
+
+       if (mode & (S_ISUID | S_ISGID)) {
+               if (extract->pst != NULL) {
+                       /* Already have stat() data available. */
+#ifdef HAVE_FSTAT
+               } else if (fd >= 0 && fstat(fd, &extract->st) == 0) {
+                       extract->pst = &extract->st;
+#endif
+               } else if (stat(name, &extract->st) == 0) {
+                       extract->pst = &extract->st;
+               } else {
+                       archive_set_error(a, errno,
+                           "Couldn't stat file");
+                       return (ARCHIVE_WARN);
+               }
+
+               /*
+                * TODO: Use the uid/gid looked up in set_ownership
+                * above rather than the uid/gid stored in the entry.
+                */
+               if (extract->pst->st_uid != archive_entry_uid(entry))
+                       mode &= ~ S_ISUID;
+               if (extract->pst->st_gid != archive_entry_gid(entry))
+                       mode &= ~ S_ISGID;
+       }
+
+       /*
+        * Ensure we change permissions on the object we extracted,
+        * and not any incidental symlink that might have gotten in
+        * the way.
+        */
+       if (!S_ISLNK(archive_entry_mode(entry))) {
+#ifdef HAVE_FCHMOD
+               if (fd >= 0) {
+                       if (fchmod(fd, mode) != 0) {
+                               archive_set_error(a, errno,
+                                   "Can't set permissions");
+                               return (ARCHIVE_WARN);
+                       }
+               } else
+#endif
+               if (chmod(name, mode) != 0) {
+                       archive_set_error(a, errno, "Can't set permissions");
+                       return (ARCHIVE_WARN);
+               }
+#ifdef HAVE_LCHMOD
+       } else {
+               /*
+                * If lchmod() isn't supported, it's no big deal.
+                * Permissions on symlinks are actually ignored on
+                * most platforms.
+                */
+               if (lchmod(name, mode) != 0) {
+                       archive_set_error(a, errno, "Can't set permissions");
+                       return (ARCHIVE_WARN);
+               }
+#endif
+       }
+
+       if (flags & ARCHIVE_EXTRACT_ACL) {
+               r = set_acls(a, fd, entry);
+               if (r != ARCHIVE_OK)
+                       return (r);
+       }
+
+       if (flags & ARCHIVE_EXTRACT_XATTR) {
+               r = set_xattrs(a, fd, entry);
+               if (r != ARCHIVE_OK)
+                       return (r);
+       }
+
+       /*
+        * Make 'critical_flags' hold all file flags that can't be
+        * immediately restored.  For example, on BSD systems,
+        * SF_IMMUTABLE prevents hardlinks from being created, so
+        * should not be set until after any hardlinks are created.  To
+        * preserve some semblance of portability, this uses #ifdef
+        * extensively.  Ugly, but it works.
+        *
+        * Yes, Virginia, this does create a security race.  It's mitigated
+        * somewhat by the practice of creating dirs 0700 until the extract
+        * is done, but it would be nice if we could do more than that.
+        * People restoring critical file systems should be wary of
+        * other programs that might try to muck with files as they're
+        * being restored.
+        */
+       /* Hopefully, the compiler will optimize this mess into a constant. */
+       critical_flags = 0;
+#ifdef SF_IMMUTABLE
+       critical_flags |= SF_IMMUTABLE;
+#endif
+#ifdef UF_IMMUTABLE
+       critical_flags |= UF_IMMUTABLE;
+#endif
+#ifdef SF_APPEND
+       critical_flags |= SF_APPEND;
+#endif
+#ifdef UF_APPEND
+       critical_flags |= UF_APPEND;
+#endif
+#ifdef EXT2_APPEND_FL
+       critical_flags |= EXT2_APPEND_FL;
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+       critical_flags |= EXT2_IMMUTABLE_FL;
+#endif
+
+       if (flags & ARCHIVE_EXTRACT_FFLAGS) {
+               archive_entry_fflags(entry, &set, &clear);
+
+               /*
+                * The first test encourages the compiler to eliminate
+                * all of this if it's not necessary.
+                */
+               if ((critical_flags != 0)  &&  (set & critical_flags)) {
+                       le = current_fixup(a, archive_entry_pathname(entry));
+                       le->fixup |= FIXUP_FFLAGS;
+                       le->fflags_set = set;
+                       /* Store the mode if it's not already there. */
+                       if ((le->fixup & FIXUP_MODE) == 0)
+                               le->mode = mode;
+               } else {
+                       r = set_fflags(a, fd, archive_entry_pathname(entry),
+                           mode, set, clear);
+                       if (r != ARCHIVE_OK)
+                               return (r);
+               }
+       }
+       return (ARCHIVE_OK);
+}
+
+
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux)
+static int
+set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
+    unsigned long set, unsigned long clear)
+{
+       struct extract *extract;
+
+       extract = a->extract;
+       if (set == 0  && clear == 0)
+               return (ARCHIVE_OK);
+
+       (void)mode; /* UNUSED */
+       /*
+        * XXX Is the stat here really necessary?  Or can I just use
+        * the 'set' flags directly?  In particular, I'm not sure
+        * about the correct approach if we're overwriting an existing
+        * file that already has flags on it. XXX
+        */
+       if (extract->pst != NULL) {
+               /* Already have stat() data available. */
+       } else if (fd >= 0 && fstat(fd, &extract->st) == 0)
+               extract->pst = &extract->st;
+       else if (stat(name, &extract->st) == 0)
+               extract->pst = &extract->st;
+       else {
+               archive_set_error(a, errno,
+                   "Couldn't stat file");
+               return (ARCHIVE_WARN);
+       }
+
+       extract->st.st_flags &= ~clear;
+       extract->st.st_flags |= set;
+#ifdef HAVE_FCHFLAGS
+       /* If platform has fchflags() and we were given an fd, use it. */
+       if (fd >= 0 && fchflags(fd, extract->st.st_flags) == 0)
+               return (ARCHIVE_OK);
+#endif
+       /*
+        * If we can't use the fd to set the flags, we'll use the
+        * pathname to set flags.  We prefer lchflags() but will use
+        * chflags() if we must.
+        */
+#ifdef HAVE_LCHFLAGS
+       if (lchflags(name, extract->st.st_flags) == 0)
+               return (ARCHIVE_OK);
+#elif defined(HAVE_CHFLAGS)
+       if (chflags(name, extract->st.st_flags) == 0)
+               return (ARCHIVE_OK);
+#endif
+       archive_set_error(a, errno,
+           "Failed to set file flags");
+       return (ARCHIVE_WARN);
+}
+
+#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS)
+
+/*
+ * Linux has flags too, but uses ioctl() to access them instead of
+ * having a separate chflags() system call.
+ */
+static int
+set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
+    unsigned long set, unsigned long clear)
+{
+       struct extract *extract;
+       int              ret;
+       int              myfd = fd;
+       unsigned long newflags, oldflags;
+       unsigned long sf_mask = 0;
+
+       extract = a->extract;
+       if (set == 0  && clear == 0)
+               return (ARCHIVE_OK);
+       /* Only regular files and dirs can have flags. */
+       if (!S_ISREG(mode) && !S_ISDIR(mode))
+               return (ARCHIVE_OK);
+
+       /* If we weren't given an fd, open it ourselves. */
+       if (myfd < 0)
+               myfd = open(name, O_RDONLY|O_NONBLOCK);
+       if (myfd < 0)
+               return (ARCHIVE_OK);
+
+       /*
+        * Linux has no define for the flags that are only settable by
+        * the root user.  This code may seem a little complex, but
+        * there seem to be some Linux systems that lack these
+        * defines. (?)  The code below degrades reasonably gracefully
+        * if sf_mask is incomplete.
+        */
+#ifdef EXT2_IMMUTABLE_FL
+       sf_mask |= EXT2_IMMUTABLE_FL;
+#endif
+#ifdef EXT2_APPEND_FL
+       sf_mask |= EXT2_APPEND_FL;
+#endif
+       /*
+        * XXX As above, this would be way simpler if we didn't have
+        * to read the current flags from disk. XXX
+        */
+       ret = ARCHIVE_OK;
+       /* Try setting the flags as given. */
+       if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
+               newflags = (oldflags & ~clear) | set;
+               if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+                       goto cleanup;
+               if (errno != EPERM)
+                       goto fail;
+       }
+       /* If we couldn't set all the flags, try again with a subset. */
+       if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
+               newflags &= ~sf_mask;
+               oldflags &= sf_mask;
+               newflags |= oldflags;
+               if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+                       goto cleanup;
+       }
+       /* We couldn't set the flags, so report the failure. */
+fail:
+       archive_set_error(a, errno,
+           "Failed to set file flags");
+       ret = ARCHIVE_WARN;
+cleanup:
+       if (fd < 0)
+               close(myfd);
+       return (ret);
+}
+
+#else /* Not HAVE_CHFLAGS && Not __linux */
+
+/*
+ * Of course, some systems have neither BSD chflags() nor Linux' flags
+ * support through ioctl().
+ */
+static int
+set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
+    unsigned long set, unsigned long clear)
+{
+       (void)a;
+       (void)fd;
+       (void)name;
+       (void)mode;
+       (void)set;
+       (void)clear;
+       return (ARCHIVE_OK);
+}
+
+#endif /* __linux */
+
+#ifndef HAVE_POSIX_ACL
+/* Default empty function body to satisfy mainline code. */
+static int
+set_acls(struct archive *a, int fd, struct archive_entry *entry)
+{
+       (void)a;
+       (void)fd;
+       (void)entry;
+
+       return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
+ */
+static int
+set_acls(struct archive *a, int fd, struct archive_entry *entry)
+{
+       int              ret;
+
+       ret = set_acl(a, fd, entry, ACL_TYPE_ACCESS,
+           ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+       if (ret != ARCHIVE_OK)
+               return (ret);
+       ret = set_acl(a, fd, entry, ACL_TYPE_DEFAULT,
+           ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+       return (ret);
+}
+
+
+static int
+set_acl(struct archive *a, int fd, struct archive_entry *entry,
+    acl_type_t acl_type, int ae_requested_type, const char *typename)
+{
+       acl_t            acl;
+       acl_entry_t      acl_entry;
+       acl_permset_t    acl_permset;
+       int              ret;
+       int              ae_type, ae_permset, ae_tag, ae_id;
+       uid_t            ae_uid;
+       gid_t            ae_gid;
+       const char      *ae_name;
+       int              entries;
+       const char      *name;
+
+       ret = ARCHIVE_OK;
+       entries = archive_entry_acl_reset(entry, ae_requested_type);
+       if (entries == 0)
+               return (ARCHIVE_OK);
+       acl = acl_init(entries);
+       while (archive_entry_acl_next(entry, ae_requested_type, &ae_type,
+                  &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+               acl_create_entry(&acl, &acl_entry);
+
+               switch (ae_tag) {
+               case ARCHIVE_ENTRY_ACL_USER:
+                       acl_set_tag_type(acl_entry, ACL_USER);
+                       ae_uid = lookup_uid(a, ae_name, ae_id);
+                       acl_set_qualifier(acl_entry, &ae_uid);
+                       break;
+               case ARCHIVE_ENTRY_ACL_GROUP:
+                       acl_set_tag_type(acl_entry, ACL_GROUP);
+                       ae_gid = lookup_gid(a, ae_name, ae_id);
+                       acl_set_qualifier(acl_entry, &ae_gid);
+                       break;
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+                       break;
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+                       break;
+               case ARCHIVE_ENTRY_ACL_MASK:
+                       acl_set_tag_type(acl_entry, ACL_MASK);
+                       break;
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       acl_set_tag_type(acl_entry, ACL_OTHER);
+                       break;
+               default:
+                       /* XXX */
+                       break;
+               }
+
+               acl_get_permset(acl_entry, &acl_permset);
+               acl_clear_perms(acl_permset);
+               if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
+                       acl_add_perm(acl_permset, ACL_EXECUTE);
+               if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
+                       acl_add_perm(acl_permset, ACL_WRITE);
+               if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
+                       acl_add_perm(acl_permset, ACL_READ);
+       }
+
+       name = archive_entry_pathname(entry);
+
+       /* Try restoring the ACL through 'fd' if we can. */
+#if HAVE_ACL_SET_FD
+       if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
+               ret = ARCHIVE_OK;
+       else
+#else
+#if HAVE_ACL_SET_FD_NP
+       if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
+               ret = ARCHIVE_OK;
+       else
+#endif
+#endif
+       if (acl_set_file(name, acl_type, acl) != 0) {
+               archive_set_error(a, errno, "Failed to set %s acl", typename);
+               ret = ARCHIVE_WARN;
+       }
+       acl_free(acl);
+       return (ret);
+}
+#endif
+
+#if HAVE_LSETXATTR
+/*
+ * Restore extended attributes -  Linux implementation
+ */
+static int
+set_xattrs(struct archive *a, int fd, struct archive_entry *entry)
+{
+       static int warning_done = 0;
+       int ret = ARCHIVE_OK;
+       int i = archive_entry_xattr_reset(entry);
+
+       while (i--) {
+               const char *name;
+               const void *value;
+               size_t size;
+               archive_entry_xattr_next(entry, &name, &value, &size);
+               if (name != NULL &&
+                               strncmp(name, "xfsroot.", 8) != 0 &&
+                               strncmp(name, "system.", 7) != 0) {
+                       int e;
+#if HAVE_FSETXATTR
+                       if (fd >= 0)
+                               e = fsetxattr(fd, name, value, size, 0);
+                       else
+#endif
+                       {
+                               e = lsetxattr(archive_entry_pathname(entry),
+                                   name, value, size, 0);
+                       }
+                       if (e == -1) {
+                               if (errno == ENOTSUP) {
+                                       if (!warning_done) {
+                                               warning_done = 1;
+                                               archive_set_error(a, errno,
+                                                   "Cannot restore extended "
+                                                   "attributes on this file "
+                                                   "system");
+                                       }
+                               } else
+                                       archive_set_error(a, errno,
+                                           "Failed to set extended attribute");
+                               ret = ARCHIVE_WARN;
+                       }
+               } else {
+                       archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Invalid extended attribute encountered");
+                       ret = ARCHIVE_WARN;
+               }
+       }
+       return (ret);
+}
+#else
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive *a, int fd, struct archive_entry *entry)
+{
+       static int warning_done = 0;
+       (void)a; /* UNUSED */
+       (void)fd; /* UNUSED */
+
+       /* If there aren't any extended attributes, then it's okay not
+        * to extract them, otherwise, issue a single warning. */
+       if (archive_entry_xattr_count(entry) != 0 && !warning_done) {
+               warning_done = 1;
+               archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Cannot restore extended attributes on this system");
+               return (ARCHIVE_WARN);
+       }
+       /* Warning was already emitted; suppress further warnings. */
+       return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * The following routines do some basic caching of uname/gname
+ * lookups.  All such lookups go through these routines, including ACL
+ * conversions.  Even a small cache here provides an enormous speedup,
+ * especially on systems using NIS, LDAP, or a similar networked
+ * directory system.
+ *
+ * TODO: Provide an API for clients to override these routines.
+ */
+static gid_t
+lookup_gid(struct archive *a, const char *gname, gid_t gid)
+{
+       struct group    *grent;
+       struct extract *extract;
+       int h;
+       struct bucket *b;
+       int cache_size;
+
+       extract = a->extract;
+       cache_size = sizeof(extract->gcache) / sizeof(extract->gcache[0]);
+
+       /* If no gname, just use the gid provided. */
+       if (gname == NULL || *gname == '\0')
+               return (gid);
+
+       /* Try to find gname in the cache. */
+       h = hash(gname);
+       b = &extract->gcache[h % cache_size ];
+       if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
+               return ((gid_t)b->id);
+
+       /* Free the cache slot for a new entry. */
+       if (b->name != NULL)
+               free(b->name);
+       b->name = strdup(gname);
+       /* Note: If strdup fails, that's okay; we just won't cache. */
+       b->hash = h;
+       grent = getgrnam(gname);
+       if (grent != NULL)
+               gid = grent->gr_gid;
+       b->id = gid;
+
+       return (gid);
+}
+
+static uid_t
+lookup_uid(struct archive *a, const char *uname, uid_t uid)
+{
+       struct passwd   *pwent;
+       struct extract *extract;
+       int h;
+       struct bucket *b;
+       int cache_size;
+
+       extract = a->extract;
+       cache_size = sizeof(extract->ucache) / sizeof(extract->ucache[0]);
+
+       /* If no uname, just use the uid provided. */
+       if (uname == NULL || *uname == '\0')
+               return (uid);
+
+       /* Try to find uname in the cache. */
+       h = hash(uname);
+       b = &extract->ucache[h % cache_size ];
+       if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
+               return ((uid_t)b->id);
+
+       /* Free the cache slot for a new entry. */
+       if (b->name != NULL)
+               free(b->name);
+       b->name = strdup(uname);
+       /* Note: If strdup fails, that's okay; we just won't cache. */
+       b->hash = h;
+       pwent = getpwnam(uname);
+       if (pwent != NULL)
+               uid = pwent->pw_uid;
+       b->id = uid;
+
+       return (uid);
+}
+
+static unsigned int
+hash(const char *p)
+{
+       /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+          as used by ELF for hashing function names. */
+       unsigned g, h = 0;
+       while (*p != '\0') {
+               h = ( h << 4 ) + *p++;
+               if (( g = h & 0xF0000000 )) {
+                       h ^= g >> 24;
+                       h &= 0x0FFFFFFF;
+               }
+       }
+       return h;
+}
+
+void
+archive_read_extract_set_progress_callback(struct archive *a,
+    void (*progress_func)(void *), void *user_data)
+{
+       a->extract_progress = progress_func;
+       a->extract_progress_user_data = user_data;
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_open_fd.c b/contrib/libarchive-1.3.1/libarchive/archive_read_open_fd.c
new file mode 100644 (file)
index 0000000..95e7b87
--- /dev/null
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_open_fd.c,v 1.6 2006/07/30 00:29:00 kientzle Exp $");
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+
+struct read_fd_data {
+       int      fd;
+       size_t   block_size;
+       void    *buffer;
+};
+
+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);
+static ssize_t file_skip(struct archive *, void *, size_t request);
+
+int
+archive_read_open_fd(struct archive *a, int fd, size_t block_size)
+{
+       struct read_fd_data *mine;
+
+       mine = malloc(sizeof(*mine));
+       if (mine == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       mine->block_size = block_size;
+       mine->buffer = malloc(mine->block_size);
+       if (mine->buffer == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               free(mine);
+               return (ARCHIVE_FATAL);
+       }
+       mine->fd = fd;
+       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 = 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);
+       }
+
+       if (S_ISREG(st.st_mode))
+               archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_fd_data *mine = client_data;
+       ssize_t bytes_read;
+
+       *buff = mine->buffer;
+       bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+       if (bytes_read < 0) {
+               archive_set_error(a, errno, "Error reading fd %d", mine->fd);
+       }
+       return (bytes_read);
+}
+
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+{
+       struct read_fd_data *mine = client_data;
+       off_t old_offset, new_offset;
+       
+       /* Reduce request to the next smallest multiple of block_size */
+       request = (request / mine->block_size) * mine->block_size;
+       /*
+        * Hurray for lazy evaluation: if the first lseek fails, the second
+        * one will not be executed.
+        */
+       if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
+           ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
+       {
+               if (errno == ESPIPE)
+               {
+                       /*
+                        * Failure to lseek() can be caused by the file
+                        * descriptor pointing to a pipe, socket or FIFO.
+                        * Return 0 here, so the compression layer will use
+                        * read()s instead to advance the file descriptor.
+                        * It's slower of course, but works as well.
+                        */
+                       return (0);
+               }
+               /*
+                * There's been an error other than ESPIPE. This is most
+                * likely caused by a programmer error (too large request)
+                * or a corrupted archive file.
+                */
+               archive_set_error(a, errno, "Error seeking");
+               return (-1);
+       }
+       return (new_offset - old_offset);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_fd_data *mine = client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_open_file.c b/contrib/libarchive-1.3.1/libarchive/archive_read_open_file.c
new file mode 100644 (file)
index 0000000..1897940
--- /dev/null
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2003-2006 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: src/lib/libarchive/archive_read_open_file.c,v 1.11 2006/09/05 05:59:46 kientzle Exp $");
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+struct read_FILE_data {
+       FILE    *f;
+       size_t   block_size;
+       void    *buffer;
+};
+
+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);
+static ssize_t file_skip(struct archive *, void *, size_t request);
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+       struct read_FILE_data *mine;
+
+       mine = 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) {
+               archive_set_error(a, ENOMEM, "No memory");
+               free(mine);
+               return (ARCHIVE_FATAL);
+       }
+       mine->f = f;
+       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 = 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 (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode))
+               archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_FILE_data *mine = client_data;
+       ssize_t bytes_read;
+
+       *buff = mine->buffer;
+       bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
+       if (bytes_read < 0) {
+               archive_set_error(a, errno, "Error reading file");
+       }
+       return (bytes_read);
+}
+
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+{
+       struct read_FILE_data *mine = client_data;
+       off_t old_offset, new_offset;
+
+       /* Reduce request to the next smallest multiple of block_size */
+       request = (request / mine->block_size) * mine->block_size;
+       /*
+        * Note: the 'fd' and 'filename' versions round the request
+        * down to a multiple of the block size to ensure proper
+        * operation on block-oriented media such as tapes.  But stdio
+        * doesn't work with such media (it doesn't ensure blocking),
+        * so we don't need to bother.
+        */
+       old_offset = ftello(mine->f);
+       fseeko(mine->f, request, SEEK_CUR);
+       new_offset = ftello(mine->f);
+       if (old_offset < 0 || new_offset < 0) {
+               archive_set_error(a, errno, "Error skipping forward");
+               return (ARCHIVE_FATAL);
+       }
+       return (new_offset - old_offset);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_FILE_data *mine = client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_open_filename.c b/contrib/libarchive-1.3.1/libarchive/archive_read_open_filename.c
new file mode 100644 (file)
index 0000000..20509ae
--- /dev/null
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2003-2004 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 <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+
+struct read_file_data {
+       int      fd;
+       size_t   block_size;
+       void    *buffer;
+       mode_t   st_mode;  /* Mode bits for opened file. */
+       char     filename[1]; /* Must be last! */
+};
+
+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);
+static ssize_t file_skip(struct archive *, void *, size_t request);
+
+int
+archive_read_open_file(struct archive *a, const char *filename,
+    size_t block_size)
+{
+       return (archive_read_open_filename(a, filename, block_size));
+}
+
+int
+archive_read_open_filename(struct archive *a, const char *filename,
+    size_t block_size)
+{
+       struct read_file_data *mine;
+
+       if (filename == NULL || filename[0] == '\0') {
+               mine = malloc(sizeof(*mine));
+               if (mine == NULL) {
+                       archive_set_error(a, ENOMEM, "No memory");
+                       return (ARCHIVE_FATAL);
+               }
+               mine->filename[0] = '\0';
+       } else {
+               mine = 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;
+       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 = client_data;
+       struct stat st;
+
+       mine->buffer = malloc(mine->block_size);
+       if (mine->buffer == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       if (mine->filename[0] != '\0')
+               mine->fd = open(mine->filename, O_RDONLY);
+       else
+               mine->fd = 0; /* Fake "open" for stdin. */
+       if (mine->fd < 0) {
+               archive_set_error(a, errno, "Failed to open '%s'",
+                   mine->filename);
+               return (ARCHIVE_FATAL);
+       }
+       if (fstat(mine->fd, &st) == 0) {
+               if (S_ISREG(st.st_mode)) {
+                       /* Set dev/ino of archive file so extract
+                          won't overwrite. */
+                       archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+               }
+               /* 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);
+               return (ARCHIVE_FATAL);
+       }
+       return (0);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_file_data *mine = client_data;
+       ssize_t bytes_read;
+
+       *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);
+       }
+       return (bytes_read);
+}
+
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+{
+       struct read_file_data *mine = client_data;
+       off_t old_offset, new_offset;
+       
+       /* Reduce request to the next smallest multiple of block_size */
+       request = (request / mine->block_size) * mine->block_size;
+       /*
+        * Hurray for lazy evaluation: if the first lseek fails, the second
+        * one will not be executed.
+        */
+       if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
+           ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
+       {
+               if (errno == ESPIPE)
+               {
+                       /*
+                        * Failure to lseek() can be caused by the file
+                        * descriptor pointing to a pipe, socket or FIFO.
+                        * Return 0 here, so the compression layer will use
+                        * read()s instead to advance the file descriptor.
+                        * It's slower of course, but works as well.
+                        */
+                       return (0);
+               }
+               /*
+                * There's been an error other than ESPIPE. This is most
+                * 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);
+               return (-1);
+       }
+       return (new_offset - old_offset);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_file_data *mine = 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')
+               close(mine->fd);
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_open_memory.c b/contrib/libarchive-1.3.1/libarchive/archive_read_open_memory.c
new file mode 100644 (file)
index 0000000..3f2b5c9
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2003-2006 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: src/lib/libarchive/archive_read_open_fd.c,v 1.5 2006/01/17 04:49:04 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * Glue to read an archive from a block of memory.
+ *
+ * This is mostly a huge help in building test harnesses;
+ * test programs can build archives in memory and read them
+ * back again without having to mess with files on disk.
+ */
+
+struct read_memory_data {
+       unsigned char   *buffer;
+       unsigned char   *end;
+       ssize_t  read_size;
+};
+
+static int     memory_read_close(struct archive *, void *);
+static int     memory_read_open(struct archive *, void *);
+static ssize_t memory_read_skip(struct archive *, void *, size_t request);
+static ssize_t memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, void *buff, size_t size)
+{
+       return archive_read_open_memory2(a, buff, size, size);
+}
+
+/*
+ * Don't use this in production code; the archive_read_open_memory()
+ * version is the one you really want.  This is just here so that
+ * test harnesses can exercise block operations inside the library.
+ */
+int
+archive_read_open_memory2(struct archive *a, void *buff,
+    size_t size, size_t read_size)
+{
+       struct read_memory_data *mine;
+
+       mine = malloc(sizeof(*mine));
+       if (mine == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       memset(mine, 0, sizeof(*mine));
+       mine->buffer = buff;
+       mine->end = mine->buffer + size;
+       mine->read_size = read_size;
+       return (archive_read_open2(a, mine, memory_read_open,
+                   memory_read, memory_read_skip, memory_read_close));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+       (void)a; /* UNUSED */
+       (void)client_data; /* UNUSED */
+       return (ARCHIVE_OK);
+}
+
+/*
+ * This is scary simple:  Just advance a pointer.  Limiting
+ * to read_size is not technically necessary, but it exercises
+ * more of the internal logic when used with a small block size
+ * in a test harness.  Production use should not specify a block
+ * size; then this is much faster.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_memory_data *mine = client_data;
+       ssize_t size;
+
+       (void)a; /* UNUSED */
+       *buff = mine->buffer;
+       size = mine->end - mine->buffer;
+       if (size > mine->read_size)
+               size = mine->read_size;
+        mine->buffer += size;
+       return (size);
+}
+
+/*
+ * Advancing is just as simple.  Again, this is doing more than
+ * necessary in order to better exercise internal code when used
+ * as a test harness.
+ */
+static ssize_t
+memory_read_skip(struct archive *a, void *client_data, size_t skip)
+{
+       struct read_memory_data *mine = client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer + skip > mine->end)
+               skip = mine->end - mine->buffer;
+       /* Round down to block size. */
+       skip /= mine->read_size;
+       skip *= mine->read_size;
+       mine->buffer += skip;
+       return (skip);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+       struct read_memory_data *mine = client_data;
+       (void)a; /* UNUSED */
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_all.c b/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_all.c
new file mode 100644 (file)
index 0000000..b9c4584
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_support_compression_all.c,v 1.5 2004/07/30 04:14:47 kientzle Exp $");
+
+#include "archive.h"
+
+int
+archive_read_support_compression_all(struct archive *a)
+{
+#if HAVE_BZLIB_H
+       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
+       archive_read_support_compression_gzip(a);
+#endif
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_bzip2.c b/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_bzip2.c
new file mode 100644 (file)
index 0000000..9704d1e
--- /dev/null
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.9 2006/07/30 00:29:00 kientzle Exp $");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+#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;
+};
+
+static int     finish(struct archive *);
+static ssize_t read_ahead(struct archive *, const void **, size_t);
+static ssize_t read_consume(struct archive *, size_t);
+static int     drive_decompressor(struct archive *a, struct private_data *);
+#endif
+
+/* These two functions are defined even if we lack bzlib.  See below. */
+static int     bid(const void *, size_t);
+static int     init(struct archive *, const void *, size_t);
+
+int
+archive_read_support_compression_bzip2(struct archive *a)
+{
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = buff;
+       bits_checked = 0;
+       if (buffer[0] != 'B')   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 'Z')   /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       if (buffer[2] != 'h')   /* Verify third ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 4)
+               return (bits_checked);
+
+       if (buffer[3] < '1' || buffer[3] > '9')
+               return (0);
+       bits_checked += 5;
+
+       /*
+        * Research Question: Can we do any more to verify that this
+        * really is BZip2 format??  For 99.9% of the time, the above
+        * test is sufficient, but it would be nice to do a more
+        * thorough check.  It's especially troubling that the BZip2
+        * signature begins with all ASCII characters; a tar archive
+        * whose first filename begins with 'BZh3' would potentially
+        * fool this logic.  (It may also be possible to gaurd against
+        * such anomalies in archive_read_support_compression_none.)
+        */
+
+       return (bits_checked);
+}
+
+#ifndef HAVE_BZLIB_H
+
+/*
+ * If we don't have bzlib on this system, we can't actually do the
+ * decompression.  We can, however, still detect bzip2-compressed
+ * archives and emit a useful message.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       (void)a;        /* UNUSED */
+       (void)buff;     /* UNUSED */
+       (void)n;        /* UNUSED */
+
+       archive_set_error(a, -1,
+           "This version of libarchive was compiled without bzip2 support");
+       return (ARCHIVE_FATAL);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int ret;
+
+       a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
+       a->compression_name = "bzip2";
+
+       state = malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(a, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = 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, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->compression_name);
+               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 = (void *)(uintptr_t)(const void *)buff;
+       state->stream.avail_in = n;
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_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->compression_data = state;
+               return (ARCHIVE_OK);
+       }
+
+       /* Library setup failed: Clean up. */
+       archive_set_error(a, ARCHIVE_ERRNO_MISC,
+           "Internal error initializing %s library", a->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_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid setup parameter");
+               break;
+       case BZ_MEM_ERROR:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "out of memory");
+               break;
+       case BZ_CONFIG_ERROR:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "mis-compiled library");
+               break;
+       }
+
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->stream.next_out - state->read_next;
+
+       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;
+       }
+
+       while (was_avail < read_avail &&        /* Made some progress. */
+           read_avail < (int)min &&            /* Haven't satisfied min. */
+           read_avail < (int)state->uncompressed_buffer_size) { /* !full */
+               if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
+                       return (ret);
+               was_avail = read_avail;
+               read_avail = state->stream.next_out - state->read_next;
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive *a, size_t n)
+{
+       struct private_data *state;
+
+       state = a->compression_data;
+       a->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);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive *a)
+{
+       struct private_data *state;
+       int ret;
+
+       state = a->compression_data;
+       ret = ARCHIVE_OK;
+       switch (BZ2_bzDecompressEnd(&(state->stream))) {
+       case BZ_OK:
+               break;
+       default:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Failed to clean up %s compressor", a->compression_name);
+               ret = ARCHIVE_FATAL;
+       }
+
+       free(state->uncompressed_buffer);
+       free(state);
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               (a->client_closer)(a, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Utility function to pull data through decompressor, reading input
+ * blocks as necessary.
+ */
+static int
+drive_decompressor(struct archive *a, struct private_data *state)
+{
+       ssize_t ret;
+       int decompressed, total_decompressed;
+       char *output;
+
+       total_decompressed = 0;
+       for (;;) {
+               if (state->stream.avail_in == 0) {
+                       ret = (a->client_reader)(a, a->client_data,
+                           (const void **)&state->stream.next_in);
+                       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, EIO,
+                                   "Premature end of %s compressed data",
+                                   a->compression_name);
+                               return (ARCHIVE_FATAL);
+                       }
+                       a->raw_position += ret;
+                       state->stream.avail_in = ret;
+               }
+
+               {
+                       output = state->stream.next_out;
+
+                       /* Decompress some data. */
+                       ret = BZ2_bzDecompress(&(state->stream));
+                       decompressed = state->stream.next_out - output;
+
+                       /* Accumulate the total bytes of output. */
+                       state->total_out += decompressed;
+                       total_decompressed += decompressed;
+
+                       switch (ret) {
+                       case BZ_OK: /* Decompressor made some progress. */
+                               if (decompressed > 0)
+                                       return (ARCHIVE_OK);
+                               break;
+                       case BZ_STREAM_END:     /* Found end of stream. */
+                               return (ARCHIVE_OK);
+                       default:
+                               /* Any other return value is an error. */
+                               goto fatal;
+                       }
+               }
+       }
+       return (ARCHIVE_OK);
+
+       /* Return a fatal error. */
+fatal:
+       archive_set_error(a, ARCHIVE_ERRNO_MISC, "%s decompression failed",
+           a->compression_name);
+       return (ARCHIVE_FATAL);
+}
+
+#endif /* HAVE_BZLIB_H */
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_compress.c b/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_compress.c
new file mode 100644 (file)
index 0000000..80e4dc8
--- /dev/null
@@ -0,0 +1,483 @@
+/*-
+ * Copyright (c) 2003-2004 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.
+ */
+
+/*
+ * This code borrows heavily from "compress" source code, which is
+ * protected by the following copyright.  (Clause 3 dropped by request
+ * of the Regents.)
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.5 2006/07/30 00:29:00 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "archive_private.h"
+
+/*
+ * Because LZW decompression is pretty simple, I've just implemented
+ * the whole decompressor here (cribbing from "compress" source code,
+ * of course), rather than relying on an external library.  I have
+ * made an effort to clarify and simplify the algorithm, so the
+ * names and structure here don't exactly match those used by compress.
+ */
+
+struct private_data {
+       /* Input variables. */
+       const unsigned char     *next_in;
+       size_t                   avail_in;
+       int                      bit_buffer;
+       int                      bits_avail;
+       size_t                   bytes_in_section;
+
+       /* Output variables. */
+       size_t                   uncompressed_buffer_size;
+       void                    *uncompressed_buffer;
+       unsigned char           *read_next;   /* Data for client. */
+       unsigned char           *next_out;    /* Where to write new data. */
+       size_t                   avail_out;   /* Space at end of buffer. */
+
+       /* Decompression status variables. */
+       int                      use_reset_code;
+       int                      end_of_stream; /* EOF status. */
+       int                      maxcode;       /* Largest code. */
+       int                      maxcode_bits;  /* Length of largest code. */
+       int                      section_end_code; /* When to increase bits. */
+       int                      bits;          /* Current code length. */
+       int                      oldcode;       /* Previous code. */
+       int                      finbyte;       /* Last byte of prev code. */
+
+       /* Dictionary. */
+       int                      free_ent;       /* Next dictionary entry. */
+       unsigned char            suffix[65536];
+       uint16_t                 prefix[65536];
+
+       /*
+        * Scratch area for expanding dictionary entries.  Note:
+        * "worst" case here comes from compressing /dev/zero: the
+        * last code in the dictionary will code a sequence of
+        * 65536-256 zero bytes.  Thus, we need stack space to expand
+        * a 65280-byte dictionary entry.  (Of course, 32640:1
+        * compression could also be considered the "best" case. ;-)
+        */
+       unsigned char           *stackp;
+       unsigned char            stack[65300];
+};
+
+static int     bid(const void *, size_t);
+static int     finish(struct archive *);
+static int     init(struct archive *, const void *, size_t);
+static ssize_t read_ahead(struct archive *, const void **, size_t);
+static ssize_t read_consume(struct archive *, size_t);
+static int     getbits(struct archive *, struct private_data *, int n);
+static int     next_code(struct archive *a, struct private_data *state);
+
+int
+archive_read_support_compression_compress(struct archive *a)
+{
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = buff;
+       bits_checked = 0;
+       if (buffer[0] != 037)   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 0235)  /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       /*
+        * TODO: Verify more.
+        */
+
+       return (bits_checked);
+}
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int code;
+
+       a->compression_code = ARCHIVE_COMPRESSION_COMPRESS;
+       a->compression_name = "compress (.Z)";
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_finish = finish;
+
+       state = malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(a, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+       a->compression_data = state;
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = malloc(state->uncompressed_buffer_size);
+
+       if (state->uncompressed_buffer == NULL) {
+               archive_set_error(a, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->compression_name);
+               goto fatal;
+       }
+
+       state->next_in = buff;
+       state->avail_in = n;
+       state->read_next = state->next_out = state->uncompressed_buffer;
+       state->avail_out = state->uncompressed_buffer_size;
+
+       code = getbits(a, state, 8);
+       if (code != 037) /* This should be impossible. */
+               goto fatal;
+
+       code = getbits(a, state, 8);
+       if (code != 0235) {
+               /* This can happen if the library is receiving 1-byte
+                * blocks and gzip and compress are both enabled.
+                * You can't distinguish gzip and compress only from
+                * the first byte. */
+               archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Compress signature did not match.");
+               goto fatal;
+       }
+
+       code = getbits(a, state, 8);
+       state->maxcode_bits = code & 0x1f;
+       state->maxcode = (1 << state->maxcode_bits);
+       state->use_reset_code = code & 0x80;
+
+       /* Initialize decompressor. */
+       state->free_ent = 256;
+       state->stackp = state->stack;
+       if (state->use_reset_code)
+               state->free_ent++;
+       state->bits = 9;
+       state->section_end_code = (1<<state->bits) - 1;
+       state->oldcode = -1;
+       for (code = 255; code >= 0; code--) {
+               state->prefix[code] = 0;
+               state->suffix[code] = code;
+       }
+       next_code(a, state);
+       return (ARCHIVE_OK);
+
+fatal:
+       finish(a);
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->next_out - state->read_next;
+
+       if (read_avail < (int)min  &&  state->end_of_stream) {
+               if (state->end_of_stream == ARCHIVE_EOF)
+                       return (0);
+               else
+                       return (-1);
+       }
+
+       if (read_avail < (int)min) {
+               memmove(state->uncompressed_buffer, state->read_next,
+                   read_avail);
+               state->read_next = state->uncompressed_buffer;
+               state->next_out = state->read_next + read_avail;
+               state->avail_out
+                   = state->uncompressed_buffer_size - read_avail;
+
+               while (read_avail < (int)state->uncompressed_buffer_size
+                       && !state->end_of_stream) {
+                       if (state->stackp > state->stack) {
+                               *state->next_out++ = *--state->stackp;
+                               state->avail_out--;
+                               read_avail++;
+                       } else {
+                               ret = next_code(a, state);
+                               if (ret == ARCHIVE_EOF)
+                                       state->end_of_stream = ret;
+                               else if (ret != ARCHIVE_OK)
+                                       return (ret);
+                       }
+               }
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive *a, size_t n)
+{
+       struct private_data *state;
+
+       state = a->compression_data;
+       a->file_position += n;
+       state->read_next += n;
+       if (state->read_next > state->next_out)
+               __archive_errx(1, "Request to consume too many "
+                   "bytes from compress decompressor");
+       return (n);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive *a)
+{
+       struct private_data *state;
+       int ret = ARCHIVE_OK;
+
+       state = a->compression_data;
+
+       if (state != NULL) {
+               if (state->uncompressed_buffer != NULL)
+                       free(state->uncompressed_buffer);
+               free(state);
+       }
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               ret = (a->client_closer)(a, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Process the next code and fill the stack with the expansion
+ * of the code.  Returns ARCHIVE_FATAL if there is a fatal I/O or
+ * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
+ */
+static int
+next_code(struct archive *a, struct private_data *state)
+{
+       int code, newcode;
+
+       static int debug_buff[1024];
+       static unsigned debug_index;
+
+       code = newcode = getbits(a, state, state->bits);
+       if (code < 0)
+               return (code);
+
+       debug_buff[debug_index++] = code;
+       if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))
+               debug_index = 0;
+
+       /* If it's a reset code, reset the dictionary. */
+       if ((code == 256) && state->use_reset_code) {
+               /*
+                * The original 'compress' implementation blocked its
+                * I/O in a manner that resulted in junk bytes being
+                * inserted after every reset.  The next section skips
+                * this junk.  (Yes, the number of *bytes* to skip is
+                * a function of the current *bit* length.)
+                */
+               int skip_bytes =  state->bits -
+                   (state->bytes_in_section % state->bits);
+               skip_bytes %= state->bits;
+               state->bits_avail = 0; /* Discard rest of this byte. */
+               while (skip_bytes-- > 0) {
+                       code = getbits(a, state, 8);
+                       if (code < 0)
+                               return (code);
+               }
+               /* Now, actually do the reset. */
+               state->bytes_in_section = 0;
+               state->bits = 9;
+               state->section_end_code = (1 << state->bits) - 1;
+               state->free_ent = 257;
+               state->oldcode = -1;
+               return (next_code(a, state));
+       }
+
+       if (code > state->free_ent) {
+               /* An invalid code is a fatal error. */
+               archive_set_error(a, -1, "Invalid compressed data: code %d is larger than max code %d (input address %X)", code, state->free_ent, state->next_in);
+               return (ARCHIVE_FATAL);
+       }
+
+       /* Special case for KwKwK string. */
+       if (code >= state->free_ent) {
+               *state->stackp++ = state->finbyte;
+               code = state->oldcode;
+       }
+
+       /* Generate output characters in reverse order. */
+       while (code >= 256) {
+               *state->stackp++ = state->suffix[code];
+               code = state->prefix[code];
+       }
+       *state->stackp++ = state->finbyte = code;
+
+       /* Generate the new entry. */
+       code = state->free_ent;
+       if (code < state->maxcode && state->oldcode >= 0) {
+               state->prefix[code] = state->oldcode;
+               state->suffix[code] = state->finbyte;
+               ++state->free_ent;
+       }
+       if (state->free_ent > state->section_end_code) {
+               state->bits++;
+               state->bytes_in_section = 0;
+               if (state->bits == state->maxcode_bits)
+                       state->section_end_code = state->maxcode;
+               else
+                       state->section_end_code = (1 << state->bits) - 1;
+       }
+
+       /* Remember previous code. */
+       state->oldcode = newcode;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Return next 'n' bits from stream.
+ *
+ * -1 indicates end of available data.
+ */
+static int
+getbits(struct archive *a, struct private_data *state, int n)
+{
+       int code, ret;
+       static const int mask[] = {
+               0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
+               0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+       };
+
+
+       while (state->bits_avail < n) {
+               if (state->avail_in <= 0) {
+                       ret = (a->client_reader)(a, a->client_data,
+                           (const void **)&state->next_in);
+                       if (ret < 0)
+                               return (ARCHIVE_FATAL);
+                       if (ret == 0)
+                               return (ARCHIVE_EOF);
+                       a->raw_position += ret;
+                       state->avail_in = ret;
+               }
+               state->bit_buffer |= *state->next_in++ << state->bits_avail;
+               state->avail_in--;
+               state->bits_avail += 8;
+               state->bytes_in_section++;
+       }
+
+       code = state->bit_buffer;
+       state->bit_buffer >>= n;
+       state->bits_avail -= n;
+
+       return (code & mask[n]);
+}
diff --git a/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_gzip.c b/contrib/libarchive-1.3.1/libarchive/archive_read_support_compression_gzip.c
new file mode 100644 (file)
index 0000000..95d0894
--- /dev/null
@@ -0,0 +1,532 @@
+/*-
+ * Copyright (c) 2003-2004 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: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.10 2006/07/30 00:29:01 kientzle Exp $");
+
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+#ifdef HAVE_ZLIB_H
+struct private_data {
+       z_stream         stream;
+       unsigned char   *uncompressed_buffer;
+       size_t           uncompressed_buffer_size;
+       unsigned char   *read_next;
+       int64_t          total_out;
+       unsigned long    crc;
+       char             header_done;
+};
+
+static int     finish(struct archive *);
+static ssize_t read_ahead(struct archive *, const void **, size_t);
+static ssize_t read_consume(struct archive *, size_t);
+static int     drive_decompressor(struct archive *a, struct private_data *);
+#endif
+
+/* These two functions are defined even if we lack zlib.  See below. */
+static int     bid(const void *, size_t);
+static int     init(struct archive *, const void *, size_t);
+
+int
+archive_read_support_compression_gzip(struct archive *a)
+{
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = buff;
+       bits_checked = 0;
+       if (buffer[0] != 037)   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 0213)  /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       if (buffer[2] != 8)     /* Compression must be 'deflate'. */
+               return (0);
+       bits_checked += 8;
+       if (len < 4)
+               return (bits_checked);
+
+       if ((buffer[3] & 0xE0)!= 0)     /* No reserved flags set. */
+               return (0);
+       bits_checked += 3;
+       if (len < 5)
+               return (bits_checked);
+
+       /*
+        * TODO: Verify more; in particular, gzip has an optional
+        * header CRC, which would give us 16 more verified bits.  We
+        * may also be able to verify certain constraints on other
+        * fields.
+        */
+
+       return (bits_checked);
+}
+
+
+#ifndef HAVE_ZLIB_H
+
+/*
+ * If we don't have zlib on this system, we can't actually do the
+ * decompression.  We can, however, still detect gzip-compressed
+ * archives and emit a useful message.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       (void)a;        /* UNUSED */
+       (void)buff;     /* UNUSED */
+       (void)n;        /* UNUSED */
+
+       archive_set_error(a, -1,
+           "This version of libarchive was compiled without gzip support");
+       return (ARCHIVE_FATAL);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int ret;
+
+       a->compression_code = ARCHIVE_COMPRESSION_GZIP;
+       a->compression_name = "gzip";
+
+       state = malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(a, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+
+       state->crc = crc32(0L, NULL, 0);
+       state->header_done = 0; /* We've not yet begun to parse header... */
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = 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, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->compression_name);
+               free(state);
+               return (ARCHIVE_FATAL);
+       }
+
+       /*
+        * A bug in zlib.h: stream.next_in should be marked 'const'
+        * but isn't (the library never alters data through the
+        * next_in pointer, only reads it).  The result: this ugly
+        * cast to remove 'const'.
+        */
+       state->stream.next_in = (void *)(uintptr_t)(const void *)buff;
+       state->stream.avail_in = n;
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_finish = finish;
+
+       /*
+        * TODO: Do I need to parse the gzip header before calling
+        * inflateInit2()?  In particular, one of the header bytes
+        * marks "best compression" or "fastest", which may be
+        * appropriate for setting the second parameter here.
+        * However, I think the only penalty for not setting it
+        * correctly is wasted memory.  If this is necessary, it
+        * should probably go into drive_decompressor() below.
+        */
+
+       /* Initialize compression library. */
+       ret = inflateInit2(&(state->stream),
+           -15 /* Don't check for zlib header */);
+       if (ret == Z_OK) {
+               a->compression_data = state;
+               return (ARCHIVE_OK);
+       }
+
+       /* Library setup failed: Clean up. */
+       archive_set_error(a, ARCHIVE_ERRNO_MISC,
+           "Internal error initializing %s library", a->compression_name);
+       free(state->uncompressed_buffer);
+       free(state);
+
+       /* Override the error message if we know what really went wrong. */
+       switch (ret) {
+       case Z_STREAM_ERROR:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid setup parameter");
+               break;
+       case Z_MEM_ERROR:
+               archive_set_error(a, ENOMEM,
+                   "Internal error initializing compression library: "
+                   "out of memory");
+               break;
+       case Z_VERSION_ERROR:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid library version");
+               break;
+       }
+
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->stream.next_out - state->read_next;
+
+       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;
+       }
+
+       while (was_avail < read_avail &&        /* Made some progress. */
+           read_avail < (int)min &&            /* Haven't satisfied min. */
+           read_avail < (int)state->uncompressed_buffer_size) { /* !full */
+               if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
+                       return (ret);
+               was_avail = read_avail;
+               read_avail = state->stream.next_out - state->read_next;
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive *a, size_t n)
+{
+       struct private_data *state;
+
+       state = a->compression_data;
+       a->file_position += n;
+       state->read_next += n;
+       if (state->read_next > state->stream.next_out)
+               __archive_errx(1, "Request to consume too many "
+                   "bytes from gzip decompressor");
+       return (n);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive *a)
+{
+       struct private_data *state;
+       int ret;
+
+       state = a->compression_data;
+       ret = ARCHIVE_OK;
+       switch (inflateEnd(&(state->stream))) {
+       case Z_OK:
+               break;
+       default:
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Failed to clean up %s compressor", a->compression_name);
+               ret = ARCHIVE_FATAL;
+       }
+
+       free(state->uncompressed_buffer);
+       free(state);
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               (a->client_closer)(a, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Utility function to pull data through decompressor, reading input
+ * blocks as necessary.
+ */
+static int
+drive_decompressor(struct archive *a, struct private_data *state)
+{
+       ssize_t ret;
+       int decompressed, total_decompressed;
+       int count, flags, header_state;
+       unsigned char *output;
+       unsigned char b;
+
+       flags = 0;
+       count = 0;
+       header_state = 0;
+       total_decompressed = 0;
+       for (;;) {
+               if (state->stream.avail_in == 0) {
+                       ret = (a->client_reader)(a, a->client_data,
+                           (const void **)&state->stream.next_in);
+                       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, EIO,
+                                   "Premature end of %s compressed data",
+                                   a->compression_name);
+                               return (ARCHIVE_FATAL);
+                       }
+                       a->raw_position += ret;
+                       state->stream.avail_in = ret;
+               }
+
+               if (!state->header_done) {
+                       /*
+                        * If still parsing the header, interpret the
+                        * next byte.
+                        */
+                       b = *(state->stream.next_in++);
+                       state->stream.avail_in--;
+
+                       /*
+                        * Yes, this is somewhat crude, but it works,
+                        * GZip format isn't likely to change anytime
+                        * in the near future, and header parsing is
+                        * certainly not a performance issue, so
+                        * there's little point in making this more
+                        * elegant.  Of course, if you see an easy way
+                        * to make this more elegant, please let me
+                        * know.. ;-)
+                        */
+                       switch (header_state) {
+                       case 0: /* First byte of signature. */
+                               if (b != 037)
+                                       goto fatal;
+                               header_state = 1;
+                               break;
+                       case 1: /* Second byte of signature. */
+                               if (b != 0213)
+                                       goto fatal;
+                               header_state = 2;
+                               break;
+                       case 2: /* Compression type must be 8. */
+                               if (b != 8)
+                                       goto fatal;
+                               header_state = 3;
+                               break;
+                       case 3: /* GZip flags. */
+                               flags = b;
+                               header_state = 4;
+                               break;
+                       case 4: case 5: case 6: case 7: /* Mod time. */
+                               header_state++;
+                               break;
+                       case 8: /* Deflate flags. */
+                               header_state = 9;
+                               break;
+                       case 9: /* OS. */
+                               header_state = 10;
+                               break;
+                       case 10: /* Optional Extra: First byte of Length. */
+                               if ((flags & 4)) {
+                                       count = 255 & (int)b;
+                                       header_state = 11;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 11: /* Optional Extra: Second byte of Length. */
+                               if ((flags & 4)) {
+                                       count = (0xff00 & ((int)b << 8)) | count;
+                                       header_state = 12;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 12: /* Optional Extra Field: counted length. */
+                               if ((flags & 4)) {
+                                       --count;
+                                       if (count == 0) header_state = 13;
+                                       else header_state = 12;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 13: /* Optional Original Filename. */
+                               if ((flags & 8)) {
+                                       if (b == 0) header_state = 14;
+                                       else header_state = 13;
+                                       break;
+                               }
+                               /*
+                                * Fall through if no Optional
+                                * Original Filename.
+                                */
+                       case 14: /* Optional Comment. */
+                               if ((flags & 16)) {
+                                       if (b == 0) header_state = 15;
+                                       else header_state = 14;
+                                       break;
+                               }
+                               /* Fall through if no Optional Comment. */
+                       case 15: /* Optional Header CRC: First byte. */
+                               if ((flags & 2)) {
+                                       header_state = 16;
+                                       break;
+                               }
+                               /* Fall through if no Optional Header CRC. */
+                       case 16: /* Optional Header CRC: Second byte. */
+                               if ((flags & 2)) {
+                                       header_state = 17;
+                                       break;
+                               }
+                               /* Fall through if no Optional Header CRC. */
+                       case 17: /* First byte of compressed data. */
+                               state->header_done = 1; /* done with header */
+                               state->stream.avail_in++;
+                               state->stream.next_in--;
+                       }
+
+                       /*
+                        * TODO: Consider moving the inflateInit2 call
+                        * here so it can include the compression type
+                        * from the header?
+                        */
+               } else {
+                       output = state->stream.next_out;
+
+                       /* Decompress some data. */
+                       ret = inflate(&(state->stream), 0);
+                       decompressed = state->stream.next_out - output;
+
+                       /* Accumulate the CRC of the uncompressed data. */
+                       state->crc = crc32(state->crc, output, decompressed);
+
+                       /* Accumulate the total bytes of output. */
+                       state->total_out += decompressed;
+                       total_decompressed += decompressed;
+
+                       switch (ret) {
+                       case Z_OK: /* Decompressor made some progress. */
+                               if (decompressed > 0)
+                                       return (ARCHIVE_OK);
+                               break;
+                       case Z_STREAM_END: /* Found end of stream. */
+                               /*
+                                * TODO: Verify gzip trailer
+                                * (uncompressed length and CRC).
+                                */
+                               return (ARCHIVE_OK);
+                       default:
+                               /* Any other return value is an error. */
+                               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                                   "gzip decompression failed (%s)",
+                                   state->stream.msg);
+                               goto fatal;
+                       }
+ &n