Import libarchive-3.0.4.
authorPeter Avalos <pavalos@dragonflybsd.org>
Wed, 11 Jul 2012 03:25:58 +0000 (20:25 -0700)
committerPeter Avalos <pavalos@dragonflybsd.org>
Wed, 11 Jul 2012 03:25:58 +0000 (20:25 -0700)
103 files changed:
contrib/libarchive/NEWS
contrib/libarchive/README
contrib/libarchive/build/version
contrib/libarchive/cpio/bsdcpio.1
contrib/libarchive/cpio/cmdline.c
contrib/libarchive/cpio/cpio.c
contrib/libarchive/cpio/cpio.h
contrib/libarchive/libarchive/archive.h
contrib/libarchive/libarchive/archive_acl.c
contrib/libarchive/libarchive/archive_check_magic.c
contrib/libarchive/libarchive/archive_endian.h
contrib/libarchive/libarchive/archive_entry.3
contrib/libarchive/libarchive/archive_entry.c
contrib/libarchive/libarchive/archive_entry.h
contrib/libarchive/libarchive/archive_entry_acl.3
contrib/libarchive/libarchive/archive_entry_link_resolver.c
contrib/libarchive/libarchive/archive_entry_linkify.3
contrib/libarchive/libarchive/archive_entry_paths.3
contrib/libarchive/libarchive/archive_entry_perms.3
contrib/libarchive/libarchive/archive_entry_stat.3
contrib/libarchive/libarchive/archive_entry_stat.c
contrib/libarchive/libarchive/archive_entry_time.3
contrib/libarchive/libarchive/archive_getdate.c [moved from contrib/libarchive/tar/getdate.c with 99% similarity]
contrib/libarchive/libarchive/archive_match.c [new file with mode: 0644]
contrib/libarchive/libarchive/archive_options.c
contrib/libarchive/libarchive/archive_pathmatch.c [moved from contrib/libarchive/libarchive_fe/pathmatch.c with 57% similarity]
contrib/libarchive/libarchive/archive_pathmatch.h [moved from contrib/libarchive/libarchive_fe/pathmatch.h with 78% similarity]
contrib/libarchive/libarchive/archive_ppmd7.c
contrib/libarchive/libarchive/archive_ppmd_private.h
contrib/libarchive/libarchive/archive_private.h
contrib/libarchive/libarchive/archive_rb.c
contrib/libarchive/libarchive/archive_read.3
contrib/libarchive/libarchive/archive_read.c
contrib/libarchive/libarchive/archive_read_data_into_fd.c
contrib/libarchive/libarchive/archive_read_disk.3
contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
contrib/libarchive/libarchive/archive_read_disk_posix.c
contrib/libarchive/libarchive/archive_read_disk_private.h
contrib/libarchive/libarchive/archive_read_open_fd.c
contrib/libarchive/libarchive/archive_read_open_filename.c
contrib/libarchive/libarchive/archive_read_open_memory.c
contrib/libarchive/libarchive/archive_read_private.h
contrib/libarchive/libarchive/archive_read_set_options.3
contrib/libarchive/libarchive/archive_read_set_options.c
contrib/libarchive/libarchive/archive_read_support_filter_rpm.c
contrib/libarchive/libarchive/archive_read_support_format_7zip.c
contrib/libarchive/libarchive/archive_read_support_format_cab.c
contrib/libarchive/libarchive/archive_read_support_format_cpio.c
contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
contrib/libarchive/libarchive/archive_read_support_format_lha.c
contrib/libarchive/libarchive/archive_read_support_format_mtree.c
contrib/libarchive/libarchive/archive_read_support_format_rar.c
contrib/libarchive/libarchive/archive_read_support_format_tar.c
contrib/libarchive/libarchive/archive_read_support_format_xar.c
contrib/libarchive/libarchive/archive_read_support_format_zip.c
contrib/libarchive/libarchive/archive_string.c
contrib/libarchive/libarchive/archive_string.h
contrib/libarchive/libarchive/archive_string_composition.h
contrib/libarchive/libarchive/archive_string_sprintf.c
contrib/libarchive/libarchive/archive_util.3
contrib/libarchive/libarchive/archive_util.c
contrib/libarchive/libarchive/archive_write.3
contrib/libarchive/libarchive/archive_write.c
contrib/libarchive/libarchive/archive_write_add_filter.c [moved from contrib/libarchive/libarchive_fe/matching.h with 55% similarity]
contrib/libarchive/libarchive/archive_write_add_filter_bzip2.c
contrib/libarchive/libarchive/archive_write_add_filter_compress.c
contrib/libarchive/libarchive/archive_write_add_filter_gzip.c
contrib/libarchive/libarchive/archive_write_add_filter_program.c
contrib/libarchive/libarchive/archive_write_add_filter_xz.c
contrib/libarchive/libarchive/archive_write_disk.3
contrib/libarchive/libarchive/archive_write_disk_posix.c
contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
contrib/libarchive/libarchive/archive_write_open_filename.c
contrib/libarchive/libarchive/archive_write_private.h
contrib/libarchive/libarchive/archive_write_set_format_7zip.c
contrib/libarchive/libarchive/archive_write_set_format_ar.c
contrib/libarchive/libarchive/archive_write_set_format_cpio.c
contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
contrib/libarchive/libarchive/archive_write_set_format_iso9660.c
contrib/libarchive/libarchive/archive_write_set_format_mtree.c
contrib/libarchive/libarchive/archive_write_set_format_pax.c
contrib/libarchive/libarchive/archive_write_set_format_ustar.c
contrib/libarchive/libarchive/archive_write_set_format_xar.c
contrib/libarchive/libarchive/archive_write_set_format_zip.c
contrib/libarchive/libarchive/archive_write_set_options.3
contrib/libarchive/libarchive/archive_write_set_options.c
contrib/libarchive/libarchive/cpio.5
contrib/libarchive/libarchive/libarchive-formats.5
contrib/libarchive/libarchive/libarchive.3
contrib/libarchive/libarchive/libarchive_internals.3
contrib/libarchive/libarchive/mtree.5
contrib/libarchive/libarchive/tar.5
contrib/libarchive/libarchive_fe/err.c
contrib/libarchive/libarchive_fe/err.h
contrib/libarchive/libarchive_fe/matching.c [deleted file]
contrib/libarchive/tar/bsdtar.1
contrib/libarchive/tar/bsdtar.c
contrib/libarchive/tar/bsdtar.h
contrib/libarchive/tar/read.c
contrib/libarchive/tar/tree.c [deleted file]
contrib/libarchive/tar/tree.h [deleted file]
contrib/libarchive/tar/write.c

index e9811b5..5c28068 100644 (file)
@@ -1,14 +1,10 @@
-Jan 10, 2012: Issue 223: Skip atime tests if atime not supported
-Jan 09, 2012: Issue 222: Errors saving sparse files to pax archives
-Jan 09, 2012: Issue 221: allow archive_*_free(NULL)
-Dec 31, 2011: Issue 212: configure script on Solaris
-Dec 30, 2011: Issue 218: empty contents extracting Zip files with bsdcpio
-Dec 30, 2011: Issue 217: fix compile warning
-Dec 30, 2011: Issue 216: truncated filenames in listings
-Dec 28, 2011: Issue 210: memory leak on Windows
-Dec 28, 2011: Issue 206: fix hardlink tests on Windows 2000
-Dec 27, 2011: Issue 208: Don't hang when using external compression
-   program on Windows
+Mar 27, 2012: libarchive 3.0.4 released
+
+Feb 05, 2012: libarchive development now hosted at GitHub.
+    http://libarchive.github.com/
+Feb 05, 2012: libarchive's issue tracker remains at Google Code.
+    http://code.google.com/p/libarchive/issues/list
+Feb 05, 2012: libarchive's mailing lists remain at Google Groups.
 
 Dec 24, 2011: libarchive 3.0.2 released
 Dec 23, 2011: Various fixes merged from FreeBSD
index 6923838..4ffc3b7 100644 (file)
@@ -1,9 +1,14 @@
 README for libarchive bundle.
 
 Questions?  Issues?
-   * http://libarchive.googlecode.com/ is the home for ongoing
-     libarchive development, including issue tracker, additional
-     documentation, and links to the libarchive mailing lists.
+   * http://libarchive.github.com/ is the home for ongoing
+     libarchive development, including documentation, and
+     links to the libarchive mailing lists.
+   * To report an issue, use the issue tracker at
+     http://code.google.com/p/libarchive/issues/list
+   * To submit an enhancement to libarchive, please submit
+     a pull request via GitHub.
+     https://github.com/libarchive/libarchive/pulls
 
 This distribution bundle includes the following components:
    * libarchive: a library for reading and writing streaming archives
@@ -66,6 +71,7 @@ Currently, the library automatically detects and reads the following fomats:
   * ZIP archives (with uncompressed or "deflate" compressed entries)
   * GNU and BSD 'ar' archives
   * 'mtree' format
+  * 7-Zip archives
   * Microsoft CAB format
   * LHA and LZH archives
   * RAR archives
@@ -92,6 +98,7 @@ The library can create archives in any of the following formats:
   * GNU and BSD 'ar' archives
   * 'mtree' format
   * ISO9660 format
+  * 7-Zip archives
   * XAR archives
 
 When creating archives, the result can be filtered with any of the following:
index 1355130..5611b20 100644 (file)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd December 21, 2007
+.Dd December 24, 2011
 .Dt CPIO 1
 .Os
 .Sh NAME
index 390aebc..438c27c 100644 (file)
@@ -346,6 +346,7 @@ owner_parse(const char *spec, int *uid, int *gid)
                                snprintf(errbuff, sizeof(errbuff),
                                    "Couldn't lookup user ``%s''", user);
                                errbuff[sizeof(errbuff) - 1] = '\0';
+                               free(user);
                                return (errbuff);
                        }
                }
index 025c50c..7095b62 100644 (file)
@@ -82,7 +82,6 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/cpio.c,v 1.15 2008/12/06 07:30:40 kientzle
 #include "cpio.h"
 #include "err.h"
 #include "line_reader.h"
-#include "matching.h"
 
 /* Fixed size of uname/gname caches. */
 #define        name_cache_size 101
@@ -119,6 +118,7 @@ static void mode_in(struct cpio *);
 static void    mode_list(struct cpio *);
 static void    mode_out(struct cpio *);
 static void    mode_pass(struct cpio *, const char *);
+static const char *remove_leading_slash(const char *);
 static int     restore_time(struct cpio *, struct archive_entry *,
                    const char *, int fd);
 static void    usage(void);
@@ -155,9 +155,9 @@ main(int argc, char *argv[])
        else {
 #if defined(_WIN32) && !defined(__CYGWIN__)
                lafe_progname = strrchr(*argv, '\\');
-#else
-               lafe_progname = strrchr(*argv, '/');
+               if (strrchr(*argv, '/') > lafe_progname)
 #endif
+               lafe_progname = strrchr(*argv, '/');
                if (lafe_progname != NULL)
                        lafe_progname++;
                else
@@ -189,6 +189,10 @@ main(int argc, char *argv[])
        cpio->bytes_per_block = 512;
        cpio->filename = NULL;
 
+       cpio->matching = archive_match_new();
+       if (cpio->matching == NULL)
+               lafe_errc(1, 0, "Out of memory");
+
        while ((opt = cpio_getopt(cpio)) != -1) {
                switch (opt) {
                case '0': /* GNU convention: --null, -0 */
@@ -215,14 +219,20 @@ main(int argc, char *argv[])
                        cpio->extract_flags &= ~ARCHIVE_EXTRACT_NO_AUTODIR;
                        break;
                case 'E': /* NetBSD/OpenBSD */
-                       lafe_include_from_file(&cpio->matching,
-                           cpio->argument, cpio->option_null);
+                       if (archive_match_include_pattern_from_file(
+                           cpio->matching, cpio->argument,
+                           cpio->option_null) != ARCHIVE_OK)
+                               lafe_errc(1, 0, "Error : %s",
+                                   archive_error_string(cpio->matching));
                        break;
                case 'F': /* NetBSD/OpenBSD/GNU cpio */
                        cpio->filename = cpio->argument;
                        break;
                case 'f': /* POSIX 1997 */
-                       lafe_exclude(&cpio->matching, cpio->argument);
+                       if (archive_match_exclude_pattern(cpio->matching,
+                           cpio->argument) != ARCHIVE_OK)
+                               lafe_errc(1, 0, "Error : %s",
+                                   archive_error_string(cpio->matching));
                        break;
                case 'H': /* GNU cpio (also --format) */
                        cpio->format = cpio->argument;
@@ -368,9 +378,6 @@ main(int argc, char *argv[])
        /* -v overrides -V */
        if (cpio->dot && cpio->verbose)
                cpio->dot = 0;
-       /* -v overrides -V */
-       if (cpio->dot && cpio->verbose)
-               cpio->dot = 0;
        /* TODO: Flag other nonsensical combinations. */
 
        switch (cpio->mode) {
@@ -384,7 +391,10 @@ main(int argc, char *argv[])
                break;
        case 'i':
                while (*cpio->argv != NULL) {
-                       lafe_include(&cpio->matching, *cpio->argv);
+                       if (archive_match_include_pattern(cpio->matching,
+                           *cpio->argv) != ARCHIVE_OK)
+                               lafe_errc(1, 0, "Error : %s",
+                                   archive_error_string(cpio->matching));
                        --cpio->argc;
                        ++cpio->argv;
                }
@@ -404,6 +414,7 @@ main(int argc, char *argv[])
                    "Must specify at least one of -i, -o, or -p");
        }
 
+       archive_match_free(cpio->matching);
        free_cache(cpio->gname_cache);
        free_cache(cpio->uname_cache);
        return (cpio->return_value);
@@ -574,6 +585,49 @@ mode_out(struct cpio *cpio)
        archive_write_free(cpio->archive);
 }
 
+static const char *
+remove_leading_slash(const char *p)
+{
+       const char *rp;
+
+       /* Remove leading "//./" or "//?/" or "//?/UNC/"
+        * (absolute path prefixes used by Windows API) */
+       if ((p[0] == '/' || p[0] == '\\') &&
+           (p[1] == '/' || p[1] == '\\') &&
+           (p[2] == '.' || p[2] == '?') &&
+           (p[3] == '/' || p[3] == '\\'))
+       {
+               if (p[2] == '?' &&
+                   (p[4] == 'U' || p[4] == 'u') &&
+                   (p[5] == 'N' || p[5] == 'n') &&
+                   (p[6] == 'C' || p[6] == 'c') &&
+                   (p[7] == '/' || p[7] == '\\'))
+                       p += 8;
+               else
+                       p += 4;
+       }
+       do {
+               rp = p;
+               /* Remove leading drive letter from archives created
+                * on Windows. */
+               if (((p[0] >= 'a' && p[0] <= 'z') ||
+                    (p[0] >= 'A' && p[0] <= 'Z')) &&
+                        p[1] == ':') {
+                       p += 2;
+               }
+               /* Remove leading "/../", "//", etc. */
+               while (p[0] == '/' || p[0] == '\\') {
+                       if (p[1] == '.' && p[2] == '.' &&
+                               (p[3] == '/' || p[3] == '\\')) {
+                               p += 3; /* Remove "/..", leave "/"
+                                        * for next pass. */
+                       } else
+                               p += 1; /* Remove "/". */
+               }
+       } while (rp != p);
+       return (p);
+}
+
 /*
  * This is used by both out mode (to copy objects from disk into
  * an archive) and pass mode (to copy objects from disk to
@@ -585,7 +639,6 @@ file_to_archive(struct cpio *cpio, const char *srcpath)
        const char *destpath;
        struct archive_entry *entry, *spare;
        size_t len;
-       const char *p;
        int r;
 
        /*
@@ -639,10 +692,7 @@ file_to_archive(struct cpio *cpio, const char *srcpath)
                                    "Can't allocate path buffer");
                }
                strcpy(cpio->pass_destpath, cpio->destdir);
-               p = srcpath;
-               while (p[0] == '/')
-                       ++p;
-               strcat(cpio->pass_destpath, p);
+               strcat(cpio->pass_destpath, remove_leading_slash(srcpath));
                destpath = cpio->pass_destpath;
        }
        if (cpio->option_rename)
@@ -869,7 +919,7 @@ mode_in(struct cpio *cpio)
                        lafe_errc(1, archive_errno(a),
                            "%s", archive_error_string(a));
                }
-               if (lafe_excluded(cpio->matching, archive_entry_pathname(entry)))
+               if (archive_match_path_excluded(cpio->matching, entry))
                        continue;
                if (cpio->option_rename) {
                        destpath = cpio_rename(archive_entry_pathname(entry));
@@ -971,7 +1021,7 @@ mode_list(struct cpio *cpio)
                        lafe_errc(1, archive_errno(a),
                            "%s", archive_error_string(a));
                }
-               if (lafe_excluded(cpio->matching, archive_entry_pathname(entry)))
+               if (archive_match_path_excluded(cpio->matching, entry))
                        continue;
                if (cpio->verbose)
                        list_item_verbose(cpio, entry);
@@ -1139,12 +1189,24 @@ cpio_rename(const char *name)
        static char buff[1024];
        FILE *t;
        char *p, *ret;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       FILE *to;
 
+       t = fopen("CONIN$", "r");
+       if (t == NULL)
+               return (name);
+       to = fopen("CONOUT$", "w");
+       if (to == NULL)
+               return (name);
+       fprintf(to, "%s (Enter/./(new name))? ", name);
+       fclose(to);
+#else
        t = fopen("/dev/tty", "r+");
        if (t == NULL)
                return (name);
        fprintf(t, "%s (Enter/./(new name))? ", name);
        fflush(t);
+#endif
 
        p = fgets(buff, sizeof(buff), t);
        fclose(t);
@@ -1254,7 +1316,8 @@ lookup_uname_helper(struct cpio *cpio, const char **name, id_t id)
        if (pwent == NULL) {
                *name = NULL;
                if (errno != 0 && errno != ENOENT)
-                       lafe_warnc(errno, "getpwuid(%d) failed", id);
+                       lafe_warnc(errno, "getpwuid(%s) failed",
+                           cpio_i64toa((int64_t)id));
                return (errno);
        }
 
@@ -1281,7 +1344,8 @@ lookup_gname_helper(struct cpio *cpio, const char **name, id_t id)
        if (grent == NULL) {
                *name = NULL;
                if (errno != 0)
-                       lafe_warnc(errno, "getgrgid(%d) failed", id);
+                       lafe_warnc(errno, "getgrgid(%s) failed",
+                           cpio_i64toa((int64_t)id));
                return (errno);
        }
 
index dc68e66..7e276bd 100644 (file)
@@ -31,8 +31,6 @@
 #include "cpio_platform.h"
 #include <stdio.h>
 
-#include "matching.h"
-
 /*
  * The internal state for the "cpio" program.
  *
@@ -88,7 +86,7 @@ struct cpio {
        struct name_cache *gname_cache;
 
        /* Work data. */
-       struct lafe_matching  *matching;
+       struct archive   *matching;
        char             *buff;
        size_t            buff_size;
 };
index 47191e7..d5316ca 100644 (file)
 # else
 #  define      __LA_SSIZE_T    long
 # endif
-# if defined(__BORLANDC__)
-#  define      __LA_UID_T      uid_t
-#  define      __LA_GID_T      gid_t
-# else
-#  define      __LA_UID_T      short
-#  define      __LA_GID_T      short
-# endif
 #else
-# include <unistd.h>  /* ssize_t, uid_t, and gid_t */
+# include <unistd.h>  /* ssize_t */
 # if defined(_SCO_DS)
 #  define      __LA_INT64_T    long long
 # else
 #  define      __LA_INT64_T    int64_t
 # endif
 # define       __LA_SSIZE_T    ssize_t
-# define       __LA_UID_T      uid_t
-# define       __LA_GID_T      gid_t
 #endif
 
 /*
@@ -127,13 +118,13 @@ extern "C" {
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define        ARCHIVE_VERSION_NUMBER 3000003
+#define        ARCHIVE_VERSION_NUMBER 3000004
 __LA_DECL int          archive_version_number(void);
 
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define        ARCHIVE_VERSION_STRING "libarchive 3.0.3"
+#define        ARCHIVE_VERSION_STRING "libarchive 3.0.4"
 __LA_DECL const char * archive_version_string(void);
 
 /* Declare our basic types. */
@@ -567,6 +558,8 @@ __LA_DECL int archive_write_set_compression_program(struct archive *,
 __LA_DECL int archive_write_set_compression_xz(struct archive *);
 #endif
 
+/* A convenience function to set the filter based on the code. */
+__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);
 __LA_DECL int archive_write_add_filter_bzip2(struct archive *);
 __LA_DECL int archive_write_add_filter_compress(struct archive *);
 __LA_DECL int archive_write_add_filter_gzip(struct archive *);
@@ -758,11 +751,42 @@ __LA_DECL int     archive_read_disk_open_w(struct archive *, const wchar_t *);
  * traversal.
  */
 __LA_DECL int  archive_read_disk_descend(struct archive *);
+__LA_DECL int  archive_read_disk_can_descend(struct archive *);
 __LA_DECL int  archive_read_disk_current_filesystem(struct archive *);
 __LA_DECL int  archive_read_disk_current_filesystem_is_synthetic(struct archive *);
 __LA_DECL int  archive_read_disk_current_filesystem_is_remote(struct archive *);
 /* Request that the access time of the entry visited by travesal be restored. */
 __LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);
