Merge from vendor branch LIBARCHIVE:
authorPeter Avalos <pavalos@dragonflybsd.org>
Sat, 17 May 2008 21:50:43 +0000 (21:50 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sat, 17 May 2008 21:50:43 +0000 (21:50 +0000)
Import libarchive-2.4.17.  See NEWS for details.

46 files changed:
contrib/libarchive-2/NEWS
contrib/libarchive-2/libarchive/archive.h.in
contrib/libarchive-2/libarchive/archive_endian.h [new file with mode: 0644]
contrib/libarchive-2/libarchive/archive_entry.c
contrib/libarchive-2/libarchive/archive_entry.h
contrib/libarchive-2/libarchive/archive_entry_link_resolver.c
contrib/libarchive-2/libarchive/archive_entry_private.h
contrib/libarchive-2/libarchive/archive_entry_strmode.c
contrib/libarchive-2/libarchive/archive_platform.h
contrib/libarchive-2/libarchive/archive_read.c
contrib/libarchive-2/libarchive/archive_read_extract.c
contrib/libarchive-2/libarchive/archive_read_open_filename.c
contrib/libarchive-2/libarchive/archive_read_private.h
contrib/libarchive-2/libarchive/archive_read_support_compression_bzip2.c
contrib/libarchive-2/libarchive/archive_read_support_compression_gzip.c
contrib/libarchive-2/libarchive/archive_read_support_compression_none.c
contrib/libarchive-2/libarchive/archive_read_support_format_all.c
contrib/libarchive-2/libarchive/archive_read_support_format_ar.c
contrib/libarchive-2/libarchive/archive_read_support_format_cpio.c
contrib/libarchive-2/libarchive/archive_read_support_format_iso9660.c
contrib/libarchive-2/libarchive/archive_read_support_format_mtree.c
contrib/libarchive-2/libarchive/archive_read_support_format_tar.c
contrib/libarchive-2/libarchive/archive_read_support_format_zip.c
contrib/libarchive-2/libarchive/archive_util.c
contrib/libarchive-2/libarchive/archive_write_disk.c
contrib/libarchive-2/libarchive/archive_write_open_filename.c
contrib/libarchive-2/libarchive/archive_write_set_compression_bzip2.c
contrib/libarchive-2/libarchive/archive_write_set_compression_gzip.c
contrib/libarchive-2/libarchive/archive_write_set_compression_none.c
contrib/libarchive-2/libarchive/archive_write_set_format_ar.c
contrib/libarchive-2/libarchive/archive_write_set_format_cpio.c
contrib/libarchive-2/libarchive/archive_write_set_format_cpio_newc.c
contrib/libarchive-2/libarchive/archive_write_set_format_pax.c
contrib/libarchive-2/libarchive/archive_write_set_format_ustar.c
contrib/libarchive-2/libarchive/cpio.5
contrib/libarchive-2/libarchive/filter_fork.c
contrib/libarchive-2/libarchive/libarchive-formats.5
contrib/libarchive-2/libarchive/libarchive_internals.3
contrib/libarchive-2/tar/bsdtar.1
contrib/libarchive-2/tar/bsdtar.c
contrib/libarchive-2/tar/bsdtar.h
contrib/libarchive-2/tar/bsdtar_platform.h
contrib/libarchive-2/tar/read.c
contrib/libarchive-2/tar/util.c
contrib/libarchive-2/tar/write.c
contrib/libarchive-2/version

index fb0e9e5..991509e 100644 (file)
@@ -1,3 +1,43 @@
+Mar 14, 2008: libarchive 2.4.14 released.  This is identical to 2.4.13
+       except it contains a one-line fix to the uname/gname problem
+       introduced by the Feb 25 UTF-8 fix.  This bug makes libarchive
+       refuse to add a file to a pax archive if it has a valid gname
+       but not a valid uname.  In some cases, it can also cause the
+       uname to be stored for the gname.
+
+Feb 26, 2008: libarchive 2.4.13 released
+Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted
+       to/from UTF-8.  Implement "hdrcharset" attribute from SUS-2008.
+Feb 25, 2008: Fix name clash on NetBSD.
+Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang
+Feb 18, 2008: [bsdtar] Permit appending on block devices.
+Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing;
+       bsdcpio still needs to be converted to use this.
+Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg
+Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives.
+
+Jan 22, 2008: libarchive 2.4.12 released
+Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives.
+Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9.
+       bsdcpio_test complains about missing options (-y and -z), format
+       of informational messages (--version, --help), and a minor formatting
+       issue in odc format output.  After this update, bsdcpio_test uncovered
+       several more cosmetic issues in bsdcpio, all now fixed.
+Jan 22, 2008: Experimental support for self-extracting Zip archives.
+Jan 22, 2008: Extend hardlink restore strategy to work correctly with
+       hardlinks extracted from newc cpio files.  (Which store the body
+       only with the last occurrence of a link.)
+
+Dec 30, 2007: libarchive 2.4.11 released
+Dec 30, 2007: Fixed a compile error in bsdcpio on some systems.
+
+Dec 29, 2007: libarchive 2.4.10 released
+Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use.
+Dec 29, 2007: Completed initial test harness for bsdcpio.
+
+Dec 22, 2007: libarchive 2.4.9 released
+Dec 22, 2007: Implement the remaining options for bsdcpio:  -a, -q, -L, -f,
+       pattern selection for -i and -it.
 
 Dec 13, 2007: libarchive 2.4.8 released
 Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly,
index e21a710..d76c85e 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.46 2007/07/06 15:36:37 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.47 2007/12/30 04:58:21 kientzle Exp $
  */
 
 #ifndef ARCHIVE_H_INCLUDED
diff --git a/contrib/libarchive-2/libarchive/archive_endian.h b/contrib/libarchive-2/libarchive/archive_endian.h
new file mode 100644 (file)
index 0000000..259f5de
--- /dev/null
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_endian.h,v 1.2 2008/02/26 07:17:47 kientzle Exp $
+ *
+ * Borrowed from FreeBSD's <sys/endian.h>
+ */
+
+#ifndef ARCHIVE_ENDIAN_H_INCLUDED
+#define ARCHIVE_ENDIAN_H_INCLUDED
+
+/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+
+static inline uint16_t
+archive_be16dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return ((p[0] << 8) | p[1]);
+}
+
+static inline uint32_t
+archive_be32dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+static inline uint64_t
+archive_be64dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
+}
+
+static inline uint16_t
+archive_le16dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return ((p[1] << 8) | p[0]);
+}
+
+static inline uint32_t
+archive_le32dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+static inline uint64_t
+archive_le64dec(const void *pp)
+{
+       unsigned char const *p = (unsigned char const *)pp;
+
+       return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
+}
+
+static inline void
+archive_be16enc(void *pp, uint16_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       p[0] = (u >> 8) & 0xff;
+       p[1] = u & 0xff;
+}
+
+static inline void
+archive_be32enc(void *pp, uint32_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       p[0] = (u >> 24) & 0xff;
+       p[1] = (u >> 16) & 0xff;
+       p[2] = (u >> 8) & 0xff;
+       p[3] = u & 0xff;
+}
+
+static inline void
+archive_be64enc(void *pp, uint64_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       archive_be32enc(p, u >> 32);
+       archive_be32enc(p + 4, u & 0xffffffff);
+}
+
+static inline void
+archive_le16enc(void *pp, uint16_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       p[0] = u & 0xff;
+       p[1] = (u >> 8) & 0xff;
+}
+
+static inline void
+archive_le32enc(void *pp, uint32_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       p[0] = u & 0xff;
+       p[1] = (u >> 8) & 0xff;
+       p[2] = (u >> 16) & 0xff;
+       p[3] = (u >> 24) & 0xff;
+}
+
+static inline void
+archive_le64enc(void *pp, uint64_t u)
+{
+       unsigned char *p = (unsigned char *)pp;
+
+       archive_le32enc(p, u & 0xffffffff);
+       archive_le32enc(p + 4, u >> 32);
+}
+
+#endif
index 5c78c92..a6c9b44 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.44 2007/07/15 19:10:34 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.45 2007/12/30 04:58:21 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -207,6 +207,8 @@ aes_get_mbs(struct aes *aes)
 static const wchar_t *
 aes_get_wcs(struct aes *aes)
 {
+       int r;
+
        if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
                return NULL;
        if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
@@ -221,8 +223,13 @@ aes_get_wcs(struct aes *aes)
                aes->aes_wcs = aes->aes_wcs_alloc;
                if (aes->aes_wcs == NULL)
                        __archive_errx(1, "No memory for aes_get_wcs()");
-               mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
+               r = mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
                aes->aes_wcs_alloc[wcs_length] = 0;
+               if (r == -1) {
+                       /* Conversion failed, don't lie to our clients. */
+                       free(aes->aes_wcs_alloc);
+                       aes->aes_wcs = aes->aes_wcs_alloc = NULL;
+               }
        }
        return (aes->aes_wcs);
 }
@@ -307,6 +314,8 @@ aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
 struct archive_entry *
 archive_entry_clear(struct archive_entry *entry)
 {
+       if (entry == NULL)
+               return (NULL);
        aes_clean(&entry->ae_fflags_text);
        aes_clean(&entry->ae_gname);
        aes_clean(&entry->ae_hardlink);
@@ -752,6 +761,28 @@ archive_entry_set_link(struct archive_entry *entry, const char *target)
                aes_set_mbs(&entry->ae_hardlink, target);
 }
 
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+       if (entry->ae_symlink.aes_mbs != NULL ||
+           entry->ae_symlink.aes_wcs != NULL)
+               aes_copy_mbs(&entry->ae_symlink, target);
+       else
+               aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+       if (entry->ae_symlink.aes_mbs != NULL ||
+           entry->ae_symlink.aes_wcs != NULL)
+               aes_copy_wcs(&entry->ae_symlink, target);
+       else
+               aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
 void
 archive_entry_set_mode(struct archive_entry *entry, mode_t m)
 {
@@ -1143,7 +1174,7 @@ const wchar_t *
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
        int count;
-       int length;
+       size_t length;
        const wchar_t *wname;
        const wchar_t *prefix;
        wchar_t separator;
index 33c5649..cebd55b 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.23 2007/07/15 19:10:34 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.24 2007/12/30 04:58:21 kientzle Exp $
  */
 
 #ifndef ARCHIVE_ENTRY_H_INCLUDED
@@ -37,7 +37,6 @@
 extern "C" {
 #endif
 
-
 /*
  * Description of an archive entry.
  *
@@ -57,7 +56,25 @@ extern "C" {
 struct archive_entry;
 
 /*
- * File-type constants.  These are returned from archive_entry_filetype().
+ * File-type constants.  These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD.  They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support.  For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK.  Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio.  If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
  */
 #define        AE_IFMT         0170000
 #define        AE_IFREG        0100000
@@ -91,7 +108,8 @@ dev_t                         archive_entry_devmajor(struct archive_entry *);
 dev_t                   archive_entry_devminor(struct archive_entry *);
 mode_t                  archive_entry_filetype(struct archive_entry *);
 void                    archive_entry_fflags(struct archive_entry *,
-                            unsigned long *set, unsigned long *clear);
+                           unsigned long * /* set */,
+                           unsigned long * /* clear */);
 const char             *archive_entry_fflags_text(struct archive_entry *);
 gid_t                   archive_entry_gid(struct archive_entry *);
 const char             *archive_entry_gname(struct archive_entry *);
@@ -130,7 +148,7 @@ void        archive_entry_set_devmajor(struct archive_entry *, dev_t);
 void   archive_entry_set_devminor(struct archive_entry *, dev_t);
 void   archive_entry_set_filetype(struct archive_entry *, unsigned int);
 void   archive_entry_set_fflags(struct archive_entry *,
-           unsigned long set, unsigned long clear);
+           unsigned long /* set */, unsigned long /* clear */);
 /* Returns pointer to start of first invalid token, or NULL if none. */
 /* Note that all recognized tokens are processed, regardless. */
 const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
@@ -144,6 +162,8 @@ void        archive_entry_copy_hardlink(struct archive_entry *, const char *);
 void   archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
 void   archive_entry_set_ino(struct archive_entry *, unsigned long);
 void   archive_entry_set_link(struct archive_entry *, const char *);
+void   archive_entry_copy_link(struct archive_entry *, const char *);
+void   archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
 void   archive_entry_set_mode(struct archive_entry *, mode_t);
 void   archive_entry_set_mtime(struct archive_entry *, time_t, long);
 void   archive_entry_set_nlink(struct archive_entry *, unsigned int);
@@ -182,6 +202,13 @@ void       archive_entry_copy_stat(struct archive_entry *, const struct stat *);
  *   = there are many different ACL text formats
  *   = would like to be able to read/convert archives containing ACLs
  *     on platforms that lack ACL libraries
+ *
+ *  This last point, in particular, forces me to implement a reasonably
+ *  complete set of ACL support routines.
+ *
+ *  TODO: Extend this to support NFSv4/NTFS permissions.  That should
+ *  allow full ACL support on Mac OS, in particular, which uses
+ *  POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
  */
 
 /*
@@ -216,21 +243,24 @@ void      archive_entry_copy_stat(struct archive_entry *, const struct stat *);
  */
 void    archive_entry_acl_clear(struct archive_entry *);
 void    archive_entry_acl_add_entry(struct archive_entry *,
-            int type, int permset, int tag, int qual, const char *name);
+           int /* type */, int /* permset */, int /* tag */,
+           int /* qual */, const char * /* name */);
 void    archive_entry_acl_add_entry_w(struct archive_entry *,
-            int type, int permset, int tag, int qual, const wchar_t *name);
+           int /* type */, int /* permset */, int /* tag */,
+           int /* qual */, const wchar_t * /* name */);
 
 /*
  * To retrieve the ACL, first "reset", then repeatedly ask for the
  * "next" entry.  The want_type parameter allows you to request only
  * access entries or only default entries.
  */
-int     archive_entry_acl_reset(struct archive_entry *, int want_type);
-int     archive_entry_acl_next(struct archive_entry *, int want_type,
-            int *type, int *permset, int *tag, int *qual, const char **name);
-int     archive_entry_acl_next_w(struct archive_entry *, int want_type,
-            int *type, int *permset, int *tag, int *qual,
-            const wchar_t **name);
+int     archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+int     archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+           int * /* type */, int * /* permset */, int * /* tag */,
+           int * /* qual */, const char ** /* name */);
+int     archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
+           int * /* type */, int * /* permset */, int * /* tag */,
+           int * /* qual */, const wchar_t ** /* name */);
 
 /*
  * Construct a text-format ACL.  The flags argument is a bitmask that
@@ -245,10 +275,11 @@ int        archive_entry_acl_next_w(struct archive_entry *, int want_type,
  */
 #define        ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID        1024
 #define        ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    2048
-const wchar_t  *archive_entry_acl_text_w(struct archive_entry *, int flags);
+const wchar_t  *archive_entry_acl_text_w(struct archive_entry *,
+                   int /* flags */);
 
 /* Return a count of entries matching 'want_type' */