+/*
+ * Set behavior. The "flags" argument selects optional behavior.
+ */
+/* Request that the access time of the entry visited by travesal be restored.
+ * This is the same as archive_read_disk_set_atime_restored. */
+#define        ARCHIVE_READDISK_RESTORE_ATIME          (0x0001)
+/* Default: Do not skip an entry which has nodump flags. */
+#define        ARCHIVE_READDISK_HONOR_NODUMP           (0x0002)
+/* Default: Skip a mac resource fork file whose prefix is "._" because of
+ * using copyfile. */
+#define        ARCHIVE_READDISK_MAC_COPYFILE           (0x0004)
+/* Default: Do not traverse mount points. */
+#define        ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS     (0x0008)
+
+__LA_DECL int  archive_read_disk_set_behavior(struct archive *,
+                   int flags);
+
+/*
+ * Set archive_match object that will be used in archive_read_disk to
+ * know whether an entry should be skipped. The callback function
+ * _excluded_func will be invoked when an entry is skipped by the result
+ * of archive_match.
+ */
+__LA_DECL int  archive_read_disk_set_matching(struct archive *,
+                   struct archive *_matching, void (*_excluded_func)
+                   (struct archive *, void *, struct archive_entry *),
+                   void *_client_data);
+__LA_DECL int  archive_read_disk_set_metadata_filter_callback(struct archive *,
+                   int (*_metadata_filter_func)(struct archive *, void *,
+                       struct archive_entry *), void *_client_data);
 
 /*
  * Accessor functions to read/set various information in
@@ -802,14 +826,116 @@ __LA_DECL void            archive_copy_error(struct archive *dest,
                            struct archive *src);
 __LA_DECL int           archive_file_count(struct archive *);
 
+/*
+ * ARCHIVE_MATCH API
+ */
+__LA_DECL struct archive *archive_match_new(void);
+__LA_DECL int  archive_match_free(struct archive *);
+
+/*
+ * Test if archive_entry is excluded.
+ * This is a convenience function. This is the same as calling all
+ * archive_match_path_excluded, archive_match_time_excluded
+ * and archive_match_owner_excluded.
+ */
+__LA_DECL int  archive_match_excluded(struct archive *,
+                   struct archive_entry *);
+
+/*
+ * Test if pathname is excluded. The conditions are set by following functions.
+ */
+__LA_DECL int  archive_match_path_excluded(struct archive *,
+                   struct archive_entry *);
+/* Add exclusion pathname pattern. */
+__LA_DECL int  archive_match_exclude_pattern(struct archive *, const char *);
+__LA_DECL int  archive_match_exclude_pattern_w(struct archive *,
+                   const wchar_t *);
+/* Add exclusion pathname pattern from file. */
+__LA_DECL int  archive_match_exclude_pattern_from_file(struct archive *,
+                   const char *, int _nullSeparator);
+__LA_DECL int  archive_match_exclude_pattern_from_file_w(struct archive *,
+                   const wchar_t *, int _nullSeparator);
+/* Add inclusion pathname pattern. */
+__LA_DECL int  archive_match_include_pattern(struct archive *, const char *);
+__LA_DECL int  archive_match_include_pattern_w(struct archive *,
+                   const wchar_t *);
+/* Add inclusion pathname pattern from file. */
+__LA_DECL int  archive_match_include_pattern_from_file(struct archive *,
+                   const char *, int _nullSeparator);
+__LA_DECL int  archive_match_include_pattern_from_file_w(struct archive *,
+                   const wchar_t *, int _nullSeparator);
+/*
+ * How to get statistic information for inclusion patterns.
+ */
+/* Return the amount number of unmatched inclusion patterns. */
+__LA_DECL int  archive_match_path_unmatched_inclusions(struct archive *);
+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
+ * Return ARCHIVE_EOF if there is no inclusion pattern. */
+__LA_DECL int  archive_match_path_unmatched_inclusions_next(
+                   struct archive *, const char **);
+__LA_DECL int  archive_match_path_unmatched_inclusions_next_w(
+                   struct archive *, const wchar_t **);
+
+/*
+ * Test if a file is excluded by its time stamp.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int  archive_match_time_excluded(struct archive *,
+                   struct archive_entry *);
+
+/*
+ * Flags to tell a matching type of time stamps. These are used for
+ * following functinos.
+ */
+/* Time flag: mtime to be tested. */
+#define ARCHIVE_MATCH_MTIME    (0x0100)
+/* Time flag: ctime to be tested. */
+#define ARCHIVE_MATCH_CTIME    (0x0200)
+/* Comparison flag: Match the time if it is newer than. */
+#define ARCHIVE_MATCH_NEWER    (0x0001)
+/* Comparison flag: Match the time if it is older than. */
+#define ARCHIVE_MATCH_OLDER    (0x0002)
+/* Comparison flag: Match the time if it is equal to. */
+#define ARCHIVE_MATCH_EQUAL    (0x0010)
+/* Set inclusion time. */
+__LA_DECL int  archive_match_include_time(struct archive *, int _flag,
+                   time_t _sec, long _nsec);
+/* Set inclusion time by a date string. */
+__LA_DECL int  archive_match_include_date(struct archive *, int _flag,
+                   const char *_datestr);
+__LA_DECL int  archive_match_include_date_w(struct archive *, int _flag,
+                   const wchar_t *_datestr);
+/* Set inclusion time by a particluar file. */
+__LA_DECL int  archive_match_include_file_time(struct archive *,
+                   int _flag, const char *_pathname);
+__LA_DECL int  archive_match_include_file_time_w(struct archive *,
+                   int _flag, const wchar_t *_pathname);
+/* Add exclusion entry. */
+__LA_DECL int  archive_match_exclude_entry(struct archive *,
+                   int _flag, struct archive_entry *);
+
+/*
+ * Test if a file is excluded by its uid ,gid, uname or gname.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int  archive_match_owner_excluded(struct archive *,
+                   struct archive_entry *);
+/* Add inclusion uid, gid, uname and gname. */
+__LA_DECL int  archive_match_include_uid(struct archive *, __LA_INT64_T);
+__LA_DECL int  archive_match_include_gid(struct archive *, __LA_INT64_T);
+__LA_DECL int  archive_match_include_uname(struct archive *, const char *);
+__LA_DECL int  archive_match_include_uname_w(struct archive *,
+                   const wchar_t *);
+__LA_DECL int  archive_match_include_gname(struct archive *, const char *);
+__LA_DECL int  archive_match_include_gname_w(struct archive *,
+                   const wchar_t *);
+
 #ifdef __cplusplus
 }
 #endif
 
 /* These are meaningless outside of this header. */
 #undef __LA_DECL
-#undef __LA_GID_T
-#undef __LA_UID_T
 
 /* These need to remain defined because they're used in the
  * callback type definitions.  XXX Fix this.  This is ugly. XXX */
index 4747a4c..bf4b610 100644 (file)
@@ -52,6 +52,9 @@ static int    acl_special(struct archive_acl *acl,
                    int type, int permset, int tag);
 static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
                    int type, int permset, int tag, int id);
+static int     archive_acl_add_entry_len_l(struct archive_acl *acl,
+                   int type, int permset, int tag, int id, const char *name,
+                   size_t len, struct archive_string_conv *sc);
 static int     isint_w(const wchar_t *start, const wchar_t *end, int *result);
 static int     ismode_w(const wchar_t *start, const wchar_t *end, int *result);
 static void    next_field_w(const wchar_t **wp, const wchar_t **start,
@@ -65,7 +68,7 @@ static int    isint(const char *start, const char *end, int *result);
 static int     ismode(const char *start, const char *end, int *result);
 static void    next_field(const char **p, const char **start,
                    const char **end, char *sep);
-static int     prefix(const char *start, const char *end,
+static int     prefix_c(const char *start, const char *end,
                    const char *test);
 static void    append_entry(char **p, const char *prefix, int tag,
                    const char *name, int perm, int id);
@@ -152,7 +155,7 @@ archive_acl_add_entry_w_len(struct archive_acl *acl,
        return ARCHIVE_OK;
 }
 
-int
+static int
 archive_acl_add_entry_len_l(struct archive_acl *acl,
     int type, int permset, int tag, int id, const char *name, size_t len,
     struct archive_string_conv *sc)
@@ -419,8 +422,11 @@ archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int
        *permset = acl->acl_p->permset;
        *tag = acl->acl_p->tag;
        *id = acl->acl_p->id;
-       if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0)
+       if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
+               if (errno == ENOMEM)
+                       return (ARCHIVE_FATAL);
                *name = NULL;
+       }
        acl->acl_p = acl->acl_p->next;
        return (ARCHIVE_OK);
 }
@@ -438,7 +444,7 @@ archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
        const wchar_t *prefix;
        wchar_t separator;
        struct archive_acl_entry *ap;
-       int id;
+       int id, r;
        wchar_t *wp;
 
        if (acl->acl_text_w != NULL) {
@@ -458,9 +464,11 @@ archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
                                length += 8; /* "default:" */
                        length += 5; /* tag name */
                        length += 1; /* colon */
-                       if (archive_mstring_get_wcs(a, &ap->name, &wname) == 0 &&
-                           wname != NULL)
+                       r = archive_mstring_get_wcs(a, &ap->name, &wname);
+                       if (r == 0 && wname != NULL)
                                length += wcslen(wname);
+                       else if (r < 0 && errno == ENOMEM)
+                               return (NULL);
                        else
                                length += sizeof(uid_t) * 3 + 1;
                        length ++; /* colon */
@@ -484,7 +492,7 @@ archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
        /* Now, allocate the string and actually populate it. */
        wp = acl->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
        if (wp == NULL)
-               __archive_errx(1, "No memory to generate the text version of the ACL");
+               return (NULL);
        count = 0;
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
                append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
@@ -499,16 +507,19 @@ archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
 
                ap = acl->acl_head;
                while (ap != NULL) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
-                               archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
-                               *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++;
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+                               r = archive_mstring_get_wcs(a, &ap->name, &wname);
+                               if (r == 0) {
+                                       *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++;
+                               } else if (r < 0 && errno == ENOMEM)
+                                       return (NULL);
                        }
                        ap = ap->next;
                }
@@ -523,17 +534,20 @@ archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
                ap = acl->acl_head;
                count = 0;
                while (ap != NULL) {
-                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 &&
-                               archive_mstring_get_wcs(a, &ap->name, &wname) == 0) {
-                               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 ++;
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+                               r = archive_mstring_get_wcs(a, &ap->name, &wname);
+                               if (r == 0) {
+                                       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 ++;
+                               } else if (r < 0 && errno == ENOMEM)
+                                       return (NULL);
                        }
                        ap = ap->next;
                }
@@ -672,7 +686,7 @@ archive_acl_text_l(struct archive_acl *acl, int flags,
        /* Now, allocate the string and actually populate it. */
        p = acl->acl_text = (char *)malloc(length);
        if (p == NULL)
-               __archive_errx(1, "No memory to generate the text version of the ACL");
+               return (-1);
        count = 0;
        if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
                append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
@@ -1088,7 +1102,7 @@ archive_acl_parse_l(struct archive_acl *acl,
                        type = default_type;
 
                name.start = name.end = NULL;
-               if (prefix(field[0].start, field[0].end, "user")) {
+               if (prefix_c(field[0].start, field[0].end, "user")) {
                        if (!ismode(field[2].start, field[2].end, &permset))
                                return (ARCHIVE_WARN);
                        if (id != -1 || field[1].start < field[1].end) {
@@ -1096,7 +1110,7 @@ archive_acl_parse_l(struct archive_acl *acl,
                                name = field[1];
                        } else
                                tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-               } else if (prefix(field[0].start, field[0].end, "group")) {
+               } else if (prefix_c(field[0].start, field[0].end, "group")) {
                        if (!ismode(field[2].start, field[2].end, &permset))
                                return (ARCHIVE_WARN);
                        if (id != -1 || field[1].start < field[1].end) {
@@ -1104,7 +1118,7 @@ archive_acl_parse_l(struct archive_acl *acl,
                                name = field[1];
                        } else
                                tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-               } else if (prefix(field[0].start, field[0].end, "other")) {
+               } else if (prefix_c(field[0].start, field[0].end, "other")) {
                        if (fields == 2
                            && field[1].start < field[1].end
                            && ismode(field[1].start, field[1].end, &permset)) {
@@ -1117,7 +1131,7 @@ archive_acl_parse_l(struct archive_acl *acl,
                        } else
                                return (ARCHIVE_WARN);
                        tag = ARCHIVE_ENTRY_ACL_OTHER;
-               } else if (prefix(field[0].start, field[0].end, "mask")) {
+               } else if (prefix_c(field[0].start, field[0].end, "mask")) {
                        if (fields == 2
                            && field[1].start < field[1].end
                            && ismode(field[1].start, field[1].end, &permset)) {
@@ -1246,7 +1260,7 @@ next_field(const char **p, const char **start,
  * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
  */
 static int
-prefix(const char *start, const char *end, const char *test)
+prefix_c(const char *start, const char *end, const char *test)
 {
        if (start == end)
                return (0);
index 9122955..c695e58 100644 (file)
@@ -94,6 +94,7 @@ archive_handle_type_name(unsigned m)
        case ARCHIVE_READ_MAGIC:        return ("archive_read");
        case ARCHIVE_WRITE_DISK_MAGIC:  return ("archive_write_disk");
        case ARCHIVE_READ_DISK_MAGIC:   return ("archive_read_disk");
+       case ARCHIVE_MATCH_MAGIC:       return ("archive_match");
        default:                        return NULL;
        }
 }
index edc90ee..68123b0 100644 (file)
@@ -126,8 +126,8 @@ archive_be64enc(void *pp, uint64_t u)
 {
        unsigned char *p = (unsigned char *)pp;
 
-       archive_be32enc(p, u >> 32);
-       archive_be32enc(p + 4, u & 0xffffffff);
+       archive_be32enc(p, (uint32_t)(u >> 32));
+       archive_be32enc(p + 4, (uint32_t)(u & 0xffffffff));
 }
 
 static inline void
@@ -155,8 +155,8 @@ archive_le64enc(void *pp, uint64_t u)
 {
        unsigned char *p = (unsigned char *)pp;
 
-       archive_le32enc(p, u & 0xffffffff);
-       archive_le32enc(p + 4, u >> 32);
+       archive_le32enc(p, (uint32_t)(u & 0xffffffff));
+       archive_le32enc(p + 4, (uint32_t)(u >> 32));
 }
 
 #endif
index 10e3c34..f77f385 100644 (file)
@@ -23,9 +23,9 @@
 .\" 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.18 2008/05/26 17:00:22 kientzle Exp $
+.\" $FreeBSD$
 .\"
-.Dd Feburary 22, 2010
+.Dd Feburary 2, 2012
 .Dt ARCHIVE_ENTRY 3
 .Os
 .Sh NAME
@@ -34,6 +34,8 @@
 .Nm archive_entry_free ,
 .Nm archive_entry_new ,
 .Nd functions for managing archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft "struct archive_entry *"
index cbdda8a..b531c77 100644 (file)
@@ -375,8 +375,11 @@ archive_entry_fflags_text(struct archive_entry *entry)
        char *p;
 
        if (archive_mstring_get_mbs(entry->archive,
-           &entry->ae_fflags_text, &f) == 0 && f != NULL)
-               return (f);
+           &entry->ae_fflags_text, &f) == 0) {
+               if (f != NULL)
+                       return (f);
+       } else if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
 
        if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
                return (NULL);
@@ -390,6 +393,8 @@ archive_entry_fflags_text(struct archive_entry *entry)
        if (archive_mstring_get_mbs(entry->archive,
            &entry->ae_fflags_text, &f) == 0)
                return (f);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -405,6 +410,8 @@ archive_entry_gname(struct archive_entry *entry)
        const char *p;
        if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -414,6 +421,8 @@ archive_entry_gname_w(struct archive_entry *entry)
        const wchar_t *p;
        if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -428,9 +437,13 @@ const char *
 archive_entry_hardlink(struct archive_entry *entry)
 {
        const char *p;
-       if ((entry->ae_set & AE_SET_HARDLINK) && archive_mstring_get_mbs(
+       if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+               return (NULL);
+       if (archive_mstring_get_mbs(
            entry->archive, &entry->ae_hardlink, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -438,9 +451,13 @@ const wchar_t *
 archive_entry_hardlink_w(struct archive_entry *entry)
 {
        const wchar_t *p;
-       if ((entry->ae_set & AE_SET_HARDLINK) && archive_mstring_get_wcs(
+       if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+               return (NULL);
+       if (archive_mstring_get_wcs(
            entry->archive, &entry->ae_hardlink, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -511,6 +528,8 @@ archive_entry_pathname(struct archive_entry *entry)
        if (archive_mstring_get_mbs(
            entry->archive, &entry->ae_pathname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -521,6 +540,8 @@ archive_entry_pathname_w(struct archive_entry *entry)
        if (archive_mstring_get_wcs(
            entry->archive, &entry->ae_pathname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -584,6 +605,8 @@ archive_entry_sourcepath(struct archive_entry *entry)
        if (archive_mstring_get_mbs(
            entry->archive, &entry->ae_sourcepath, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -601,9 +624,13 @@ const char *
 archive_entry_symlink(struct archive_entry *entry)
 {
        const char *p;
-       if ((entry->ae_set & AE_SET_SYMLINK) && archive_mstring_get_mbs(
+       if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+               return (NULL);
+       if (archive_mstring_get_mbs(
            entry->archive, &entry->ae_symlink, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -611,9 +638,13 @@ const wchar_t *
 archive_entry_symlink_w(struct archive_entry *entry)
 {
        const wchar_t *p;
-       if ((entry->ae_set & AE_SET_SYMLINK) && archive_mstring_get_wcs(
+       if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+               return (NULL);
+       if (archive_mstring_get_wcs(
            entry->archive, &entry->ae_symlink, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -641,6 +672,8 @@ archive_entry_uname(struct archive_entry *entry)
        const char *p;
        if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -650,6 +683,8 @@ archive_entry_uname_w(struct archive_entry *entry)
        const wchar_t *p;
        if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
                return (p);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (NULL);
 }
 
@@ -730,6 +765,8 @@ archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
        if (archive_mstring_update_utf8(entry->archive,
            &entry->ae_gname, name) == 0)
                return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (0);
 }
 
@@ -796,6 +833,8 @@ archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *targ
        if (archive_mstring_update_utf8(entry->archive,
            &entry->ae_hardlink, target) == 0)
                return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (0);
 }
 
@@ -932,7 +971,11 @@ archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
        else
                r = archive_mstring_update_utf8(entry->archive,
                    &entry->ae_hardlink, target);
-       return ((r == 0)? 1: 0);
+       if (r == 0)
+               return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
+       return (0);
 }
 
 int
@@ -1005,6 +1048,8 @@ archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name
        if (archive_mstring_update_utf8(entry->archive,
            &entry->ae_pathname, name) == 0)
                return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (0);
 }
 
@@ -1115,6 +1160,8 @@ archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkn
        if (archive_mstring_update_utf8(entry->archive,
            &entry->ae_symlink, linkname) == 0)
                return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (0);
 }
 
@@ -1164,6 +1211,8 @@ archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
        if (archive_mstring_update_utf8(entry->archive,
            &entry->ae_uname, name) == 0)
                return (1);
+       if (errno == ENOMEM)
+               __archive_errx(1, "No memory");
        return (0);
 }
 
@@ -1269,7 +1318,12 @@ int
 archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
     int *permset, int *tag, int *id, const char **name)
 {
-       return archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name);
+       int r;
+       r = archive_acl_next(entry->archive, &entry->acl, want_type, type,
+               permset, tag, id, name);
+       if (r == ARCHIVE_FATAL && errno == ENOMEM)
+               __archive_errx(1, "No memory");
+       return (r);
 }
 
 /*
@@ -1279,7 +1333,11 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
 const wchar_t *
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
-       return archive_acl_text_w(entry->archive, &entry->acl, flags);
+       const wchar_t *r;
+       r = archive_acl_text_w(entry->archive, &entry->acl, flags);
+       if (r == NULL && errno == ENOMEM)
+               __archive_errx(1, "No memory");
+       return (r);
 }
 
 const char *
@@ -1288,7 +1346,7 @@ archive_entry_acl_text(struct archive_entry *entry, int flags)
        const char *p;
        if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0
            && errno == ENOMEM)
-               return (NULL);
+               __archive_errx(1, "No memory");
        return (p);
 }
 
index 7a1e389..5fd7cab 100644 (file)
@@ -29,7 +29,7 @@
 #define        ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define        ARCHIVE_VERSION_NUMBER 3000003
+#define        ARCHIVE_VERSION_NUMBER 3000004
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
 #include <windows.h>
 #endif
 
-/* Get appropriate definitions of standard POSIX-style types. */
-/* These should match the types used in 'struct stat' */
+/* Get a suitable 64-bit integer type. */
 #if defined(_WIN32) && !defined(__CYGWIN__)
-#define        __LA_INT64_T    __int64
-# if defined(__BORLANDC__)
-#  define      __LA_UID_T      uid_t  /* Remove in libarchive 3.2 */
-#  define      __LA_GID_T      gid_t  /* Remove in libarchive 3.2 */
-#  define      __LA_DEV_T      dev_t
-#  define      __LA_MODE_T     mode_t
-# else
-#  define      __LA_UID_T      short  /* Remove in libarchive 3.2 */
-#  define      __LA_GID_T      short  /* Remove in libarchive 3.2 */
-#  define      __LA_DEV_T      unsigned int
-#  define      __LA_MODE_T     unsigned short
-# endif
+# define       __LA_INT64_T    __int64
 #else
 #include <unistd.h>
 # if defined(_SCO_DS)
 # else
 #  define      __LA_INT64_T    int64_t
 # endif
-# define       __LA_UID_T      uid_t /* Remove in libarchive 3.2 */
-# define       __LA_GID_T      gid_t /* Remove in libarchive 3.2 */
-# define       __LA_DEV_T      dev_t
-# define       __LA_MODE_T     mode_t
 #endif
 
-/*
- * Remove this for libarchive 3.2, since ino_t is no longer used.
- */
-#define        __LA_INO_T      ino_t
-
+/* Get a suitable definition for mode_t */
+#if ARCHIVE_VERSION_NUMBER >= 3999000
+/* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */
+# define       __LA_MODE_T     int
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__)
+# define       __LA_MODE_T     unsigned short
+#else
+# define       __LA_MODE_T     mode_t
+#endif
 
 /*
  * On Windows, define LIBARCHIVE_STATIC if you're building or using a
@@ -149,14 +137,18 @@ struct archive_entry;
  * portable values to platform-native values when entries are read from
  * or written to disk.
  */
-#define        AE_IFMT         0170000
-#define        AE_IFREG        0100000
-#define        AE_IFLNK        0120000
-#define        AE_IFSOCK       0140000
-#define        AE_IFCHR        0020000
-#define        AE_IFBLK        0060000
-#define        AE_IFDIR        0040000
-#define        AE_IFIFO        0010000
+/*
+ * In libarchive 4.0, we can drop the casts here.
+ * They're needed to work around Borland C's broken mode_t.
+ */
+#define AE_IFMT                ((__LA_MODE_T)0170000)
+#define AE_IFREG       ((__LA_MODE_T)0100000)
+#define AE_IFLNK       ((__LA_MODE_T)0120000)
+#define AE_IFSOCK      ((__LA_MODE_T)0140000)
+#define AE_IFCHR       ((__LA_MODE_T)0020000)
+#define AE_IFBLK       ((__LA_MODE_T)0060000)
+#define AE_IFDIR       ((__LA_MODE_T)0040000)
+#define AE_IFIFO       ((__LA_MODE_T)0010000)
 
 /*
  * Basic object manipulation
@@ -321,7 +313,10 @@ __LA_DECL int      archive_entry_update_uname_utf8(struct archive_entry *, const char
  * manipulate archives on systems different than the ones they were
  * created on.
  *
- * TODO: On Linux, provide both stat32 and stat64 versions of these functions.
+ * TODO: On Linux and other LFS systems, provide both stat32 and
+ * stat64 versions of these functions and all of the macro glue so
+ * that archive_entry_stat is magically defined to
+ * archive_entry_stat32 or archive_entry_stat64 as appropriate.
  */
 __LA_DECL const struct stat    *archive_entry_stat(struct archive_entry *);
 __LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
index 8c4f0cd..f5c3377 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 21, 2010
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY_ACL 3
 .Os
 .Sh NAME
@@ -35,6 +35,8 @@
 .Nm archive_entry_acl_reset ,
 .Nm archive_entry_acl_text_w
 .Nd functions for manipulating Access Control Lists in archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft void
index a18144d..07f0d36 100644 (file)
@@ -362,7 +362,7 @@ insert_entry(struct archive_entry_linkresolver *res,
        if (res->number_entries > res->number_buckets * 2)
                grow_hash(res);
 
-       hash = archive_entry_dev(entry) ^ archive_entry_ino64(entry);
+       hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
        bucket = hash & (res->number_buckets - 1);
 
        /* If we could allocate the entry, record it. */
index a34b095..8c19fdd 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 20, 2010
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY_LINKIFY 3
 .Os
 .Sh NAME
@@ -33,7 +33,7 @@
 .Nm archive_entry_linkify
 .Nd hardlink resolver functions
 .Sh LIBRARY
-.Lb libarchive
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft struct archive_entry_linkresolver *
index 621f655..51c8b8c 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 22, 2010
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY_PATHS 3
 .Os
 .Sh NAME
@@ -51,6 +51,8 @@
 .Nm archive_entry_copy_symlink_w ,
 .Nm archve_entry_update_symlink_utf8
 .Nd functions for manipulating path names in archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft const char *
index 164af97..5b7b5d9 100644 (file)
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 22, 2010
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY_PERMS 3
 .Os
 .Sh NAME
@@ -52,6 +52,8 @@
 .Nm archive_entry_copy_fflags_text ,
 .Nm archive_entry_copy_fflags_text_w
 .Nd functions for manipulating ownership and permissions in archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft gid_t
index 36a7efb..84a4ea1 100644 (file)
@@ -22,8 +22,8 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 12, 2008
-.Dt ARCHIVE_ENTRY 3
+.Dd February 2, 2012
+.Dt ARCHIVE_ENTRY_STAT 3
 .Os
 .Sh NAME
 .Nm archive_entry_stat ,
@@ -56,6 +56,8 @@
 .Nm archive_entry_rdevminor ,
 .Nm archive_entry_set_rdevminor ,
 .Nd accessor functions for manipulating archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft const struct stat *
index 65ff1cf..71a407b 100644 (file)
@@ -70,12 +70,12 @@ archive_entry_stat(struct archive_entry *entry)
        st->st_ctime = archive_entry_ctime(entry);
        st->st_mtime = archive_entry_mtime(entry);
        st->st_dev = archive_entry_dev(entry);
-       st->st_gid = archive_entry_gid(entry);
-       st->st_uid = archive_entry_uid(entry);
-       st->st_ino = archive_entry_ino64(entry);
+       st->st_gid = (gid_t)archive_entry_gid(entry);
+       st->st_uid = (uid_t)archive_entry_uid(entry);
+       st->st_ino = (ino_t)archive_entry_ino64(entry);
        st->st_nlink = archive_entry_nlink(entry);
        st->st_rdev = archive_entry_rdev(entry);
-       st->st_size = archive_entry_size(entry);
+       st->st_size = (off_t)archive_entry_size(entry);
        st->st_mode = archive_entry_mode(entry);
 
        /*
index 85a8209..17c658a 100644 (file)
@@ -23,9 +23,9 @@
 .\" 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.18 2008/05/26 17:00:22 kientzle Exp $
+.\" $FreeBSD$
 .\"
-.Dd February 21, 2010
+.Dd February 2, 2012
 .Dt ARCHIVE_ENTRY_TIME 3
 .Os
 .Sh NAME
@@ -50,6 +50,8 @@
 .Nm archive_entry_set_mtime ,
 .Nm archive_entry_unset_mtime ,
 .Nd functions for manipulating times in archive entry descriptions
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive_entry.h
 .Ft time_t
similarity index 99%
rename from contrib/libarchive/tar/getdate.c
rename to contrib/libarchive/libarchive/archive_getdate.c
index 0e15d9c..f8b5a28 100644 (file)
@@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
 #include <time.h>
 
 /* This file defines a single public function. */
-time_t get_date(time_t now, char *);
+time_t __archive_get_date(time_t now, char *);
 
 /* Basic time units. */
 #define        EPOCH           1970
@@ -894,7 +894,7 @@ difftm (struct tm *a, struct tm *b)
  * TODO: tokens[] array should be dynamically sized.
  */
 time_t
-get_date(time_t now, char *p)
+__archive_get_date(time_t now, char *p)
 {
        struct token    tokens[256];
        struct gdstate  _gds;
diff --git a/contrib/libarchive/libarchive/archive_match.c b/contrib/libarchive/libarchive/archive_match.c
new file mode 100644 (file)
index 0000000..6b533e6
--- /dev/null
@@ -0,0 +1,1836 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+
+struct match {
+       struct match            *next;
+       int                      matches;
+       struct archive_mstring   pattern;
+};
+
+struct match_list {
+       struct match            *first;
+       struct match            **last;
+       int                      count;
+       int                      unmatched_count;
+       struct match            *unmatched_next;
+       int                      unmatched_eof;
+};
+
+struct match_file {
+       struct archive_rb_node   node;
+       struct match_file       *next;
+       struct archive_mstring   pathname;
+       int                      flag;
+       time_t                   mtime_sec;
+       long                     mtime_nsec;
+       time_t                   ctime_sec;
+       long                     ctime_nsec;
+};
+
+struct entry_list {
+       struct match_file       *first;
+       struct match_file       **last;
+       int                      count;
+};
+
+struct id_array {
+       size_t                   size;/* Allocated size */
+       size_t                   count;
+       int64_t                 *ids;
+};
+
+#define PATTERN_IS_SET         1
+#define TIME_IS_SET            2
+#define ID_IS_SET              4
+
+struct archive_match {
+       struct archive           archive;
+
+       /* exclusion/inclusion set flag. */
+       int                      setflag;
+
+       /*
+        * Matching filename patterns.
+        */
+       struct match_list        exclusions;
+       struct match_list        inclusions;
+
+       /*
+        * Matching time stamps.
+        */
+       time_t                   now;
+       int                      newer_mtime_filter;
+       time_t                   newer_mtime_sec;
+       long                     newer_mtime_nsec;
+       int                      newer_ctime_filter;
+       time_t                   newer_ctime_sec;
+       long                     newer_ctime_nsec;
+       int                      older_mtime_filter;
+       time_t                   older_mtime_sec;
+       long                     older_mtime_nsec;
+       int                      older_ctime_filter;
+       time_t                   older_ctime_sec;
+       long                     older_ctime_nsec;
+       /*
+        * Matching time stamps with its filename.
+        */
+       struct archive_rb_tree   exclusion_tree;
+       struct entry_list        exclusion_entry_list;
+
+       /*
+        * Matching file owners.
+        */
+       struct id_array          inclusion_uids;
+       struct id_array          inclusion_gids;
+       struct match_list        inclusion_unames;
+       struct match_list        inclusion_gnames;
+};
+
+static int     add_pattern_from_file(struct archive_match *,
+                   struct match_list *, int, const void *, int);
+static int     add_entry(struct archive_match *, int,
+                   struct archive_entry *);
+static int     add_owner_id(struct archive_match *, struct id_array *,
+                   int64_t);
+static int     add_owner_name(struct archive_match *, struct match_list *,
+                   int, const void *);
+static int     add_pattern_mbs(struct archive_match *, struct match_list *,
+                   const char *);
+static int     add_pattern_wcs(struct archive_match *, struct match_list *,
+                   const wchar_t *);
+static int     cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int     cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int     cmp_node_mbs(const struct archive_rb_node *,
+                   const struct archive_rb_node *);
+static int     cmp_node_wcs(const struct archive_rb_node *,
+                   const struct archive_rb_node *);
+static void    entry_list_add(struct entry_list *, struct match_file *);
+static void    entry_list_free(struct entry_list *);
+static void    entry_list_init(struct entry_list *);
+static int     error_nomem(struct archive_match *);
+static void    match_list_add(struct match_list *, struct match *);
+static void    match_list_free(struct match_list *);
+static void    match_list_init(struct match_list *);
+static int     match_list_unmatched_inclusions_next(struct archive_match *,
+                   struct match_list *, int, const void **);
+static int     match_owner_id(struct id_array *, int64_t);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int     match_owner_name_mbs(struct archive_match *,
+                   struct match_list *, const char *);
+#else
+static int     match_owner_name_wcs(struct archive_match *,
+                   struct match_list *, const wchar_t *);
+#endif
+static int     match_path_exclusion(struct archive_match *,
+                   struct match *, int, const void *);
+static int     match_path_inclusion(struct archive_match *,
+                   struct match *, int, const void *);
+static int     owner_excluded(struct archive_match *,
+                   struct archive_entry *);
+static int     path_excluded(struct archive_match *, int, const void *);
+static int     set_timefilter(struct archive_match *, int, time_t, long,
+                   time_t, long);
+static int     set_timefilter_pathname_mbs(struct archive_match *,
+                   int, const char *);
+static int     set_timefilter_pathname_wcs(struct archive_match *,
+                   int, const wchar_t *);
+static int     set_timefilter_date(struct archive_match *, int, const char *);
+static int     set_timefilter_date_w(struct archive_match *, int,
+                   const wchar_t *);
+static int     time_excluded(struct archive_match *,
+                   struct archive_entry *);
+static int     validate_time_flag(struct archive *, int, const char *);
+
+time_t __archive_get_date(time_t now, const char *);
+#define get_date __archive_get_date
+
+static const struct archive_rb_tree_ops rb_ops_mbs = {
+       cmp_node_mbs, cmp_key_mbs
+};
+
+static const struct archive_rb_tree_ops rb_ops_wcs = {
+       cmp_node_wcs, cmp_key_wcs
+};
+
+/*
+ * The matching logic here needs to be re-thought.  I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+static int
+error_nomem(struct archive_match *a)
+{
+       archive_set_error(&(a->archive), ENOMEM, "No memory");
+       a->archive.state = ARCHIVE_STATE_FATAL;
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Create an ARCHIVE_MATCH object.
+ */
+struct archive *
+archive_match_new(void)
+{
+       struct archive_match *a;
+
+       a = (struct archive_match *)calloc(1, sizeof(*a));
+       if (a == NULL)
+               return (NULL);
+       a->archive.magic = ARCHIVE_MATCH_MAGIC;
+       a->archive.state = ARCHIVE_STATE_NEW;
+       match_list_init(&(a->inclusions));
+       match_list_init(&(a->exclusions));
+       __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
+       entry_list_init(&(a->exclusion_entry_list));
+       match_list_init(&(a->inclusion_unames));
+       match_list_init(&(a->inclusion_gnames));
+       time(&a->now);
+       return (&(a->archive));
+}
+
+/*
+ * Free an ARCHIVE_MATCH object.
+ */
+int
+archive_match_free(struct archive *_a)
+{
+       struct archive_match *a;
+
+       if (_a == NULL)
+               return (ARCHIVE_OK);
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
+       a = (struct archive_match *)_a;
+       match_list_free(&(a->inclusions));
+       match_list_free(&(a->exclusions));
+       entry_list_free(&(a->exclusion_entry_list));
+       free(a->inclusion_uids.ids);
+       free(a->inclusion_gids.ids);
+       match_list_free(&(a->inclusion_unames));
+       match_list_free(&(a->inclusion_gnames));
+       free(a);
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Convenience function to perform all exclusion tests.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_excluded(struct archive *_a, struct archive_entry *entry)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
+
+       a = (struct archive_match *)_a;
+       if (entry == NULL) {
+               archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+               return (ARCHIVE_FAILED);
+       }
+
+       r = 0;
+       if (a->setflag & PATTERN_IS_SET) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+               r = path_excluded(a, 0, archive_entry_pathname_w(entry));
+#else
+               r = path_excluded(a, 1, archive_entry_pathname(entry));
+#endif
+               if (r != 0)
+                       return (r);
+       }
+
+       if (a->setflag & TIME_IS_SET) {
+               r = time_excluded(a, entry);
+               if (r != 0)
+                       return (r);
+       }
+
+       if (a->setflag & ID_IS_SET)
+               r = owner_excluded(a, entry);
+       return (r);
+}
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+archive_match_exclude_pattern(struct archive *_a, const char *pattern)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
+       a = (struct archive_match *)_a;
+
+       if (pattern == NULL || *pattern == '\0') {
+               archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+               return (ARCHIVE_FAILED);
+       }
+       if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+               return (r);
+       return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
+       a = (struct archive_match *)_a;
+
+       if (pattern == NULL || *pattern == L'\0') {
+               archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+               return (ARCHIVE_FAILED);
+       }
+       if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+               return (r);
+       return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_from_file(struct archive *_a,
+    const char *pathname, int nullSeparator)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
+       a = (struct archive_match *)_a;
+
+       return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
+               nullSeparator);
+}
+
+int
+archive_match_exclude_pattern_from_file_w(struct archive *_a,
+    const wchar_t *pathname, int nullSeparator)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
+       a = (struct archive_match *)_a;
+
+       return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
+               nullSeparator);
+}
+
+int
+archive_match_include_pattern(struct archive *_a, const char *pattern)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_pattern");
+       a = (struct archive_match *)_a;
+
+       if (pattern == NULL || *pattern == '\0') {
+               archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+               return (ARCHIVE_FAILED);
+       }
+       if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+               return (r);
+       return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
+       a = (struct archive_match *)_a;
+
+       if (pattern == NULL || *pattern == L'\0') {
+               archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+               return (ARCHIVE_FAILED);
+       }
+       if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+               return (r);
+       return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_from_file(struct archive *_a,
+    const char *pathname, int nullSeparator)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
+       a = (struct archive_match *)_a;
+
+       return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
+               nullSeparator);
+}
+
+int
+archive_match_include_pattern_from_file_w(struct archive *_a,
+    const wchar_t *pathname, int nullSeparator)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
+       a = (struct archive_match *)_a;
+
+       return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
+               nullSeparator);
+}
+
+/*
+ * Test functions for pathname patterns.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_path_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_path_excluded");
+
+       a = (struct archive_match *)_a;
+       if (entry == NULL) {
+               archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+               return (ARCHIVE_FAILED);
+       }
+
+       /* If we don't have exclusion/inclusion pattern set at all,
+        * the entry is always not excluded. */
+       if ((a->setflag & PATTERN_IS_SET) == 0)
+               return (0);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+#else
+       return (path_excluded(a, 1, archive_entry_pathname(entry)));
+#endif
+}
+
+/*
+ * Utilty functions to get statistic information for inclusion patterns.
+ */
+int
+archive_match_path_unmatched_inclusions(struct archive *_a)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
+       a = (struct archive_match *)_a;
+
+       return (a->inclusions.unmatched_count);
+}
+
+int
+archive_match_path_unmatched_inclusions_next(struct archive *_a,
+    const char **_p)
+{
+       struct archive_match *a;
+       const void *v;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
+       a = (struct archive_match *)_a;
+
+       r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
+       *_p = (const char *)v;
+       return (r);
+}
+
+int
+archive_match_path_unmatched_inclusions_next_w(struct archive *_a,
+    const wchar_t **_p)
+{
+       struct archive_match *a;
+       const void *v;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
+       a = (struct archive_match *)_a;
+
+       r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
+       *_p = (const wchar_t *)v;
+       return (r);
+}
+
+/*
+ * Add inclusion/exclusion patterns.
+ */
+static int
+add_pattern_mbs(struct archive_match *a, struct match_list *list,
+    const char *pattern)
+{
+       struct match *match;
+       size_t len;
+
+       match = calloc(1, sizeof(*match));
+       if (match == NULL)
+               return (error_nomem(a));
+       /* Both "foo/" and "foo" should match "foo/bar". */
+       len = strlen(pattern);
+       if (len && pattern[len - 1] == '/')
+               --len;
+       archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
+       match_list_add(list, match);
+       a->setflag |= PATTERN_IS_SET;
+       return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_wcs(struct archive_match *a, struct match_list *list,
+    const wchar_t *pattern)
+{
+       struct match *match;
+       size_t len;
+
+       match = calloc(1, sizeof(*match));
+       if (match == NULL)
+               return (error_nomem(a));
+       /* Both "foo/" and "foo" should match "foo/bar". */
+       len = wcslen(pattern);
+       if (len && pattern[len - 1] == L'/')
+               --len;
+       archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
+       match_list_add(list, match);
+       a->setflag |= PATTERN_IS_SET;
+       return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
+    int mbs, const void *pathname, int nullSeparator)
+{
+       struct archive *ar;
+       struct archive_entry *ae;
+       struct archive_string as;
+       const void *buff;
+       size_t size;
+       int64_t offset;
+       int r;
+
+       ar = archive_read_new(); 
+       if (ar == NULL) {
+               archive_set_error(&(a->archive), ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       r = archive_read_support_format_raw(ar);
+       if (r != ARCHIVE_OK) {
+               archive_copy_error(&(a->archive), ar);
+               archive_read_free(ar);
+               return (r);
+       }
+       if (mbs)
+               r = archive_read_open_filename(ar, pathname, 512*20);
+       else
+               r = archive_read_open_filename_w(ar, pathname, 512*20);
+       if (r != ARCHIVE_OK) {
+               archive_copy_error(&(a->archive), ar);
+               archive_read_free(ar);
+               return (r);
+       }
+       r = archive_read_next_header(ar, &ae);
+       if (r != ARCHIVE_OK) {
+               archive_copy_error(&(a->archive), ar);
+               archive_read_free(ar);
+               return (r);
+       }
+
+       archive_string_init(&as);
+
+       while ((r = archive_read_data_block(ar, &buff, &size, &offset))
+           == ARCHIVE_OK) {
+               const char *b = (const char *)buff;
+
+               while (size) {
+                       const char *s = (const char *)b;
+                       size_t length = 0;
+                       int found_separator = 0;
+
+                       while (length < size) {
+                               if (nullSeparator) {
+                                       if (*b == '\0') {
+                                               found_separator = 1;
+                                               break;
+                                       }
+                               } else {
+                                       if (*b == 0x0d || *b == 0x0a) {
+                                               found_separator = 1;
+                                               break;
+                                       }
+                               }
+                               b++;
+                               length++;
+                       }
+                       if (!found_separator) {
+                               archive_strncat(&as, s, length);
+                               /* Read next data block. */
+                               break;
+                       }
+                       b++;
+                       size -= length + 1;
+                       archive_strncat(&as, s, length);
+
+                       /* If the line is not empty, add the pattern. */
+                       if (archive_strlen(&as) > 0) {
+                               /* Add pattern. */
+                               r = add_pattern_mbs(a, mlist, as.s);
+                               if (r != ARCHIVE_OK) {
+                                       archive_read_free(ar);
+                                       archive_string_free(&as);
+                                       return (r);
+                               }
+                               archive_string_empty(&as);
+                       }
+               }
+       }
+
+       /* If something error happend, report it immediately. */ 
+       if (r < ARCHIVE_OK) {
+               archive_copy_error(&(a->archive), ar);
+               archive_read_free(ar);
+               archive_string_free(&as);
+               return (r);
+       }
+
+       /* If the line is not empty, add the pattern. */
+       if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
+               /* Add pattern. */
+               r = add_pattern_mbs(a, mlist, as.s);
+               if (r != ARCHIVE_OK) {
+                       archive_read_free(ar);
+                       archive_string_free(&as);
+                       return (r);
+               }
+       }
+       archive_read_free(ar);
+       archive_string_free(&as);
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Test if pathname is excluded by inclusion/exclusion patterns.
+ */
+static int
+path_excluded(struct archive_match *a, int mbs, const void *pathname)
+{
+       struct match *match;
+       struct match *matched;
+       int r;
+
+       if (a == NULL)
+               return (0);
+
+       /* Mark off any unmatched inclusions. */
+       /* In particular, if a filename does appear in the archive and
+        * is explicitly included and excluded, then we don't report
+        * it as missing even though we don't extract it.
+        */
+       matched = NULL;
+       for (match = a->inclusions.first; match != NULL;
+           match = match->next){
+               if (match->matches == 0 &&
+                   (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+                       if (r < 0)
+                               return (r);
+                       a->inclusions.unmatched_count--;
+                       match->matches++;
+                       matched = match;
+               }
+       }
+
+       /* Exclusions take priority */
+       for (match = a->exclusions.first; match != NULL;
+           match = match->next){
+               r = match_path_exclusion(a, match, mbs, pathname);
+               if (r)
+                       return (r);
+       }
+
+       /* It's not excluded and we found an inclusion above, so it's
+        * included. */
+       if (matched != NULL)
+               return (0);
+
+
+       /* We didn't find an unmatched inclusion, check the remaining ones. */
+       for (match = a->inclusions.first; match != NULL;
+           match = match->next){
+               /* We looked at previously-unmatched inclusions already. */
+               if (match->matches > 0 &&
+                   (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+                       if (r < 0)
+                               return (r);
+                       match->matches++;
+                       return (0);
+               }
+       }
+
+       /* If there were inclusions, default is to exclude. */
+       if (a->inclusions.first != NULL)
+           return (1);
+
+       /* No explicit inclusions, default is to match. */
+       return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+static int
+match_path_exclusion(struct archive_match *a, struct match *m,
+    int mbs, const void *pn)
+{
+       int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
+       int r;
+
+       if (mbs) {
+               const char *p;
+               r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+               if (r == 0)
+                       return (archive_pathmatch(p, (const char *)pn, flag));
+       } else {
+               const wchar_t *p;
+               r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+               if (r == 0)
+                       return (archive_pathmatch_w(p, (const wchar_t *)pn,
+                               flag));
+       }
+       if (errno == ENOMEM)
+               return (error_nomem(a));
+       return (0);
+}
+
+/*
+ * Again, mimic gtar:  inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+static int
+match_path_inclusion(struct archive_match *a, struct match *m,
+    int mbs, const void *pn)
+{
+       int flag = PATHMATCH_NO_ANCHOR_END;
+       int r;
+
+       if (mbs) {
+               const char *p;
+               r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+               if (r == 0)
+                       return (archive_pathmatch(p, (const char *)pn, flag));
+       } else {
+               const wchar_t *p;
+               r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+               if (r == 0)
+                       return (archive_pathmatch_w(p, (const wchar_t *)pn,
+                               flag));
+       }
+       if (errno == ENOMEM)
+               return (error_nomem(a));
+       return (0);
+}
+
+static void
+match_list_init(struct match_list *list)
+{
+       list->first = NULL;
+       list->last = &(list->first);
+       list->count = 0;
+}
+
+static void
+match_list_free(struct match_list *list)
+{
+       struct match *p, *q;
+
+       for (p = list->first; p != NULL; ) {
+               q = p;
+               p = p->next;
+               archive_mstring_clean(&(q->pattern));
+               free(q);
+       }
+}
+
+static void
+match_list_add(struct match_list *list, struct match *m)
+{
+       *list->last = m;
+       list->last = &(m->next);
+       list->count++;
+       list->unmatched_count++;
+}
+
+static int
+match_list_unmatched_inclusions_next(struct archive_match *a,
+    struct match_list *list, int mbs, const void **vp)
+{
+       struct match *m;
+
+       *vp = NULL;
+       if (list->unmatched_eof) {
+               list->unmatched_eof = 0;
+               return (ARCHIVE_EOF);
+       }
+       if (list->unmatched_next == NULL) {
+               if (list->unmatched_count == 0)
+                       return (ARCHIVE_EOF);
+               list->unmatched_next = list->first;
+       }
+
+       for (m = list->unmatched_next; m != NULL; m = m->next) {
+               int r;
+
+               if (m->matches)
+                       continue;
+               if (mbs) {
+                       const char *p;
+                       r = archive_mstring_get_mbs(&(a->archive),
+                               &(m->pattern), &p);
+                       if (r < 0 && errno == ENOMEM)
+                               return (error_nomem(a));
+                       if (p == NULL)
+                               p = "";
+                       *vp = p;
+               } else {
+                       const wchar_t *p;
+                       r = archive_mstring_get_wcs(&(a->archive),
+                               &(m->pattern), &p);
+                       if (r < 0 && errno == ENOMEM)
+                               return (error_nomem(a));
+                       if (p == NULL)
+                               p = L"";
+                       *vp = p;
+               }
+               list->unmatched_next = m->next;
+               if (list->unmatched_next == NULL)
+                       /* To return EOF next time. */
+                       list->unmatched_eof = 1;
+               return (ARCHIVE_OK);
+       }
+       list->unmatched_next = NULL;
+       return (ARCHIVE_EOF);
+}
+
+/*
+ * Utility functions to manage inclusion timestamps.
+ */
+int
+archive_match_include_time(struct archive *_a, int flag, time_t sec,
+    long nsec)
+{
+       int r;
+
+       r = validate_time_flag(_a, flag, "archive_match_include_time");
+       if (r != ARCHIVE_OK)
+               return (r);
+       return set_timefilter((struct archive_match *)_a, flag,
+                       sec, nsec, sec, nsec);
+}
+
+int
+archive_match_include_date(struct archive *_a, int flag,
+    const char *datestr)
+{
+       int r;
+
+       r = validate_time_flag(_a, flag, "archive_match_include_date");
+       if (r != ARCHIVE_OK)
+               return (r);
+       return set_timefilter_date((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_date_w(struct archive *_a, int flag,
+    const wchar_t *datestr)
+{
+       int r;
+
+       r = validate_time_flag(_a, flag, "archive_match_include_date_w");
+       if (r != ARCHIVE_OK)
+               return (r);
+
+       return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_file_time(struct archive *_a, int flag,
+    const char *pathname)
+{
+       int r;
+
+       r = validate_time_flag(_a, flag, "archive_match_include_file_time");
+       if (r != ARCHIVE_OK)
+               return (r);
+       return set_timefilter_pathname_mbs((struct archive_match *)_a,
+                       flag, pathname);
+}
+
+int
+archive_match_include_file_time_w(struct archive *_a, int flag,
+    const wchar_t *pathname)
+{
+       int r;
+
+       r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
+       if (r != ARCHIVE_OK)
+               return (r);
+       return set_timefilter_pathname_wcs((struct archive_match *)_a,
+                       flag, pathname);
+}
+
+int
+archive_match_exclude_entry(struct archive *_a, int flag,
+    struct archive_entry *entry)
+{
+       struct archive_match *a;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
+       a = (struct archive_match *)_a;
+
+       if (entry == NULL) {
+               archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+               return (ARCHIVE_FAILED);
+       }
+       r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
+       if (r != ARCHIVE_OK)
+               return (r);
+       return (add_entry(a, flag, entry));
+}
+
+/*
+ * Test function for time stamps.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_time_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
+
+       a = (struct archive_match *)_a;
+       if (entry == NULL) {
+               archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+               return (ARCHIVE_FAILED);
+       }
+
+       /* If we don't have inclusion time set at all, the entry is always
+        * not excluded. */
+       if ((a->setflag & TIME_IS_SET) == 0)
+               return (0);
+       return (time_excluded(a, entry));
+}
+
+static int
+validate_time_flag(struct archive *_a, int flag, const char *_fn)
+{
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, _fn);
+
+       /* Check a type of time. */
+       if (flag &
+          ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
+               archive_set_error(_a, EINVAL, "Invalid time flag");
+               return (ARCHIVE_FAILED);
+       }
+       if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
+               archive_set_error(_a, EINVAL, "No time flag");
+               return (ARCHIVE_FAILED);
+       }
+
+       /* Check a type of comparison. */
+       if (flag &
+          ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+                       | ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
+               archive_set_error(_a, EINVAL, "Invalid comparison flag");
+               return (ARCHIVE_FAILED);
+       }
+       if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+           | ARCHIVE_MATCH_EQUAL)) == 0) {
+               archive_set_error(_a, EINVAL, "No comparison flag");
+               return (ARCHIVE_FAILED);
+       }
+
+       return (ARCHIVE_OK);
+}
+
+#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
+       ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
+static int
+set_timefilter(struct archive_match *a, int timetype,
+    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
+{
+       if (timetype & ARCHIVE_MATCH_MTIME) {
+               if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+                       a->newer_mtime_filter = timetype;
+                       a->newer_mtime_sec = mtime_sec;
+                       a->newer_mtime_nsec = mtime_nsec;
+                       a->setflag |= TIME_IS_SET;
+               }
+               if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+                       a->older_mtime_filter = timetype;
+                       a->older_mtime_sec = mtime_sec;
+                       a->older_mtime_nsec = mtime_nsec;
+                       a->setflag |= TIME_IS_SET;
+               }
+       }
+       if (timetype & ARCHIVE_MATCH_CTIME) {
+               if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+                       a->newer_ctime_filter = timetype;
+                       a->newer_ctime_sec = ctime_sec;
+                       a->newer_ctime_nsec = ctime_nsec;
+                       a->setflag |= TIME_IS_SET;
+               }
+               if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+                       a->older_ctime_filter = timetype;
+                       a->older_ctime_sec = ctime_sec;
+                       a->older_ctime_nsec = ctime_nsec;
+                       a->setflag |= TIME_IS_SET;
+               }
+       }
+       return (ARCHIVE_OK);
+}
+
+static int
+set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
+{
+       time_t t;
+
+       if (datestr == NULL || *datestr == '\0') {
+               archive_set_error(&(a->archive), EINVAL, "date is empty");
+               return (ARCHIVE_FAILED);
+       }
+       t = get_date(a->now, datestr);
+       if (t == (time_t)-1) {
+               archive_set_error(&(a->archive), EINVAL, "invalid date string");
+               return (ARCHIVE_FAILED);
+       }
+       return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+static int
+set_timefilter_date_w(struct archive_match *a, int timetype,
+    const wchar_t *datestr)
+{
+       struct archive_string as;
+       time_t t;
+
+       if (datestr == NULL || *datestr == L'\0') {
+               archive_set_error(&(a->archive), EINVAL, "date is empty");
+               return (ARCHIVE_FAILED);
+       }
+
+       archive_string_init(&as);
+       if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
+               archive_string_free(&as);
+               if (errno == ENOMEM)
+                       return (error_nomem(a));
+               archive_set_error(&(a->archive), -1,
+                   "Failed to convert WCS to MBS");
+               return (ARCHIVE_FAILED);
+       }
+       t = get_date(a->now, as.s);
+       archive_string_free(&as);
+       if (t == (time_t)-1) {
+               archive_set_error(&(a->archive), EINVAL, "invalid date string");
+               return (ARCHIVE_FAILED);
+       }
+       return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static int
+set_timefilter_find_data(struct archive_match *a, int timetype,
+    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
+    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
+{
+       ULARGE_INTEGER utc;
+       time_t ctime_sec, mtime_sec;
+       long ctime_ns, mtime_ns;
+
+       utc.HighPart = ftCreationTime_dwHighDateTime;
+       utc.LowPart = ftCreationTime_dwLowDateTime;
+       if (utc.QuadPart >= EPOC_TIME) {
+               utc.QuadPart -= EPOC_TIME;
+               ctime_sec = (time_t)(utc.QuadPart / 10000000);
+               ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
+       } else {
+               ctime_sec = 0;
+               ctime_ns = 0;
+       }
+       utc.HighPart = ftLastWriteTime_dwHighDateTime;
+       utc.LowPart = ftLastWriteTime_dwLowDateTime;
+       if (utc.QuadPart >= EPOC_TIME) {
+               utc.QuadPart -= EPOC_TIME;
+               mtime_sec = (time_t)(utc.QuadPart / 10000000);
+               mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
+       } else {
+               mtime_sec = 0;
+               mtime_ns = 0;
+       }
+       return set_timefilter(a, timetype,
+                       mtime_sec, mtime_ns, ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+    const char *path)
+{
+       /* NOTE: stat() on Windows cannot handle nano seconds. */
+       HANDLE h;
+       WIN32_FIND_DATA d;
+
+       if (path == NULL || *path == '\0') {
+               archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+               return (ARCHIVE_FAILED);
+       }
+       h = FindFirstFileA(path, &d);
+       if (h == INVALID_HANDLE_VALUE) {
+               la_dosmaperr(GetLastError());
+               archive_set_error(&(a->archive), errno,
+                   "Failed to FindFirstFileA");
+               return (ARCHIVE_FAILED);
+       }
+       FindClose(h);
+       return set_timefilter_find_data(a, timetype,
+           d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+           d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+    const wchar_t *path)
+{
+       HANDLE h;
+       WIN32_FIND_DATAW d;
+
+       if (path == NULL || *path == L'\0') {
+               archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+               return (ARCHIVE_FAILED);
+       }
+       h = FindFirstFileW(path, &d);
+       if (h == INVALID_HANDLE_VALUE) {
+               la_dosmaperr(GetLastError());
+               archive_set_error(&(a->archive), errno,
+                   "Failed to FindFirstFile");
+               return (ARCHIVE_FAILED);
+       }
+       FindClose(h);
+       return set_timefilter_find_data(a, timetype,
+           d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+           d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+#else /* _WIN32 && !__CYGWIN__ */
+
+static int
+set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
+{
+       struct archive_entry *ae;
+       time_t ctime_sec, mtime_sec;
+       long ctime_ns, mtime_ns;
+
+       ae = archive_entry_new();
+       if (ae == NULL)
+               return (error_nomem(a));
+       archive_entry_copy_stat(ae, st);
+       ctime_sec = archive_entry_ctime(ae);
+       ctime_ns = archive_entry_ctime_nsec(ae);
+       mtime_sec = archive_entry_mtime(ae);
+       mtime_ns = archive_entry_mtime_nsec(ae);
+       archive_entry_free(ae);
+       return set_timefilter(a, timetype, mtime_sec, mtime_ns,
+                       ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+    const char *path)
+{
+       struct stat st;
+
+       if (path == NULL || *path == '\0') {
+               archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+               return (ARCHIVE_FAILED);
+       }
+       if (stat(path, &st) != 0) {
+               archive_set_error(&(a->archive), errno, "Failed to stat()");
+               return (ARCHIVE_FAILED);
+       }
+       return (set_timefilter_stat(a, timetype, &st));
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+    const wchar_t *path)
+{
+       struct archive_string as;
+       int r;
+
+       if (path == NULL || *path == L'\0') {
+               archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+               return (ARCHIVE_FAILED);
+       }
+
+       /* Convert WCS filename to MBS filename. */
+       archive_string_init(&as);
+       if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
+               archive_string_free(&as);
+               if (errno == ENOMEM)
+                       return (error_nomem(a));
+               archive_set_error(&(a->archive), -1,
+                   "Failed to convert WCS to MBS");
+               return (ARCHIVE_FAILED);
+       }
+
+       r = set_timefilter_pathname_mbs(a, timetype, as.s);
+       archive_string_free(&as);
+
+       return (r);
+}
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Call back funtions for archive_rb.
+ */
+static int
+cmp_node_mbs(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+       struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+       struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+       const char *p1, *p2;
+
+       archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
+       archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
+       if (p1 == NULL)
+               return (1);
+       if (p2 == NULL)
+               return (-1);
+       return (strcmp(p1, p2));
+}
+        
+static int
+cmp_key_mbs(const struct archive_rb_node *n, const void *key)
+{
+       struct match_file *f = (struct match_file *)(uintptr_t)n;
+       const char *p;
+
+       archive_mstring_get_mbs(NULL, &(f->pathname), &p);
+       if (p == NULL)
+               return (-1);
+       return (strcmp(p, (const char *)key));
+}
+
+static int
+cmp_node_wcs(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+       struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+       struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+       const wchar_t *p1, *p2;
+
+       archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
+       archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
+       if (p1 == NULL)
+               return (1);
+       if (p2 == NULL)
+               return (-1);
+       return (wcscmp(p1, p2));
+}
+        
+static int
+cmp_key_wcs(const struct archive_rb_node *n, const void *key)
+{
+       struct match_file *f = (struct match_file *)(uintptr_t)n;
+       const wchar_t *p;
+
+       archive_mstring_get_wcs(NULL, &(f->pathname), &p);
+       if (p == NULL)
+               return (-1);
+       return (wcscmp(p, (const wchar_t *)key));
+}
+
+static void
+entry_list_init(struct entry_list *list)
+{
+       list->first = NULL;
+       list->last = &(list->first);
+       list->count = 0;
+}
+
+static void
+entry_list_free(struct entry_list *list)
+{
+       struct match_file *p, *q;
+
+       for (p = list->first; p != NULL; ) {
+               q = p;
+               p = p->next;
+               archive_mstring_clean(&(q->pathname));
+               free(q);
+       }
+}
+
+static void
+entry_list_add(struct entry_list *list, struct match_file *file)
+{
+       *list->last = file;
+       list->last = &(file->next);
+       list->count++;
+}
+
+static int
+add_entry(struct archive_match *a, int flag,
+    struct archive_entry *entry)
+{
+       struct match_file *f;
+       const void *pathname;
+       int r;
+
+       f = calloc(1, sizeof(*f));
+       if (f == NULL)
+               return (error_nomem(a));
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       pathname = archive_entry_pathname_w(entry);
+       if (pathname == NULL) {
+               free(f);
+               archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+               return (ARCHIVE_FAILED);
+       }
+       archive_mstring_copy_wcs(&(f->pathname), pathname);
+       a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+       pathname = archive_entry_pathname(entry);
+       if (pathname == NULL) {
+               free(f);
+               archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+               return (ARCHIVE_FAILED);
+       }
+       archive_mstring_copy_mbs(&(f->pathname), pathname);
+       a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+       f->flag = flag;
+       f->mtime_sec = archive_entry_mtime(entry);
+       f->mtime_nsec = archive_entry_mtime_nsec(entry);
+       f->ctime_sec = archive_entry_ctime(entry);
+       f->ctime_nsec = archive_entry_ctime_nsec(entry);
+       r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
+       if (!r) {
+               struct match_file *f2;
+
+               /* Get the duplicated file. */
+               f2 = (struct match_file *)__archive_rb_tree_find_node(
+                       &(a->exclusion_tree), pathname);
+
+               /*
+                * We always overwrite comparison condision.
+                * If you do not want to overwrite it, you should not
+                * call archive_match_exclude_entry(). We cannot know
+                * what behavior you really expect since overwriting
+                * condition might be different with the flag.
+                */
+               if (f2 != NULL) {
+                       f2->flag = f->flag;
+                       f2->mtime_sec = f->mtime_sec;
+                       f2->mtime_nsec = f->mtime_nsec;
+                       f2->ctime_sec = f->ctime_sec;
+                       f2->ctime_nsec = f->ctime_nsec;
+               }
+               /* Release the duplicated file. */
+               archive_mstring_clean(&(f->pathname));
+               free(f);
+               return (ARCHIVE_OK);
+       }
+       entry_list_add(&(a->exclusion_entry_list), f);
+       a->setflag |= TIME_IS_SET;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Test if entry is excluded by its timestamp.
+ */
+static int
+time_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+       struct match_file *f;
+       const void *pathname;
+       time_t sec;
+       long nsec;
+
+       /*
+        * If this file/dir is excluded by a time comparison, skip it.
+        */
+       if (a->newer_ctime_filter) {
+               /* If ctime is not set, use mtime instead. */
+               if (archive_entry_ctime_is_set(entry))
+                       sec = archive_entry_ctime(entry);
+               else
+                       sec = archive_entry_mtime(entry);
+               if (sec < a->newer_ctime_sec)
+                       return (1); /* Too old, skip it. */
+               if (sec == a->newer_ctime_sec) {
+                       if (archive_entry_ctime_is_set(entry))
+                               nsec = archive_entry_ctime_nsec(entry);
+                       else
+                               nsec = archive_entry_mtime_nsec(entry);
+                       if (nsec < a->newer_ctime_nsec)
+                               return (1); /* Too old, skip it. */
+                       if (nsec == a->newer_ctime_nsec &&
+                           (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
+                             == 0)
+                               return (1); /* Equal, skip it. */
+               }
+       }
+       if (a->older_ctime_filter) {
+               /* If ctime is not set, use mtime instead. */
+               if (archive_entry_ctime_is_set(entry))
+                       sec = archive_entry_ctime(entry);
+               else
+                       sec = archive_entry_mtime(entry);
+               if (sec > a->older_ctime_sec)
+                       return (1); /* Too new, skip it. */
+               if (sec == a->older_ctime_sec) {
+                       if (archive_entry_ctime_is_set(entry))
+                               nsec = archive_entry_ctime_nsec(entry);
+                       else
+                               nsec = archive_entry_mtime_nsec(entry);
+                       if (nsec > a->older_ctime_nsec)
+                               return (1); /* Too new, skip it. */
+                       if (nsec == a->older_ctime_nsec &&
+                           (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
+                             == 0)
+                               return (1); /* Eeual, skip it. */
+               }
+       }
+       if (a->newer_mtime_filter) {
+               sec = archive_entry_mtime(entry);
+               if (sec < a->newer_mtime_sec)
+                       return (1); /* Too old, skip it. */
+               if (sec == a->newer_mtime_sec) {
+                       nsec = archive_entry_mtime_nsec(entry);
+                       if (nsec < a->newer_mtime_nsec)
+                               return (1); /* Too old, skip it. */
+                       if (nsec == a->newer_mtime_nsec &&
+                           (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
+                              == 0)
+                               return (1); /* Equal, skip it. */
+               }
+       }
+       if (a->older_mtime_filter) {
+               sec = archive_entry_mtime(entry);
+               if (sec > a->older_mtime_sec)
+                       return (1); /* Too new, skip it. */
+               nsec = archive_entry_mtime_nsec(entry);
+               if (sec == a->older_mtime_sec) {
+                       if (nsec > a->older_mtime_nsec)
+                               return (1); /* Too new, skip it. */
+                       if (nsec == a->older_mtime_nsec &&
+                           (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
+                              == 0)
+                               return (1); /* Equal, skip it. */
+               }
+       }
+
+       /* If there is no excluson list, include the file. */
+       if (a->exclusion_entry_list.count == 0)
+               return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       pathname = archive_entry_pathname_w(entry);
+       a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+       pathname = archive_entry_pathname(entry);
+       a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+       if (pathname == NULL)
+               return (0);
+
+       f = (struct match_file *)__archive_rb_tree_find_node(
+               &(a->exclusion_tree), pathname);
+       /* If the file wasn't rejected, include it. */
+       if (f == NULL)
+               return (0);
+
+       if (f->flag & ARCHIVE_MATCH_CTIME) {
+               sec = archive_entry_ctime(entry);
+               if (f->ctime_sec > sec) {
+                       if (f->flag & ARCHIVE_MATCH_OLDER)
+                               return (1);
+               } else if (f->ctime_sec < sec) {
+                       if (f->flag & ARCHIVE_MATCH_NEWER)
+                               return (1);
+               } else {
+                       nsec = archive_entry_ctime_nsec(entry);
+                       if (f->ctime_nsec > nsec) {
+                               if (f->flag & ARCHIVE_MATCH_OLDER)
+                                       return (1);
+                       } else if (f->ctime_nsec < nsec) {
+                               if (f->flag & ARCHIVE_MATCH_NEWER)
+                                       return (1);
+                       } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+                               return (1);
+               }
+       }
+       if (f->flag & ARCHIVE_MATCH_MTIME) {
+               sec = archive_entry_mtime(entry);
+               if (f->mtime_sec > sec) {
+                       if (f->flag & ARCHIVE_MATCH_OLDER)
+                               return (1);
+               } else if (f->mtime_sec < sec) {
+                       if (f->flag & ARCHIVE_MATCH_NEWER)
+                               return (1);
+               } else {
+                       nsec = archive_entry_mtime_nsec(entry);
+                       if (f->mtime_nsec > nsec) {
+                               if (f->flag & ARCHIVE_MATCH_OLDER)
+                                       return (1);
+                       } else if (f->mtime_nsec < nsec) {
+                               if (f->flag & ARCHIVE_MATCH_NEWER)
+                                       return (1);
+                       } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+                               return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * Utility functions to manage inclusion owners
+ */
+
+int
+archive_match_include_uid(struct archive *_a, int64_t uid)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_uid");
+       a = (struct archive_match *)_a;
+       return (add_owner_id(a, &(a->inclusion_uids), uid));
+}
+
+int
+archive_match_include_gid(struct archive *_a, int64_t gid)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_gid");
+       a = (struct archive_match *)_a;
+       return (add_owner_id(a, &(a->inclusion_gids), gid));
+}
+
+int
+archive_match_include_uname(struct archive *_a, const char *uname)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_uname");
+       a = (struct archive_match *)_a;
+       return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
+}
+
+int
+archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
+       a = (struct archive_match *)_a;
+       return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
+}
+
+int
+archive_match_include_gname(struct archive *_a, const char *gname)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_gname");
+       a = (struct archive_match *)_a;
+       return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
+}
+
+int
+archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
+       a = (struct archive_match *)_a;
+       return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
+}
+
+/*
+ * Test function for owner(uid, gid, uname, gname).
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_owner_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+       struct archive_match *a;
+
+       archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+           ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
+
+       a = (struct archive_match *)_a;
+       if (entry == NULL) {
+               archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+               return (ARCHIVE_FAILED);
+       }
+
+       /* If we don't have inclusion id set at all, the entry is always
+        * not excluded. */
+       if ((a->setflag & ID_IS_SET) == 0)
+               return (0);
+       return (owner_excluded(a, entry));
+}
+
+static int
+add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
+{
+       unsigned i;
+
+       if (ids->count + 1 >= ids->size) {
+               if (ids->size == 0)
+                       ids->size = 8;
+               else
+                       ids->size *= 2;
+               ids->ids = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
+               if (ids->ids == NULL)
+                       return (error_nomem(a));
+       }
+
+       /* Find an insert point. */
+       for (i = 0; i < ids->count; i++) {
+               if (ids->ids[i] >= id)
+                       break;
+       }
+
+       /* Add oowner id. */
+       if (i == ids->count)
+               ids->ids[ids->count++] = id;
+       else if (ids->ids[i] != id) {
+               memmove(&(ids->ids[i+1]), &(ids->ids[i]),
+                   (ids->count - i) * sizeof(ids->ids[0]));
+               ids->ids[i] = id;
+               ids->count++;
+       }
+       a->setflag |= ID_IS_SET;
+       return (ARCHIVE_OK);
+}
+
+static int
+match_owner_id(struct id_array *ids, int64_t id)
+{
+       unsigned b, m, t;
+
+       t = 0;
+       b = ids->count;
+       while (t < b) {
+               m = (t + b)>>1;
+               if (ids->ids[m] == id)
+                       return (1);
+               if (ids->ids[m] < id)
+                       t = m + 1;
+               else
+                       b = m;
+       }
+       return (0);
+}
+
+static int
+add_owner_name(struct archive_match *a, struct match_list *list,
+    int mbs, const void *name)
+{
+       struct match *match;
+
+       match = calloc(1, sizeof(*match));
+       if (match == NULL)
+               return (error_nomem(a));
+       if (mbs)
+               archive_mstring_copy_mbs(&(match->pattern), name);
+       else
+               archive_mstring_copy_wcs(&(match->pattern), name);
+       match_list_add(list, match);
+       a->setflag |= ID_IS_SET;
+       return (ARCHIVE_OK);
+}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+match_owner_name_mbs(struct archive_match *a, struct match_list *list,
+    const char *name)
+{
+       struct match *m;
+       const char *p;
+
+       if (name == NULL || *name == '\0')
+               return (0);
+       for (m = list->first; m; m = m->next) {
+               if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
+                   < 0 && errno == ENOMEM)
+                       return (error_nomem(a));
+               if (p != NULL && strcmp(p, name) == 0) {
+                       m->matches++;
+                       return (1);
+               }
+       }
+       return (0);
+}
+#else
+static int
+match_owner_name_wcs(struct archive_match *a, struct match_list *list,
+    const wchar_t *name)
+{
+       struct match *m;
+       const wchar_t *p;
+
+       if (name == NULL || *name == L'\0')
+               return (0);
+       for (m = list->first; m; m = m->next) {
+               if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
+                   < 0 && errno == ENOMEM)
+                       return (error_nomem(a));
+               if (p != NULL && wcscmp(p, name) == 0) {
+                       m->matches++;
+                       return (1);
+               }
+       }
+       return (0);
+}
+#endif
+
+/*
+ * Test if entry is excluded by uid, gid, uname or gname.
+ */
+static int
+owner_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+       int r;
+
+       if (a->inclusion_uids.count) {
+               if (!match_owner_id(&(a->inclusion_uids),
+                   archive_entry_uid(entry)))
+                       return (1);
+       }
+
+       if (a->inclusion_gids.count) {
+               if (!match_owner_id(&(a->inclusion_gids),
+                   archive_entry_gid(entry)))
+                       return (1);
+       }
+
+       if (a->inclusion_unames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+               r = match_owner_name_wcs(a, &(a->inclusion_unames),
+                       archive_entry_uname_w(entry));
+#else
+               r = match_owner_name_mbs(a, &(a->inclusion_unames),
+                       archive_entry_uname(entry));
+#endif
+               if (!r)
+                       return (1);
+               else if (r < 0)
+                       return (r);
+       }
+
+       if (a->inclusion_gnames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+               r = match_owner_name_wcs(a, &(a->inclusion_gnames),
+                       archive_entry_gname_w(entry));
+#else
+               r = match_owner_name_mbs(a, &(a->inclusion_gnames),
+                       archive_entry_gname(entry));
+#endif
+               if (!r)
+                       return (1);
+               else if (r < 0)
+                       return (r);
+       }
+       return (0);
+}
+
index 962572c..08a348f 100644 (file)
@@ -38,6 +38,7 @@ _archive_set_option(struct archive *a,
     int magic, const char *fn, option_handler use_option)
 {
        const char *mp, *op, *vp;
+       int r;
 
        archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
 
@@ -47,10 +48,24 @@ _archive_set_option(struct archive *a,
 
        if (op == NULL && vp == NULL)
                return (ARCHIVE_OK);
-       if (op == NULL)
+       if (op == NULL) {
+               archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");
                return (ARCHIVE_FAILED);
+       }
 
-       return use_option(a, mp, op, vp);
+       r = use_option(a, mp, op, vp);
+       if (r == ARCHIVE_WARN - 1) {
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Unknown module name: `%s'", mp);
+               return (ARCHIVE_FAILED);
+       }
+       if (r == ARCHIVE_WARN) {
+               archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                   "Undefined option: `%s%s%s%s%s%s'",
+                   vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");
+               return (ARCHIVE_FAILED);
+       }
+       return (r);
 }
 
 int
@@ -102,6 +117,25 @@ _archive_set_options(struct archive *a, const char *options,
                        free(data);
                        return (ARCHIVE_FATAL);
                }
+               if (r == ARCHIVE_FAILED && mod != NULL) {
+                       free(data);
+                       return (ARCHIVE_FAILED);
+               }
+               if (r == ARCHIVE_WARN - 1) {
+                       /* The module name is wrong. */
+                       archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                           "Unknown module name: `%s'", mod);
+                       free(data);
+                       return (ARCHIVE_FAILED);
+               }
+               if (r == ARCHIVE_WARN) {
+                       /* The option name is wrong. No-one used this. */
+                       archive_set_error(a, ARCHIVE_ERRNO_MISC,
+                           "Undefined option: `%s%s%s'",
+                           mod?mod:"", mod?":":"", opt);
+                       free(data);
+                       return (ARCHIVE_FAILED);
+               }
                if (r == ARCHIVE_OK)
                        anyok = 1;
                else
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "lafe_platform.h"
+#include "archive_platform.h"
 __FBSDID("$FreeBSD$");
 
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
 
-#include "pathmatch.h"
+#include "archive_pathmatch.h"
 
 /*
  * Check whether a character 'c' is matched by a list specification [...]:
@@ -96,6 +99,53 @@ pm_list(const char *start, const char *end, const char c, int flags)
        return (nomatch);
 }
 
+static int
+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
+{
+       const wchar_t *p = start;
+       wchar_t rangeStart = L'\0', nextRangeStart;
+       int match = 1, nomatch = 0;
+
+       /* This will be used soon... */
+       (void)flags; /* UNUSED */
+
+       /* If this is a negated class, return success for nomatch. */
+       if ((*p == L'!' || *p == L'^') && p < end) {
+               match = 0;
+               nomatch = 1;
+               ++p;
+       }
+
+       while (p < end) {
+               nextRangeStart = L'\0';
+               switch (*p) {
+               case L'-':
+                       /* Trailing or initial '-' is not special. */
+                       if ((rangeStart == L'\0') || (p == end - 1)) {
+                               if (*p == c)
+                                       return (match);
+                       } else {
+                               wchar_t rangeEnd = *++p;
+                               if (rangeEnd == L'\\')
+                                       rangeEnd = *++p;
+                               if ((rangeStart <= c) && (c <= rangeEnd))
+                                       return (match);
+                       }
+                       break;
+               case L'\\':
+                       ++p;
+                       /* Fall through */
+               default:
+                       if (*p == c)
+                               return (match);
+                       nextRangeStart = *p; /* Possible start of range. */
+               }
+               rangeStart = nextRangeStart;
+               ++p;
+       }
+       return (nomatch);
+}
+
 /*
  * If s is pointing to "./", ".//", "./././" or the like, skip it.
  */
@@ -108,6 +158,15 @@ pm_slashskip(const char *s) {
        return (s);
 }
 
+static const wchar_t *
+pm_slashskip_w(const wchar_t *s) {
+       while ((*s == L'/')
+           || (s[0] == L'.' && s[1] == L'/')
+           || (s[0] == L'.' && s[1] == L'\0'))
+               ++s;
+       return (s);
+}
+
 static int
 pm(const char *p, const char *s, int flags)
 {
@@ -144,7 +203,7 @@ pm(const char *p, const char *s, int flags)
                        if (*p == '\0')
                                return (1);
                        while (*s) {
-                               if (lafe_pathmatch(p, s, flags))
+                               if (archive_pathmatch(p, s, flags))
                                        return (1);
                                ++s;
                        }
@@ -213,9 +272,114 @@ pm(const char *p, const char *s, int flags)
        }
 }
 
+static int
+pm_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+       const wchar_t *end;
+
+       /*
+        * Ignore leading './', './/', '././', etc.
+        */
+       if (s[0] == L'.' && s[1] == L'/')
+               s = pm_slashskip_w(s + 1);
+       if (p[0] == L'.' && p[1] == L'/')
+               p = pm_slashskip_w(p + 1);
+
+       for (;;) {
+               switch (*p) {
+               case L'\0':
+                       if (s[0] == L'/') {
+                               if (flags & PATHMATCH_NO_ANCHOR_END)
+                                       return (1);
+                               /* "dir" == "dir/" == "dir/." */
+                               s = pm_slashskip_w(s);
+                       }
+                       return (*s == L'\0');
+               case L'?':
+                       /* ? always succeeds, unless we hit end of 's' */
+                       if (*s == L'\0')
+                               return (0);
+                       break;
+               case L'*':
+                       /* "*" == "**" == "***" ... */
+                       while (*p == L'*')
+                               ++p;
+                       /* Trailing '*' always succeeds. */
+                       if (*p == L'\0')
+                               return (1);
+                       while (*s) {
+                               if (archive_pathmatch_w(p, s, flags))
+                                       return (1);
+                               ++s;
+                       }
+                       return (0);
+               case L'[':
+                       /*
+                        * Find the end of the [...] character class,
+                        * ignoring \] that might occur within the class.
+                        */
+                       end = p + 1;
+                       while (*end != L'\0' && *end != L']') {
+                               if (*end == L'\\' && end[1] != L'\0')
+                                       ++end;
+                               ++end;
+                       }
+                       if (*end == L']') {
+                               /* We found [...], try to match it. */
+                               if (!pm_list_w(p + 1, end, *s, flags))
+                                       return (0);
+                               p = end; /* Jump to trailing ']' char. */
+                               break;
+                       } else
+                               /* No final ']', so just match '['. */
+                               if (*p != *s)
+                                       return (0);
+                       break;
+               case L'\\':
+                       /* Trailing '\\' matches itself. */
+                       if (p[1] == L'\0') {
+                               if (*s != L'\\')
+                                       return (0);
+                       } else {
+                               ++p;
+                               if (*p != *s)
+                                       return (0);
+                       }
+                       break;
+               case L'/':
+                       if (*s != L'/' && *s != L'\0')
+                               return (0);
+                       /* Note: pattern "/\./" won't match "/";
+                        * pm_slashskip() correctly stops at backslash. */
+                       p = pm_slashskip_w(p);
+                       s = pm_slashskip_w(s);
+                       if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+                               return (1);
+                       --p; /* Counteract the increment below. */
+                       --s;
+                       break;
+               case L'$':
+                       /* '$' is special only at end of pattern and only
+                        * if PATHMATCH_NO_ANCHOR_END is specified. */
+                       if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+                               /* "dir" == "dir/" == "dir/." */
+                               return (*pm_slashskip_w(s) == L'\0');
+                       }
+                       /* Otherwise, '$' is not special. */
+                       /* FALL THROUGH */
+               default:
+                       if (*p != *s)
+                               return (0);
+                       break;
+               }
+               ++p;
+               ++s;
+       }
+}
+
 /* Main entry point. */
 int
-lafe_pathmatch(const char *p, const char *s, int flags)
+__archive_pathmatch(const char *p, const char *s, int flags)
 {
        /* Empty pattern only matches the empty string. */
        if (p == NULL || *p == '\0')
@@ -253,3 +417,43 @@ lafe_pathmatch(const char *p, const char *s, int flags)
        /* Default: Match from beginning. */
        return (pm(p, s, flags));
 }
+
+int
+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+       /* Empty pattern only matches the empty string. */
+       if (p == NULL || *p == L'\0')
+               return (s == NULL || *s == L'\0');
+
+       /* Leading '^' anchors the start of the pattern. */
+       if (*p == L'^') {
+               ++p;
+               flags &= ~PATHMATCH_NO_ANCHOR_START;
+       }
+
+       if (*p == L'/' && *s != L'/')
+               return (0);
+
+       /* Certain patterns and file names anchor implicitly. */
+       if (*p == L'*' || *p == L'/' || *p == L'/') {
+               while (*p == L'/')
+                       ++p;
+               while (*s == L'/')
+                       ++s;
+               return (pm_w(p, s, flags));
+       }
+
+       /* If start is unanchored, try to match start of each path element. */
+       if (flags & PATHMATCH_NO_ANCHOR_START) {
+               for ( ; s != NULL; s = wcschr(s, L'/')) {
+                       if (*s == L'/')
+                               s++;
+                       if (pm_w(p, s, flags))
+                               return (1);
+               }
+               return (0);
+       }
+
+       /* Default: Match from beginning. */
+       return (pm_w(p, s, flags));
+}
  * $FreeBSD$
  */
 
-#ifndef LAFE_PATHMATCH_H
-#define LAFE_PATHMATCH_H
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#ifndef ARCHIVE_PATHMATCH_H
+#define ARCHIVE_PATHMATCH_H
 
 /* Don't anchor at beginning unless the pattern starts with "^" */
 #define PATHMATCH_NO_ANCHOR_START      1
 /* Note that "^" and "$" are not special unless you set the corresponding
  * flag above. */
 
-int lafe_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);
+
+#define archive_pathmatch(p, s, f)     __archive_pathmatch(p, s, f)
+#define archive_pathmatch_w(p, s, f)   __archive_pathmatch_w(p, s, f)
 
 #endif
index b2e8c3a..13a2581 100644 (file)
@@ -415,7 +415,7 @@ static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
     upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
   }
 
-  do
+  while (numPs != 0)
   {
     /* Create Child */
     CTX_PTR c1; /* = AllocContext(p); */
@@ -435,7 +435,6 @@ static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
     SetSuccessor(ps[--numPs], REF(c1));
     c = c1;
   }
-  while (numPs != 0);
   
   return c;
 }
@@ -778,7 +777,7 @@ static void Range_Normalize(CPpmd7z_RangeDec *p)
       if(p->Range >= p->Bottom)
         break;
       else
-        p->Range = -p->Low & (p->Bottom - 1);
+        p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1);
     }
     p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
     p->Range <<= 8;
index 2666768..e78bde5 100644 (file)
@@ -152,7 +152,7 @@ typedef
   CPpmd_Byte_Ref;
 
 #define PPMD_SetAllBitsIn256Bytes(p) \
-  { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \
-  p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }}
+  { unsigned j; for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \
+  p[j+7] = p[j+6] = p[j+5] = p[j+4] = p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; }}
 
 #endif
index 9941e96..470f500 100644 (file)
@@ -50,6 +50,7 @@
 #define        ARCHIVE_READ_MAGIC      (0xdeb0c5U)
 #define        ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
 #define        ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
+#define        ARCHIVE_MATCH_MAGIC     (0xcad11c9U)
 
 #define        ARCHIVE_STATE_NEW       1U
 #define        ARCHIVE_STATE_HEADER    2U
index fa307be..70bf7e6 100644 (file)
@@ -96,7 +96,7 @@ __archive_rb_tree_init(struct archive_rb_tree *rbt,
     const struct archive_rb_tree_ops *ops)
 {
        rbt->rbt_ops = ops;
-       *((const struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;
+       *((struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;
 }
 
 struct archive_rb_node *
@@ -683,7 +683,7 @@ __archive_rb_tree_iterate(struct archive_rb_tree *rbt,
         */
        if (RB_SENTINEL_P(self->rb_nodes[direction])) {
                while (!RB_ROOT_P(rbt, self)) {
-                       if (other == RB_POSITION(self))
+                       if (other == (unsigned int)RB_POSITION(self))
                                return RB_FATHER(self);
                        self = RB_FATHER(self);
                }
index 5285192..a29cc1e 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/lib/libarchive/archive_read.3 191595 2009-04-27 20:13:13Z kientzle $
+.\" $FreeBSD$
 .\"
-.Dd March 23, 2011
+.Dd February 2, 2012
 .Dt ARCHIVE_READ 3
 .Os
 .Sh NAME
 .Nm archive_read
 .Nd functions for reading streaming archives
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive.h
 .Sh DESCRIPTION
index b1d4914..e99906f 100644 (file)
@@ -201,7 +201,6 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request)
                        request -= get;
                        total += get;
                }
-               return total;
        } else if (self->archive->client.seeker != NULL
                && request > 64 * 1024) {
                /* If the client provided a seeker but not a skipper,
@@ -633,13 +632,13 @@ archive_read_data(struct archive *_a, void *buff, size_t s)
                }
 
                /* Compute the amount of zero padding needed. */
-               if (a->read_data_output_offset + s <
+               if (a->read_data_output_offset + (int64_t)s <
                    a->read_data_offset) {
                        len = s;
                } else if (a->read_data_output_offset <
                    a->read_data_offset) {
-                       len = a->read_data_offset -
-                           a->read_data_output_offset;
+                       len = (size_t)(a->read_data_offset -
+                           a->read_data_output_offset);
                } else
                        len = 0;
 
@@ -1231,7 +1230,7 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request)
 
        /* Use up the copy buffer first. */
        if (filter->avail > 0) {
-               min = minimum(request, (int64_t)filter->avail);
+               min = (size_t)minimum(request, (int64_t)filter->avail);
                filter->next += min;
                filter->avail -= min;
                request -= min;
@@ -1241,7 +1240,7 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request)
 
        /* Then use up the client buffer. */
        if (filter->client_avail > 0) {
-               min = minimum(request, (int64_t)filter->client_avail);
+               min = (size_t)minimum(request, (int64_t)filter->client_avail);
                filter->client_next += min;
                filter->client_avail -= min;
                request -= min;
@@ -1283,7 +1282,7 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request)
                if (bytes_read >= request) {
                        filter->client_next =
                            ((const char *)filter->client_buff) + request;
-                       filter->client_avail = bytes_read - request;
+                       filter->client_avail = (size_t)(bytes_read - request);
                        filter->client_total = bytes_read;
                        total_bytes_skipped += request;
                        filter->position += request;
index 14f9410..b4398f1 100644 (file)
@@ -64,7 +64,7 @@ pad_to(struct archive *a, int fd, int can_lseek,
        }
        while (target_offset > actual_offset) {
                to_write = nulls_size;
-               if (target_offset < actual_offset + nulls_size)
+               if (target_offset < actual_offset + (int64_t)nulls_size)
                        to_write = (size_t)(target_offset - actual_offset);
                bytes_written = write(fd, nulls, to_write);
                if (bytes_written < 0) {
index 3c49bff..525dc59 100644 (file)
@@ -22,9 +22,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/lib/libarchive/archive_read_disk.3 190957 2009-04-12 05:04:02Z kientzle $
+.\" $FreeBSD$
 .\"
-.Dd March 10, 2009
+.Dd February 2, 2012
 .Dt ARCHIVE_READ_DISK 3
 .Os
 .Sh NAME
@@ -42,6 +42,8 @@
 .Nm archive_read_finish ,
 .Nm archive_read_free
 .Nd functions for reading objects from disk
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .In archive.h
 .Ft struct archive *
index 8ce88b3..a43c0ad 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
- * Copyright (c) 2010 Michihiro NAKAJIMA
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,6 +73,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
 #ifdef HAVE_LINUX_FIEMAP_H
 #include <linux/fiemap.h>
 #endif
@@ -112,13 +115,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #endif
 
 static int setup_acls_posix1e(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_mac_metadata(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_xattrs(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_sparse(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 
 int
 archive_read_disk_entry_from_file(struct archive *_a,
@@ -165,16 +168,15 @@ archive_read_disk_entry_from_file(struct archive *_a,
                        st = &s;
                }
                archive_entry_copy_stat(entry, st);
+               /* Lookup uname/gname */
+               name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+               if (name != NULL)
+                       archive_entry_copy_uname(entry, name);
+               name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+               if (name != NULL)
+                       archive_entry_copy_gname(entry, name);
        }
 
-       /* Lookup uname/gname */
-       name = archive_read_disk_uname(_a, archive_entry_uid(entry));
-       if (name != NULL)
-               archive_entry_copy_uname(entry, name);
-       name = archive_read_disk_gname(_a, archive_entry_gid(entry));
-       if (name != NULL)
-               archive_entry_copy_gname(entry, name);
-
 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
        /* On FreeBSD, we get flags for free with the stat. */
        /* TODO: Does this belong in copy_stat()? */
@@ -187,11 +189,16 @@ archive_read_disk_entry_from_file(struct archive *_a,
         * this is an extra step, it has a nice side-effect: We get an
         * open file descriptor which we can use in the subsequent lookups. */
        if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
-               if (fd < 0)
-                       fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (fd < 0) {
+                       if (a->tree != NULL)
+                               fd = a->open_on_current_dir(a->tree, path,
+                                       O_RDONLY | O_NONBLOCK);
+                       else
+                               fd = open(path, O_RDONLY | O_NONBLOCK);
+               }
                if (fd >= 0) {
                        unsigned long stflags;
-                       int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
+                       r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
                        if (r == 0 && stflags != 0)
                                archive_entry_set_fflags(entry, stflags, 0);
                }
@@ -210,13 +217,21 @@ archive_read_disk_entry_from_file(struct archive *_a,
                            "Couldn't read link data");
                        return (ARCHIVE_FAILED);
                }
+               if (a->tree != NULL) {
 #ifdef HAVE_READLINKAT
-               if (a->entry_wd_fd >= 0)
-                       lnklen = readlinkat(a->entry_wd_fd, path,
-                           linkbuffer, linkbuffer_len);
-               else
+                       lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
+                           path, linkbuffer, linkbuffer_len);
+#else
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't read link data");
+                               free(linkbuffer);
+                               return (ARCHIVE_FAILED);
+                       }
+                       lnklen = readlink(path, linkbuffer, linkbuffer_len);
 #endif /* HAVE_READLINKAT */
-               lnklen = readlink(path, linkbuffer, linkbuffer_len);
+               } else
+                       lnklen = readlink(path, linkbuffer, linkbuffer_len);
                if (lnklen < 0) {
                        archive_set_error(&a->archive, errno,
                            "Couldn't read link data");
@@ -229,14 +244,16 @@ archive_read_disk_entry_from_file(struct archive *_a,
        }
 #endif /* HAVE_READLINK || HAVE_READLINKAT */
 
-       r = setup_acls_posix1e(a, entry, fd);
-       r1 = setup_xattrs(a, entry, fd);
-       if (r1 < r)
-               r = r1;
-       r1 = setup_mac_metadata(a, entry, fd);
+       r = setup_acls_posix1e(a, entry, &fd);
+       r1 = setup_xattrs(a, entry, &fd);
        if (r1 < r)
                r = r1;
-       r1 = setup_sparse(a, entry, fd);
+       if (a->enable_copyfile) {
+               r1 = setup_mac_metadata(a, entry, &fd);
+               if (r1 < r)
+                       r = r1;
+       }
+       r1 = setup_sparse(a, entry, &fd);
        if (r1 < r)
                r = r1;
 
@@ -262,7 +279,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
  */
 static int
 setup_mac_metadata(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        int tempfd = -1;
        int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
@@ -272,6 +289,7 @@ setup_mac_metadata(struct archive_read_disk *a,
        int have_attrs;
        const char *name, *tempdir, *tempfile = NULL;
 
+       (void)fd; /* UNUSED */
        name = archive_entry_sourcepath(entry);
        if (name == NULL)
                name = archive_entry_pathname(entry);
@@ -281,6 +299,14 @@ setup_mac_metadata(struct archive_read_disk *a,
                return (ARCHIVE_WARN);
        }
 
+       if (a->tree != NULL) {
+               if (a->tree_enter_working_dir(a->tree) != 0) {
+                       archive_set_error(&a->archive, errno,
+                                   "Couldn't change dir");
+                               return (ARCHIVE_FAILED);
+               }
+       }
+
        /* Short-circuit if there's nothing to do. */
        have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
        if (have_attrs == -1) {
@@ -351,7 +377,7 @@ cleanup:
  */
 static int
 setup_mac_metadata(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a; /* UNUSED */
        (void)entry; /* UNUSED */
@@ -367,7 +393,7 @@ static void setup_acl_posix1e(struct archive_read_disk *a,
 
 static int
 setup_acls_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        const char      *accpath;
        acl_t            acl;
@@ -378,9 +404,20 @@ setup_acls_posix1e(struct archive_read_disk *a,
 
        archive_entry_acl_clear(entry);
 
+       if (*fd < 0 && a->tree != NULL &&
+           (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)){
+               *fd = a->open_on_current_dir(a->tree, accpath,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       archive_set_error(&a->archive, errno,
+                           "Couldn't access %s", accpath);
+                       return (ARCHIVE_FAILED);
+               }
+       }
+
        /* Retrieve access ACL from file. */
-       if (fd >= 0)
-               acl = acl_get_fd(fd);
+       if (*fd >= 0)
+               acl = acl_get_fd(*fd);
 #if HAVE_ACL_GET_LINK_NP
        else if (!a->follow_symlinks)
                acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
@@ -474,7 +511,7 @@ setup_acl_posix1e(struct archive_read_disk *a,
 #else
 static int
 setup_acls_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;      /* UNUSED */
        (void)entry;  /* UNUSED */
@@ -568,7 +605,7 @@ setup_xattr(struct archive_read_disk *a,
 
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char *list, *p;
        const char *path;
@@ -578,16 +615,30 @@ setup_xattrs(struct archive_read_disk *a,
        if (path == NULL)
                path = archive_entry_pathname(entry);
 
+       if (*fd < 0 && a->tree != NULL) {
+               if (a->follow_symlinks ||
+                   archive_entry_filetype(entry) != AE_IFLNK)
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't access %s", path);
+                               return (ARCHIVE_FAILED);
+                       }
+               }
+       }
+
 #if HAVE_FLISTXATTR
-       if (fd >= 0)
-               list_size = flistxattr(fd, NULL, 0);
+       if (*fd >= 0)
+               list_size = flistxattr(*fd, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = llistxattr(path, NULL, 0);
        else
                list_size = listxattr(path, NULL, 0);
 #elif HAVE_FLISTEA
-       if (fd >= 0)
-               list_size = flistea(fd, NULL, 0);
+       if (*fd >= 0)
+               list_size = flistea(*fd, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = llistea(path, NULL, 0);
        else
@@ -611,15 +662,15 @@ setup_xattrs(struct archive_read_disk *a,
        }
 
 #if HAVE_FLISTXATTR
-       if (fd >= 0)
-               list_size = flistxattr(fd, list, list_size);
+       if (*fd >= 0)
+               list_size = flistxattr(*fd, list, list_size);
        else if (!a->follow_symlinks)
                list_size = llistxattr(path, list, list_size);
        else
                list_size = listxattr(path, list, list_size);
 #elif HAVE_FLISTEA
-       if (fd >= 0)
-               list_size = flistea(fd, list, list_size);
+       if (*fd >= 0)
+               list_size = flistea(*fd, list, list_size);
        else if (!a->follow_symlinks)
                list_size = llistea(path, list, list_size);
        else
@@ -637,7 +688,7 @@ setup_xattrs(struct archive_read_disk *a,
                if (strncmp(p, "system.", 7) == 0 ||
                                strncmp(p, "xfsroot.", 8) == 0)
                        continue;
-               setup_xattr(a, entry, p, fd);
+               setup_xattr(a, entry, p, *fd);
        }
 
        free(list);
@@ -698,6 +749,7 @@ setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
                size = extattr_get_file(accpath, namespace, name, value, size);
 
        if (size == -1) {
+               free(value);
                archive_set_error(&a->archive, errno,
                    "Couldn't read extended attribute");
                return (ARCHIVE_WARN);
@@ -711,7 +763,7 @@ setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
 
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char buff[512];
        char *list, *p;
@@ -723,8 +775,22 @@ setup_xattrs(struct archive_read_disk *a,
        if (path == NULL)
                path = archive_entry_pathname(entry);
 
-       if (fd >= 0)
-               list_size = extattr_list_fd(fd, namespace, NULL, 0);
+       if (*fd < 0 && a->tree != NULL) {
+               if (a->follow_symlinks ||
+                   archive_entry_filetype(entry) != AE_IFLNK)
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't access %s", path);
+                               return (ARCHIVE_FAILED);
+                       }
+               }
+       }
+
+       if (*fd >= 0)
+               list_size = extattr_list_fd(*fd, namespace, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = extattr_list_link(path, namespace, NULL, 0);
        else
@@ -746,8 +812,8 @@ setup_xattrs(struct archive_read_disk *a,
                return (ARCHIVE_FATAL);
        }
 
-       if (fd >= 0)
-               list_size = extattr_list_fd(fd, namespace, list, list_size);
+       if (*fd >= 0)
+               list_size = extattr_list_fd(*fd, namespace, list, list_size);
        else if (!a->follow_symlinks)
                list_size = extattr_list_link(path, namespace, list, list_size);
        else
@@ -769,7 +835,7 @@ setup_xattrs(struct archive_read_disk *a,
                name = buff + strlen(buff);
                memcpy(name, p + 1, len);
                name[len] = '\0';
-               setup_xattr(a, entry, namespace, name, buff, fd);
+               setup_xattr(a, entry, namespace, name, buff, *fd);
                p += 1 + len;
        }
 
@@ -784,7 +850,7 @@ setup_xattrs(struct archive_read_disk *a,
  */
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;     /* UNUSED */
        (void)entry; /* UNUSED */
@@ -813,14 +879,13 @@ setup_xattrs(struct archive_read_disk *a,
 
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char buff[4096];
        struct fiemap *fm;
        struct fiemap_extent *fe;
        int64_t size;
        int count, do_fiemap;
-       int initial_fd = fd;
        int exit_sts = ARCHIVE_OK;
 
        if (archive_entry_filetype(entry) != AE_IFREG
@@ -828,14 +893,18 @@ setup_sparse(struct archive_read_disk *a,
            || archive_entry_hardlink(entry) != NULL)
                return (ARCHIVE_OK);
 
-       if (fd < 0) {
+       if (*fd < 0) {
                const char *path;
 
                path = archive_entry_sourcepath(entry);
                if (path == NULL)
                        path = archive_entry_pathname(entry);
-               fd = open(path, O_RDONLY | O_NONBLOCK);
-               if (fd < 0) {
+               if (a->tree != NULL)
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               else
+                       *fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
                        archive_set_error(&a->archive, errno,
                            "Can't open `%s'", path);
                        return (ARCHIVE_FAILED);
@@ -853,29 +922,22 @@ setup_sparse(struct archive_read_disk *a,
        for (;;) {
                int i, r;
 
-               r = ioctl(fd, FS_IOC_FIEMAP, fm); 
+               r = ioctl(*fd, FS_IOC_FIEMAP, fm); 
                if (r < 0) {
-                       /* When errno is ENOTTY, it is better we should
-                        * return ARCHIVE_OK because an earlier version
-                        *(<2.6.28) cannot perfom FS_IOC_FIEMAP.
-                        * We should also check if errno is EOPNOTSUPP,
-                        * it means "Operation not supported". */
-                       if (errno != ENOTTY && errno != EOPNOTSUPP) {
-                               archive_set_error(&a->archive, errno,
-                                   "FIEMAP failed");
-                               exit_sts = ARCHIVE_FAILED;
-                       }
+                       /* When something error happens, it is better we
+                        * should return ARCHIVE_OK because an earlier
+                        * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */
                        goto exit_setup_sparse;
                }
                if (fm->fm_mapped_extents == 0)
                        break;
                fe = fm->fm_extents;
-               for (i = 0; i < fm->fm_mapped_extents; i++, fe++) {
+               for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
                        if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
                                /* The fe_length of the last block does not
                                 * adjust itself to its size files. */
                                int64_t length = fe->fe_length;
-                               if (fe->fe_logical + length > size)
+                               if (fe->fe_logical + length > (uint64_t)size)
                                        length -= fe->fe_logical + length - size;
                                if (fe->fe_logical == 0 && length == size) {
                                        /* This is not sparse. */
@@ -896,8 +958,6 @@ setup_sparse(struct archive_read_disk *a,
                        break;
        }
 exit_setup_sparse:
-       if (initial_fd != fd)
-               close(fd);
        return (exit_sts);
 }
 
@@ -909,10 +969,9 @@ exit_setup_sparse:
 
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        int64_t size;
-       int initial_fd = fd;
        off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */
        off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */
        int exit_sts = ARCHIVE_OK;
@@ -923,22 +982,38 @@ setup_sparse(struct archive_read_disk *a,
                return (ARCHIVE_OK);
 
        /* Does filesystem support the reporting of hole ? */
-       if (fd >= 0) {
-               if (fpathconf(fd, _PC_MIN_HOLE_SIZE) <= 0)
+       if (*fd < 0 && a->tree != NULL) {
+               const char *path;
+
+               path = archive_entry_sourcepath(entry);
+               if (path == NULL)
+                       path = archive_entry_pathname(entry);
+               *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       archive_set_error(&a->archive, errno,
+                           "Can't open `%s'", path);
+                       return (ARCHIVE_FAILED);
+               }
+       }
+
+       if (*fd >= 0) {
+               if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
                        return (ARCHIVE_OK);
-               initial_off = lseek(fd, 0, SEEK_CUR);
+               initial_off = lseek(*fd, 0, SEEK_CUR);
                if (initial_off != 0)
-                       lseek(fd, 0, SEEK_SET);
+                       lseek(*fd, 0, SEEK_SET);
        } else {
                const char *path;
 
                path = archive_entry_sourcepath(entry);
                if (path == NULL)
                        path = archive_entry_pathname(entry);
+                       
                if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
                        return (ARCHIVE_OK);
-               fd = open(path, O_RDONLY | O_NONBLOCK);
-               if (fd < 0) {
+               *fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
                        archive_set_error(&a->archive, errno,
                            "Can't open `%s'", path);
                        return (ARCHIVE_FAILED);
@@ -949,7 +1024,7 @@ setup_sparse(struct archive_read_disk *a,
        off_s = 0;
        size = archive_entry_size(entry);
        while (off_s < size) {
-               off_s = lseek(fd, off_s, SEEK_DATA);
+               off_s = lseek(*fd, off_s, SEEK_DATA);
                if (off_s == (off_t)-1) {
                        if (errno == ENXIO)
                                break;/* no more hole */
@@ -958,10 +1033,10 @@ setup_sparse(struct archive_read_disk *a,
                        exit_sts = ARCHIVE_FAILED;
                        goto exit_setup_sparse;
                }
-               off_e = lseek(fd, off_s, SEEK_HOLE);
-               if (off_s == (off_t)-1) {
+               off_e = lseek(*fd, off_s, SEEK_HOLE);
+               if (off_e == (off_t)-1) {
                        if (errno == ENXIO) {
-                               off_e = lseek(fd, 0, SEEK_END);
+                               off_e = lseek(*fd, 0, SEEK_END);
                                if (off_e != (off_t)-1)
                                        break;/* no more data */
                        }
@@ -977,10 +1052,7 @@ setup_sparse(struct archive_read_disk *a,
                off_s = off_e;
        }
 exit_setup_sparse:
-       if (initial_fd != fd)
-               close(fd);
-       else
-               lseek(fd, initial_off, SEEK_SET);
+       lseek(*fd, initial_off, SEEK_SET);
        return (exit_sts);
 }
 
@@ -991,7 +1063,7 @@ exit_setup_sparse:
  */
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;     /* UNUSED */
        (void)entry; /* UNUSED */
index b81ab30..698600e 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
- * Copyright (c) 2010,2011 Michihiro NAKAJIMA
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -52,6 +52,19 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_LINUX_MAGIC_H
 #include <linux/magic.h>
 #endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>      /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
+#endif
 #ifdef HAVE_DIRECT_H
 #include <direct.h>
 #endif
@@ -76,6 +89,9 @@ __FBSDID("$FreeBSD$");
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
 
 #include "archive.h"
 #include "archive_string.h"
@@ -222,6 +238,7 @@ struct tree {
        char                     symlink_mode;
        struct filesystem       *current_filesystem;
        struct filesystem       *filesystem_table;
+       int                      initial_filesystem_id;
        int                      current_filesystem_id;
        int                      max_filesystem_id;
        int                      allocated_filesytem;
@@ -240,6 +257,7 @@ struct tree {
 #define        onWorkingDir    64 /* We are on the working dir where we are
                            * reading directory entry at this time. */
 #define        needsRestoreTimes 128
+#define        onInitialDir    256 /* We are on the initial dir. */
 
 static int
 tree_dir_next_posix(struct tree *t);
@@ -342,6 +360,7 @@ static const char *trivial_lookup_uname(void *, int64_t uid);
 static int     setup_sparse(struct archive_read_disk *, struct archive_entry *);
 static int     close_and_restore_time(int fd, struct tree *,
                    struct restore_time *);
+static int     open_on_current_dir(struct tree *, const char *, int);
 
 
 static struct archive_vtable *
@@ -430,16 +449,19 @@ archive_read_disk_new(void)
 {
        struct archive_read_disk *a;
 
-       a = (struct archive_read_disk *)malloc(sizeof(*a));
+       a = (struct archive_read_disk *)calloc(1, sizeof(*a));
        if (a == NULL)
                return (NULL);
-       memset(a, 0, sizeof(*a));
        a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
        a->archive.state = ARCHIVE_STATE_NEW;
        a->archive.vtable = archive_read_disk_vtable();
        a->lookup_uname = trivial_lookup_uname;
        a->lookup_gname = trivial_lookup_gname;
-       a->entry_wd_fd = -1;
+       a->enable_copyfile = 1;
+       a->traverse_mount_points = 1;
+       a->open_on_current_dir = open_on_current_dir;
+       a->tree_current_dir_fd = tree_current_dir_fd;
+       a->tree_enter_working_dir = tree_enter_working_dir;
        return (&a->archive);
 }
 
@@ -555,6 +577,37 @@ archive_read_disk_set_atime_restored(struct archive *_a)
 #endif
 }
 
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       int r = ARCHIVE_OK;
+
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+       if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+               r = archive_read_disk_set_atime_restored(_a);
+       else {
+               a->restore_time = 0;
+               if (a->tree != NULL)
+                       a->tree->flags &= ~needsRestoreTimes;
+       }
+       if (flags & ARCHIVE_READDISK_HONOR_NODUMP)
+               a->honor_nodump = 1;
+       else
+               a->honor_nodump = 0;
+       if (flags & ARCHIVE_READDISK_MAC_COPYFILE)
+               a->enable_copyfile = 1;
+       else
+               a->enable_copyfile = 0;
+       if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS)
+               a->traverse_mount_points = 0;
+       else
+               a->traverse_mount_points = 1;
+       return (r);
+}
+
 /*
  * Trivial implementations of gname/uname lookup functions.
  * These are normally overridden by the client, but these stub
@@ -685,13 +738,8 @@ _archive_read_data_block(struct archive *_a, const void **buff,
                        flags |= O_NOATIME;
                do {
 #endif
-#ifdef HAVE_OPENAT
-                       t->entry_fd = openat(tree_current_dir_fd(t),
+                       t->entry_fd = open_on_current_dir(t,
                            tree_current_access_path(t), flags);
-#else
-                       tree_enter_working_dir(t);
-                       t->entry_fd = open(tree_current_access_path(t), flags);
-#endif
 #if defined(O_NOATIME)
                        /*
                         * When we did open the file with O_NOATIME flag,
@@ -733,7 +781,7 @@ _archive_read_data_block(struct archive *_a, const void **buff,
        t->entry_buff_size = t->current_filesystem->buff_size;
 
        buffbytes = t->entry_buff_size;
-       if (buffbytes > t->current_sparse->length)
+       if ((int64_t)buffbytes > t->current_sparse->length)
                buffbytes = t->current_sparse->length;
 
        /*
@@ -802,29 +850,17 @@ abort_read_data:
 }
 
 static int
-_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+next_entry(struct archive_read_disk *a, struct tree *t,
+    struct archive_entry *entry)
 {
-       struct archive_read_disk *a = (struct archive_read_disk *)_a;
-       struct tree *t;
        const struct stat *st; /* info to use for this entry */
        const struct stat *lst;/* lstat() information */
-       int descend, fd = -1, r;
-
-       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
-           ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
-           "archive_read_next_header2");
+       const char *name;
+       int descend, r;
 
-       t = a->tree;
-       if (t->entry_fd >= 0) {
-               close_and_restore_time(t->entry_fd, t, &t->restore_time);
-               t->entry_fd = -1;
-       }
-#if !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR))
-       /* Restore working directory. */
-       tree_enter_working_dir(t);
-#endif
        st = NULL;
        lst = NULL;
+       t->descend = 0;
        do {
                switch (tree_next(t)) {
                case TREE_ERROR_FATAL:
@@ -859,6 +895,38 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
                }       
        } while (lst == NULL);
 
+#ifdef __APPLE__
+       if (a->enable_copyfile) {
+               /* If we're using copyfile(), ignore "._XXX" files. */
+               const char *bname = strrchr(tree_current_path(t), '/');
+               if (bname == NULL)
+                       bname = tree_current_path(t);
+               else
+                       ++bname;
+               if (bname[0] == '.' && bname[1] == '_')
+                       return (ARCHIVE_RETRY);
+       }
+#endif
+
+       archive_entry_copy_pathname(entry, tree_current_path(t));
+       /*
+        * Perform path matching.
+        */
+       if (a->matching) {
+               r = archive_match_path_excluded(a->matching, entry);
+               if (r < 0) {
+                       archive_set_error(&(a->archive), errno,
+                           "Faild : %s", archive_error_string(a->matching));
+                       return (r);
+               }
+               if (r) {
+                       if (a->excluded_cb_func)
+                               a->excluded_cb_func(&(a->archive),
+                                   a->excluded_cb_data, entry);
+                       return (ARCHIVE_RETRY);
+               }
+       }
+
        /*
         * Distinguish 'L'/'P'/'H' symlink following.
         */
@@ -897,13 +965,44 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
                tree_enter_initial_dir(t);
                return (ARCHIVE_FATAL);
        }
+       if (t->initial_filesystem_id == -1)
+               t->initial_filesystem_id = t->current_filesystem_id;
+       if (!a->traverse_mount_points) {
+               if (t->initial_filesystem_id != t->current_filesystem_id)
+                       return (ARCHIVE_RETRY);
+       }
        t->descend = descend;
 
-       archive_entry_set_pathname(entry, tree_current_path(t));
-       archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
+       /*
+        * Honor nodump flag.
+        * If the file is marked with nodump flag, do not return this entry.
+        */
+       if (a->honor_nodump) {
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+               if (st->st_flags & UF_NODUMP)
+                       return (ARCHIVE_RETRY);
+#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\
+      defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+               if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
+                       unsigned long stflags;
+
+                       t->entry_fd = open_on_current_dir(t,
+                           tree_current_access_path(t), O_RDONLY | O_NONBLOCK);
+                       if (t->entry_fd >= 0) {
+                               r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS,
+                                       &stflags);
+                               if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)
+                                       return (ARCHIVE_RETRY);
+                       }
+               }
+#endif
+       }
+
        archive_entry_copy_stat(entry, st);
 
-       /* Save the times to be restored. */
+       /* Save the times to be restored. This must be in before
+        * calling archive_read_disk_descend() or any chance of it,
+        * especially, invokng a callback. */
        t->restore_time.mtime = archive_entry_mtime(entry);
        t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);
        t->restore_time.atime = archive_entry_atime(entry);
@@ -911,39 +1010,102 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
        t->restore_time.filetype = archive_entry_filetype(entry);
        t->restore_time.noatime = t->current_filesystem->noatime;
 
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
        /*
-        * Open the current file to freely gather its metadata anywhere in
-        * working directory.
-        * Note: A symbolic link file cannot be opened with O_NOFOLLOW.
+        * Perform time matching.
         */
-       if (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)
-               fd = openat(tree_current_dir_fd(t), tree_current_access_path(t),
-                   O_RDONLY | O_NONBLOCK);
-       /* Restore working directory if openat() operation failed or
-        * the file is a symbolic link. */
-       if (fd < 0)
-               tree_enter_working_dir(t);
+       if (a->matching) {
+               r = archive_match_time_excluded(a->matching, entry);
+               if (r < 0) {
+                       archive_set_error(&(a->archive), errno,
+                           "Faild : %s", archive_error_string(a->matching));
+                       return (r);
+               }
+               if (r) {
+                       if (a->excluded_cb_func)
+                               a->excluded_cb_func(&(a->archive),
+                                   a->excluded_cb_data, entry);
+                       return (ARCHIVE_RETRY);
+               }
+       }
 
-       /* The current direcotry fd is needed at
-        * archive_read_disk_entry_from_file() function to read link data
-        * with readlinkat(). */
-       a->entry_wd_fd = tree_current_dir_fd(t);
-#endif
+       /* Lookup uname/gname */
+       name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+       if (name != NULL)
+               archive_entry_copy_uname(entry, name);
+       name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+       if (name != NULL)
+               archive_entry_copy_gname(entry, name);
+
+       /*
+        * Perform owner matching.
+        */
+       if (a->matching) {
+               r = archive_match_owner_excluded(a->matching, entry);
+               if (r < 0) {
+                       archive_set_error(&(a->archive), errno,
+                           "Faild : %s", archive_error_string(a->matching));
+                       return (r);
+               }
+               if (r) {
+                       if (a->excluded_cb_func)
+                               a->excluded_cb_func(&(a->archive),
+                                   a->excluded_cb_data, entry);
+                       return (ARCHIVE_RETRY);
+               }
+       }
+
+       /*
+        * Invoke a meta data filter callback.
+        */
+       if (a->metadata_filter_func) {
+               if (!a->metadata_filter_func(&(a->archive),
+                   a->metadata_filter_data, entry))
+                       return (ARCHIVE_RETRY);
+       }
 
        /*
         * Populate the archive_entry with metadata from the disk.
         */
-       r = archive_read_disk_entry_from_file(&(a->archive), entry, fd, st);
+       archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
+       r = archive_read_disk_entry_from_file(&(a->archive), entry,
+               t->entry_fd, st);
 
-       /* Close the file descriptor used for reding the current file
-        * metadata at archive_read_disk_entry_from_file(). */
-       if (fd >= 0)
-               close(fd);
+       return (r);
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       struct tree *t;
+       int r;
+
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+           "archive_read_next_header2");
+
+       t = a->tree;
+       if (t->entry_fd >= 0) {
+               close_and_restore_time(t->entry_fd, t, &t->restore_time);
+               t->entry_fd = -1;
+       }
+
+       for (;;) {
+               r = next_entry(a, t, entry);
+               if (t->entry_fd >= 0) {
+                       close(t->entry_fd);
+                       t->entry_fd = -1;
+               }
+
+               if (r == ARCHIVE_RETRY) {
+                       archive_entry_clear(entry);
+                       continue;
+               }
+               break;
+       }
 
        /* Return to the initial directory. */
        tree_enter_initial_dir(t);
-       archive_entry_copy_sourcepath(entry, tree_current_path(t));
 
        /*
         * EOF and FATAL are persistent at this layer.  By
@@ -956,6 +1118,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
                break;
        case ARCHIVE_OK:
        case ARCHIVE_WARN:
+               /* Overwrite the sourcepath based on the initial directory. */
+               archive_entry_copy_sourcepath(entry, tree_current_path(t));
                t->entry_total = 0;
                if (archive_entry_filetype(entry) == AE_IFREG) {
                        t->nlink = archive_entry_nlink(entry);
@@ -1018,6 +1182,48 @@ setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
        return (ARCHIVE_OK);
 }
 
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+    void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+    void *_client_data)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+       a->matching = _ma;
+       a->excluded_cb_func = _excluded_func;
+       a->excluded_cb_data = _client_data;
+       return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+    int (*_metadata_filter_func)(struct archive *, void *,
+    struct archive_entry *), void *_client_data)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+           "archive_read_disk_set_metadata_filter_callback");
+
+       a->metadata_filter_func = _metadata_filter_func;
+       a->metadata_filter_data = _client_data;
+       return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       struct tree *t = a->tree;
+
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+           "archive_read_disk_can_descend");
+
+       return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
 /*
  * Called by the client to mark the directory just returned from
  * tree_next() as needing to be visited.
@@ -1028,14 +1234,12 @@ archive_read_disk_descend(struct archive *_a)
        struct archive_read_disk *a = (struct archive_read_disk *)_a;
        struct tree *t = a->tree;
 
-       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+       archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
            "archive_read_disk_descend");
 
-       if (t->visit_type != TREE_REGULAR || !t->descend) {
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "Ignored the request descending the current object");
-               return (ARCHIVE_WARN);
-       }
+       if (t->visit_type != TREE_REGULAR || !t->descend)
+               return (ARCHIVE_OK);
 
        if (tree_current_is_physical_dir(t)) {
                tree_push(t, t->basename, t->current_filesystem_id,
@@ -1079,8 +1283,12 @@ archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
        archive_string_init(&path);
        if (archive_string_append_from_wcs(&path, pathname,
            wcslen(pathname)) != 0) {
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "Can't convert a path to a char string");
+               if (errno == ENOMEM)
+                       archive_set_error(&a->archive, ENOMEM,
+                           "Can't allocate memory");
+               else
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "Can't convert a path to a char string");
                a->archive.state = ARCHIVE_STATE_FATAL;
                ret = ARCHIVE_FATAL;
        } else
@@ -1268,7 +1476,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        t->current_filesystem->synthetic = -1;
        t->current_filesystem->remote = -1;
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1285,6 +1493,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statfs(tree_current_access_path(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, tree_current_access_path(t));
@@ -1334,9 +1546,13 @@ setup_current_filesystem(struct archive_read_disk *a)
        t->current_filesystem->name_max = sfs.f_namemax;
 #else
        /* Mac OS X does not have f_namemax in struct statfs. */
-       if (tree_current_is_symblic_link_target(t))
+       if (tree_current_is_symblic_link_target(t)) {
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
-       else
+       else
                nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
        if (nm == -1)
                t->current_filesystem->name_max = NAME_MAX;
@@ -1360,6 +1576,10 @@ setup_current_filesystem(struct archive_read_disk *a)
        int r, xr = 0;
 
        t->current_filesystem->synthetic = -1;
+       if (tree_enter_working_dir(t) != 0) {
+               archive_set_error(&a->archive, errno, "fchdir failed");
+               return (ARCHIVE_FAILED);
+       }
        if (tree_current_is_symblic_link_target(t)) {
                r = statvfs(tree_current_access_path(t), &sfs);
                if (r == 0)
@@ -1384,17 +1604,24 @@ setup_current_filesystem(struct archive_read_disk *a)
                 * for pathconf() function. */
                t->current_filesystem->xfer_align = sfs.f_frsize;
                t->current_filesystem->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
                t->current_filesystem->min_xfer_size = sfs.f_iosize;
                t->current_filesystem->incr_xfer_size = sfs.f_iosize;
+#else
+               t->current_filesystem->min_xfer_size = sfs.f_bsize;
+               t->current_filesystem->incr_xfer_size = sfs.f_bsize;
+#endif
        }
        if (sfs.f_flag & ST_LOCAL)
                t->current_filesystem->remote = 0;
        else
                t->current_filesystem->remote = 1;
 
+#if defined(ST_NOATIME)
        if (sfs.f_flag & ST_NOATIME)
                t->current_filesystem->noatime = 1;
        else
+#endif
                t->current_filesystem->noatime = 0;
 
        /* Set maximum filename length. */
@@ -1427,7 +1654,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        int r, vr = 0, xr = 0;
 
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1445,6 +1672,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                vr = statvfs(tree_current_access_path(t), &svfs);
                r = statfs(tree_current_access_path(t), &sfs);
                if (r == 0)
@@ -1456,9 +1687,11 @@ setup_current_filesystem(struct archive_read_disk *a)
                r = fstatfs(tree_current_dir_fd(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
-#elif defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-#error "Unexpected case. Please tell us about this error."
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                vr = statvfs(".", &svfs);
                r = statfs(".", &sfs);
                if (r == 0)
@@ -1529,7 +1762,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        t->current_filesystem->synthetic = -1;/* Not supported */
        t->current_filesystem->remote = -1;/* Not supported */
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1546,6 +1779,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statvfs(tree_current_access_path(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, tree_current_access_path(t));
@@ -1555,9 +1792,11 @@ setup_current_filesystem(struct archive_read_disk *a)
                r = fstatvfs(tree_current_dir_fd(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
-#elif defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-#error "Unexpected case. Please tell us about this error."
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statvfs(".", &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, ".");
@@ -1615,9 +1854,13 @@ setup_current_filesystem(struct archive_read_disk *a)
 #if defined(HAVE_READDIR_R)
        /* Set maximum filename length. */
 #  if defined(_PC_NAME_MAX)
-       if (tree_current_is_symblic_link_target(t))
+       if (tree_current_is_symblic_link_target(t)) {
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
-       else
+       else
                nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
        if (nm == -1)
 #  endif /* _PC_NAME_MAX */
@@ -1697,6 +1940,18 @@ close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)
        return (0);
 }
 
+static int
+open_on_current_dir(struct tree *t, const char *path, int flags)
+{
+#ifdef HAVE_OPENAT
+       return (openat(tree_current_dir_fd(t), path, flags));
+#else
+       if (tree_enter_working_dir(t) != 0)
+               return (-1);
+       return (open(path, flags));
+#endif
+}
+
 /*
  * Add a directory path to the current stack.
  */
@@ -1778,6 +2033,7 @@ static struct tree *
 tree_reopen(struct tree *t, const char *path, int restore_time)
 {
        t->flags = (restore_time)?needsRestoreTimes:0;
+       t->flags |= onInitialDir;
        t->visit_type = 0;
        t->tree_errno = 0;
        t->dirname_length = 0;
@@ -1790,6 +2046,7 @@ tree_reopen(struct tree *t, const char *path, int restore_time)
        t->entry_fd = -1;
        t->entry_eof = 0;
        t->entry_remaining_bytes = 0;
+       t->initial_filesystem_id = -1;
 
        /* First item is set up a lot like a symlink traversal. */
        tree_push(t, path, 0, 0, 0, NULL);
@@ -1803,12 +2060,14 @@ tree_reopen(struct tree *t, const char *path, int restore_time)
 static int
 tree_descent(struct tree *t)
 {
-       int r = 0;
+       int flag, new_fd, r = 0;
 
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-       int new_fd;
        t->dirname_length = archive_strlen(&t->path);
-       new_fd = openat(t->working_dir_fd, t->stack->name.s, O_RDONLY);
+       flag = O_RDONLY;
+#if defined(O_DIRECTORY)
+       flag |= O_DIRECTORY;
+#endif
+       new_fd = open_on_current_dir(t, t->stack->name.s, flag);
        if (new_fd < 0) {
                t->tree_errno = errno;
                r = TREE_ERROR_DIR;
@@ -1822,30 +2081,10 @@ tree_descent(struct tree *t)
                                t->maxOpenCount = t->openCount;
                } else
                        close(t->working_dir_fd);
+               /* Renew the current working directory. */
                t->working_dir_fd = new_fd;
+               t->flags &= ~onWorkingDir;
        }
-#else
-       /* If it is a link, set up fd for the ascent. */
-       if (t->stack->flags & isDirLink)
-               t->stack->symlink_parent_fd = t->working_dir_fd;
-       else {
-               close(t->working_dir_fd);
-               t->openCount--;
-       }
-       t->working_dir_fd = -1;
-       t->dirname_length = archive_strlen(&t->path);
-       if (chdir(t->stack->name.s) != 0)
-       {
-               t->tree_errno = errno;
-               r = TREE_ERROR_DIR;
-       } else {
-               t->depth++;
-               t->working_dir_fd = open(".", O_RDONLY);
-               t->openCount++;
-               if (t->openCount > t->maxOpenCount)
-                       t->maxOpenCount = t->openCount;
-       }
-#endif
        return (r);
 }
 
@@ -1856,37 +2095,21 @@ static int
 tree_ascend(struct tree *t)
 {
        struct tree_entry *te;
-       int r = 0, prev_dir_fd;
+       int new_fd, r = 0, prev_dir_fd;
 
        te = t->stack;
        prev_dir_fd = t->working_dir_fd;
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
        if (te->flags & isDirLink)
-               t->working_dir_fd = te->symlink_parent_fd;
-       else {
-               int new_fd = openat(t->working_dir_fd, "..", O_RDONLY);
-               if (new_fd < 0) {
-                       t->tree_errno = errno;
-                       r = TREE_ERROR_FATAL;
-               } else
-                       t->working_dir_fd = new_fd;
-       }
-#else
-       if (te->flags & isDirLink) {
-               if (fchdir(te->symlink_parent_fd) != 0) {
-                       t->tree_errno = errno;
-                       r = TREE_ERROR_FATAL;
-               } else
-                       t->working_dir_fd = te->symlink_parent_fd;
+               new_fd = te->symlink_parent_fd;
+       else
+               new_fd = open_on_current_dir(t, "..", O_RDONLY);
+       if (new_fd < 0) {
+               t->tree_errno = errno;
+               r = TREE_ERROR_FATAL;
        } else {
-               if (chdir("..") != 0) {
-                       t->tree_errno = errno;
-                       r = TREE_ERROR_FATAL;
-               } else
-                       t->working_dir_fd = open(".", O_RDONLY);
-       }
-#endif
-       if (r == 0) {
+               /* Renew the current working directory. */
+               t->working_dir_fd = new_fd;
+               t->flags &= ~onWorkingDir;
                /* Current directory has been changed, we should
                 * close an fd of previous working directory. */
                close_and_restore_time(prev_dir_fd, t, &te->restore_time);
@@ -1907,10 +2130,12 @@ tree_enter_initial_dir(struct tree *t)
 {
        int r = 0;
 
-       if (t->flags & onWorkingDir) {
+       if ((t->flags & onInitialDir) == 0) {
                r = fchdir(t->initial_dir_fd);
-               if (r == 0)
+               if (r == 0) {
                        t->flags &= ~onWorkingDir;
+                       t->flags |= onInitialDir;
+               }
        }
        return (r);
 }
@@ -1930,8 +2155,10 @@ tree_enter_working_dir(struct tree *t)
         */
        if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {
                r = fchdir(t->working_dir_fd);
-               if (r == 0)
+               if (r == 0) {
+                       t->flags &= ~onInitialDir;
                        t->flags |= onWorkingDir;
+               }
        }
        return (r);
 }
@@ -2040,7 +2267,8 @@ tree_dir_next_posix(struct tree *t)
 #if defined(HAVE_FDOPENDIR)
                if ((t->d = fdopendir(dup(t->working_dir_fd))) == NULL) {
 #else
-               if ((t->d = opendir(".")) == NULL) {
+               if (tree_enter_working_dir(t) != 0 ||
+                   (t->d = opendir(".")) == NULL) {
 #endif
                        r = tree_ascend(t); /* Undo "chdir" */
                        tree_pop(t);
@@ -2111,6 +2339,8 @@ tree_current_stat(struct tree *t)
                if (fstatat(tree_current_dir_fd(t),
                    tree_current_access_path(t), &t->st, 0) != 0)
 #else
+               if (tree_enter_working_dir(t) != 0)
+                       return NULL;
                if (stat(tree_current_access_path(t), &t->st) != 0)
 #endif
                        return NULL;
@@ -2131,6 +2361,8 @@ tree_current_lstat(struct tree *t)
                    tree_current_access_path(t), &t->lst,
                    AT_SYMLINK_NOFOLLOW) != 0)
 #else
+               if (tree_enter_working_dir(t) != 0)
+                       return NULL;
                if (lstat(tree_current_access_path(t), &t->lst) != 0)
 #endif
                        return NULL;
@@ -2152,7 +2384,10 @@ tree_current_is_dir(struct tree *t)
         */
        if (t->flags & hasLstat) {
                /* If lstat() says it's a dir, it must be a dir. */
-               if (S_ISDIR(tree_current_lstat(t)->st_mode))
+               st = tree_current_lstat(t);
+               if (st == NULL)
+                       return 0;
+               if (S_ISDIR(st->st_mode))
                        return 1;
                /* Not a dir; might be a link to a dir. */
                /* If it's not a link, then it's not a link to a dir. */
@@ -2186,9 +2421,13 @@ tree_current_is_physical_dir(struct tree *t)
         * If stat() says it isn't a dir, then it's not a dir.
         * If stat() data is cached, this check is free, so do it first.
         */
-       if ((t->flags & hasStat)
-           && (!S_ISDIR(tree_current_stat(t)->st_mode)))
-               return 0;
+       if (t->flags & hasStat) {
+               st = tree_current_stat(t);
+               if (st == NULL)
+                       return (0);
+               if (!S_ISDIR(st->st_mode))
+                       return (0);
+       }
 
        /*
         * Either stat() said it was a dir (in which case, we have
@@ -2214,7 +2453,8 @@ tree_target_is_same_as_parent(struct tree *t, const struct stat *st)
        struct tree_entry *te;
 
        for (te = t->current->parent; te != NULL; te = te->parent) {
-               if (te->dev == st->st_dev && te->ino == st->st_ino)
+               if (te->dev == (int64_t)st->st_dev &&
+                   te->ino == (int64_t)st->st_ino)
                        return (1);
        }
        return (0);
@@ -2231,7 +2471,8 @@ tree_current_is_symblic_link_target(struct tree *t)
 
        lst = tree_current_lstat(t);
        st = tree_current_stat(t);
-       return (st != NULL && st->st_dev == t->current_filesystem->dev &&
+       return (st != NULL && lst != NULL &&
+           (int64_t)st->st_dev == t->current_filesystem->dev &&
            st->st_dev != lst->st_dev);
 }
 
index 4446474..e5af16b 100644 (file)
@@ -34,6 +34,7 @@
 #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
 
 struct tree;
+struct archive_entry;
 
 struct archive_read_disk {
        struct archive  archive;
@@ -55,10 +56,18 @@ struct archive_read_disk {
 
        /* Directory traversals. */
        struct tree *tree;
+       int     (*open_on_current_dir)(struct tree*, const char *, int);
+       int     (*tree_current_dir_fd)(struct tree*);
+       int     (*tree_enter_working_dir)(struct tree*);
 
        /* Set 1 if users request to restore atime . */
        int              restore_time;
-       int              entry_wd_fd;
+       /* Set 1 if users request to honor nodump flag . */
+       int              honor_nodump;
+       /* Set 1 if users request to enable mac copyfile. */
+       int              enable_copyfile;
+       /* Set 1 if users request to traverse mount points. */
+       int              traverse_mount_points;
 
        const char * (*lookup_gname)(void *private, int64_t gid);
        void    (*cleanup_gname)(void *private);
@@ -66,6 +75,18 @@ struct archive_read_disk {
        const char * (*lookup_uname)(void *private, int64_t uid);
        void    (*cleanup_uname)(void *private);
        void     *lookup_uname_data;
+
+       int     (*metadata_filter_func)(struct archive *, void *,
+                       struct archive_entry *);
+       void    *metadata_filter_data;
+
+       /* ARCHIVE_MATCH object. */
+       struct archive  *matching;
+       /* Callback function, this will be invoked when ARCHIVE_MATCH
+        * archive_match_*_excluded_ae return true. */
+       void    (*excluded_cb_func)(struct archive *, void *,
+                        struct archive_entry *);
+       void    *excluded_cb_data;
 };
 
 #endif
index d8f6572..7972841 100644 (file)
@@ -129,8 +129,8 @@ static int64_t
 file_skip(struct archive *a, void *client_data, int64_t request)
 {
        struct read_fd_data *mine = (struct read_fd_data *)client_data;
-       off_t skip = (off_t)request;
-       off_t old_offset, new_offset;
+       int64_t skip = request;
+       int64_t old_offset, new_offset;
        int skip_bits = sizeof(skip) * 8 - 1;  /* off_t is a signed type. */
 
        if (!mine->use_lseek)
index bf52697..69c44ea 100644 (file)
@@ -130,9 +130,13 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
                archive_string_init(&fn);
                if (archive_string_append_from_wcs(&fn, wfilename,
                    wcslen(wfilename)) != 0) {
-                       archive_set_error(a, EINVAL,
-                           "Failed to convert a wide-character filename to"
-                           " a multi-byte filename");
+                       if (errno == ENOMEM)
+                               archive_set_error(a, errno,
+                                   "Can't allocate memory");
+                       else
+                               archive_set_error(a, EINVAL,
+                                   "Failed to convert a wide-character"
+                                   " filename to a multi-byte filename");
                        archive_string_free(&fn);
                        return (ARCHIVE_FATAL);
                }
@@ -450,7 +454,7 @@ static int64_t
 file_seek(struct archive *a, void *client_data, int64_t request, int whence)
 {
        struct read_file_data *mine = (struct read_file_data *)client_data;
-       off_t r;
+       int64_t r;
 
        /* We use off_t here because lseek() is declared that way. */
        /* See above for notes about when off_t is less than 64 bits. */
index 07940a2..bcc7d6f 100644 (file)
@@ -149,6 +149,7 @@ memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whenc
 {
        struct read_memory_data *mine = (struct read_memory_data *)client_data;
 
+       (void)a; /* UNUSED */
        switch (whence) {
        case SEEK_SET:
                mine->p = mine->start + offset;
index 76d0b91..445c557 100644 (file)
@@ -134,8 +134,8 @@ struct archive_read {
 
        /* Dev/ino of the archive being read/written. */
        int               skip_file_set;
-       dev_t             skip_file_dev;
-       ino_t             skip_file_ino;
+       int64_t           skip_file_dev;
+       int64_t           skip_file_ino;
 
        /*
         * Used by archive_read_data() to track blocks and copy
index 81efb08..6fe9f90 100644 (file)
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd April 13, 2009
+.Dd February 2, 2012
 .Dt ARCHIVE_READ_OPTIONS 3
 .Os
 .Sh NAME
@@ -34,6 +34,8 @@
 .Nm archive_read_set_options
 .Nd functions controlling options for reading archives
 .\"
+.Sh LIBRARY
+Streaming Archive Library (libarchive, -larchive)
 .Sh SYNOPSIS
 .Ft int
 .Fo archive_read_set_filter_option
index d6a5f45..793f8f7 100644 (file)
@@ -78,7 +78,7 @@ archive_set_format_option(struct archive *_a, const char *m, const char *o,
        struct archive_read *a = (struct archive_read *)_a;
        struct archive_format_descriptor *format;
        size_t i;
-       int r, rv = ARCHIVE_FAILED;
+       int r, rv = ARCHIVE_WARN;
 
        for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {
                format = &a->formats[i];
@@ -102,6 +102,10 @@ archive_set_format_option(struct archive *_a, const char *m, const char *o,
                if (r == ARCHIVE_OK)
                        rv = ARCHIVE_OK;
        }
+       /* If the format name didn't match, return a special code for
+        * _archive_set_option[s]. */
+       if (rv == ARCHIVE_WARN && m != NULL)
+               rv = ARCHIVE_WARN - 1;
        return (rv);
 }
 
@@ -112,7 +116,7 @@ archive_set_filter_option(struct archive *_a, const char *m, const char *o,
        struct archive_read *a = (struct archive_read *)_a;
        struct archive_read_filter *filter;
        struct archive_read_filter_bidder *bidder;
-       int r, rv = ARCHIVE_FAILED;
+       int r, rv = ARCHIVE_WARN;
 
        for (filter = a->filter; filter != NULL; filter = filter->upstream) {
                bidder = filter->bidder;
@@ -135,6 +139,10 @@ archive_set_filter_option(struct archive *_a, const char *m, const char *o,
                if (r == ARCHIVE_OK)
                        rv = ARCHIVE_OK;
        }
+       /* If the filter name didn't match, return a special code for
+        * _archive_set_option[s]. */
+       if (rv == ARCHIVE_WARN && m != NULL)
+               rv = ARCHIVE_WARN - 1;
        return (rv);
 }
 
index 7dbfc0e..1b3e124 100644 (file)
@@ -188,7 +188,7 @@ rpm_filter_read(struct archive_read_filter *self, const void **buff)
                        if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
                                used += avail_in;
                        else {
-                               n = RPM_LEAD_SIZE - rpm->total_in;
+                               n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
                                used += n;
                                b += n;
                                rpm->state = ST_HEADER;
index e211e67..39a46ed 100644 (file)
@@ -481,7 +481,7 @@ check_7zip_header_in_sfx(const char *p)
                 * Magic Code, so we should do this in order not to
                 * make a mis-detection.
                 */
-               if (crc32(0, (unsigned char *)p + 12, 20)
+               if (crc32(0, (const unsigned char *)p + 12, 20)
                        != archive_le32dec(p + 8))
                        return (6); 
                /* Hit the header! */
@@ -580,7 +580,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
                free_Header(&header);
                if (r != ARCHIVE_OK)
                        return (r);
-               zip->entries_remaining = zip->numFiles;
+               zip->entries_remaining = (size_t)zip->numFiles;
                zip->entry = zip->entries;
        } else {
                ++zip->entry;
@@ -630,7 +630,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
        if (zip_entry->flg & ATIME_IS_SET)
                archive_entry_set_atime(entry, zip_entry->atime,
                    zip_entry->atime_ns);
-       if (zip_entry->ssIndex != -1) {
+       if (zip_entry->ssIndex != (uint32_t)-1) {
                zip->entry_bytes_remaining =
                    zip->si.ss.unpackSizes[zip_entry->ssIndex];
                archive_entry_set_size(entry, zip->entry_bytes_remaining);
@@ -646,7 +646,6 @@ archive_read_format_7zip_read_header(struct archive_read *a,
        if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {
                unsigned char *symname = NULL;
                size_t symsize = 0;
-               int r;
 
                /*
                 * Symbolic-name is recorded as its contents. We have to
@@ -654,19 +653,24 @@ archive_read_format_7zip_read_header(struct archive_read *a,
                 */
                while (zip->entry_bytes_remaining > 0) {
                        const void *buff;
+                       unsigned char *mem;
                        size_t size;
                        int64_t offset;
 
                        r = archive_read_format_7zip_read_data(a, &buff,
                                &size, &offset);
-                       if (r < ARCHIVE_WARN)
+                       if (r < ARCHIVE_WARN) {
+                               free(symname);
                                return (r);
-                       symname = realloc(symname, symsize + size + 1);
-                       if (symname == NULL) {
+                       }
+                       mem = realloc(symname, symsize + size + 1);
+                       if (mem == NULL) {
+                               free(symname);
                                archive_set_error(&a->archive, ENOMEM,
                                    "Can't allocate memory for Symname");
                                return (ARCHIVE_FATAL);
                        }
+                       symname = mem;
                        memcpy(symname+symsize, buff, size);
                        symsize += size;
                }
@@ -716,7 +720,8 @@ archive_read_format_7zip_read_data(struct archive_read *a,
                return (ARCHIVE_EOF);
        }
 
-       bytes = read_stream(a, buff, zip->entry_bytes_remaining, 0);
+       bytes = read_stream(a, buff,
+               (size_t)zip->entry_bytes_remaining, 0);
        if (bytes < 0)
                return ((int)bytes);
        if (bytes == 0) {
@@ -774,7 +779,7 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a)
         * If the length is at the beginning, we can skip the
         * compressed data much more quickly.
         */
-       bytes_skipped = skip_stream(a, zip->entry_bytes_remaining);
+       bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
        if (bytes_skipped < 0)
                return (ARCHIVE_FATAL);
        zip->entry_bytes_remaining = 0;
@@ -1054,7 +1059,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
                ff = &filters[fi];
 #endif
                r = lzma_properties_decode(&filters[fi], NULL,
-                   coder1->properties, coder1->propertiesSize);
+                   coder1->properties, (size_t)coder1->propertiesSize);
                if (r != LZMA_OK) {
                        set_error(a, r);
                        return (ARCHIVE_FAILED);
@@ -1442,8 +1447,8 @@ decompress(struct archive_read *a, struct _7zip *zip,
                } while (zip->ppstream.avail_out &&
                        (zip->ppstream.avail_in || flush_bytes));
 
-               t_avail_in = zip->ppstream.avail_in;
-               t_avail_out = zip->ppstream.avail_out;
+               t_avail_in = (size_t)zip->ppstream.avail_in;
+               t_avail_out = (size_t)zip->ppstream.avail_out;
                break;
        }
        default:
@@ -1506,6 +1511,10 @@ free_decompression(struct archive_read *a, struct _7zip *zip)
 {
        int r = ARCHIVE_OK;
 
+#if !defined(HAVE_ZLIB_H) &&\
+       !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))
+       (void)a;/* UNUSED */
+#endif
 #ifdef HAVE_LZMA_H
        if (zip->lzstream_valid)
                lzma_end(&(zip->lzstream));
@@ -1672,8 +1681,8 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
                return (0);
        if (*p != kSize)
                return (-1);
-       pi->sizes = calloc(pi->numPackStreams, sizeof(uint64_t));
-       pi->positions = calloc(pi->numPackStreams, sizeof(uint64_t));
+       pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+       pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
        if (pi->sizes == NULL || pi->positions == NULL)
                return (-1);
 
@@ -1690,9 +1699,9 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
        if (*p == kEnd) {
                /* PackStreamDigests[num] are not present. */
                pi->digest.defineds =
-                   calloc(pi->numPackStreams, sizeof(*pi->digest.defineds));
+                   calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
                pi->digest.digests =
-                   calloc(pi->numPackStreams, sizeof(*pi->digest.digests));
+                   calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
                if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
                        return (-1);
                return (0);
@@ -1701,7 +1710,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
        if (*p != kSize)
                return (-1);
 
-       if (read_Digests(a, &(pi->digest), pi->numPackStreams) < 0)
+       if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
                return (-1);
 
        /*
@@ -1750,7 +1759,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
                /* Too many coders. */
                return (-1);
 
-       f->coders = calloc(f->numCoders, sizeof(*f->coders));
+       f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
        if (f->coders == NULL)
                return (-1);
        for (i = 0; i< f->numCoders; i++) {
@@ -1802,14 +1811,14 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
                            a, &(f->coders[i].propertiesSize)) < 0)
                                return (-1);
                        if ((p = header_bytes(
-                           a, f->coders[i].propertiesSize)) == NULL)
+                           a, (size_t)f->coders[i].propertiesSize)) == NULL)
                                return (-1);
                        f->coders[i].properties =
-                           malloc(f->coders[i].propertiesSize);
+                           malloc((size_t)f->coders[i].propertiesSize);
                        if (f->coders[i].properties == NULL)
                                return (-1);
                        memcpy(f->coders[i].properties, p,
-                           f->coders[i].propertiesSize);
+                           (size_t)f->coders[i].propertiesSize);
                }
 
                numInStreamsTotal += f->coders[i].numInStreams;
@@ -1823,9 +1832,13 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
        f->numBindPairs = numOutStreamsTotal - 1;
        if (zip->header_bytes_remaining < f->numBindPairs)
                        return (-1);
-       f->bindPairs = calloc(f->numBindPairs, sizeof(*f->bindPairs));
-       if (f->bindPairs == NULL)
-               return (-1);
+       if (f->numBindPairs > 0) {
+               f->bindPairs =
+                       calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+               if (f->bindPairs == NULL)
+                       return (-1);
+       } else
+               f->bindPairs = NULL;
        for (i = 0; i < f->numBindPairs; i++) {
                if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
                        return (-1);
@@ -1839,7 +1852,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
 
        f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
        f->packedStreams =
-           calloc(f->numPackedStreams, sizeof(*f->packedStreams));
+           calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
        if (f->packedStreams == NULL)
                return (-1);
        if (f->numPackedStreams == 1) {
@@ -1911,7 +1924,8 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
                goto failed;
        switch (*p) {
        case 0:
-               ci->folders = calloc(ci->numFolders, sizeof(*ci->folders));
+               ci->folders =
+                       calloc((size_t)ci->numFolders, sizeof(*ci->folders));
                if (ci->folders == NULL)
                        return (-1);
                for (i = 0; i < ci->numFolders; i++) {
@@ -1937,7 +1951,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
                unsigned j;
 
                folder->unPackSize =
-                   calloc(folder->numOutStreams, sizeof(*folder->unPackSize));
+                   calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
                if (folder->unPackSize == NULL)
                        goto failed;
                for (j = 0; j < folder->numOutStreams; j++) {
@@ -1955,7 +1969,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
                return (0);
        if (*p != kCRC)
                goto failed;
-       if (read_Digests(a, &digest, ci->numFolders) < 0)
+       if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
                goto failed;
        for (i = 0; i < ci->numFolders; i++) {
                ci->folders[i].digest_defined = digest.defineds[i];
@@ -1979,13 +1993,13 @@ failed:
 static uint64_t
 folder_uncompressed_size(struct _7z_folder *f)
 {
-       int n = f->numOutStreams;
-       unsigned pairs = f->numBindPairs;
+       int n = (int)f->numOutStreams;
+       unsigned pairs = (unsigned)f->numBindPairs;
 
        while (--n >= 0) {
                unsigned i;
                for (i = 0; i < pairs; i++) {
-                       if (f->bindPairs[i].outIndex == n)
+                       if (f->bindPairs[i].outIndex == (uint64_t)n)
                                break;
                }
                if (i >= pairs)
@@ -2029,7 +2043,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
                                return (-1);
                        if (1000000 < f[i].numUnpackStreams)
                                return (-1);
-                       unpack_streams += f[i].numUnpackStreams;
+                       unpack_streams += (size_t)f[i].numUnpackStreams;
                }
                if ((p = header_bytes(a, 1)) == NULL)
                        return (-1);
@@ -2083,7 +2097,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
        numDigests = 0;
        for (i = 0; i < numFolders; i++) {
                if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
-                       numDigests += f[i].numUnpackStreams;
+                       numDigests += (uint32_t)f[i].numUnpackStreams;
        }
 
        if (type == kCRC) {
@@ -2181,7 +2195,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
                f = si->ci.folders;
                for (i = 0; i < si->ci.numFolders; i++) {
                        f[i].packIndex = packIndex;
-                       packIndex += f[i].numPackedStreams;
+                       packIndex += (uint32_t)f[i].numPackedStreams;
                        if (packIndex > si->pi.numPackStreams)
                                return (-1);
                }
@@ -2191,7 +2205,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
 
        if (*p == kSubStreamsInfo) {
                if (read_SubStreamsInfo(a, &(si->ss),
-                   si->ci.folders, si->ci.numFolders) < 0)
+                   si->ci.folders, (size_t)si->ci.numFolders) < 0)
                        return (-1);
                if ((p = header_bytes(a, 1)) == NULL)
                        return (-1);
@@ -2279,7 +2293,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
        if (1000000 < zip->numFiles)
                        return (-1);
 
-       zip->entries = calloc(zip->numFiles, sizeof(*zip->entries));
+       zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
        if (zip->entries == NULL)
                return (-1);
        entries = zip->entries;
@@ -2304,12 +2318,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 
                switch (type) {
                case kEmptyStream:
-                       h->emptyStreamBools = calloc(zip->numFiles,
+                       h->emptyStreamBools = calloc((size_t)zip->numFiles,
                            sizeof(*h->emptyStreamBools));
                        if (h->emptyStreamBools == NULL)
                                return (-1);
                        if (read_Bools(
-                           a, h->emptyStreamBools, zip->numFiles) < 0)
+                           a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
                                return (-1);
                        empty_streams = 0;
                        for (i = 0; i < zip->numFiles; i++) {
@@ -2318,6 +2332,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
                        }
                        break;
                case kEmptyFile:
+                       if (empty_streams <= 0) {
+                               /* Unexcepted sequence. Skip this. */
+                               if (header_bytes(a, ll) == NULL)
+                                       return (-1);
+                               break;
+                       }
                        h->emptyFileBools = calloc(empty_streams,
                            sizeof(*h->emptyFileBools));
                        if (h->emptyFileBools == NULL)
@@ -2326,6 +2346,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
                                return (-1);
                        break;
                case kAnti:
+                       if (empty_streams <= 0) {
+                               /* Unexcepted sequence. Skip this. */
+                               if (header_bytes(a, ll) == NULL)
+                                       return (-1);
+                               break;
+                       }
                        h->antiBools = calloc(empty_streams,
                            sizeof(*h->antiBools));
                        if (h->antiBools == NULL)
@@ -2404,15 +2430,15 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
                        if ((p = header_bytes(a, 2)) == NULL)
                                return (-1);
                        allAreDefined = *p;
-                       h->attrBools = calloc(zip->numFiles,
+                       h->attrBools = calloc((size_t)zip->numFiles,
                            sizeof(*h->attrBools));
                        if (h->attrBools == NULL)
                                return (-1);
                        if (allAreDefined)
-                               memset(h->attrBools, 1, zip->numFiles);
+                               memset(h->attrBools, 1, (size_t)zip->numFiles);
                        else {
                                if (read_Bools(a, h->attrBools,
-                                     zip->numFiles) < 0)
+                                     (size_t)zip->numFiles) < 0)
                                        return (-1);
                        }
                        for (i = 0; i < zip->numFiles; i++) {
@@ -2446,7 +2472,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
                        if ((size_t)sindex >= si->ss.unpack_streams)
                                return (-1);
                        if (entries[i].mode == 0)
-                               entries[i].mode = AE_IFREG | 0777;
+                               entries[i].mode = AE_IFREG | 0666;
                        if (si->ss.digestsDefined[sindex])
                                entries[i].flg |= CRC32_IS_SET;
                        entries[i].ssIndex = sindex;
@@ -2466,7 +2492,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
                                if (dir)
                                        entries[i].mode = AE_IFDIR | 0777;
                                else
-                                       entries[i].mode = AE_IFREG | 0777;
+                                       entries[i].mode = AE_IFREG | 0666;
                        } else if (dir &&
                            (entries[i].mode & AE_IFMT) != AE_IFDIR) {
                                entries[i].mode &= ~AE_IFMT;
@@ -2517,17 +2543,17 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
 
 #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
 static void
-fileTimeToUtc(uint64_t fileTime, time_t *time, long *ns)
+fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)
 {
 
        if (fileTime >= EPOC_TIME) {
                fileTime -= EPOC_TIME;
                /* milli seconds base */
-               *time = (time_t)(fileTime / 10000000);
+               *timep = (time_t)(fileTime / 10000000);
                /* nano seconds base */
                *ns = (long)(fileTime % 10000000) * 100;
        } else {
-               *time = 0;
+               *timep = 0;
                *ns = 0;
        }
 }
@@ -2542,7 +2568,7 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
        int allAreDefined;
        unsigned i;
 
-       timeBools = calloc(zip->numFiles, sizeof(*timeBools));
+       timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
        if (timeBools == NULL)
                return (-1);
 
@@ -2551,9 +2577,9 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
                goto failed;
        allAreDefined = *p;
        if (allAreDefined)
-               memset(timeBools, 1, zip->numFiles);
+               memset(timeBools, 1, (size_t)zip->numFiles);
        else {
-               if (read_Bools(a, timeBools, zip->numFiles) < 0)
+               if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
                        goto failed;
        }
 
@@ -2564,7 +2590,7 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
                if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
                        goto failed;
                if (1000000 < h->dataIndex)
-                       return (-1);
+                       goto failed;
        }
 
        for (i = 0; i < zip->numFiles; i++) {
@@ -2695,7 +2721,8 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
        }
 
        /* CRC check. */
-       if (crc32(0, (unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) {
+       if (crc32(0, (const unsigned char *)p + 12, 20)
+           != archive_le32dec(p + 8)) {
                archive_set_error(&a->archive, -1, "Header CRC error");
                return (ARCHIVE_FATAL);
        }
@@ -2714,7 +2741,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
        }
        __archive_read_consume(a, 32);
        if (next_header_offset != 0) {
-               if (bytes_avail >= next_header_offset)
+               if (bytes_avail >= (ssize_t)next_header_offset)
                        __archive_read_consume(a, next_header_offset);
                else if (__archive_read_seek(a,
                    next_header_offset + zip->seek_base, SEEK_SET) < 0)
@@ -2827,7 +2854,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
        struct _7zip *zip = (struct _7zip *)a->format->data;
        ssize_t bytes_avail;
 
-       if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
+       if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
                /* Copy mode. */
 
                /*
@@ -2886,7 +2913,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
        ssize_t bytes_avail;
        int r;
 
-       if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
+       if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
                if (minimum == 0)
                        minimum = 1;
                if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
@@ -2896,11 +2923,11 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
                            "Truncated 7-Zip file body");
                        return (ARCHIVE_FATAL);
                }
-               if (bytes_avail > zip->pack_stream_inbytes_remaining)
-                       bytes_avail = zip->pack_stream_inbytes_remaining;
+               if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining)
+                       bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
                zip->pack_stream_inbytes_remaining -= bytes_avail;
-               if (bytes_avail > zip->folder_outbytes_remaining)
-                       bytes_avail = zip->folder_outbytes_remaining;
+               if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining)
+                       bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
                zip->folder_outbytes_remaining -= bytes_avail;
                zip->uncompressed_buffer_bytes_remaining = bytes_avail;
                return (ARCHIVE_OK);
@@ -2965,7 +2992,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
                size_t bytes_in, bytes_out;
                const void *buff_in;
                unsigned char *buff_out;
-               int eof;
+               int end_of_data;
 
                /*
                 * Note: '1' here is a performance optimization.
@@ -2987,23 +3014,23 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
                        - zip->uncompressed_buffer_bytes_remaining;
                bytes_in = bytes_avail;
                if (bytes_in > zip->pack_stream_inbytes_remaining)
-                       bytes_in = zip->pack_stream_inbytes_remaining;
+                       bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
                /* Drive decompression. */
                r = decompress(a, zip, buff_out, &bytes_out,
                        buff_in, &bytes_in);
                switch (r) {
                case ARCHIVE_OK:
-                       eof = 0;
+                       end_of_data = 0;
                        break;
                case ARCHIVE_EOF:
-                       eof = 1;
+                       end_of_data = 1;
                        break;
                default:
                        return (ARCHIVE_FATAL);
                }
                zip->pack_stream_inbytes_remaining -= bytes_in;
                if (bytes_out > zip->folder_outbytes_remaining)
-                       bytes_out = zip->folder_outbytes_remaining;
+                       bytes_out = (size_t)zip->folder_outbytes_remaining;
                zip->folder_outbytes_remaining -= bytes_out;
                zip->uncompressed_buffer_bytes_remaining += bytes_out;
                zip->pack_stream_bytes_unconsumed = bytes_in;
@@ -3021,7 +3048,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
                if (zip->pack_stream_inbytes_remaining == 0 &&
                    zip->folder_outbytes_remaining == 0)
                        break;
-               if (eof || (bytes_in == 0 && bytes_out == 0)) {
+               if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {
                        archive_set_error(&(a->archive),
                            ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
                        return (ARCHIVE_FATAL);
@@ -3041,7 +3068,7 @@ static int
 seek_pack(struct archive_read *a)
 {
        struct _7zip *zip = (struct _7zip *)a->format->data;
-       uint64_t pack_offset;
+       int64_t pack_offset;
 
        if (zip->pack_stream_remaining <= 0) {
                archive_set_error(&(a->archive),
@@ -3160,7 +3187,8 @@ read_stream(struct archive_read *a, const void **buff, size_t size,
                                return (ARCHIVE_FATAL);
                        }
                }
-               skipped = get_uncompressed_data(a, buff, skip_bytes, 0);
+               skipped = get_uncompressed_data(
+                       a, buff, (size_t)skip_bytes, 0);
                if (skipped < 0)
                        return (skipped);
                skip_bytes -= skipped;
@@ -3292,13 +3320,13 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
                        }
                        coder2 = &(fc[3]);
                        zip->main_stream_bytes_remaining =
-                               folder->unPackSize[2];
+                               (size_t)folder->unPackSize[2];
                } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
                    zip->pack_stream_remaining == 4 &&
                    folder->numInStreams == 5 && folder->numOutStreams == 2) {
                        /* Source type 0 made by 7z */
                        zip->main_stream_bytes_remaining =
-                               folder->unPackSize[0];
+                               (size_t)folder->unPackSize[0];
                } else {
                        /* We got an unexpected form. */
                        archive_set_error(&(a->archive),
@@ -3311,7 +3339,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
                if ((r = seek_pack(a)) < 0)
                        return (r);
                zip->pack_stream_bytes_unconsumed =
-                   zip->pack_stream_inbytes_remaining;
+                   (size_t)zip->pack_stream_inbytes_remaining;
                read_consume(a);
 
                /* Read following three sub streams. */
@@ -3321,7 +3349,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
                        if ((r = seek_pack(a)) < 0)
                                return (r);
 
-                       if (sunpack[i] == -1)
+                       if (sunpack[i] == (uint64_t)-1)
                                zip->folder_outbytes_remaining =
                                    zip->pack_stream_inbytes_remaining;
                        else
@@ -3333,7 +3361,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
 
                        /* Allocate memory for the decorded data of a sub
                         * stream. */
-                       b[i] = malloc(zip->folder_outbytes_remaining);
+                       b[i] = malloc((size_t)zip->folder_outbytes_remaining);
                        if (b[i] == NULL) {
                                archive_set_error(&a->archive, ENOMEM,
                                    "No memory for 7-Zip decompression");
@@ -3428,7 +3456,7 @@ skip_stream(struct archive_read *a, size_t skip_bytes)
                            "Truncated 7-Zip file body");
                        return (ARCHIVE_FATAL);
                }
-               bytes -= skipped_bytes;
+               bytes -= (size_t)skipped_bytes;
                if (zip->pack_stream_bytes_unconsumed)
                        read_consume(a);
        }
@@ -3506,16 +3534,16 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
                        uint32_t dest;
                        for (;;) {
                                uint8_t b;
-                               int index;
+                               int b_index;
 
                                dest = src - (ip + (uint32_t)bufferPos);
                                if (prevMask == 0)
                                        break;
-                               index = kMaskToBitNumber[prevMask] * 8;
-                               b = (uint8_t)(dest >> (24 - index));
+                               b_index = kMaskToBitNumber[prevMask] * 8;
+                               b = (uint8_t)(dest >> (24 - b_index));
                                if (!Test86MSByte(b))
                                        break;
-                               src = dest ^ ((1 << (32 - index)) - 1);
+                               src = dest ^ ((1 << (32 - b_index)) - 1);
                        }
                        p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
                        p[3] = (uint8_t)(dest >> 16);
@@ -3556,7 +3584,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
 #define RC_READ_BYTE (*buffer++)
 #define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
 #define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \
-  { int i; for (i = 0; i < 5; i++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
+  { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
 
 #define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }
 
@@ -3622,14 +3650,14 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
 
                if (zip->bcj_state == 1) {
                        while (limit != 0) {
-                               uint8_t b = buf0[inPos];
-                               outBuf[outPos++] = b;
-                               if (IsJ(zip->bcj2_prevByte, b)) {
+                               uint8_t bb = buf0[inPos];
+                               outBuf[outPos++] = bb;
+                               if (IsJ(zip->bcj2_prevByte, bb)) {
                                        zip->bcj_state = 2;
                                        break;
                                }
                                inPos++;
-                               zip->bcj2_prevByte = b;
+                               zip->bcj2_prevByte = bb;
                                limit--;
                        }
                }
index 1b81ee9..aa0152a 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2010-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -292,6 +292,8 @@ struct cab {
        char                     end_of_archive;
        char                     end_of_entry;
        char                     end_of_entry_cleanup;
+       char                     read_data_invoked;
+       int64_t                  bytes_skipped;
 
        unsigned char           *uncompressed_buffer;
        size_t                   uncompressed_buffer_size;
@@ -349,7 +351,7 @@ static int  lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
 static int     lzx_huffman_init(struct huffman *, size_t, int);
 static void    lzx_huffman_free(struct huffman *);
 static int     lzx_make_huffman_table(struct huffman *);
-static int inline lzx_decode_huffman(struct huffman *, unsigned);
+static inline int lzx_decode_huffman(struct huffman *, unsigned);
 static int     lzx_decode_huffman_tree(struct huffman *, unsigned, int);
 
 
@@ -478,11 +480,13 @@ archive_read_format_cab_options(struct archive_read *a,
                        else
                                ret = ARCHIVE_FATAL;
                }
-       } else
-               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                   "cab: unknown keyword ``%s''", key);
+               return (ret);
+       }
 
-       return (ret);
+       /* Note: The "warn" return is just to inform the options
+        * supervisor that we didn't handle it.  It will generate
+        * a suitable error if no one used this option. */
+       return (ARCHIVE_WARN);
 }
 
 static int
@@ -796,7 +800,7 @@ cab_read_header(struct archive_read *a)
                file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);
                file->folder = archive_le16dec(p + CFFILE_iFolder);
                file->mtime = cab_dos_time(p + CFFILE_date_time);
-               file->attr = archive_le16dec(p + CFFILE_attribs);
+               file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);
                __archive_read_consume(a, 16);
 
                cab->cab_offset += 16;
@@ -986,7 +990,7 @@ archive_read_format_cab_read_header(struct archive_read *a,
        if (file->attr & ATTR_RDONLY)
                archive_entry_set_mode(entry, AE_IFREG | 0555);
        else
-               archive_entry_set_mode(entry, AE_IFREG | 0777);
+               archive_entry_set_mode(entry, AE_IFREG | 0666);
        archive_entry_set_mtime(entry, file->mtime, 0);
 
        cab->entry_bytes_remaining = file->uncompressed_size;
@@ -1024,9 +1028,22 @@ archive_read_format_cab_read_data(struct archive_read *a,
        default:
                break;
        }
+       if (cab->read_data_invoked == 0) {
+               if (cab->bytes_skipped) {
+                       if (cab->entry_cfdata == NULL) {
+                               r = cab_next_cfdata(a);
+                               if (r < 0)
+                                       return (r);
+                       }
+                       if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)
+                               return (ARCHIVE_FATAL);
+                       cab->bytes_skipped = 0;
+               }
+               cab->read_data_invoked = 1;
+       }
        if (cab->entry_unconsumed) {
                /* Consume as much as the compressor actually used. */
-               r = cab_consume_cfdata(a, cab->entry_unconsumed);
+               r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
                cab->entry_unconsumed = 0;
                if (r < 0)
                        return (r);
@@ -1356,46 +1373,25 @@ cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
        struct cab *cab = (struct cab *)(a->format->data);
        struct cfdata *cfdata;
        const void *d;
-       int64_t skipped_bytes;
 
        cfdata = cab->entry_cfdata;
 
-       if (cfdata->uncompressed_avail == 0 &&
-               cfdata->read_offset > 0) {
-               /* we've already skipped some bytes before really read. */
-               skipped_bytes = cfdata->read_offset;
-               cfdata->read_offset = 0;
-               cfdata->uncompressed_bytes_remaining += skipped_bytes;
-       } else
-               skipped_bytes = 0;
-       do {
-               /*
-                * Note: '1' here is a performance optimization.
-                * Recall that the decompression layer returns a count of
-                * available bytes; asking for more than that forces the
-                * decompressor to combine reads by copying data.
-                */
-               d = __archive_read_ahead(a, 1, avail);
-               if (*avail <= 0) {
-                       *avail = truncated_error(a);
-                       return (NULL);
-               }
-               if (*avail > cfdata->uncompressed_bytes_remaining)
-                       *avail = cfdata->uncompressed_bytes_remaining;
-               cfdata->uncompressed_avail = cfdata->uncompressed_size;
-               cfdata->unconsumed = *avail;
-               cfdata->sum_ptr = d;
-               if (skipped_bytes > 0) {
-                       skipped_bytes =
-                           cab_minimum_consume_cfdata(a, skipped_bytes);
-                       if (skipped_bytes < 0) {
-                               *avail = ARCHIVE_FATAL;
-                               return (NULL);
-                       }
-                       continue;
-               }
-       } while (0);
-
+       /*
+        * Note: '1' here is a performance optimization.
+        * Recall that the decompression layer returns a count of
+        * available bytes; asking for more than that forces the
+        * decompressor to combine reads by copying data.
+        */
+       d = __archive_read_ahead(a, 1, avail);
+       if (*avail <= 0) {
+               *avail = truncated_error(a);
+               return (NULL);
+       }
+       if (*avail > cfdata->uncompressed_bytes_remaining)
+               *avail = cfdata->uncompressed_bytes_remaining;
+       cfdata->uncompressed_avail = cfdata->uncompressed_size;
+       cfdata->unconsumed = *avail;
+       cfdata->sum_ptr = d;
        return (d);
 }
 
@@ -1541,7 +1537,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
                        return (NULL);
                }
        }
-       uavail = cab->stream.total_out;
+       uavail = (uint16_t)cab->stream.total_out;
 
        if (uavail < cfdata->uncompressed_size) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -1719,7 +1715,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
                }
        }
 
-       uavail = cab->xstrm.total_out;
+       uavail = (uint16_t)cab->xstrm.total_out;
        /*
         * Make sure a read pointer advances to next CFDATA.
         */
@@ -1791,9 +1787,8 @@ cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
                rbytes -= cbytes;
 
                if (cfdata->uncompressed_avail == 0 &&
-                   (cab->entry_cffolder->comptype == COMPTYPE_NONE ||
-                    cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
-                        cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
+                  (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
+                   cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
                        /* We have not read any data yet. */
                        if (cbytes == cfdata->uncompressed_bytes_remaining) {
                                /* Skip whole current CFDATA. */
@@ -1819,8 +1814,8 @@ cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
                                }
                                continue;
                        }
-                       cfdata->read_offset += cbytes;
-                       cfdata->uncompressed_bytes_remaining -= cbytes;
+                       cfdata->read_offset += (uint16_t)cbytes;
+                       cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
                        break;
                } else if (cbytes == 0) {
                        err = cab_next_cfdata(a);
@@ -1844,7 +1839,7 @@ cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
                        if (avail <= 0)
                                return (ARCHIVE_FATAL);
                 &nbs