-int     archive_entry_acl_count(struct archive_entry *, int want_type);
+int     archive_entry_acl_count(struct archive_entry *, int /* want_type */);
 
 /*
  * Private ACL parser.  This is private because it handles some
@@ -259,9 +290,12 @@ int         archive_entry_acl_count(struct archive_entry *, int want_type);
  * this interface are likely to be surprised when it changes.
  *
  * You were warned!
+ *
+ * TODO: Move this declaration out of the public header and into
+ * a private header.  Warnings above are silly.
  */
 int             __archive_entry_acl_parse_w(struct archive_entry *,
-                    const wchar_t *, int type);
+                   const wchar_t *, int /* type */);
 
 /*
  * extended attributes
@@ -269,7 +303,8 @@ int          __archive_entry_acl_parse_w(struct archive_entry *,
 
 void    archive_entry_xattr_clear(struct archive_entry *);
 void    archive_entry_xattr_add_entry(struct archive_entry *,
-            const char *name, const void *value, size_t size);
+           const char * /* name */, const void * /* value */,
+           size_t /* size */);
 
 /*
  * To retrieve the xattr list, first "reset", then repeatedly ask for the
@@ -279,29 +314,94 @@ void       archive_entry_xattr_add_entry(struct archive_entry *,
 int    archive_entry_xattr_count(struct archive_entry *);
 int    archive_entry_xattr_reset(struct archive_entry *);
 int    archive_entry_xattr_next(struct archive_entry *,
-            const char **name, const void **value, size_t *);
+           const char ** /* name */, const void ** /* value */, size_t *);
 
 /*
  * Utility to detect hardlinks.
  *
- * The 'struct archive_hardlink_lookup' is a cache of entry
- * names and dev/ino numbers.  Here's how to use it:
- *   1. Create a lookup object with archive_hardlink_lookup_new()
- *   2. Hand each archive_entry to archive_hardlink_lookup().
- *      That function will return NULL (this is not a hardlink to
- *      a previous entry) or the pathname of the first entry
- *      that matched this.
- *   3. Use archive_hardlink_lookup_free() to release the cache.
+ * The 'struct archive_entry_linkresolver' is a cache of archive entries
+ * for files with multiple links.  Here's how to use it:
+ *   1. Create a lookup object with archive_entry_linkresolver_new()
+ *   2. Set the appropriate strategy.
+ *   3. Hand each archive_entry to archive_entry_linkify().
+ *      That function will return 0, 1, or 2 entries that should
+ *      be written.
+ *   4. Call archive_entry_linkify(resolver, NULL) until
+ *      no more entries are returned.
+ *   5. Call archive_entry_link_resolver_free(resolver) to free resources.
+ *
+ * The entries returned have their hardlink and size fields updated
+ * appropriately.  If an entry is passed in that does not refer to
+ * a file with multiple links, it is returned unchanged.  The intention
+ * is that you should be able to simply filter all entries through
+ * this machine.
  *
  * To make things more efficient, be sure that each entry has a valid
  * nlinks value.  The hardlink cache uses this to track when all links
  * have been found.  If the nlinks value is zero, it will keep every
  * name in the cache indefinitely, which can use a lot of memory.
+ *
+ * Note that archive_entry_size() is reset to zero if the file
+ * body should not be written to the archive.  Pay attention!
  */
 struct archive_entry_linkresolver;
 
+/*
+ * This machine supports three different strategies for marking
+ * hardlinks.  The names come from the best-known
+ * formats that rely on each strategy:
+ *
+ * "Old cpio" is the simplest, it always returns any entry unmodified.
+ *    As far as I know, only cpio formats use this.  Old cpio archives
+ *    store every link with the full body; the onus is on the dearchiver
+ *    to detect and properly link the files as they are restored.
+ * "tar" is also pretty simple; it caches a copy the first time it sees
+ *    any link.  Subsequent appearances are modified to be hardlink
+ *    references without any body to the first one.  Used by all tar
+ *    formats, although the newest tar formats permit the "old cpio" strategy
+ *    as well.  This strategy is very simple for the dearchiver,
+ *    and reasonably straightforward for the archiver.
+ * "new cpio" is trickier.  It stores the body only with the last
+ *    occurrence.  The complication is that we might not
+ *    see every link to a particular file in a single session, so
+ *    there's no easy way to know when we've seen the last occurrence.
+ *    The solution here is to queue one link until we see the next.
+ *    At the end of the session, you can enumerate any remaining
+ *    entries by calling archive_entry_linkify(NULL) and store those
+ *    bodies.  If you have a file with three links l1, l2, and l3,
+ *    you'll get the following behavior if you see all three links:
+ *           linkify(l1) => NULL   (the resolver stores l1 internally)
+ *           linkify(l2) => l1     (resolver stores l2, you write l1)
+ *           linkify(l3) => l2, l3 (all links seen, you can write both).
+ *    If you only see l1 and l2, you'll get this behavior:
+ *           linkify(l1) => NULL
+ *           linkify(l2) => l1
+ *           linkify(NULL) => l2   (at end, you retrieve remaining links)
+ *    As the name suggests, this strategy is used by newer cpio variants.
+ *    It's noticably more complex for the archiver, slightly more complex
+ *    for the dearchiver than the tar strategy, but makes it straightforward
+ *    to restore a file using any link by simply continuing to scan until
+ *    you see a link that is stored with a body.  In contrast, the tar
+ *    strategy requires you to rescan the archive from the beginning to
+ *    correctly extract an arbitrary link.
+ */
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 1
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 2
+
 struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
+void archive_entry_linkresolver_set_strategy(
+       struct archive_entry_linkresolver *, int /* strategy */);
 void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
+void archive_entry_linkify(struct archive_entry_linkresolver *,
+    struct archive_entry **, struct archive_entry **);
+
+/*
+ * DEPRECATED: This will be removed in libarchive 3.0.  It was an
+ * early attempt at providing library-level hardlink recognition
+ * support, but it only handles the tar strategy and cannot easily
+ * be extended, so it's being replaced with the "linkify" function.
+ */
 const char *archive_entry_linkresolve(struct archive_entry_linkresolver *,
     struct archive_entry *);
 
index 0f07e06..37131ff 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_link_resolver.c,v 1.1 2007/12/30 04:58:21 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -42,133 +42,194 @@ __FBSDID("$FreeBSD$");
 
 #include "archive_entry.h"
 
+/*
+ * This is mostly a pretty straightforward hash table implementation.
+ * The only interesting bit is the different strategies used to
+ * match up links.  These strategies match those used by various
+ * archiving formats:
+ *   tar - content stored with first link, remainder refer back to it.
+ *       This requires us to match each subsequent link up with the
+ *       first appearance.
+ *   cpio - Old cpio just stored body with each link, match-ups were
+ *       implicit.  This is trivial.
+ *   new cpio - New cpio only stores body with last link, match-ups
+ *       are implicit.  This is actually quite tricky; see the notes
+ *       below.
+ */
+
 /* Initial size of link cache. */
 #define        links_cache_initial_size 1024
 
+struct links_entry {
+       struct links_entry      *next;
+       struct links_entry      *previous;
+       int                      links; /* # links not yet seen */
+       int                      hash;
+       struct archive_entry    *entry;
+};
+
 struct archive_entry_linkresolver {
-       char                     *last_name;
+       struct links_entry      **buckets;
+       struct links_entry       *spare;
        unsigned long             number_entries;
        size_t                    number_buckets;
-       struct links_entry      **buckets;
+       int                       strategy;
 };
 
-struct links_entry {
-       struct links_entry      *next;
-       struct links_entry      *previous;
-       int                      links;
-       dev_t                    dev;
-       ino_t                    ino;
-       char                    *name;
-};
+static struct links_entry *find_entry(struct archive_entry_linkresolver *,
+                   struct archive_entry *);
+static void grow_hash(struct archive_entry_linkresolver *);
+static void insert_entry(struct archive_entry_linkresolver *,
+                   struct archive_entry *);
 
 struct archive_entry_linkresolver *
 archive_entry_linkresolver_new(void)
 {
-       struct archive_entry_linkresolver *links_cache;
+       struct archive_entry_linkresolver *res;
        size_t i;
 
-       links_cache = malloc(sizeof(struct archive_entry_linkresolver));
-       if (links_cache == NULL)
+       res = malloc(sizeof(struct archive_entry_linkresolver));
+       if (res == NULL)
                return (NULL);
-       memset(links_cache, 0, sizeof(struct archive_entry_linkresolver));
-       links_cache->number_buckets = links_cache_initial_size;
-       links_cache->buckets = malloc(links_cache->number_buckets *
-           sizeof(links_cache->buckets[0]));
-       if (links_cache->buckets == NULL) {
-               free(links_cache);
+       memset(res, 0, sizeof(struct archive_entry_linkresolver));
+       res->number_buckets = links_cache_initial_size;
+       res->buckets = malloc(res->number_buckets *
+           sizeof(res->buckets[0]));
+       if (res->buckets == NULL) {
+               free(res);
                return (NULL);
        }
-       for (i = 0; i < links_cache->number_buckets; i++)
-               links_cache->buckets[i] = NULL;
-       return (links_cache);
+       for (i = 0; i < res->number_buckets; i++)
+               res->buckets[i] = NULL;
+       return (res);
+}
+
+void
+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
+    int strategy)
+{
+       res->strategy = strategy;
 }
 
 void
-archive_entry_linkresolver_free(struct archive_entry_linkresolver *links_cache)
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
 {
        size_t i;
 
-       if (links_cache->buckets == NULL)
+       if (res->buckets == NULL)
                return;
 
-       for (i = 0; i < links_cache->number_buckets; i++) {
-               while (links_cache->buckets[i] != NULL) {
-                       struct links_entry *lp = links_cache->buckets[i]->next;
-                       if (links_cache->buckets[i]->name != NULL)
-                               free(links_cache->buckets[i]->name);
-                       free(links_cache->buckets[i]);
-                       links_cache->buckets[i] = lp;
+       for (i = 0; i < res->number_buckets; i++) {
+               while (res->buckets[i] != NULL) {
+                       struct links_entry *lp = res->buckets[i]->next;
+                       archive_entry_free(res->buckets[i]->entry);
+                       free(res->buckets[i]);
+                       res->buckets[i] = lp;
                }
        }
-       free(links_cache->buckets);
-       links_cache->buckets = NULL;
+       free(res->buckets);
+       res->buckets = NULL;
 }
 
+/* Always uses tar-like semantics. */
 const char *
-archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
+archive_entry_linkresolve(struct archive_entry_linkresolver *res,
     struct archive_entry *entry)
 {
-       struct links_entry      *le, **new_buckets;
-       int                      hash;
-       size_t                   i, new_size;
-       dev_t                    dev;
-       ino_t                    ino;
-       int                      nlinks;
+       struct links_entry *le;
 
+       /* If it has only one link, then we're done. */
+       if (archive_entry_nlink(entry) == 1)
+               return (NULL);
 
-       /* Free a held name. */
-       free(links_cache->last_name);
-       links_cache->last_name = NULL;
+       /* Look it up in the hash. */
+       le = find_entry(res, entry);
+       if (le != NULL)
+               return (archive_entry_pathname(le->entry));
+       /* If it's not there, insert it. */
+       insert_entry(res, entry);
+       return (NULL);
+}
 
-       /* If the links cache overflowed and got flushed, don't bother. */
-       if (links_cache->buckets == NULL)
-               return (NULL);
+void
+archive_entry_linkify(struct archive_entry_linkresolver *res,
+    struct archive_entry **e, struct archive_entry **f)
+{
+       struct links_entry *le;
+       struct archive_entry *t;
 
-       dev = archive_entry_dev(entry);
-       ino = archive_entry_ino(entry);
-       nlinks = archive_entry_nlink(entry);
+       *f = NULL; /* Default: Don't return a second entry. */
 
-       /* An entry with one link can't be a hard link. */
-       if (nlinks == 1)
-               return (NULL);
+       /* If it has only one link, then we're done. */
+       if (archive_entry_nlink(*e) == 1)
+               return;
 
-       /* If the links cache is getting too full, enlarge the hash table. */
-       if (links_cache->number_entries > links_cache->number_buckets * 2)
-       {
-               /* Try to enlarge the bucket list. */
-               new_size = links_cache->number_buckets * 2;
-               new_buckets = malloc(new_size * sizeof(struct links_entry *));
-
-               if (new_buckets != NULL) {
-                       memset(new_buckets, 0,
-                           new_size * sizeof(struct links_entry *));
-                       for (i = 0; i < links_cache->number_buckets; i++) {
-                               while (links_cache->buckets[i] != NULL) {
-                                       /* Remove entry from old bucket. */
-                                       le = links_cache->buckets[i];
-                                       links_cache->buckets[i] = le->next;
-
-                                       /* Add entry to new bucket. */
-                                       hash = (le->dev ^ le->ino) % new_size;
-
-                                       if (new_buckets[hash] != NULL)
-                                               new_buckets[hash]->previous =
-                                                   le;
-                                       le->next = new_buckets[hash];
-                                       le->previous = NULL;
-                                       new_buckets[hash] = le;
-                               }
+       switch (res->strategy) {
+       case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
+               le = find_entry(res, *e);
+               if (le != NULL) {
+                       archive_entry_set_size(*e, 0);
+                       archive_entry_set_hardlink(*e,
+                           archive_entry_pathname(le->entry));
+               } else
+                       insert_entry(res, *e);
+               return;
+       case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
+               /* This one is trivial. */
+               return;
+       case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
+               le = find_entry(res, *e);
+               if (le != NULL) {
+                       t = *e;
+                       *e = le->entry;
+                       le->entry = t;
+                       archive_entry_set_size(*e, 0);
+                       archive_entry_set_hardlink(*e,
+                           archive_entry_pathname(le->entry));
+                       if (le->links == 0) {
+                               *f = le->entry;
                        }
-                       free(links_cache->buckets);
-                       links_cache->buckets = new_buckets;
-                       links_cache->number_buckets = new_size;
+               } else {
+                       insert_entry(res, *e);
+                       *e = NULL;
                }
+               return;
+       default:
+               break;
        }
+       return;
+}
+
+static struct links_entry *
+find_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+       struct links_entry      *le;
+       int                      hash, bucket;
+       dev_t                    dev;
+       ino_t                    ino;
+
+       /* Free a held entry. */
+       if (res->spare != NULL) {
+               archive_entry_free(res->spare->entry);
+               free(res->spare);
+               res->spare = NULL;
+       }
+
+       /* If the links cache overflowed and got flushed, don't bother. */
+       if (res->buckets == NULL)
+               return (NULL);
+
+       dev = archive_entry_dev(entry);
+       ino = archive_entry_ino(entry);
+       hash = dev ^ ino;
 
        /* Try to locate this entry in the links cache. */
-       hash = ( dev ^ ino ) % links_cache->number_buckets;
-       for (le = links_cache->buckets[hash]; le != NULL; le = le->next) {
-               if (le->dev == dev && le->ino == ino) {
+       bucket = hash % res->number_buckets;
+       for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+               if (le->hash == hash
+                   && dev == archive_entry_dev(le->entry)
+                   && ino == archive_entry_ino(le->entry)) {
                        /*
                         * Decrement link count each time and release
                         * the entry if it hits zero.  This saves
@@ -177,46 +238,91 @@ archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache,
                         */
                        --le->links;
                        if (le->links > 0)
-                               return (le->name);
-                       /*
-                        * When we release the entry, save the name
-                        * until the next call.
-                        */
-                       links_cache->last_name = le->name;
-                       /*
-                        * Release the entry.
-                        */
+                               return (le);
+                       /* Remove it from this hash bucket. */
                        if (le->previous != NULL)
                                le->previous->next = le->next;
                        if (le->next != NULL)
                                le->next->previous = le->previous;
-                       if (links_cache->buckets[hash] == le)
-                               links_cache->buckets[hash] = le->next;
-                       links_cache->number_entries--;
-                       free(le);
-                       return (links_cache->last_name);
+                       if (res->buckets[bucket] == le)
+                               res->buckets[bucket] = le->next;
+                       res->number_entries--;
+                       /* Defer freeing this entry. */
+                       res->spare = le;
+                       return (le);
                }
        }
+       return (NULL);
+}
+
+static void
+insert_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+       struct links_entry *le;
+       int                      hash, bucket;
 
        /* Add this entry to the links cache. */
        le = malloc(sizeof(struct links_entry));
        if (le == NULL)
-               return (NULL);
-       le->name = strdup(archive_entry_pathname(entry));
-       if (le->name == NULL) {
+               return;
+       le->entry = archive_entry_clone(entry);
+       if (le->entry == NULL) {
                free(le);
-               return (NULL);
+               return;
        }
 
+       /* If the links cache is getting too full, enlarge the hash table. */
+       if (res->number_entries > res->number_buckets * 2)
+               grow_hash(res);
+
+       hash = archive_entry_dev(entry) ^ archive_entry_ino(entry);
+       bucket = hash % res->number_buckets;
+
        /* If we could allocate the entry, record it. */
-       if (links_cache->buckets[hash] != NULL)
-               links_cache->buckets[hash]->previous = le;
-       links_cache->number_entries++;
-       le->next = links_cache->buckets[hash];
+       if (res->buckets[bucket] != NULL)
+               res->buckets[bucket]->previous = le;
+       res->number_entries++;
+       le->next = res->buckets[bucket];
        le->previous = NULL;
-       links_cache->buckets[hash] = le;
-       le->dev = dev;
-       le->ino = ino;
-       le->links = nlinks - 1;
-       return (NULL);
+       res->buckets[bucket] = le;
+       le->hash = hash;
+       le->links = archive_entry_nlink(entry) - 1;
+}
+
+static void
+grow_hash(struct archive_entry_linkresolver *res)
+{
+       struct links_entry *le, **new_buckets;
+       size_t new_size;
+       size_t i, bucket;
+
+       /* Try to enlarge the bucket list. */
+       new_size = res->number_buckets * 2;
+       new_buckets = malloc(new_size * sizeof(struct links_entry *));
+
+       if (new_buckets != NULL) {
+               memset(new_buckets, 0,
+                   new_size * sizeof(struct links_entry *));
+               for (i = 0; i < res->number_buckets; i++) {
+                       while (res->buckets[i] != NULL) {
+                               /* Remove entry from old bucket. */
+                               le = res->buckets[i];
+                               res->buckets[i] = le->next;
+
+                               /* Add entry to new bucket. */
+                               bucket = le->hash % new_size;
+
+                               if (new_buckets[bucket] != NULL)
+                                       new_buckets[bucket]->previous =
+                                           le;
+                               le->next = new_buckets[bucket];
+                               le->previous = NULL;
+                               new_buckets[bucket] = le;
+                       }
+               }
+               free(res->buckets);
+               res->buckets = new_buckets;
+               res->number_buckets = new_size;
+       }
 }
index 50ad7b9..57fe296 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.1 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.2 2007/12/30 04:58:21 kientzle Exp $
  */
 
 #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
index 1a01873..dc08d97 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.2 2008/02/19 05:49:02 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -65,15 +65,15 @@ archive_entry_strmode(struct archive_entry *entry)
                        bp[i+1] = '-';
 
        if (mode & S_ISUID) {
-               if (mode & S_IXUSR) bp[3] = 's';
+               if (mode & 0100) bp[3] = 's';
                else bp[3] = 'S';
        }
        if (mode & S_ISGID) {
-               if (mode & S_IXGRP) bp[6] = 's';
+               if (mode & 0010) bp[6] = 's';
                else bp[6] = 'S';
        }
        if (mode & S_ISVTX) {
-               if (mode & S_IXOTH) bp[9] = 't';
+               if (mode & 0001) bp[9] = 't';
                else bp[9] = 'T';
        }
        if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
index 193a52c..b14ccd8 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.27 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.29 2008/02/19 06:06:13 kientzle Exp $
  */
 
 /*
 #ifndef ARCHIVE_PLATFORM_H_INCLUDED
 #define        ARCHIVE_PLATFORM_H_INCLUDED
 
-#if defined(PLATFORM_CONFIG_H)
+#ifdef _WIN32
+#include "config_windows.h"
+#include "archive_windows.h"
+#elif defined(PLATFORM_CONFIG_H)
 /* Use hand-built config.h in environments that need it. */
 #include PLATFORM_CONFIG_H
 #elif defined(HAVE_CONFIG_H)
index 7803fd2..19be775 100644 (file)
@@ -32,7 +32,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.35 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.37 2008/01/03 17:54:26 des Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -741,3 +741,14 @@ __archive_read_register_compression(struct archive_read *a,
        __archive_errx(1, "Not enough slots for compression registration");
        return (NULL); /* Never actually executed. */
 }
+
+/* used internally to simplify read-ahead */
+const void *
+__archive_read_ahead(struct archive_read *a, size_t len)
+{
+       const void *h;
+
+       if ((a->decompressor->read_ahead)(a, &h, len) < (ssize_t)len)
+               return (NULL);
+       return (h);
+}
index 423ff7d..bb5add7 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.59 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.60 2008/01/18 04:53:45 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -67,6 +67,7 @@ get_extract(struct archive_read *a)
                        archive_set_error(&a->archive, ENOMEM, "Can't extract");
                        return (NULL);
                }
+               memset(a->extract, 0, sizeof(*a->extract));
                a->extract->ad = archive_write_disk_new();
                if (a->extract->ad == NULL) {
                        archive_set_error(&a->archive, ENOMEM, "Can't extract");
@@ -130,11 +131,13 @@ archive_read_extract_set_progress_callback(struct archive *_a,
 static int
 copy_data(struct archive *ar, struct archive *aw)
 {
-       int r;
+       off_t offset;
        const void *buff;
+       struct extract *extract;
        size_t size;
-       off_t offset;
+       int r;
 
+       extract = get_extract((struct archive_read *)ar);
        for (;;) {
                r = archive_read_data_block(ar, &buff, &size, &offset);
                if (r == ARCHIVE_EOF)
@@ -149,6 +152,9 @@ copy_data(struct archive *ar, struct archive *aw)
                            "%s", archive_error_string(aw));
                        return (r);
                }
+               if (extract->extract_progress)
+                       (extract->extract_progress)
+                           (extract->extract_progress_user_data);
        }
 }
 
index 02e7902..3d6376f 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.21 2008/02/19 06:10:48 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,6 +47,10 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.20 2007/
 
 #include "archive.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct read_file_data {
        int      fd;
        size_t   block_size;
@@ -113,7 +117,7 @@ file_open(struct archive *a, void *client_data)
                return (ARCHIVE_FATAL);
        }
        if (mine->filename[0] != '\0')
-               mine->fd = open(mine->filename, O_RDONLY);
+               mine->fd = open(mine->filename, O_RDONLY | O_BINARY);
        else
                mine->fd = 0; /* Fake "open" for stdin. */
        if (mine->fd < 0) {
index f0f5202..90fb7db 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.3 2007/05/29 01:00:18 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.4 2008/01/03 17:54:26 des Exp $
  */
 
 #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
@@ -173,4 +173,7 @@ struct decompressor_t
            int (*bid)(const void *, size_t),
            int (*init)(struct archive_read *, const void *, size_t));
 
+const void
+       *__archive_read_ahead(struct archive_read *, size_t);
+
 #endif
index aa664d1..372eff0 100644 (file)
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.16 2007/05/29 01:00:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.17 2008/02/19 05:44:59 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -145,7 +145,7 @@ init(struct archive_read *a, const void *buff, size_t n)
        (void)buff;     /* UNUSED */
        (void)n;        /* UNUSED */
 
-       archive_set_error(a, -1,
+       archive_set_error(&a->archive, -1,
            "This version of libarchive was compiled without bzip2 support");
        return (ARCHIVE_FATAL);
 }
index 89784b8..2dac54d 100644 (file)
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.16 2008/02/19 05:44:59 kientzle Exp $");
 
 
 #ifdef HAVE_ERRNO_H
@@ -146,7 +146,7 @@ init(struct archive_read *a, const void *buff, size_t n)
        (void)buff;     /* UNUSED */
        (void)n;        /* UNUSED */
 
-       archive_set_error(a, -1,
+       archive_set_error(&a->archive, -1,
            "This version of libarchive was compiled without gzip support");
        return (ARCHIVE_FATAL);
 }
index 1cafd83..3f17756 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.18 2007/10/27 22:45:40 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.19 2007/12/30 04:58:21 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
index c9165c8..24e31ef 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9 2007/04/07 05:54:23 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.10 2007/12/30 04:58:21 kientzle Exp $");
 
 #include "archive.h"
 
index 5fad4ae..a74791c 100644 (file)
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.6 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.8 2008/02/19 05:54:24 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -268,8 +268,7 @@ archive_read_format_ar_read_header(struct archive_read *a,
                /* This must come before any call to _read_ahead. */
                ar_parse_common_header(ar, entry, h);
                archive_entry_copy_pathname(entry, filename);
-               archive_entry_set_mode(entry,
-                   S_IFREG | (archive_entry_mode(entry) & 0777));
+               archive_entry_set_filetype(entry, AE_IFREG);
                /* Get the size of the filename table. */
                number = ar_atol10(h + AR_size_offset, AR_size_size);
                if (number > SIZE_MAX) {
@@ -375,8 +374,7 @@ archive_read_format_ar_read_header(struct archive_read *a,
                /* Parse the time, owner, mode, size fields. */
                r = ar_parse_common_header(ar, entry, h);
                /* Force the file type to a regular file. */
-               archive_entry_set_mode(entry,
-                   S_IFREG | (archive_entry_mode(entry) & 0777));
+               archive_entry_set_filetype(entry, AE_IFREG);
                return (r);
        }
 
index 8c531fa..2c50abc 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.24 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.26 2008/01/15 04:56:48 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -321,10 +321,12 @@ static int
 is_hex(const char *p, size_t len)
 {
        while (len-- > 0) {
-               if (*p < '0' || (*p > '9' && *p < 'a') || *p > 'f') {
+               if ((*p >= '0' && *p <= '9')
+                   || (*p >= 'a' && *p <= 'f')
+                   || (*p >= 'A' && *p <= 'F'))
+                       ++p;
+               else
                        return (0);
-               }
-               ++p;
        }
        return (1);
 }
index d2419ea..d333f0c 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.23 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.25 2008/02/19 06:02:01 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -181,6 +181,7 @@ struct file_info {
        time_t           mtime; /* File last modified time. */
        time_t           atime; /* File last accessed time. */
        time_t           ctime; /* File creation time. */
+       uint64_t         rdev; /* Device number */
        mode_t           mode;
        uid_t            uid;
        gid_t            gid;
@@ -360,6 +361,8 @@ archive_read_format_iso9660_read_header(struct archive_read *a,
        archive_entry_set_mtime(entry, file->mtime, 0);
        archive_entry_set_ctime(entry, file->ctime, 0);
        archive_entry_set_atime(entry, file->atime, 0);
+       /* N.B.: Rock Ridge supports 64-bit device numbers. */
+       archive_entry_set_rdev(entry, (dev_t)file->rdev);
        archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
        archive_string_empty(&iso9660->pathname);
        archive_entry_set_pathname(entry,
@@ -675,6 +678,14 @@ parse_rockridge(struct iso9660 *iso9660, struct file_info *file,
                                 */
                                break;
                        }
+                       if (p[0] == 'P' && p[1] == 'N' && version == 1) {
+                               if (data_length == 16) {
+                                       file->rdev = toi(data,4);
+                                       file->rdev <<= 32;
+                                       file->rdev |= toi(data + 8, 4);
+                               }
+                               break;
+                       }
                        if (p[0] == 'P' && p[1] == 'X' && version == 1) {
                                /*
                                 * PX extension comprises:
@@ -1053,24 +1064,28 @@ time_from_tm(struct tm *t)
        if (t->tm_isdst)
                t->tm_hour -= 1;
        return (mktime(t)); /* Re-convert. */
-#else
-       /*
-        * If you don't have tm_gmtoff, let's try resetting the timezone
-        * (yecch!).
-        */
+#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && defined(HAVE_TZSET)
+       /* No timegm() and no tm_gmtoff, let's try forcing mktime() to UTC. */
        time_t ret;
        char *tz;
 
+       /* Reset the timezone, remember the old one. */
        tz = getenv("TZ");
        setenv("TZ", "UTC 0", 1);
        tzset();
+
        ret = mktime(t);
+
+       /* Restore the previous timezone. */
        if (tz)
            setenv("TZ", tz, 1);
        else
            unsetenv("TZ");
        tzset();
        return ret;
+#else
+       /* <sigh> We have no choice but to use localtime instead of UTC. */
+       return (mktime(t));
 #endif
 }
 
index e6ac99f..7db6db3 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_mtree.c,v 1.2 2008/02/19 06:07:10 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$");
 #include "archive_read_private.h"
 #include "archive_string.h"
 
+#ifndef O_BINARY
+#define        O_BINARY 0
+#endif
+
 struct mtree_entry {
        struct mtree_entry *next;
        char *name;
@@ -371,7 +375,8 @@ read_header(struct archive_read *a, struct archive_entry *entry)
                 * the contents file on disk.)
                 */
                if (archive_strlen(&mtree->contents_name) > 0) {
-                       mtree->fd = open(mtree->contents_name.s, O_RDONLY);
+                       mtree->fd = open(mtree->contents_name.s,
+                           O_RDONLY | O_BINARY);
                        if (mtree->fd < 0) {
                                archive_set_error(&a->archive, errno,
                                    "Can't open content=\"%s\"",
@@ -380,7 +385,8 @@ read_header(struct archive_read *a, struct archive_entry *entry)
                        }
                } else {
                        /* If the specified path opens, use it. */
-                       mtree->fd = open(mtree->current_dir.s, O_RDONLY);
+                       mtree->fd = open(mtree->current_dir.s,
+                           O_RDONLY | O_BINARY);
                        /* But don't fail if it's not there. */
                }
 
index 7d57425..26b2abb 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.62 2007/10/24 04:01:31 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.65 2008/01/31 07:41:45 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -144,8 +144,8 @@ struct sparse_block {
 
 struct tar {
        struct archive_string    acl_text;
-       struct archive_string    entry_name;
-       struct archive_string    entry_linkname;
+       struct archive_string    entry_pathname;
+       struct archive_string    entry_linkpath;
        struct archive_string    entry_uname;
        struct archive_string    entry_gname;
        struct archive_string    longlink;
@@ -153,6 +153,7 @@ struct tar {
        struct archive_string    pax_header;
        struct archive_string    pax_global;
        struct archive_string    line;
+       int                      pax_hdrcharset_binary;
        wchar_t                 *pax_entry;
        size_t                   pax_entry_length;
        int                      header_recursion_depth;
@@ -169,9 +170,9 @@ struct tar {
        char                     sparse_gnu_pending;
 };
 
-static size_t  UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
+static ssize_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
 static int     archive_block_is_null(const unsigned char *p);
-static char    *base64_decode(const wchar_t *, size_t, size_t *);
+static char    *base64_decode(const char *, size_t, size_t *);
 static void     gnu_add_sparse_entry(struct tar *,
                    off_t offset, off_t remaining);
 static void    gnu_clear_sparse_list(struct tar *);
@@ -179,7 +180,7 @@ static int  gnu_sparse_old_read(struct archive_read *, struct tar *,
                    const struct archive_entry_header_gnutar *header);
 static void    gnu_sparse_old_parse(struct tar *,
                    const struct gnu_sparse *sparse, int length);
-static int     gnu_sparse_01_parse(struct tar *, const wchar_t *);
+static int     gnu_sparse_01_parse(struct tar *, const char *);
 static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *);
 static int     header_Solaris_ACL(struct archive_read *,  struct tar *,
                    struct archive_entry *, const void *);
@@ -210,24 +211,23 @@ static int        archive_read_format_tar_read_header(struct archive_read *,
                    struct archive_entry *);
 static int     checksum(struct archive_read *, const void *);
 static int     pax_attribute(struct tar *, struct archive_entry *,
-                   wchar_t *key, wchar_t *value);
+                   char *key, char *value);
 static int     pax_header(struct archive_read *, struct tar *,
                    struct archive_entry *, char *attr);
-static void    pax_time(const wchar_t *, int64_t *sec, long *nanos);
+static void    pax_time(const char *, int64_t *sec, long *nanos);
 static ssize_t readline(struct archive_read *, struct tar *, const char **,
                    ssize_t limit);
 static int     read_body_to_string(struct archive_read *, struct tar *,
                    struct archive_string *, const void *h);
 static int64_t tar_atol(const char *, unsigned);
-static int64_t tar_atol10(const wchar_t *, unsigned);
+static int64_t tar_atol10(const char *, unsigned);
 static int64_t tar_atol256(const char *, unsigned);
 static int64_t tar_atol8(const char *, unsigned);
 static int     tar_read_header(struct archive_read *, struct tar *,
                    struct archive_entry *);
 static int     tohex(int c);
 static char    *url_decode(const char *);
-static int     utf8_decode(wchar_t *, const char *, size_t length);
-static char    *wide_to_narrow(const wchar_t *wval);
+static wchar_t *utf8_decode(struct tar *, const char *, size_t length);
 
 int
 archive_read_support_format_gnutar(struct archive *a)
@@ -271,8 +271,8 @@ archive_read_format_tar_cleanup(struct archive_read *a)
        tar = (struct tar *)(a->format->data);
        gnu_clear_sparse_list(tar);
        archive_string_free(&tar->acl_text);
-       archive_string_free(&tar->entry_name);
-       archive_string_free(&tar->entry_linkname);
+       archive_string_free(&tar->entry_pathname);
+       archive_string_free(&tar->entry_linkpath);
        archive_string_free(&tar->entry_uname);
        archive_string_free(&tar->entry_gname);
        archive_string_free(&tar->line);
@@ -295,24 +295,8 @@ archive_read_format_tar_bid(struct archive_read *a)
        const void *h;
        const struct archive_entry_header_ustar *header;
 
-       /*
-        * If we're already reading a non-tar file, don't
-        * bother to bid.
-        */
-       if (a->archive.archive_format != 0 &&
-           (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
-           ARCHIVE_FORMAT_TAR)
-               return (0);
        bid = 0;
 
-       /*
-        * If we're already reading a tar format, start the bid at 1 as
-        * a failsafe.
-        */
-       if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
-           ARCHIVE_FORMAT_TAR)
-               bid++;
-
        /* Now let's look at the actual header and see if it matches. */
        if (a->decompressor->read_ahead != NULL)
                bytes_read = (a->decompressor->read_ahead)(a, &h, 512);
@@ -324,13 +308,14 @@ archive_read_format_tar_bid(struct archive_read *a)
                return (0);
 
        /* If it's an end-of-archive mark, we can handle it. */
-       if ((*(const char *)h) == 0 && archive_block_is_null((const unsigned char *)h)) {
-               /* If it's a known tar file, end-of-archive is definite. */
-               if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
-                   ARCHIVE_FORMAT_TAR)
-                       return (512);
-               /* Empty archive? */
-               return (1);
+       if ((*(const char *)h) == 0
+           && archive_block_is_null((const unsigned char *)h)) {
+               /*
+                * Usually, I bid the number of bits verified, but
+                * in this case, 4096 seems excessive so I picked 10 as
+                * an arbitrary but reasonable-seeming value.
+                */
+               return (10);
        }
 
        /* If it's not an end-of-archive mark, it must have a valid checksum.*/
@@ -590,6 +575,10 @@ tar_read_header(struct archive_read *a, struct tar *tar,
                if (bytes > 0)
                        (a->decompressor->consume)(a, bytes);
                archive_set_error(&a->archive, 0, NULL);
+               if (a->archive.archive_format_name == NULL) {
+                       a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+                       a->archive.archive_format_name = "tar";
+               }
                return (ARCHIVE_EOF);
        }
 
@@ -777,16 +766,9 @@ header_Solaris_ACL(struct archive_read *a, struct tar *tar,
        while (*p != '\0' && p < acl + size)
                p++;
 
-       wp = (wchar_t *)malloc((p - acl + 1) * sizeof(wchar_t));
-       if (wp == NULL) {
-               archive_set_error(&a->archive, ENOMEM,
-                   "Can't allocate work buffer for ACL parsing");
-               return (ARCHIVE_FATAL);
-       }
-       utf8_decode(wp, acl, p - acl);
+       wp = utf8_decode(tar, acl, p - acl);
        err = __archive_entry_acl_parse_w(entry, wp,
            ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-       free(wp);
        return (err);
 }
 
@@ -806,7 +788,7 @@ header_longlink(struct archive_read *a, struct tar *tar,
        if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
                return (err);
        /* Set symlink if symlink already set, else hardlink. */
-       archive_entry_set_link(entry, tar->longlink.s);
+       archive_entry_copy_link(entry, tar->longlink.s);
        return (ARCHIVE_OK);
 }
 
@@ -826,7 +808,7 @@ header_longname(struct archive_read *a, struct tar *tar,
        err = tar_read_header(a, tar, entry);
        if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
                return (err);
-       archive_entry_set_pathname(entry, tar->longname.s);
+       archive_entry_copy_pathname(entry, tar->longname.s);
        return (ARCHIVE_OK);
 }
 
@@ -918,10 +900,10 @@ header_common(struct archive_read *a, struct tar *tar,
 
        header = (const struct archive_entry_header_ustar *)h;
        if (header->linkname[0])
-               archive_strncpy(&(tar->entry_linkname), header->linkname,
+               archive_strncpy(&(tar->entry_linkpath), header->linkname,
                    sizeof(header->linkname));
        else
-               archive_string_empty(&(tar->entry_linkname));
+               archive_string_empty(&(tar->entry_linkpath));
 
        /* Parse out the numeric fields (all are octal) */
        archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode)));
@@ -937,7 +919,7 @@ header_common(struct archive_read *a, struct tar *tar,
 
        switch (tartype) {
        case '1': /* Hard link */
-               archive_entry_set_hardlink(entry, tar->entry_linkname.s);
+               archive_entry_copy_hardlink(entry, tar->entry_linkpath.s);
                /*
                 * The following may seem odd, but: Technically, tar
                 * does not store the file type for a "hard link"
@@ -954,36 +936,52 @@ header_common(struct archive_read *a, struct tar *tar,
                 * A tricky point: Traditionally, tar readers have
                 * ignored the size field when reading hardlink
                 * entries, and some writers put non-zero sizes even
-                * though the body is empty.  POSIX.1-2001 broke with
-                * this tradition by permitting hardlink entries to
-                * store valid bodies in pax interchange format, but
-                * not in ustar format.  Since there is no hard and
-                * fast way to distinguish pax interchange from
-                * earlier archives (the 'x' and 'g' entries are
-                * optional, after all), we need a heuristic.  Here, I
-                * use the bid function to test whether or not there's
-                * a valid header following.  Of course, if we know
-                * this is pax interchange format, then we must obey
-                * the size.
-                *
-                * This heuristic will only fail for a pax interchange
-                * archive that is storing hardlink bodies, no pax
-                * extended attribute entries have yet occurred, and
-                * we encounter a hardlink entry for a file that is
-                * itself an uncompressed tar archive.
+                * though the body is empty.  POSIX blessed this
+                * convention in the 1988 standard, but broke with
+                * this tradition in 2001 by permitting hardlink
+                * entries to store valid bodies in pax interchange
+                * format, but not in ustar format.  Since there is no
+                * hard and fast way to distinguish pax interchange
+                * from earlier archives (the 'x' and 'g' entries are
+                * optional, after all), we need a heuristic.
                 */
-               if (archive_entry_size(entry) > 0  &&
-                   a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE  &&
-                   archive_read_format_tar_bid(a) > 50) {
+               if (archive_entry_size(entry) == 0) {
+                       /* If the size is already zero, we're done. */
+               }  else if (a->archive.archive_format
+                   == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+                       /* Definitely pax extended; must obey hardlink size. */
+               } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR
+                   || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)
+               {
+                       /* Old-style or GNU tar: we must ignore the size. */
+                       archive_entry_set_size(entry, 0);
+                       tar->entry_bytes_remaining = 0;
+               } else if (archive_read_format_tar_bid(a) > 50) {
+                       /*
+                        * We don't know if it's pax: If the bid
+                        * function sees a valid ustar header
+                        * immediately following, then let's ignore
+                        * the hardlink size.
+                        */
                        archive_entry_set_size(entry, 0);
                        tar->entry_bytes_remaining = 0;
                }
+               /*
+                * TODO: There are still two cases I'd like to handle:
+                *   = a ustar non-pax archive with a hardlink entry at
+                *     end-of-archive.  (Look for block of nulls following?)
+                *   = a pax archive that has not seen any pax headers
+                *     and has an entry which is a hardlink entry storing
+                *     a body containing an uncompressed tar archive.
+                * The first is worth addressing; I don't see any reliable
+                * way to deal with the second possibility.
+                */
                break;
        case '2': /* Symlink */
                archive_entry_set_filetype(entry, AE_IFLNK);
                archive_entry_set_size(entry, 0);
                tar->entry_bytes_remaining = 0;
-               archive_entry_set_symlink(entry, tar->entry_linkname.s);
+               archive_entry_copy_symlink(entry, tar->entry_linkpath.s);
                break;
        case '3': /* Character device */
                archive_entry_set_filetype(entry, AE_IFCHR);
@@ -1055,8 +1053,8 @@ header_old_tar(struct archive_read *a, struct tar *tar,
 
        /* Copy filename over (to ensure null termination). */
        header = (const struct archive_entry_header_ustar *)h;
-       archive_strncpy(&(tar->entry_name), header->name, sizeof(header->name));
-       archive_entry_set_pathname(entry, tar->entry_name.s);
+       archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name));
+       archive_entry_copy_pathname(entry, tar->entry_pathname.s);
 
        /* Grab rest of common fields */
        header_common(a, tar, entry, h);
@@ -1127,7 +1125,7 @@ header_ustar(struct archive_read *a, struct tar *tar,
        header = (const struct archive_entry_header_ustar *)h;
 
        /* Copy name into an internal buffer to ensure null-termination. */
-       as = &(tar->entry_name);
+       as = &(tar->entry_pathname);
        if (header->prefix[0]) {
                archive_strncpy(as, header->prefix, sizeof(header->prefix));
                if (as->s[archive_strlen(as) - 1] != '/')
@@ -1136,7 +1134,7 @@ header_ustar(struct archive_read *a, struct tar *tar,
        } else
                archive_strncpy(as, header->name, sizeof(header->name));
 
-       archive_entry_set_pathname(entry, as->s);
+       archive_entry_copy_pathname(entry, as->s);
 
        /* Handle rest of common fields. */
        header_common(a, tar, entry, h);
@@ -1144,11 +1142,11 @@ header_ustar(struct archive_read *a, struct tar *tar,
        /* Handle POSIX ustar fields. */
        archive_strncpy(&(tar->entry_uname), header->uname,
            sizeof(header->uname));
-       archive_entry_set_uname(entry, tar->entry_uname.s);
+       archive_entry_copy_uname(entry, tar->entry_uname.s);
 
        archive_strncpy(&(tar->entry_gname), header->gname,
            sizeof(header->gname));
-       archive_entry_set_gname(entry, tar->entry_gname.s);
+       archive_entry_copy_gname(entry, tar->entry_gname.s);
 
        /* Parse out device numbers only for char and block specials. */
        if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1175,10 +1173,16 @@ pax_header(struct archive_read *a, struct tar *tar,
 {
        size_t attr_length, l, line_length;
        char *line, *p;
-       wchar_t *key, *wp, *value;
+       char *key, *value;
+       wchar_t *wp;
        int err, err2;
 
        attr_length = strlen(attr);
+       tar->pax_hdrcharset_binary = 0;
+       archive_string_empty(&(tar->entry_gname));
+       archive_string_empty(&(tar->entry_linkpath));
+       archive_string_empty(&(tar->entry_pathname));
+       archive_string_empty(&(tar->entry_uname));
        err = ARCHIVE_OK;
        while (attr_length > 0) {
                /* Parse decimal length field at start of line. */
@@ -1221,49 +1225,24 @@ pax_header(struct archive_read *a, struct tar *tar,
                        return (ARCHIVE_WARN);
                }
 
-               /* Ensure pax_entry buffer is big enough. */
-               if (tar->pax_entry_length <= line_length) {
-                       wchar_t *old_entry = tar->pax_entry;
-
-                       if (tar->pax_entry_length <= 0)
-                               tar->pax_entry_length = 1024;
-                       while (tar->pax_entry_length <= line_length + 1)
-                               tar->pax_entry_length *= 2;
-
-                       old_entry = tar->pax_entry;
-                       tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
-                           tar->pax_entry_length * sizeof(wchar_t));
-                       if (tar->pax_entry == NULL) {
-                               free(old_entry);
-                               archive_set_error(&a->archive, ENOMEM,
-                                       "No memory");
-                               return (ARCHIVE_FATAL);
-                       }
-               }
-
-               /* Decode UTF-8 to wchar_t, null-terminate result. */
-               if (utf8_decode(tar->pax_entry, p,
-                       line_length - (p - attr) - 1)) {
-                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-                          "Invalid UTF8 character in pax extended attribute");
-                       err = err_combine(err, ARCHIVE_WARN);
-               }
+               /* Null-terminate the line. */
+               attr[line_length - 1] = '\0';
 
-               /* Null-terminate 'key' value. */
-               wp = key = tar->pax_entry;
-               if (key[0] == L'=')
+               /* Find end of key and null terminate it. */
+               key = p;
+               if (key[0] == '=')
                        return (-1);
-               while (*wp && *wp != L'=')
-                       ++wp;
-               if (*wp == L'\0') {
+               while (*p && *p != '=')
+                       ++p;
+               if (*p == '\0') {
                        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
                            "Invalid pax extended attributes");
                        return (ARCHIVE_WARN);
                }
-               *wp = 0;
+               *p = '\0';
 
                /* Identify null-terminated 'value' portion. */
-               value = wp + 1;
+               value = p + 1;
 
                /* Identify this attribute and set it in the entry. */
                err2 = pax_attribute(tar, entry, key, value);
@@ -1273,33 +1252,77 @@ pax_header(struct archive_read *a, struct tar *tar,
                attr += line_length;
                attr_length -= line_length;
        }
+       if (archive_strlen(&(tar->entry_gname)) > 0) {
+               value = tar->entry_gname.s;
+               if (tar->pax_hdrcharset_binary)
+                       archive_entry_copy_gname(entry, value);
+               else {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       if (wp == NULL)
+                               archive_entry_copy_gname(entry, value);
+                       else
+                               archive_entry_copy_gname_w(entry, wp);
+               }
+       }
+       if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+               value = tar->entry_linkpath.s;
+               if (tar->pax_hdrcharset_binary)
+                       archive_entry_copy_link(entry, value);
+               else {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       if (wp == NULL)
+                               archive_entry_copy_link(entry, value);
+                       else
+                               archive_entry_copy_link_w(entry, wp);
+               }
+       }
+       if (archive_strlen(&(tar->entry_pathname)) > 0) {
+               value = tar->entry_pathname.s;
+               if (tar->pax_hdrcharset_binary)
+                       archive_entry_copy_pathname(entry, value);
+               else {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       if (wp == NULL)
+                               archive_entry_copy_pathname(entry, value);
+                       else
+                               archive_entry_copy_pathname_w(entry, wp);
+               }
+       }
+       if (archive_strlen(&(tar->entry_uname)) > 0) {
+               value = tar->entry_uname.s;
+               if (tar->pax_hdrcharset_binary)
+                       archive_entry_copy_uname(entry, value);
+               else {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       if (wp == NULL)
+                               archive_entry_copy_uname(entry, value);
+                       else
+                               archive_entry_copy_uname_w(entry, wp);
+               }
+       }
        return (err);
 }
 
 static int
 pax_attribute_xattr(struct archive_entry *entry,
-       wchar_t *name, wchar_t *value)
+       char *name, char *value)
 {
-       char *name_decoded, *name_narrow;
+       char *name_decoded;
        void *value_decoded;
        size_t value_len;
 
-       if (wcslen(name) < 18 || (wcsncmp(name, L"LIBARCHIVE.xattr.", 17)) != 0)
+       if (strlen(name) < 18 || (strncmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
                return 3;
 
        name += 17;
 
        /* URL-decode name */
-       name_narrow = wide_to_narrow(name);
-       if (name_narrow == NULL)
-               return 2;
-       name_decoded = url_decode(name_narrow);
-       free(name_narrow);
+       name_decoded = url_decode(name);
        if (name_decoded == NULL)
                return 2;
 
        /* Base-64 decode value */
-       value_decoded = base64_decode(value, wcslen(value), &value_len);
+       value_decoded = base64_decode(value, strlen(value), &value_len);
        if (value_decoded == NULL) {
                free(name_decoded);
                return 1;
@@ -1328,22 +1351,23 @@ pax_attribute_xattr(struct archive_entry *entry,
  */
 static int
 pax_attribute(struct tar *tar, struct archive_entry *entry,
-    wchar_t *key, wchar_t *value)
+    char *key, char *value)
 {
        int64_t s;
        long n;
+       wchar_t *wp;
 
        switch (key[0]) {
        case 'G':
                /* GNU "0.0" sparse pax format. */
-               if (wcscmp(key, L"GNU.sparse.numblocks") == 0) {
+               if (strcmp(key, "GNU.sparse.numblocks") == 0) {
                        tar->sparse_offset = -1;
                        tar->sparse_numbytes = -1;
                        tar->sparse_gnu_major = 0;
                        tar->sparse_gnu_minor = 0;
                }
-               if (wcscmp(key, L"GNU.sparse.offset") == 0) {
-                       tar->sparse_offset = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.offset") == 0) {
+                       tar->sparse_offset = tar_atol10(value, strlen(value));
                        if (tar->sparse_numbytes != -1) {
                                gnu_add_sparse_entry(tar,
                                    tar->sparse_offset, tar->sparse_numbytes);
@@ -1351,8 +1375,8 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
                                tar->sparse_numbytes = -1;
                        }
                }
-               if (wcscmp(key, L"GNU.sparse.numbytes") == 0) {
-                       tar->sparse_numbytes = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+                       tar->sparse_numbytes = tar_atol10(value, strlen(value));
                        if (tar->sparse_numbytes != -1) {
                                gnu_add_sparse_entry(tar,
                                    tar->sparse_offset, tar->sparse_numbytes);
@@ -1360,13 +1384,13 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
                                tar->sparse_numbytes = -1;
                        }
                }
-               if (wcscmp(key, L"GNU.sparse.size") == 0) {
-                       tar->realsize = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.size") == 0) {
+                       tar->realsize = tar_atol10(value, strlen(value));
                        archive_entry_set_size(entry, tar->realsize);
                }
 
                /* GNU "0.1" sparse pax format. */
-               if (wcscmp(key, L"GNU.sparse.map") == 0) {
+               if (strcmp(key, "GNU.sparse.map") == 0) {
                        tar->sparse_gnu_major = 0;
                        tar->sparse_gnu_minor = 1;
                        if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK)
@@ -1374,18 +1398,23 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
                }
 
                /* GNU "1.0" sparse pax format */
-               if (wcscmp(key, L"GNU.sparse.major") == 0) {
-                       tar->sparse_gnu_major = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.major") == 0) {
+                       tar->sparse_gnu_major = tar_atol10(value, strlen(value));
                        tar->sparse_gnu_pending = 1;
                }
-               if (wcscmp(key, L"GNU.sparse.minor") == 0) {
-                       tar->sparse_gnu_minor = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.minor") == 0) {
+                       tar->sparse_gnu_minor = tar_atol10(value, strlen(value));
                        tar->sparse_gnu_pending = 1;
                }
-               if (wcscmp(key, L"GNU.sparse.name") == 0)
-                       archive_entry_copy_pathname_w(entry, value);
-               if (wcscmp(key, L"GNU.sparse.realsize") == 0) {
-                       tar->realsize = tar_atol10(value, wcslen(value));
+               if (strcmp(key, "GNU.sparse.name") == 0) {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       if (wp != NULL)
+                               archive_entry_copy_pathname_w(entry, wp);
+                       else
+                               archive_entry_copy_pathname(entry, value);
+               }
+               if (strcmp(key, "GNU.sparse.realsize") == 0) {
+                       tar->realsize = tar_atol10(value, strlen(value));
                        archive_entry_set_size(entry, tar->realsize);
                }
                break;
@@ -1396,85 +1425,107 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
                if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0)
                        archive_entry_set_xxxxxx(entry, value);
 */
-               if (wcsncmp(key, L"LIBARCHIVE.xattr.", 17)==0)
+               if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0)
                        pax_attribute_xattr(entry, key, value);
                break;
        case 'S':
                /* We support some keys used by the "star" archiver */
-               if (wcscmp(key, L"SCHILY.acl.access")==0)
-                       __archive_entry_acl_parse_w(entry, value,
+               if (strcmp(key, "SCHILY.acl.access")==0) {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       /* TODO: if (wp == NULL) */
+                       __archive_entry_acl_parse_w(entry, wp,
                            ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-               else if (wcscmp(key, L"SCHILY.acl.default")==0)
-                       __archive_entry_acl_parse_w(entry, value,
+               } else if (strcmp(key, "SCHILY.acl.default")==0) {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       /* TODO: if (wp == NULL) */
+                       __archive_entry_acl_parse_w(entry, wp,
                            ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
-               else if (wcscmp(key, L"SCHILY.devmajor")==0)
-                       archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"SCHILY.devminor")==0)
-                       archive_entry_set_rdevminor(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"SCHILY.fflags")==0)
-                       archive_entry_copy_fflags_text_w(entry, value);
-               else if (wcscmp(key, L"SCHILY.dev")==0)
-                       archive_entry_set_dev(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"SCHILY.ino")==0)
-                       archive_entry_set_ino(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"SCHILY.nlink")==0)
-                       archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"SCHILY.realsize")==0) {
-                       tar->realsize = tar_atol10(value, wcslen(value));
+               } else if (strcmp(key, "SCHILY.devmajor")==0) {
+                       archive_entry_set_rdevmajor(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "SCHILY.devminor")==0) {
+                       archive_entry_set_rdevminor(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "SCHILY.fflags")==0) {
+                       wp = utf8_decode(tar, value, strlen(value));
+                       /* TODO: if (wp == NULL) */
+                       archive_entry_copy_fflags_text_w(entry, wp);
+               } else if (strcmp(key, "SCHILY.dev")==0) {
+                       archive_entry_set_dev(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "SCHILY.ino")==0) {
+                       archive_entry_set_ino(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "SCHILY.nlink")==0) {
+                       archive_entry_set_nlink(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "SCHILY.realsize")==0) {
+                       tar->realsize = tar_atol10(value, strlen(value));
                        archive_entry_set_size(entry, tar->realsize);
                }
                break;
        case 'a':
-               if (wcscmp(key, L"atime")==0) {
+               if (strcmp(key, "atime")==0) {
                        pax_time(value, &s, &n);
                        archive_entry_set_atime(entry, s, n);
                }
                break;
        case 'c':
-               if (wcscmp(key, L"ctime")==0) {
+               if (strcmp(key, "ctime")==0) {
                        pax_time(value, &s, &n);
                        archive_entry_set_ctime(entry, s, n);
-               } else if (wcscmp(key, L"charset")==0) {
+               } else if (strcmp(key, "charset")==0) {
                        /* TODO: Publish charset information in entry. */
-               } else if (wcscmp(key, L"comment")==0) {
+               } else if (strcmp(key, "comment")==0) {
                        /* TODO: Publish comment in entry. */
                }
                break;
        case 'g':
-               if (wcscmp(key, L"gid")==0)
-                       archive_entry_set_gid(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"gname")==0)
-                       archive_entry_copy_gname_w(entry, value);
+               if (strcmp(key, "gid")==0) {
+                       archive_entry_set_gid(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "gname")==0) {
+                       archive_strcpy(&(tar->entry_gname), value);
+               }
+               break;
+       case 'h':
+               if (strcmp(key, "hdrcharset") == 0) {
+                       if (strcmp(value, "BINARY") == 0)
+                               tar->pax_hdrcharset_binary = 1;
+                       else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+                               tar->pax_hdrcharset_binary = 0;
+                       else {
+                               /* TODO: Warn about unsupported hdrcharset */
+                       }
+               }
                break;
        case 'l':
                /* pax interchange doesn't distinguish hardlink vs. symlink. */
-               if (wcscmp(key, L"linkpath")==0) {
-                       if (archive_entry_hardlink(entry))
-                               archive_entry_copy_hardlink_w(entry, value);
-                       else
-                               archive_entry_copy_symlink_w(entry, value);
+               if (strcmp(key, "linkpath")==0) {
+                       archive_strcpy(&(tar->entry_linkpath), value);
                }
                break;
        case 'm':
-               if (wcscmp(key, L"mtime")==0) {
+               if (strcmp(key, "mtime")==0) {
                        pax_time(value, &s, &n);
                        archive_entry_set_mtime(entry, s, n);
                }
                break;
        case 'p':
-               if (wcscmp(key, L"path")==0)
-                       archive_entry_copy_pathname_w(entry, value);
+               if (strcmp(key, "path")==0) {
+                       archive_strcpy(&(tar->entry_pathname), value);
+               }
                break;
        case 'r':
                /* POSIX has reserved 'realtime.*' */
                break;
        case 's':
                /* POSIX has reserved 'security.*' */
-               /* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */
-               if (wcscmp(key, L"size")==0) {
+               /* Someday: if (strcmp(key, "security.acl")==0) { ... } */
+               if (strcmp(key, "size")==0) {
                        /* "size" is the size of the data in the entry. */
                        tar->entry_bytes_remaining
-                           = tar_atol10(value, wcslen(value));
+                           = tar_atol10(value, strlen(value));
                        /*
                         * But, "size" is not necessarily the size of
                         * the file on disk; if this is a sparse file,
@@ -1492,10 +1543,12 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
                }
                break;
        case 'u':
-               if (wcscmp(key, L"uid")==0)
-                       archive_entry_set_uid(entry, tar_atol10(value, wcslen(value)));
-               else if (wcscmp(key, L"uname")==0)
-                       archive_entry_copy_uname_w(entry, value);
+               if (strcmp(key, "uid")==0) {
+                       archive_entry_set_uid(entry,
+                           tar_atol10(value, strlen(value)));
+               } else if (strcmp(key, "uname")==0) {
+                       archive_strcpy(&(tar->entry_uname), value);
+               }
                break;
        }
        return (0);
@@ -1507,7 +1560,7 @@ pax_attribute(struct tar *tar, struct archive_entry *entry,
  * parse a decimal time value, which may include a fractional portion
  */
 static void
-pax_time(const wchar_t *p, int64_t *ps, long *pn)
+pax_time(const char *p, int64_t *ps, long *pn)
 {
        char digit;
        int64_t s;
@@ -1575,9 +1628,9 @@ header_gnutar(struct archive_read *a, struct tar *tar,
 
        /* Copy filename over (to ensure null termination). */
        header = (const struct archive_entry_header_gnutar *)h;
-       archive_strncpy(&(tar->entry_name), header->name,
+       archive_strncpy(&(tar->entry_pathname), header->name,
            sizeof(header->name));
-       archive_entry_set_pathname(entry, tar->entry_name.s);
+       archive_entry_copy_pathname(entry, tar->entry_pathname.s);
 
        /* Fields common to ustar and GNU */
        /* XXX Can the following be factored out since it's common
@@ -1585,11 +1638,11 @@ header_gnutar(struct archive_read *a, struct tar *tar,
         * header_common, perhaps?  */
        archive_strncpy(&(tar->entry_uname),
            header->uname, sizeof(header->uname));
-       archive_entry_set_uname(entry, tar->entry_uname.s);
+       archive_entry_copy_uname(entry, tar->entry_uname.s);
 
        archive_strncpy(&(tar->entry_gname),
            header->gname, sizeof(header->gname));
-       archive_entry_set_gname(entry, tar->entry_gname.s);
+       archive_entry_copy_gname(entry, tar->entry_gname.s);
 
        /* Parse out device numbers only for char and block specials */
        if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1743,9 +1796,9 @@ gnu_sparse_old_parse(struct tar *tar,
  */
 
 static int
-gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
+gnu_sparse_01_parse(struct tar *tar, const char *p)
 {
-       const wchar_t *e;
+       const char *e;
        off_t offset = -1, size = -1;
 
        for (;;) {
@@ -1780,12 +1833,11 @@ gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
  * don't support this format will extract the block map along with the
  * data and a separate post-process can restore the sparseness.
  *
- * Unfortunately, GNU tar 1.16 adds bogus padding to the end of the
- * entry that depends on the size of the map; this means we have to
- * parse the sparse map when we read the header (otherwise, entry_skip
- * will fail).  This is why sparse_10_read is called from read_header
- * above, instead of at the beginning of read_data, where it "should"
- * go.
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format.  GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants.  This code supports
+ * the later variant at the expense of not supporting the former.
  *
  * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
  * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
@@ -1949,7 +2001,7 @@ tar_atol8(const char *p, unsigned char_cnt)
  * it does obey locale.
  */
 static int64_t
-tar_atol10(const wchar_t *p, unsigned char_cnt)
+tar_atol10(const char *p, unsigned char_cnt)
 {
        int64_t l, limit, last_digit_limit;
        int base, digit, sign;
@@ -1982,10 +2034,7 @@ tar_atol10(const wchar_t *p, unsigned char_cnt)
 /*
  * Parse a base-256 integer.  This is just a straight signed binary
  * value in big-endian order, except that the high-order bit is
- * ignored.  Remember that "int64_t" may or may not be exactly 64
- * bits; the implementation here tries to avoid making any assumptions
- * about the actual size of an int64_t.  It does assume we're using
- * twos-complement arithmetic, though.
+ * ignored.
  */
 static int64_t
 tar_atol256(const char *_p, unsigned char_cnt)
@@ -2083,15 +2132,38 @@ readline(struct archive_read *a, struct tar *tar, const char **start,
        }
 }
 
-static int
-utf8_decode(wchar_t *dest, const char *src, size_t length)
+static wchar_t *
+utf8_decode(struct tar *tar, const char *src, size_t length)
 {
-       size_t n;
+       wchar_t *dest;
+       ssize_t n;
        int err;
 
+       /* Ensure pax_entry buffer is big enough. */
+       if (tar->pax_entry_length <= length) {
+               wchar_t *old_entry = tar->pax_entry;
+
+               if (tar->pax_entry_length <= 0)
+                       tar->pax_entry_length = 1024;
+               while (tar->pax_entry_length <= length + 1)
+                       tar->pax_entry_length *= 2;
+
+               old_entry = tar->pax_entry;
+               tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
+                   tar->pax_entry_length * sizeof(wchar_t));
+               if (tar->pax_entry == NULL) {
+                       free(old_entry);
+                       /* TODO: Handle this error. */
+                       return (NULL);
+               }
+       }
+
+       dest = tar->pax_entry;
        err = 0;
        while (length > 0) {
                n = UTF8_mbrtowc(dest, src, length);
+               if (n < 0)
+                       return (NULL);
                if (n == 0)
                        break;
                dest++;
@@ -2099,13 +2171,13 @@ utf8_decode(wchar_t *dest, const char *src, size_t length)
                length -= n;
        }
        *dest++ = L'\0';
-       return (err);
+       return (tar->pax_entry);
 }
 
 /*
  * Copied and simplified from FreeBSD libc/locale.
  */
-static size_t
+static ssize_t
 UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
 {
         int ch, i, len, mask;
@@ -2132,22 +2204,14 @@ UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
         } else if ((ch & 0xf8) == 0xf0) {
                 mask = 0x07;
                 len = 4;
-        } else if ((ch & 0xfc) == 0xf8) {
-                mask = 0x03;
-                len = 5;
-        } else if ((ch & 0xfe) == 0xfc) {
-                mask = 0x01;
-                len = 6;
         } else {
-               /* Invalid first byte; convert to '?' */
-               *pwc = '?';
-               return (1);
+               /* Invalid first byte. */
+               return (-1);
         }
 
         if (n < (size_t)len) {
-               /* Invalid first byte; convert to '?' */
-               *pwc = '?';
-                return (1);
+               /* Valid first byte but truncated. */
+                return (-2);
        }
 
         /*
@@ -2193,7 +2257,7 @@ UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
  * omits line breaks; RFC1341 used for MIME requires both.)
  */
 static char *
-base64_decode(const wchar_t *src, size_t len, size_t *out_len)
+base64_decode(const char *s, size_t len, size_t *out_len)
 {
        static const unsigned char digits[64] = {
                'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
@@ -2203,6 +2267,7 @@ base64_decode(const wchar_t *src, size_t len, size_t *out_len)
                '4','5','6','7','8','9','+','/' };
        static unsigned char decode_table[128];
        char *out, *d;
+       const unsigned char *src = (const unsigned char *)s;
 
        /* If the decode table is not yet initialized, prepare it. */
        if (decode_table[digits[1]] != 1) {
@@ -2263,43 +2328,6 @@ base64_decode(const wchar_t *src, size_t len, size_t *out_len)
        return (out);
 }
 
-/*
- * This is a little tricky because the C99 standard wcstombs()
- * function returns the number of bytes that were converted,
- * not the number that should be converted.  As a result,
- * we can never accurately size the output buffer (without
- * doing a tedious output size calculation in advance).
- * This approach (try a conversion, then try again if it fails)
- * will almost always succeed on the first try, and is thus
- * much faster, at the cost of sometimes requiring multiple
- * passes while we expand the buffer.
- */
-static char *
-wide_to_narrow(const wchar_t *wval)
-{
-       int converted_length;
-       /* Guess an output buffer size and try the conversion. */
-       int alloc_length = wcslen(wval) * 3;
-       char *mbs_val = (char *)malloc(alloc_length + 1);
-       if (mbs_val == NULL)
-               return (NULL);
-       converted_length = wcstombs(mbs_val, wval, alloc_length);
-
-       /* If we exhausted the buffer, resize and try again. */
-       while (converted_length >= alloc_length) {
-               free(mbs_val);
-               alloc_length *= 2;
-               mbs_val = (char *)malloc(alloc_length + 1);
-               if (mbs_val == NULL)
-                       return (NULL);
-               converted_length = wcstombs(mbs_val, wval, alloc_length);
-       }
-
-       /* Ensure a trailing null and return the final string. */
-       mbs_val[alloc_length] = '\0';
-       return (mbs_val);
-}
-
 static char *
 url_decode(const char *in)
 {
index 7dcc4ea..09e7411 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.16 2007/12/04 06:32:12 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.21 2008/02/26 07:17:47 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.16
 #include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
+#include "archive_endian.h"
 
 struct zip {
        /* entry_bytes_remaining is the number of bytes we expect. */
@@ -121,11 +122,6 @@ static int archive_read_format_zip_read_data(struct archive_read *,
 static int     archive_read_format_zip_read_data_skip(struct archive_read *a);
 static int     archive_read_format_zip_read_header(struct archive_read *,
                    struct archive_entry *);
-static int     i2(const char *);
-static int     i4(const char *);
-static unsigned int    u2(const char *);
-static unsigned int    u4(const char *);
-static uint64_t        u8(const char *);
 static int     zip_read_data_deflate(struct archive_read *a, const void **buff,
                    size_t *size, off_t *offset);
 static int     zip_read_data_none(struct archive_read *a, const void **buff,
@@ -166,18 +162,12 @@ archive_read_support_format_zip(struct archive *_a)
 static int
 archive_read_format_zip_bid(struct archive_read *a)
 {
-       int bytes_read;
-       int bid = 0;
-       const void *h;
        const char *p;
+       const void *buff;
+       size_t bytes_avail;
 
-       if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP)
-               bid += 1;
-
-       bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
-       if (bytes_read < 4)
-           return (-1);
-       p = (const char *)h;
+       if ((p = __archive_read_ahead(a, 4)) == NULL)
+               return (-1);
 
        /*
         * Bid of 30 here is: 16 bits for "PK",
@@ -188,20 +178,116 @@ archive_read_format_zip_bid(struct archive_read *a)
                if ((p[2] == '\001' && p[3] == '\002')
                    || (p[2] == '\003' && p[3] == '\004')
                    || (p[2] == '\005' && p[3] == '\006')
-                   || (p[2] == '\007' && p[3] == '\010'))
+                   || (p[2] == '\007' && p[3] == '\010')
+                   || (p[2] == '0' && p[3] == '0'))
                        return (30);
        }
+
+       /*
+        * Attempt to handle self-extracting archives
+        * by noting a PE header and searching forward
+        * up to 64k for a 'PK\003\004' marker.
+        */
+       if (p[0] == 'M' && p[1] == 'Z') {
+               /*
+                * TODO: Additional checks that this really is a PE
+                * file before we invoke the 128k lookahead below.
+                * No point in allocating a bigger lookahead buffer
+                * if we don't need to.
+                */
+               /*
+                * TODO: Of course, the compression layer lookahead
+                * buffers aren't dynamically sized yet; they should be.
+                */
+               bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024);
+               p = (const char *)buff;
+
+               /*
+                * TODO: Optimize by jumping forward based on values
+                * in the PE header.  Note that we don't need to be
+                * exact, but we mustn't skip too far.  The search
+                * below will compensate if we undershoot.  Skipping
+                * will also reduce the chance of false positives
+                * (which is not really all that high to begin with,
+                * so maybe skipping isn't really necessary).
+                */
+
+               while (p < bytes_avail + (const char *)buff) {
+                       if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */
+                           && p[2] == 3 && p[3] == 4 /* File entry */
+                           && p[8] == 8 /* compression == deflate */
+                           && p[9] == 0 /* High byte of compression */
+                               )
+                       {
+                               return (30);
+                       }
+                       ++p;
+               }
+       }
+
        return (0);
 }
 
+/*
+ * Search forward for a "PK\003\004" file header.  This handles the
+ * case of self-extracting archives, where there is an executable
+ * prepended to the ZIP archive.
+ */
+static int
+skip_sfx(struct archive_read *a)
+{
+       const void *h;
+       const char *p, *q;
+       size_t skip, bytes;
+
+       /*
+        * TODO: We should be able to skip forward by a bunch
+        * by lifting some values from the PE header.  We don't
+        * need to be exact (we're still going to search forward
+        * to find the header), but it will speed things up and
+        * reduce the chance of a false positive.
+        */
+       for (;;) {
+               bytes = (a->decompressor->read_ahead)(a, &h, 4096);
+               if (bytes < 4)
+                       return (ARCHIVE_FATAL);
+               p = h;
+               q = p + bytes;
+
+               /*
+                * Scan ahead until we find something that looks
+                * like the zip header.
+                */
+               while (p + 4 < q) {
+                       switch (p[3]) {
+                       case '\004':
+                               /* TODO: Additional verification here. */
+                               if (memcmp("PK\003\004", p, 4) == 0) {
+                                       skip = p - (const char *)h;
+                                       (a->decompressor->consume)(a, skip);
+                                       return (ARCHIVE_OK);
+                               }
+                               p += 4;
+                               break;
+                       case '\003': p += 1; break;
+                       case 'K': p += 2; break;
+                       case 'P': p += 3; break;
+                       default: p += 4; break;
+                       }
+               }
+               skip = p - (const char *)h;
+               (a->decompressor->consume)(a, skip);
+       }
+}
+
 static int
 archive_read_format_zip_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-       int bytes_read;
        const void *h;
        const char *signature;
        struct zip *zip;
+       int r = ARCHIVE_OK, r1;
 
        a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
        if (a->archive.archive_format_name == NULL)
@@ -213,17 +299,43 @@ archive_read_format_zip_read_header(struct archive_read *a,
        zip->end_of_entry_cleanup = 0;
        zip->entry_uncompressed_bytes_read = 0;
        zip->entry_compressed_bytes_read = 0;
-       bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
-       if (bytes_read < 4)
+       if ((h = __archive_read_ahead(a, 4)) == NULL)
                return (ARCHIVE_FATAL);
 
        signature = (const char *)h;
+       if (signature[0] == 'M' && signature[1] == 'Z') {
+               /* This is an executable?  Must be self-extracting... */
+               r = skip_sfx(a);
+               if (r < ARCHIVE_WARN)
+                       return (r);
+               if ((h = __archive_read_ahead(a, 4)) == NULL)
+                       return (ARCHIVE_FATAL);
+               signature = (const char *)h;
+       }
+
        if (signature[0] != 'P' || signature[1] != 'K') {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Bad ZIP file");
                return (ARCHIVE_FATAL);
        }
 
+       /*
+        * "PK00" signature is used for "split" archives that
+        * only have a single segment.  This means we can just
+        * skip the PK00; the first real file header should follow.
+        */
+       if (signature[2] == '0' && signature[3] == '0') {
+               (a->decompressor->consume)(a, 4);
+               if ((h = __archive_read_ahead(a, 4)) == NULL)
+                       return (ARCHIVE_FATAL);
+               signature = (const char *)h;
+               if (signature[0] != 'P' || signature[1] != 'K') {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Bad ZIP file");
+                       return (ARCHIVE_FATAL);
+               }
+       }
+
        if (signature[2] == '\001' && signature[3] == '\002') {
                /* Beginning of central directory. */
                return (ARCHIVE_EOF);
@@ -231,7 +343,10 @@ archive_read_format_zip_read_header(struct archive_read *a,
 
        if (signature[2] == '\003' && signature[3] == '\004') {
                /* Regular file entry. */
-               return (zip_read_file_header(a, entry, zip));
+               r1 = zip_read_file_header(a, entry, zip);
+               if (r1 != ARCHIVE_OK)
+                       return (r1);
+               return (r);
        }
 
        if (signature[2] == '\005' && signature[3] == '\006') {
@@ -261,21 +376,17 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
 {
        const struct zip_file_header *p;
        const void *h;
-       int bytes_read;
 
-       bytes_read =
-           (a->decompressor->read_ahead)(a, &h, sizeof(struct zip_file_header));
-       if (bytes_read < (int)sizeof(struct zip_file_header)) {
+       if ((p = __archive_read_ahead(a, sizeof *p)) == NULL) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Truncated ZIP file header");
                return (ARCHIVE_FATAL);
        }
-       p = (const struct zip_file_header *)h;
 
        zip->version = p->version[0];
        zip->system = p->version[1];
-       zip->flags = i2(p->flags);
-       zip->compression = i2(p->compression);
+       zip->flags = archive_le16dec(p->flags);
+       zip->compression = archive_le16dec(p->compression);
        if (zip->compression <
            sizeof(compression_names)/sizeof(compression_names[0]))
                zip->compression_name = compression_names[zip->compression];
@@ -287,25 +398,24 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
        zip->mode = 0;
        zip->uid = 0;
        zip->gid = 0;
-       zip->crc32 = i4(p->crc32);
-       zip->filename_length = i2(p->filename_length);
-       zip->extra_length = i2(p->extra_length);
-       zip->uncompressed_size = u4(p->uncompressed_size);
-       zip->compressed_size = u4(p->compressed_size);
+       zip->crc32 = archive_le32dec(p->crc32);
+       zip->filename_length = archive_le16dec(p->filename_length);
+       zip->extra_length = archive_le16dec(p->extra_length);
+       zip->uncompressed_size = archive_le32dec(p->uncompressed_size);
+       zip->compressed_size = archive_le32dec(p->compressed_size);
 
        (a->decompressor->consume)(a, sizeof(struct zip_file_header));
 
 
        /* Read the filename. */
-       bytes_read = (a->decompressor->read_ahead)(a, &h, zip->filename_length);
-       if (bytes_read < zip->filename_length) {
+       if ((h = __archive_read_ahead(a, zip->filename_length)) == NULL) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Truncated ZIP file header");
                return (ARCHIVE_FATAL);
        }
        if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
                __archive_errx(1, "Out of memory");
-       archive_strncpy(&zip->pathname, (const char *)h, zip->filename_length);
+       archive_strncpy(&zip->pathname, h, zip->filename_length);
        (a->decompressor->consume)(a, zip->filename_length);
        archive_entry_set_pathname(entry, zip->pathname.s);
 
@@ -315,8 +425,7 @@ zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
                zip->mode = AE_IFREG | 0777;
 
        /* Read the extra data. */
-       bytes_read = (a->decompressor->read_ahead)(a, &h, zip->extra_length);
-       if (bytes_read < zip->extra_length) {
+       if ((h = __archive_read_ahead(a, zip->extra_length)) == NULL) {
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                    "Truncated ZIP file header");
                return (ARCHIVE_FATAL);
@@ -387,21 +496,18 @@ archive_read_format_zip_read_data(struct archive_read *a,
        if (zip->end_of_entry) {
                if (!zip->end_of_entry_cleanup) {
                        if (zip->flags & ZIP_LENGTH_AT_END) {
-                               const void *h;
                                const char *p;
-                               int bytes_read =
-                                   (a->decompressor->read_ahead)(a, &h, 16);
-                               if (bytes_read < 16) {
+
+                               if ((p = __archive_read_ahead(a, 16)) == NULL) {
                                        archive_set_error(&a->archive,
                                            ARCHIVE_ERRNO_FILE_FORMAT,
                                            "Truncated ZIP end-of-file record");
                                        return (ARCHIVE_FATAL);
                                }
-                               p = (const char *)h;
-                               zip->crc32 = i4(p + 4);
-                               zip->compressed_size = u4(p + 8);
-                               zip->uncompressed_size = u4(p + 12);
-                               bytes_read = (a->decompressor->consume)(a, 16);
+                               zip->crc32 = archive_le32dec(p + 4);
+                               zip->compressed_size = archive_le32dec(p + 8);
+                               zip->uncompressed_size = archive_le32dec(p + 12);
+                               (a->decompressor->consume)(a, 16);
                        }
 
                        /* Check file size, CRC against these values. */
@@ -696,37 +802,6 @@ archive_read_format_zip_cleanup(struct archive_read *a)
        return (ARCHIVE_OK);
 }
 
-static int
-i2(const char *p)
-{
-       return ((0xff & (int)p[0]) + 256 * (0xff & (int)p[1]));
-}
-
-
-static int
-i4(const char *p)
-{
-       return ((0xffff & i2(p)) + 0x10000 * (0xffff & i2(p+2)));
-}
-
-static unsigned int
-u2(const char *p)
-{
-       return ((0xff & (unsigned int)p[0]) + 256 * (0xff & (unsigned int)p[1]));
-}
-
-static unsigned int
-u4(const char *p)
-{
-       return u2(p) + 0x10000 * u2(p+2);
-}
-
-static uint64_t
-u8(const char *p)
-{
-       return u4(p) + 0x100000000LL * u4(p+4);
-}
-
 /*
  * The extra data is stored as a list of
  *     id1+size1+data1 + id2+size2+data2 ...
@@ -739,8 +814,8 @@ process_extra(const void* extra, struct zip* zip)
        const char *p = (const char *)extra;
        while (offset < zip->extra_length - 4)
        {
-               unsigned short headerid = u2(p + offset);
-               unsigned short datasize = u2(p + offset + 2);
+               unsigned short headerid = archive_le16dec(p + offset);
+               unsigned short datasize = archive_le16dec(p + offset + 2);
                offset += 4;
                if (offset + datasize > zip->extra_length)
                        break;
@@ -752,9 +827,9 @@ process_extra(const void* extra, struct zip* zip)
                case 0x0001:
                        /* Zip64 extended information extra field. */
                        if (datasize >= 8)
-                               zip->uncompressed_size = u8(p + offset);
+                               zip->uncompressed_size = archive_le64dec(p + offset);
                        if (datasize >= 16)
-                               zip->compressed_size = u8(p + offset + 8);
+                               zip->compressed_size = archive_le64dec(p + offset + 8);
                        break;
                case 0x5455:
                {
@@ -767,11 +842,12 @@ process_extra(const void* extra, struct zip* zip)
                        {
 #ifdef DEBUG
                                fprintf(stderr, "mtime: %lld -> %d\n",
-                                   (long long)zip->mtime, i4(p + offset));
+                                   (long long)zip->mtime,
+                                   archive_le32dec(p + offset));
 #endif
                                if (datasize < 4)
                                        break;
-                               zip->mtime = i4(p + offset);
+                               zip->mtime = archive_le32dec(p + offset);
                                offset += 4;
                                datasize -= 4;
                        }
@@ -779,7 +855,7 @@ process_extra(const void* extra, struct zip* zip)
                        {
                                if (datasize < 4)
                                        break;
-                               zip->atime = i4(p + offset);
+                               zip->atime = archive_le32dec(p + offset);
                                offset += 4;
                                datasize -= 4;
                        }
@@ -787,7 +863,7 @@ process_extra(const void* extra, struct zip* zip)
                        {
                                if (datasize < 4)
                                        break;
-                               zip->ctime = i4(p + offset);
+                               zip->ctime = archive_le32dec(p + offset);
                                offset += 4;
                                datasize -= 4;
                        }
@@ -797,12 +873,13 @@ process_extra(const void* extra, struct zip* zip)
                        /* Info-ZIP Unix Extra Field (type 2) "Ux". */
 #ifdef DEBUG
                        fprintf(stderr, "uid %d gid %d\n",
-                           i2(p + offset), i2(p + offset + 2));
+                           archive_le16dec(p + offset),
+                           archive_le16dec(p + offset + 2));
 #endif
                        if (datasize >= 2)
-                               zip->uid = i2(p + offset);
+                               zip->uid = archive_le16dec(p + offset);
                        if (datasize >= 4)
-                               zip->gid = i2(p + offset + 2);
+                               zip->gid = archive_le16dec(p + offset + 2);
                        break;
                default:
                        break;
index 2bc2844..413199a 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.15 2007/07/06 15:36:38 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.16 2007/12/30 04:58:21 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
index ec22086..dfb6d35 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17 2007/09/21 04:52:42 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.22 2008/02/19 05:39:35 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -45,6 +45,9 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17 2007/09/21 04
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
 
 #ifdef HAVE_EXT2FS_EXT2_FS_H
 #include <ext2fs/ext2_fs.h>    /* for Linux file flags */
@@ -89,6 +92,10 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17 2007/09/21 04
 #include "archive_entry.h"
 #include "archive_private.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct fixup_entry {
        struct fixup_entry      *next;
        mode_t                   mode;
@@ -171,6 +178,8 @@ struct archive_write_disk {
        int                      fd;
        /* Current offset for writing data to the file. */
        off_t                    offset;
+       /* Maximum size of file. */
+       off_t                    filesize;
        /* Dir we were in before this restore; only for deep paths. */
        int                      restore_pwd;
        /* Mode we should use for this entry; affected by _PERM and umask. */
@@ -302,6 +311,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
        a->offset = 0;
        a->uid = a->user_uid;
        a->mode = archive_entry_mode(a->entry);
+       a->filesize = archive_entry_size(a->entry);
        archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
        a->name = a->_name_data.s;
        archive_clear_error(&a->archive);
@@ -425,8 +435,10 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
         * If it's not open, tell our client not to try writing.
         * In particular, dirs, links, etc, don't get written to.
         */
-       if (a->fd < 0)
+       if (a->fd < 0) {
                archive_entry_set_size(entry, 0);
+               a->filesize = 0;
+       }
 done:
        /* Restore the user's umask before returning. */
        umask(a->user_umask);
@@ -451,6 +463,7 @@ _archive_write_data_block(struct archive *_a,
 {
        struct archive_write_disk *a = (struct archive_write_disk *)_a;
        ssize_t bytes_written = 0;
+       int r = ARCHIVE_OK;
 
        __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
            ARCHIVE_STATE_DATA, "archive_write_disk_block");
@@ -470,7 +483,13 @@ _archive_write_data_block(struct archive *_a,
        }
 
        /* Write the data. */
-       while (size > 0) {
+       while (size > 0 && a->offset < a->filesize) {
+               if ((off_t)(a->offset + size) > a->filesize) {
+                       size = (size_t)(a->filesize - a->offset);
+                       archive_set_error(&a->archive, errno,
+                           "Write request too large");
+                       r = ARCHIVE_WARN;
+               }
                bytes_written = write(a->fd, buff, size);
                if (bytes_written < 0) {
                        archive_set_error(&a->archive, errno, "Write failed");
@@ -479,13 +498,14 @@ _archive_write_data_block(struct archive *_a,
                size -= bytes_written;
                a->offset += bytes_written;
        }
-       return (ARCHIVE_OK);
+       return (r);
 }
 
 static ssize_t
 _archive_write_data(struct archive *_a, const void *buff, size_t size)
 {
        struct archive_write_disk *a = (struct archive_write_disk *)_a;
+       off_t offset;
        int r;
 
        __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
@@ -493,10 +513,11 @@ _archive_write_data(struct archive *_a, const void *buff, size_t size)
        if (a->fd < 0)
                return (ARCHIVE_OK);
 
+       offset = a->offset;
        r = _archive_write_data_block(_a, buff, size, a->offset);
        if (r < ARCHIVE_OK)
                return (r);
-       return (size);
+       return (a->offset - offset);
 }
 
 static int
@@ -622,7 +643,9 @@ archive_write_disk_new(void)
        a->archive.vtable = archive_write_disk_vtable();
        a->lookup_uid = trivial_lookup_uid;
        a->lookup_gid = trivial_lookup_gid;
+#ifdef HAVE_GETEUID
        a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
        if (archive_string_ensure(&a->path_safe, 512) == NULL) {
                free(a);
                return (NULL);
@@ -653,7 +676,7 @@ edit_deep_directories(struct archive_write_disk *a)
                return;
 
        /* Try to record our starting dir. */
-       a->restore_pwd = open(".", O_RDONLY);
+       a->restore_pwd = open(".", O_RDONLY | O_BINARY);
        if (a->restore_pwd < 0)
                return;
 
@@ -691,6 +714,14 @@ restore_entry(struct archive_write_disk *a)
        int ret = ARCHIVE_OK, en;
 
        if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+               /*
+                * TODO: Fix this.  Apparently, there are platforms
+                * that still allow root to hose the entire filesystem
+                * by unlinking a dir.  The S_ISDIR() test above
+                * prevents us from using unlink() here if the new
+                * object is a dir, but that doesn't mean the old
+                * object isn't a dir.
+                */
                if (unlink(a->name) == 0) {
                        /* We removed it, we're done. */
                } else if (errno == ENOENT) {
@@ -829,8 +860,20 @@ create_filesystem_object(struct archive_write_disk *a)
        /* We identify hard/symlinks according to the link names. */
        /* Since link(2) and symlink(2) don't handle modes, we're done here. */
        linkname = archive_entry_hardlink(a->entry);
-       if (linkname != NULL)
-               return link(linkname, a->name) ? errno : 0;
+       if (linkname != NULL) {
+               r = link(linkname, a->name) ? errno : 0;
+               /*
+                * New cpio and pax formats allow hardlink entries
+                * to carry data, so we may have to open the file
+                * for hardlink entries.
+                */
+               if (r == 0 && a->filesize > 0) {
+                       a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY);
+                       if (a->fd < 0)
+                               r = errno;
+               }
+               return (r);
+       }
        linkname = archive_entry_symlink(a->entry);
        if (linkname != NULL)
                return symlink(linkname, a->name) ? errno : 0;
@@ -850,24 +893,38 @@ create_filesystem_object(struct archive_write_disk *a)
         */
        mode = final_mode & 0777;
 
-       switch (a->mode & S_IFMT) {
+       switch (a->mode & AE_IFMT) {
        default:
                /* POSIX requires that we fall through here. */
                /* FALLTHROUGH */
-       case S_IFREG:
+       case AE_IFREG:
                a->fd = open(a->name,
-                   O_WRONLY | O_CREAT | O_EXCL, mode);
+                   O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode);
                r = (a->fd < 0);
                break;
-       case S_IFCHR:
+       case AE_IFCHR:
+#ifdef HAVE_MKNOD
+               /* Note: we use AE_IFCHR for the case label, and
+                * S_IFCHR for the mknod() call.  This is correct.  */
                r = mknod(a->name, mode | S_IFCHR,
                    archive_entry_rdev(a->entry));
+#else
+               /* TODO: Find a better way to warn about our inability
+                * to restore a char device node. */
+               return (EINVAL);
+#endif /* HAVE_MKNOD */
                break;
-       case S_IFBLK:
+       case AE_IFBLK:
+#ifdef HAVE_MKNOD
                r = mknod(a->name, mode | S_IFBLK,
                    archive_entry_rdev(a->entry));
+#else
+               /* TODO: Find a better way to warn about our inability
+                * to restore a block device node. */
+               return (EINVAL);
+#endif /* HAVE_MKNOD */
                break;
-       case S_IFDIR:
+       case AE_IFDIR:
                mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
                r = mkdir(a->name, mode);
                if (r == 0) {
@@ -880,8 +937,14 @@ create_filesystem_object(struct archive_write_disk *a)
                        a->todo &= ~TODO_MODE;
                }
                break;
-       case S_IFIFO:
+       case AE_IFIFO:
+#ifdef HAVE_MKFIFO
                r = mkfifo(a->name, mode);
+#else
+               /* TODO: Find a better way to warn about our inability
+                * to restore a fifo. */
+               return (EINVAL);
+#endif /* HAVE_MKFIFO */
                break;
        }
 
@@ -1427,28 +1490,34 @@ set_ownership(struct archive_write_disk *a)
        }
 
 #ifdef HAVE_FCHOWN
-       if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0)
-               goto success;
+       /* If we have an fd, we can avoid a race. */
+       if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+               /* We've set owner and know uid/gid are correct. */
+               a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+               return (ARCHIVE_OK);
+       }
 #endif
 
+       /* We prefer lchown() but will use chown() if that's all we have. */
+       /* Of course, if we have neither, this will always fail. */
 #ifdef HAVE_LCHOWN
-       if (lchown(a->name, a->uid, a->gid) == 0)
-               goto success;
-#else
-       if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0)
-               goto success;
+       if (lchown(a->name, a->uid, a->gid) == 0) {
+               /* We've set owner and know uid/gid are correct. */
+               a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+               return (ARCHIVE_OK);
+       }
+#elif HAVE_CHOWN
+       if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+               /* We've set owner and know uid/gid are correct. */
+               a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+               return (ARCHIVE_OK);
+       }
 #endif
 
        archive_set_error(&a->archive, errno,
            "Can't set user=%d/group=%d for %s", a->uid, a->gid,
            a->name);
        return (ARCHIVE_WARN);
-success:
-       a->todo &= ~TODO_OWNER;
-       /* We know the user/group are correct now. */
-       a->todo &= ~TODO_SGID_CHECK;
-       a->todo &= ~TODO_SUID_CHECK;
-       return (ARCHIVE_OK);
 }
 
 #ifdef HAVE_UTIMES
@@ -1786,7 +1855,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
 
        /* If we weren't given an fd, open it ourselves. */
        if (myfd < 0)
-               myfd = open(name, O_RDONLY|O_NONBLOCK);
+               myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY);
        if (myfd < 0)
                return (ARCHIVE_OK);
 
index fcaaaca..72eeb54 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.20 2008/02/19 05:46:58 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,6 +47,10 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19 2007
 
 #include "archive.h"
 
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
 struct write_file_data {
        int             fd;
        char            filename[1];
@@ -95,7 +99,7 @@ file_open(struct archive *a, void *client_data)
        struct stat st;
 
        mine = (struct write_file_data *)client_data;
-       flags = O_WRONLY | O_CREAT | O_TRUNC;
+       flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
 
        /*
         * Open the file.
index b32167c..272ae26 100644 (file)
@@ -28,7 +28,7 @@
 /* Don't compile this if we don't have bzlib. */
 #if HAVE_BZLIB_H
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.12 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.13 2007/12/30 04:58:21 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
index f43fa80..18abbdf 100644 (file)
@@ -28,7 +28,7 @@
 /* Don't compile this if we don't have zlib. */
 #if HAVE_ZLIB_H
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.14 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.16 2008/02/21 03:21:50 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -106,6 +106,21 @@ archive_compressor_gzip_init(struct archive_write *a)
                        return (ret);
        }
 
+       /*
+        * The next check is a temporary workaround until the gzip
+        * code can be overhauled some.  The code should not require
+        * that compressed_buffer_size == bytes_per_block.  Removing
+        * this assumption will allow us to compress larger chunks at
+        * a time, which should improve overall performance
+        * marginally.  As a minor side-effect, such a cleanup would
+        * allow us to support truly arbitrary block sizes.
+        */
+       if (a->bytes_per_block < 10) {
+               archive_set_error(&a->archive, EINVAL,
+                   "GZip compressor requires a minimum 10 byte block size");
+               return (ARCHIVE_FATAL);
+       }
+
        state = (struct private_data *)malloc(sizeof(*state));
        if (state == NULL) {
                archive_set_error(&a->archive, ENOMEM,
@@ -114,6 +129,10 @@ archive_compressor_gzip_init(struct archive_write *a)
        }
        memset(state, 0, sizeof(*state));
 
+       /*
+        * See comment above.  We should set compressed_buffer_size to
+        * max(bytes_per_block, 65536), but the code can't handle that yet.
+        */
        state->compressed_buffer_size = a->bytes_per_block;
        state->compressed = (unsigned char *)malloc(state->compressed_buffer_size);
        state->crc = crc32(0L, NULL, 0);
index bb8555e..bdecb24 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.16 2007/12/30 04:58:22 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
index 404b651..2e77f1b 100644 (file)
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.3 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.5 2008/01/31 08:11:01 kaiw Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -75,6 +75,7 @@ static int             archive_write_ar_header(struct archive_write *,
 static ssize_t          archive_write_ar_data(struct archive_write *,
                             const void *buff, size_t s);
 static int              archive_write_ar_destroy(struct archive_write *);
+static int              archive_write_ar_finish(struct archive_write *);
 static int              archive_write_ar_finish_entry(struct archive_write *);
 static const char      *ar_basename(const char *path);
 static int              format_octal(int64_t v, char *p, int s);
@@ -126,7 +127,7 @@ archive_write_set_format_ar(struct archive_write *a)
 
        a->format_write_header = archive_write_ar_header;
        a->format_write_data = archive_write_ar_data;
-       a->format_finish = NULL;
+       a->format_finish = archive_write_ar_finish;
        a->format_destroy = archive_write_ar_destroy;
        a->format_finish_entry = archive_write_ar_finish_entry;
        return (ARCHIVE_OK);
@@ -397,6 +398,23 @@ archive_write_ar_destroy(struct archive_write *a)
        return (ARCHIVE_OK);
 }
 
+static int
+archive_write_ar_finish(struct archive_write *a)
+{
+       int ret;
+
+       /*
+        * If we haven't written anything yet, we need to write
+        * the ar global header now to make it a valid ar archive.
+        */
+       if (a->archive.file_position == 0) {
+               ret = (a->compressor.write)(a, "!<arch>\n", 8);
+               return (ret);
+       }
+
+       return (ARCHIVE_OK);
+}
+
 static int
 archive_write_ar_finish_entry(struct archive_write *a)
 {
index c8a6a7a..45cb4e7 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.12 2007/10/12 04:11:31 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.13 2007/12/30 04:58:22 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
index e50544b..d11176c 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.2 2007/10/12 04:11:31 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.3 2008/01/23 05:43:25 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -176,9 +176,15 @@ archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
 
        cpio->entry_bytes_remaining = archive_entry_size(entry);
        cpio->padding = 3 & (-cpio->entry_bytes_remaining);
+
        /* Write the symlink now. */
-       if (p != NULL  &&  *p != '\0')
+       if (p != NULL  &&  *p != '\0') {
                ret = (a->compressor.write)(a, p, strlen(p));
+               if (ret != ARCHIVE_OK)
+                       return (ARCHIVE_FATAL);
+               pad = 0x3 & -strlen(p);
+               ret = (a->compressor.write)(a, "\0\0\0", pad);
+       }
 
        return (ret);
 }
index cedbfd7..a1bd688 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.41 2007/05/29 01:00:19 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.42 2007/12/30 04:58:22 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -383,19 +383,25 @@ archive_write_pax_header(struct archive_write *a,
     struct archive_entry *entry_original)
 {
        struct archive_entry *entry_main;
-       const char *linkname, *p;
+       const char *p;
        char *t;
-       const char *hardlink;
        const wchar_t *wp;
        const char *suffix_start;
        int need_extension, r, ret;
        struct pax *pax;
+       const char *hdrcharset = NULL;
+       const char *hardlink;
+       const char *path = NULL, *linkpath = NULL;
+       const char *uname = NULL, *gname = NULL;
+       const wchar_t *path_w = NULL, *linkpath_w = NULL;
+       const wchar_t *uname_w = NULL, *gname_w = NULL;
 
        char paxbuff[512];
        char ustarbuff[512];
        char ustar_entry_name[256];
        char pax_entry_name[256];
 
+       ret = ARCHIVE_OK;
        need_extension = 0;
        pax = (struct pax *)a->format_data;
 
@@ -441,54 +447,110 @@ archive_write_pax_header(struct archive_write *a,
        entry_main = archive_entry_clone(entry_original);
        archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
 
+       /*
+        * First, check the name fields and see if any of them
+        * require binary coding.  If any of them does, then all of
+        * them do.
+        */
+       hdrcharset = NULL;
+       path = archive_entry_pathname(entry_main);
+       path_w = archive_entry_pathname_w(entry_main);
+       if (path != NULL && path_w == NULL) {
+               archive_set_error(&a->archive, EILSEQ,
+                   "Can't translate pathname '%s' to UTF-8", path);
+               ret = ARCHIVE_WARN;
+               hdrcharset = "BINARY";
+       }
+       uname = archive_entry_uname(entry_main);
+       uname_w = archive_entry_uname_w(entry_main);
+       if (uname != NULL && uname_w == NULL) {
+               archive_set_error(&a->archive, EILSEQ,
+                   "Can't translate uname '%s' to UTF-8", uname);
+               ret = ARCHIVE_WARN;
+               hdrcharset = "BINARY";
+       }
+       gname = archive_entry_gname(entry_main);
+       gname_w = archive_entry_gname_w(entry_main);
+       if (gname != NULL && gname_w == NULL) {
+               archive_set_error(&a->archive, EILSEQ,
+                   "Can't translate gname '%s' to UTF-8", gname);
+               ret = ARCHIVE_WARN;
+               hdrcharset = "BINARY";
+       }
+       linkpath = hardlink;
+       if (linkpath != NULL) {
+               linkpath_w = archive_entry_hardlink_w(entry_main);
+       } else {
+               linkpath = archive_entry_symlink(entry_main);
+               if (linkpath != NULL)
+                       linkpath_w = archive_entry_symlink_w(entry_main);
+       }
+       if (linkpath != NULL && linkpath_w == NULL) {
+               archive_set_error(&a->archive, EILSEQ,
+                   "Can't translate linkpath '%s' to UTF-8", linkpath);
+               ret = ARCHIVE_WARN;
+               hdrcharset = "BINARY";
+       }
+
+       /* Store the header encoding first, to be nice to readers. */
+       if (hdrcharset != NULL)
+               add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset);
+
        /*
         * Determining whether or not the name is too big is ugly
         * because of the rules for dividing names between 'name' and
         * 'prefix' fields.  Here, I pick out the longest possible
         * suffix, then test whether the remaining prefix is too long.
         */
-       wp = archive_entry_pathname_w(entry_main);
-       p = archive_entry_pathname(entry_main);
-       if (strlen(p) <= 100)   /* Short enough for just 'name' field */
-               suffix_start = p;       /* Record a zero-length prefix */
+       if (strlen(path) <= 100)    /* Short enough for just 'name' field */
+               suffix_start = path;    /* Record a zero-length prefix */
        else
                /* Find the largest suffix that fits in 'name' field. */
-               suffix_start = strchr(p + strlen(p) - 100 - 1, '/');
+               suffix_start = strchr(path + strlen(path) - 100 - 1, '/');
 
        /*
         * If name is too long, or has non-ASCII characters, add
-        * 'path' to pax extended attrs.
+        * 'path' to pax extended attrs.  (Note that an unconvertible
+        * name must have non-ASCII characters.)
         */
-       if (suffix_start == NULL || suffix_start - p > 155 || has_non_ASCII(wp)) {
-               add_pax_attr_w(&(pax->pax_header), "path", wp);
+       if (suffix_start == NULL || suffix_start - path > 155
+           || path_w == NULL || has_non_ASCII(path_w)) {
+               if (path_w == NULL || hdrcharset != NULL)
+                       /* Can't do UTF-8, so store it raw. */
+                       add_pax_attr(&(pax->pax_header), "path", path);
+               else
+                       add_pax_attr_w(&(pax->pax_header), "path", path_w);
                archive_entry_set_pathname(entry_main,
-                   build_ustar_entry_name(ustar_entry_name, p, strlen(p), NULL));
+                   build_ustar_entry_name(ustar_entry_name,
+                       path, strlen(path), NULL));
                need_extension = 1;
        }
 
-       /* If link name is too long or has non-ASCII characters, add
-        * 'linkpath' to pax extended attrs. */
-       linkname = hardlink;
-       if (linkname == NULL)
-               linkname = archive_entry_symlink(entry_main);
-
-       if (linkname != NULL) {
-               /* There is a link name, get the wide version as well. */
-               if (hardlink != NULL)
-                       wp = archive_entry_hardlink_w(entry_main);
-               else
-                       wp = archive_entry_symlink_w(entry_main);
-
-               /* If the link is long or has a non-ASCII character,
-                * store it as a pax extended attribute. */
-               if (strlen(linkname) > 100 || has_non_ASCII(wp)) {
-                       add_pax_attr_w(&(pax->pax_header), "linkpath", wp);
-                       if (hardlink != NULL)
-                               archive_entry_set_hardlink(entry_main,
-                                   "././@LongHardLink");
+       if (linkpath != NULL) {
+               /* If link name is too long or has non-ASCII characters, add
+                * 'linkpath' to pax extended attrs. */
+               if (strlen(linkpath) > 100 || linkpath_w == NULL
+                   || linkpath_w == NULL || has_non_ASCII(linkpath_w)) {
+                       if (linkpath_w == NULL || hdrcharset != NULL)
+                               /* If the linkpath is not convertible
+                                * to wide, or we're encoding in
+                                * binary anyway, store it raw. */
+                               add_pax_attr(&(pax->pax_header),
+                                   "linkpath", linkpath);
                        else
-                               archive_entry_set_symlink(entry_main,
-                                   "././@LongSymLink");
+                               /* If the link is long or has a
+                                * non-ASCII character, store it as a
+                                * pax extended attribute. */
+                               add_pax_attr_w(&(pax->pax_header),
+                                   "linkpath", linkpath_w);
+                       if (strlen(linkpath) > 100) {
+                               if (hardlink != NULL)
+                                       archive_entry_set_hardlink(entry_main,
+                                           "././@LongHardLink");
+                               else
+                                       archive_entry_set_symlink(entry_main,
+                                           "././@LongSymLink");
+                       }
                        need_extension = 1;
                }
        }
@@ -509,12 +571,20 @@ archive_write_pax_header(struct archive_write *a,
 
        /* If group name is too large or has non-ASCII characters, add
         * 'gname' to pax extended attrs. */
-       p = archive_entry_gname(entry_main);
-       wp = archive_entry_gname_w(entry_main);
-       if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
-               add_pax_attr_w(&(pax->pax_header), "gname", wp);
-               archive_entry_set_gname(entry_main, NULL);
-               need_extension = 1;
+       if (gname != NULL) {
+               if (strlen(gname) > 31
+                   || gname_w == NULL
+                   || has_non_ASCII(gname_w))
+               {
+                       if (gname_w == NULL || hdrcharset != NULL) {
+                               add_pax_attr(&(pax->pax_header),
+                                   "gname", gname);
+                       } else  {
+                               add_pax_attr_w(&(pax->pax_header),
+                                   "gname", gname_w);
+                       }
+                       need_extension = 1;
+               }
        }
 
        /* If numeric UID is too large, add 'uid' to pax extended attrs. */
@@ -524,14 +594,21 @@ archive_write_pax_header(struct archive_write *a,
                need_extension = 1;
        }
 
-       /* If user name is too large, add 'uname' to pax extended attrs. */
-       /* TODO: If uname has non-ASCII characters, use pax attribute. */
-       p = archive_entry_uname(entry_main);
-       wp = archive_entry_uname_w(entry_main);
-       if (p != NULL && (strlen(p) > 31 || has_non_ASCII(wp))) {
-               add_pax_attr_w(&(pax->pax_header), "uname", wp);
-               archive_entry_set_uname(entry_main, NULL);
-               need_extension = 1;
+       /* Add 'uname' to pax extended attrs if necessary. */
+       if (uname != NULL) {
+               if (strlen(uname) > 31
+                   || uname_w == NULL
+                   || has_non_ASCII(uname_w))
+               {
+                       if (uname_w == NULL || hdrcharset != NULL) {
+                               add_pax_attr(&(pax->pax_header),
+                                   "uname", uname);
+                       } else {
+                               add_pax_attr_w(&(pax->pax_header),
+                                   "uname", uname_w);
+                       }
+                       need_extension = 1;
+               }
        }
 
        /*
@@ -733,7 +810,6 @@ archive_write_pax_header(struct archive_write *a,
        __archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0);
 
        /* If we built any extended attributes, write that entry first. */
-       ret = ARCHIVE_OK;
        if (archive_strlen(&(pax->pax_header)) > 0) {
                struct archive_entry *pax_attr_entry;
                time_t s;
@@ -793,13 +869,13 @@ archive_write_pax_header(struct archive_write *a,
                /* Standard ustar doesn't support ctime. */
                archive_entry_set_ctime(pax_attr_entry, 0, 0);
 
-               ret = __archive_write_format_header_ustar(a, paxbuff,
+               r = __archive_write_format_header_ustar(a, paxbuff,
                    pax_attr_entry, 'x', 1);
 
                archive_entry_free(pax_attr_entry);
 
                /* Note that the 'x' header shouldn't ever fail to format */
-               if (ret != 0) {
+               if (r != 0) {
                        const char *msg = "archive_write_pax_header: "
                            "'x' header failed?!  This can't happen.\n";
                        write(2, msg, strlen(msg));
@@ -986,12 +1062,19 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length,
  * The ustar header for the pax extended attributes must have a
  * reasonable name:  SUSv3 suggests 'dirname'/PaxHeader/'filename'
  *
- * Joerg Schiling has argued that this is unnecessary because, in practice,
- * if the pax extended attributes get extracted as regular files, noone is
- * going to bother reading those attributes to manually restore them.
- * Based on this, 'star' uses /tmp/PaxHeader/'basename' as the ustar header
- * name.  This is a tempting argument, but I'm not entirely convinced.
- * I'm also uncomfortable with the fact that "/tmp" is a Unix-ism.
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, noone is going to bother reading those attributes to
+ * manually restore them.  Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name.  This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced.  I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
+ *
+ * GNU tar uses 'dirname'/PaxHeader.<pid>/'filename', where the PID is
+ * the PID of the archiving process.  This seems unnecessarily complex
+ * to me, as I don't see much value to separating the headers from
+ * extracting multiple versions of an archive.
  *
  * The following routine implements the SUSv3 recommendation, and is
  * much simpler because build_ustar_entry_name() above already does
index 287afe0..a7d26c9 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.24 2007/06/11 05:17:30 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.25 2007/12/30 04:58:22 kientzle Exp $");
 
 
 #ifdef HAVE_ERRNO_H
index 2f29833..9dbdc6d 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD$
+.\" $FreeBSD: src/lib/libarchive/cpio.5,v 1.1 2007/12/30 04:58:22 kientzle Exp $
 .\"
 .Dd October 5, 2007
 .Dt CPIO 5
index c71cf68..8cad9e2 100644 (file)
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.1 2007/05/29 01:00:20 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.2 2007/12/30 04:58:22 kientzle Exp $");
 
 #if defined(HAVE_POLL)
 #  if defined(HAVE_POLL_H)
index 0606a5b..0346d8f 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14 2007/04/05 05:07:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.15 2007/12/30 04:58:22 kientzle Exp $
 .\"
 .Dd April 27, 2004
 .Dt libarchive-formats 3
index a84c940..9a42b76 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/libarchive_internals.3,v 1.1 2007/05/29 01:00:20 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive_internals.3,v 1.2 2007/12/30 04:58:22 kientzle Exp $
 .\"
 .Dd April 16, 2007
 .Dt LIBARCHIVE 3
index db6a10a..ec22160 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.35 2007/05/29 05:39:10 kientzle Exp $
+.\" $FreeBSD: src/usr.bin/tar/bsdtar.1,v 1.37 2008/01/22 07:23:44 kientzle Exp $
 .\"
 .Dd April 13, 2004
 .Dt BSDTAR 1
@@ -232,16 +232,9 @@ All symbolic links will be followed.
 Normally, symbolic links are archived as such.
 With this option, the target of the link will be archived instead.
 .It Fl l
-If
-.Ev POSIXLY_CORRECT
-is specified in the environment, this is a synonym for the
+This is a synonym for the
 .Fl -check-links
 option.
-Otherwise, an error will be displayed.
-Users who desire behavior compatible with GNU tar should use
-the
-.Fl -one-file-system
-option instead.
 .It Fl m
 (x mode only)
 Do not extract modification time.
@@ -422,11 +415,6 @@ The locale to use.
 See
 .Xr environ 7
 for more information.
-.It Ev POSIXLY_CORRECT
-If this environment variable is defined, the
-.Fl l
-option will be interpreted in accordance with
-.St -p1003.1-96 .
 .It Ev TAPE
 The default tape device.
 The
@@ -681,13 +669,16 @@ This is a complete re-implementation based on the
 .Xr libarchive 3
 library.
 .Sh BUGS
-POSIX and GNU violently disagree about the meaning of the
+This program follows
+.St -p1003.1-96
+for the definition of the
 .Fl l
 option.
-Because of the potential for disaster if someone expects
-one behavior and gets the other, the
+Note that GNU tar prior to version 1.15 treated
 .Fl l
-option is deliberately broken in this implementation.
+as a synonym for the
+.Fl -one-file-system
+option.
 .Pp
 The
 .Fl C Pa dir
index d20a60d..fbc66c1 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.77 2007/09/09 00:07:18 kientzle Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.79 2008/01/22 07:23:44 kientzle Exp $");
 
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
@@ -391,19 +391,9 @@ main(int argc, char **argv)
                case 'L': /* BSD convention */
                        bsdtar->symlink_mode = 'L';
                        break;
-               case 'l': /* SUSv2 and GNU conflict badly here */
-                       if (getenv("POSIXLY_CORRECT") != NULL) {
-                               /* User has asked for POSIX/SUS behavior. */
-                               bsdtar->option_warn_links = 1;
-                       } else {
-                               fprintf(stderr,
-"Error: -l has different behaviors in different tar programs.\n");
-                               fprintf(stderr,
-"  For the GNU behavior, use --one-file-system instead.\n");
-                               fprintf(stderr,
-"  For the POSIX behavior, use --check-links instead.\n");
-                               usage(bsdtar);
-                       }
+               case 'l': /* SUSv2 and GNU tar beginning with 1.16 */
+                       /* GNU tar 1.13  used -l for --one-file-system */
+                       bsdtar->option_warn_links = 1;
                        break;
                case 'm': /* SUSv2 */
                        bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME;
index 50f6168..fb10678 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.28 2007/05/29 05:39:10 kientzle Exp $
+ * $FreeBSD: src/usr.bin/tar/bsdtar.h,v 1.29 2008/01/02 00:21:27 kientzle Exp $
  */
 
 #include "bsdtar_platform.h"
index 66852ca..ccb9d3c 100644 (file)
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.24 2007/04/12 04:45:32 kientzle Exp $
+ * $FreeBSD: src/usr.bin/tar/bsdtar_platform.h,v 1.25 2008/01/02 00:23:00 kientzle Exp $
  */
 
 /*
index a942e85..edda317 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.34 2007/07/20 01:24:49 kientzle Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.35 2008/01/02 00:21:27 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
index c43e461..68ff8d8 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.17 2007/04/18 04:36:11 kientzle Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.18 2008/01/02 00:21:27 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
index f25b685..3b8cc51 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 #include "bsdtar_platform.h"
-__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.63 2007/05/29 05:39:10 kientzle Exp $");
+__FBSDID("$FreeBSD: src/usr.bin/tar/write.c,v 1.64 2008/02/19 05:27:17 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -1543,7 +1543,7 @@ test_for_append(struct bsdtar *bsdtar)
        if (stat(bsdtar->filename, &s) != 0)
                return;
 
-       if (!S_ISREG(s.st_mode))
+       if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
                bsdtar_errc(bsdtar, 1, 0,
                    "Cannot append to %s: not a regular file.",
                    bsdtar->filename);
index 752a79e..b29b920 100644 (file)
@@ -1 +1 @@
-2.4.8
\ No newline at end of file
+2.4.17
\ No newline at end of file