[Midnightbsd-cvs] src: lib/libarchive: Upgrade to libarchive 2.2.4.

ctriv at midnightbsd.org ctriv at midnightbsd.org
Sat Sep 29 18:13:44 EDT 2007


Log Message:
-----------
Upgrade to libarchive 2.2.4.

Modified Files:
--------------
    src/lib/libarchive:
        COPYING (r1.1.1.2 -> r1.2)
        Makefile (r1.2 -> r1.3)
        README (r1.1.1.1 -> r1.2)
        archive.h.in (r1.1.1.2 -> r1.2)
        archive_check_magic.c (r1.1.1.2 -> r1.2)
        archive_entry.3 (r1.1.1.2 -> r1.2)
        archive_entry.c (r1.1.1.2 -> r1.2)
        archive_entry.h (r1.1.1.2 -> r1.2)
        archive_platform.h (r1.1.1.2 -> r1.2)
        archive_private.h (r1.1.1.2 -> r1.2)
        archive_read.3 (r1.1.1.2 -> r1.2)
        archive_read.c (r1.1.1.2 -> r1.2)
        archive_read_data_into_fd.c (r1.1.1.2 -> r1.2)
        archive_read_extract.c (r1.1.1.2 -> r1.2)
        archive_read_open_fd.c (r1.1.1.2 -> r1.2)
        archive_read_open_file.c (r1.1.1.2 -> r1.2)
        archive_read_open_filename.c (r1.1.1.1 -> r1.2)
        archive_read_open_memory.c (r1.1.1.1 -> r1.2)
        archive_read_support_compression_all.c (r1.1.1.2 -> r1.2)
        archive_read_support_compression_bzip2.c (r1.1.1.2 -> r1.2)
        archive_read_support_compression_compress.c (r1.1.1.2 -> r1.2)
        archive_read_support_compression_gzip.c (r1.1.1.2 -> r1.2)
        archive_read_support_compression_none.c (r1.1.1.2 -> r1.2)
        archive_read_support_format_all.c (r1.1.1.2 -> r1.2)
        archive_read_support_format_cpio.c (r1.1.1.2 -> r1.2)
        archive_read_support_format_empty.c (r1.1.1.1 -> r1.2)
        archive_read_support_format_iso9660.c (r1.1.1.2 -> r1.2)
        archive_read_support_format_tar.c (r1.2 -> r1.3)
        archive_read_support_format_zip.c (r1.1.1.2 -> r1.2)
        archive_string.c (r1.1.1.2 -> r1.2)
        archive_string.h (r1.1.1.2 -> r1.2)
        archive_string_sprintf.c (r1.1.1.2 -> r1.2)
        archive_util.3 (r1.1.1.2 -> r1.2)
        archive_util.c (r1.1.1.2 -> r1.2)
        archive_write.3 (r1.1.1.2 -> r1.2)
        archive_write.c (r1.1.1.2 -> r1.2)
        archive_write_open_fd.c (r1.1.1.2 -> r1.2)
        archive_write_open_file.c (r1.1.1.2 -> r1.2)
        archive_write_open_filename.c (r1.1.1.1 -> r1.2)
        archive_write_open_memory.c (r1.1.1.1 -> r1.2)
        archive_write_set_compression_bzip2.c (r1.1.1.2 -> r1.2)
        archive_write_set_compression_gzip.c (r1.1.1.2 -> r1.2)
        archive_write_set_compression_none.c (r1.1.1.2 -> r1.2)
        archive_write_set_format.c (r1.1.1.2 -> r1.2)
        archive_write_set_format_by_name.c (r1.1.1.2 -> r1.2)
        archive_write_set_format_cpio.c (r1.1.1.2 -> r1.2)
        archive_write_set_format_pax.c (r1.1.1.2 -> r1.2)
        archive_write_set_format_shar.c (r1.1.1.2 -> r1.2)
        archive_write_set_format_ustar.c (r1.1.1.2 -> r1.2)
        config_freebsd.h (r1.1.1.1 -> r1.2)
        libarchive-formats.5 (r1.1.1.2 -> r1.2)
        libarchive.3 (r1.1.1.2 -> r1.2)
        tar.5 (r1.1.1.2 -> r1.2)

Added Files:
-----------
    src/lib/libarchive:
        archive_entry_copy_stat.c (r1.1)
        archive_entry_private.h (r1.1)
        archive_entry_stat.c (r1.1)
        archive_read_private.h (r1.1)
        archive_read_support_compression_program.c (r1.1)
        archive_read_support_format_ar.c (r1.1)
        archive_virtual.c (r1.1)
        archive_write_disk.3 (r1.1)
        archive_write_disk.c (r1.1)
        archive_write_disk_private.h (r1.1)
        archive_write_disk_set_standard_lookup.c (r1.1)
        archive_write_private.h (r1.1)
        archive_write_set_compression_program.c (r1.1)
        archive_write_set_format_ar.c (r1.1)
        archive_write_set_format_cpio_newc.c (r1.1)
        filter_fork.c (r1.1)
        filter_fork.h (r1.1)
        libarchive_internals.3 (r1.1)
    src/lib/libarchive/test:
        Makefile (r1.1)
        README (r1.1)
        libarchive_test (r1.1)
        main.c (r1.1)
        read_open_memory.c (r1.1)
        test.h (r1.1)
        test_acl_basic.c (r1.1)
        test_acl_pax.c (r1.1)
        test_archive_api_feature.c (r1.1)
        test_bad_fd.c (r1.1)
        test_entry.c (r1.1)
        test_read_compress_program.c (r1.1)
        test_read_data_large.c (r1.1)
        test_read_extract.c (r1.1)
        test_read_format_ar.c (r1.1)
        test_read_format_cpio_bin.c (r1.1)
        test_read_format_cpio_bin_Z.c (r1.1)
        test_read_format_cpio_bin_bz2.c (r1.1)
        test_read_format_cpio_bin_gz.c (r1.1)
        test_read_format_cpio_odc.c (r1.1)
        test_read_format_cpio_svr4_gzip.c (r1.1)
        test_read_format_cpio_svr4c_Z.c (r1.1)
        test_read_format_empty.c (r1.1)
        test_read_format_gtar_gz.c (r1.1)
        test_read_format_gtar_sparse.c (r1.1)
        test_read_format_iso_gz.c (r1.1)
        test_read_format_isorr_bz2.c (r1.1)
        test_read_format_pax_bz2.c (r1.1)
        test_read_format_tar.c (r1.1)
        test_read_format_tbz.c (r1.1)
        test_read_format_tgz.c (r1.1)
        test_read_format_tz.c (r1.1)
        test_read_format_zip.c (r1.1)
        test_read_large.c (r1.1)
        test_read_pax_truncated.c (r1.1)
        test_read_position.c (r1.1)
        test_read_truncated.c (r1.1)
        test_tar_filenames.c (r1.1)
        test_write_compress_program.c (r1.1)
        test_write_disk.c (r1.1)
        test_write_disk_perms.c (r1.1)
        test_write_disk_secure.c (r1.1)
        test_write_format_ar.c (r1.1)
        test_write_format_cpio.c (r1.1)
        test_write_format_cpio_empty.c (r1.1)
        test_write_format_shar_empty.c (r1.1)
        test_write_format_tar.c (r1.1)
        test_write_format_tar_empty.c (r1.1)
        test_write_open_memory.c (r1.1)

-------------- next part --------------
Index: archive_read_support_format_cpio.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_cpio.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_format_cpio.c -Llib/libarchive/archive_read_support_format_cpio.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_format_cpio.c
+++ lib/libarchive/archive_read_support_format_cpio.c
@@ -24,14 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.13.2.4 2007/01/27 06:44:53 kientzle Exp $");
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#endif
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.24 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -43,13 +36,11 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 struct cpio_bin_header {
 	unsigned char	c_magic[2];
@@ -108,8 +99,8 @@
 #define	CPIO_MAGIC   0x13141516
 struct cpio {
 	int			  magic;
-	int			(*read_header)(struct archive *, struct cpio *,
-				     struct stat *, size_t *, size_t *);
+	int			(*read_header)(struct archive_read *, struct cpio *,
+				     struct archive_entry *, size_t *, size_t *);
 	struct links_entry	 *links_head;
 	struct archive_string	  entry_name;
 	struct archive_string	  entry_linkname;
@@ -120,34 +111,34 @@
 
 static int64_t	atol16(const char *, unsigned);
 static int64_t	atol8(const char *, unsigned);
-static int	archive_read_format_cpio_bid(struct archive *);
-static int	archive_read_format_cpio_cleanup(struct archive *);
-static int	archive_read_format_cpio_read_data(struct archive *,
+static int	archive_read_format_cpio_bid(struct archive_read *);
+static int	archive_read_format_cpio_cleanup(struct archive_read *);
+static int	archive_read_format_cpio_read_data(struct archive_read *,
 		    const void **, size_t *, off_t *);
-static int	archive_read_format_cpio_read_header(struct archive *,
+static int	archive_read_format_cpio_read_header(struct archive_read *,
 		    struct archive_entry *);
 static int	be4(const unsigned char *);
-static int	header_bin_be(struct archive *, struct cpio *, struct stat *,
-		    size_t *, size_t *);
-static int	header_bin_le(struct archive *, struct cpio *, struct stat *,
-		    size_t *, size_t *);
-static int	header_newc(struct archive *, struct cpio *, struct stat *,
-		    size_t *, size_t *);
-static int	header_odc(struct archive *, struct cpio *, struct stat *,
-		    size_t *, size_t *);
+static int	header_bin_be(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_bin_le(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_newc(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_odc(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
 static int	le4(const unsigned char *);
-static void	record_hardlink(struct cpio *cpio, struct archive_entry *entry,
-		    const struct stat *st);
+static void	record_hardlink(struct cpio *cpio, struct archive_entry *entry);
 
 int
-archive_read_support_format_cpio(struct archive *a)
+archive_read_support_format_cpio(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct cpio *cpio;
 	int r;
 
 	cpio = (struct cpio *)malloc(sizeof(*cpio));
 	if (cpio == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate cpio data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(cpio, 0, sizeof(*cpio));
@@ -168,16 +159,16 @@
 
 
 static int
-archive_read_format_cpio_bid(struct archive *a)
+archive_read_format_cpio_bid(struct archive_read *a)
 {
 	int bid, bytes_read;
 	const void *h;
 	const unsigned char *p;
 	struct cpio *cpio;
 
-	cpio = (struct cpio *)*(a->pformat_data);
+	cpio = (struct cpio *)(a->format->data);
 	bid = 0;
-	bytes_read = (a->compression_read_ahead)(a, &h, 6);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 6);
 	/* Convert error code into error return. */
 	if (bytes_read < 0)
 		return ((int)bytes_read);
@@ -227,10 +218,9 @@
 }
 
 static int
-archive_read_format_cpio_read_header(struct archive *a,
+archive_read_format_cpio_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct stat st;
 	struct cpio *cpio;
 	size_t bytes;
 	const void *h;
@@ -238,33 +228,28 @@
 	size_t name_pad;
 	int r;
 
-	memset(&st, 0, sizeof(st));
-
-	cpio = (struct cpio *)*(a->pformat_data);
-	r = (cpio->read_header(a, cpio, &st, &namelength, &name_pad));
+	cpio = (struct cpio *)(a->format->data);
+	r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
 
 	if (r != ARCHIVE_OK)
 		return (r);
 
-	/* Assign all of the 'stat' fields at once. */
-	archive_entry_copy_stat(entry, &st);
-
 	/* Read name from buffer. */
-	bytes = (a->compression_read_ahead)(a, &h, namelength + name_pad);
+	bytes = (a->decompressor->read_ahead)(a, &h, namelength + name_pad);
 	if (bytes < namelength + name_pad)
 	    return (ARCHIVE_FATAL);
-	(a->compression_read_consume)(a, namelength + name_pad);
+	(a->decompressor->consume)(a, namelength + name_pad);
 	archive_strncpy(&cpio->entry_name, (const char *)h, namelength);
 	archive_entry_set_pathname(entry, cpio->entry_name.s);
 	cpio->entry_offset = 0;
 
 	/* If this is a symlink, read the link contents. */
-	if (S_ISLNK(st.st_mode)) {
-		bytes = (a->compression_read_ahead)(a, &h,
+	if (archive_entry_filetype(entry) == AE_IFLNK) {
+		bytes = (a->decompressor->read_ahead)(a, &h,
 		    cpio->entry_bytes_remaining);
 		if ((off_t)bytes < cpio->entry_bytes_remaining)
 			return (ARCHIVE_FATAL);
-		(a->compression_read_consume)(a, cpio->entry_bytes_remaining);
+		(a->decompressor->consume)(a, cpio->entry_bytes_remaining);
 		archive_strncpy(&cpio->entry_linkname, (const char *)h,
 		    cpio->entry_bytes_remaining);
 		archive_entry_set_symlink(entry, cpio->entry_linkname.s);
@@ -274,26 +259,26 @@
 	/* Compare name to "TRAILER!!!" to test for end-of-archive. */
 	if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
 	    /* TODO: Store file location of start of block. */
-	    archive_set_error(a, 0, NULL);
+	    archive_set_error(&a->archive, 0, NULL);
 	    return (ARCHIVE_EOF);
 	}
 
 	/* Detect and record hardlinks to previously-extracted entries. */
-	record_hardlink(cpio, entry, &st);
+	record_hardlink(cpio, entry);
 
 	return (ARCHIVE_OK);
 }
 
 static int
-archive_read_format_cpio_read_data(struct archive *a,
+archive_read_format_cpio_read_data(struct archive_read *a,
     const void **buff, size_t *size, off_t *offset)
 {
 	ssize_t bytes_read;
 	struct cpio *cpio;
 
-	cpio = (struct cpio *)*(a->pformat_data);
+	cpio = (struct cpio *)(a->format->data);
 	if (cpio->entry_bytes_remaining > 0) {
-		bytes_read = (a->compression_read_ahead)(a, buff, 1);
+		bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
 		if (bytes_read <= 0)
 			return (ARCHIVE_FATAL);
 		if (bytes_read > cpio->entry_bytes_remaining)
@@ -302,16 +287,16 @@
 		*offset = cpio->entry_offset;
 		cpio->entry_offset += bytes_read;
 		cpio->entry_bytes_remaining -= bytes_read;
-		(a->compression_read_consume)(a, bytes_read);
+		(a->decompressor->consume)(a, bytes_read);
 		return (ARCHIVE_OK);
 	} else {
 		while (cpio->entry_padding > 0) {
-			bytes_read = (a->compression_read_ahead)(a, buff, 1);
+			bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
 			if (bytes_read <= 0)
 				return (ARCHIVE_FATAL);
 			if (bytes_read > cpio->entry_padding)
 				bytes_read = cpio->entry_padding;
-			(a->compression_read_consume)(a, bytes_read);
+			(a->decompressor->consume)(a, bytes_read);
 			cpio->entry_padding -= bytes_read;
 		}
 		*buff = NULL;
@@ -322,44 +307,42 @@
 }
 
 static int
-header_newc(struct archive *a, struct cpio *cpio, struct stat *st,
-    size_t *namelength, size_t *name_pad)
+header_newc(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
 	const struct cpio_newc_header *header;
 	size_t bytes;
 
 	/* Read fixed-size portion of header. */
-	bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_newc_header));
+	bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_newc_header));
 	if (bytes < sizeof(struct cpio_newc_header))
 	    return (ARCHIVE_FATAL);
-	(a->compression_read_consume)(a, sizeof(struct cpio_newc_header));
+	(a->decompressor->consume)(a, sizeof(struct cpio_newc_header));
 
-	/* Parse out hex fields into struct stat. */
+	/* Parse out hex fields. */
 	header = (const struct cpio_newc_header *)h;
 
 	if (memcmp(header->c_magic, "070701", 6) == 0) {
-		a->archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
-		a->archive_format_name = "ASCII cpio (SVR4 with no CRC)";
+		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+		a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
 	} else if (memcmp(header->c_magic, "070702", 6) == 0) {
-		a->archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
-		a->archive_format_name = "ASCII cpio (SVR4 with CRC)";
+		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
+		a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
 	} else {
 		/* TODO: Abort here? */
 	}
 
-	st->st_dev = makedev(
-		atol16(header->c_devmajor, sizeof(header->c_devmajor)),
-		atol16(header->c_devminor, sizeof(header->c_devminor)));
-	st->st_ino = atol16(header->c_ino, sizeof(header->c_ino));
-	st->st_mode = atol16(header->c_mode, sizeof(header->c_mode));
-	st->st_uid = atol16(header->c_uid, sizeof(header->c_uid));
-	st->st_gid = atol16(header->c_gid, sizeof(header->c_gid));
-	st->st_nlink = atol16(header->c_nlink, sizeof(header->c_nlink));
-	st->st_rdev = makedev(
-		atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)),
-		atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
-	st->st_mtime = atol16(header->c_mtime, sizeof(header->c_mtime));
+	archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor)));
+	archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor)));
+	archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino)));
+	archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode)));
+	archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid)));
+	archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid)));
+	archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink)));
+	archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)));
+	archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
+	archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0);
 	*namelength = atol16(header->c_namesize, sizeof(header->c_namesize));
 	/* Pad name to 2 more than a multiple of 4. */
 	*name_pad = (2 - *namelength) & 3;
@@ -367,141 +350,139 @@
 	/*
 	 * Note: entry_bytes_remaining is at least 64 bits and
 	 * therefore guaranteed to be big enough for a 33-bit file
-	 * size.  struct stat.st_size may only be 32 bits, so
-	 * assigning there first could lose information.
+	 * size.
 	 */
 	cpio->entry_bytes_remaining =
 	    atol16(header->c_filesize, sizeof(header->c_filesize));
-	st->st_size = cpio->entry_bytes_remaining;
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	/* Pad file contents to a multiple of 4. */
 	cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
 	return (ARCHIVE_OK);
 }
 
 static int
-header_odc(struct archive *a, struct cpio *cpio, struct stat *st,
-    size_t *namelength, size_t *name_pad)
+header_odc(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
 	const struct cpio_odc_header *header;
 	size_t bytes;
 
-	a->archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
-	a->archive_format_name = "POSIX octet-oriented cpio";
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+	a->archive.archive_format_name = "POSIX octet-oriented cpio";
 
 	/* Read fixed-size portion of header. */
-	bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_odc_header));
+	bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_odc_header));
 	if (bytes < sizeof(struct cpio_odc_header))
 	    return (ARCHIVE_FATAL);
-	(a->compression_read_consume)(a, sizeof(struct cpio_odc_header));
+	(a->decompressor->consume)(a, sizeof(struct cpio_odc_header));
 
-	/* Parse out octal fields into struct stat. */
+	/* Parse out octal fields. */
 	header = (const struct cpio_odc_header *)h;
 
-	st->st_dev = atol8(header->c_dev, sizeof(header->c_dev));
-	st->st_ino = atol8(header->c_ino, sizeof(header->c_ino));
-	st->st_mode = atol8(header->c_mode, sizeof(header->c_mode));
-	st->st_uid = atol8(header->c_uid, sizeof(header->c_uid));
-	st->st_gid = atol8(header->c_gid, sizeof(header->c_gid));
-	st->st_nlink = atol8(header->c_nlink, sizeof(header->c_nlink));
-	st->st_rdev = atol8(header->c_rdev, sizeof(header->c_rdev));
-	st->st_mtime = atol8(header->c_mtime, sizeof(header->c_mtime));
+	archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev)));
+	archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino)));
+	archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode)));
+	archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid)));
+	archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid)));
+	archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink)));
+	archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev)));
+	archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0);
 	*namelength = atol8(header->c_namesize, sizeof(header->c_namesize));
 	*name_pad = 0; /* No padding of filename. */
 
 	/*
 	 * Note: entry_bytes_remaining is at least 64 bits and
 	 * therefore guaranteed to be big enough for a 33-bit file
-	 * size.  struct stat.st_size may only be 32 bits, so
-	 * assigning there first could lose information.
+	 * size.
 	 */
 	cpio->entry_bytes_remaining =
 	    atol8(header->c_filesize, sizeof(header->c_filesize));
-	st->st_size = cpio->entry_bytes_remaining;
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = 0;
 	return (ARCHIVE_OK);
 }
 
 static int
-header_bin_le(struct archive *a, struct cpio *cpio, struct stat *st,
-    size_t *namelength, size_t *name_pad)
+header_bin_le(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
 	const struct cpio_bin_header *header;
 	size_t bytes;
 
-	a->archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
-	a->archive_format_name = "cpio (little-endian binary)";
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
+	a->archive.archive_format_name = "cpio (little-endian binary)";
 
 	/* Read fixed-size portion of header. */
-	bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_bin_header));
+	bytes = (a->decompressor->read_ahead)(a, &h, sizeof(struct cpio_bin_header));
 	if (bytes < sizeof(struct cpio_bin_header))
 	    return (ARCHIVE_FATAL);
-	(a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
+	(a->decompressor->consume)(a, sizeof(struct cpio_bin_header));
 
-	/* Parse out binary fields into struct stat. */
+	/* Parse out binary fields. */
 	header = (const struct cpio_bin_header *)h;
 
-	st->st_dev = header->c_dev[0] + header->c_dev[1] * 256;
-	st->st_ino = header->c_ino[0] + header->c_ino[1] * 256;
-	st->st_mode = header->c_mode[0] + header->c_mode[1] * 256;
-	st->st_uid = header->c_uid[0] + header->c_uid[1] * 256;
-	st->st_gid = header->c_gid[0] + header->c_gid[1] * 256;
-	st->st_nlink = header->c_nlink[0] + header->c_nlink[1] * 256;
-	st->st_rdev = header->c_rdev[0] + header->c_rdev[1] * 256;
-	st->st_mtime = le4(header->c_mtime);
+	archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256);
+	archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256);
+	archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256);
+	archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256);
+	archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256);
+	archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256);
+	archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256);
+	archive_entry_set_mtime(entry, le4(header->c_mtime), 0);
 	*namelength = header->c_namesize[0] + header->c_namesize[1] * 256;
 	*name_pad = *namelength & 1; /* Pad to even. */
 
 	cpio->entry_bytes_remaining = le4(header->c_filesize);
-	st->st_size = cpio->entry_bytes_remaining;
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
 	return (ARCHIVE_OK);
 }
 
 static int
-header_bin_be(struct archive *a, struct cpio *cpio, struct stat *st,
-    size_t *namelength, size_t *name_pad)
+header_bin_be(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
 	const struct cpio_bin_header *header;
 	size_t bytes;
 
-	a->archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
-	a->archive_format_name = "cpio (big-endian binary)";
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
+	a->archive.archive_format_name = "cpio (big-endian binary)";
 
 	/* Read fixed-size portion of header. */
-	bytes = (a->compression_read_ahead)(a, &h,
+	bytes = (a->decompressor->read_ahead)(a, &h,
 	    sizeof(struct cpio_bin_header));
 	if (bytes < sizeof(struct cpio_bin_header))
 	    return (ARCHIVE_FATAL);
-	(a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
+	(a->decompressor->consume)(a, sizeof(struct cpio_bin_header));
 
-	/* Parse out binary fields into struct stat. */
+	/* Parse out binary fields. */
 	header = (const struct cpio_bin_header *)h;
-	st->st_dev = header->c_dev[0] * 256 + header->c_dev[1];
-	st->st_ino = header->c_ino[0] * 256 + header->c_ino[1];
-	st->st_mode = header->c_mode[0] * 256 + header->c_mode[1];
-	st->st_uid = header->c_uid[0] * 256 + header->c_uid[1];
-	st->st_gid = header->c_gid[0] * 256 + header->c_gid[1];
-	st->st_nlink = header->c_nlink[0] * 256 + header->c_nlink[1];
-	st->st_rdev = header->c_rdev[0] * 256 + header->c_rdev[1];
-	st->st_mtime = be4(header->c_mtime);
+	archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]);
+	archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]);
+	archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]);
+	archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]);
+	archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]);
+	archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]);
+	archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]);
+	archive_entry_set_mtime(entry, be4(header->c_mtime), 0);
 	*namelength = header->c_namesize[0] * 256 + header->c_namesize[1];
 	*name_pad = *namelength & 1; /* Pad to even. */
 
 	cpio->entry_bytes_remaining = be4(header->c_filesize);
-	st->st_size = cpio->entry_bytes_remaining;
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
 	return (ARCHIVE_OK);
 }
 
 static int
-archive_read_format_cpio_cleanup(struct archive *a)
+archive_read_format_cpio_cleanup(struct archive_read *a)
 {
 	struct cpio *cpio;
 
-	cpio = (struct cpio *)*(a->pformat_data);
+	cpio = (struct cpio *)(a->format->data);
         /* Free inode->name map */
         while (cpio->links_head != NULL) {
                 struct links_entry *lp = cpio->links_head->next;
@@ -511,9 +492,9 @@
                 free(cpio->links_head);
                 cpio->links_head = lp;
         }
-
+	archive_string_free(&cpio->entry_name);
 	free(cpio);
-	*(a->pformat_data) = NULL;
+	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -578,17 +559,21 @@
 }
 
 static void
-record_hardlink(struct cpio *cpio, struct archive_entry *entry,
-    const struct stat *st)
+record_hardlink(struct cpio *cpio, struct archive_entry *entry)
 {
         struct links_entry      *le;
+	dev_t dev;
+	ino_t ino;
+
+	dev = archive_entry_dev(entry);
+	ino = archive_entry_ino(entry);
 
         /*
          * First look in the list of multiply-linked files.  If we've
          * already dumped it, convert this entry to a hard link entry.
          */
         for (le = cpio->links_head; le; le = le->next) {
-                if (le->dev == st->st_dev && le->ino == st->st_ino) {
+                if (le->dev == dev && le->ino == ino) {
                         archive_entry_set_hardlink(entry, le->name);
 
                         if (--le->links <= 0) {
@@ -613,9 +598,9 @@
         le->next = cpio->links_head;
         le->previous = NULL;
         cpio->links_head = le;
-        le->dev = st->st_dev;
-        le->ino = st->st_ino;
-        le->links = st->st_nlink - 1;
+        le->dev = dev;
+        le->ino = ino;
+        le->links = archive_entry_nlink(entry) - 1;
         le->name = strdup(archive_entry_pathname(entry));
 	if (le->name == NULL)
 		__archive_errx(1, "Out of memory adding file to list");
Index: archive_read_support_compression_bzip2.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_bzip2.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_compression_bzip2.c -Llib/libarchive/archive_read_support_compression_bzip2.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_compression_bzip2.c
+++ lib/libarchive/archive_read_support_compression_bzip2.c
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.7.2.4 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.16 2007/05/29 01:00:18 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -46,6 +46,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 #if HAVE_BZLIB_H
 struct private_data {
@@ -54,22 +55,26 @@
 	size_t		 uncompressed_buffer_size;
 	char		*read_next;
 	int64_t		 total_out;
+	char		 eof; /* True = found end of compressed data. */
 };
 
-static int	finish(struct archive *);
-static ssize_t	read_ahead(struct archive *, const void **, size_t);
-static ssize_t	read_consume(struct archive *, size_t);
-static int	drive_decompressor(struct archive *a, struct private_data *);
+static int	finish(struct archive_read *);
+static ssize_t	read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t	read_consume(struct archive_read *, size_t);
+static int	drive_decompressor(struct archive_read *a, struct private_data *);
 #endif
 
-/* These two functions are defined even if we lack bzlib.  See below. */
+/* These two functions are defined even if we lack the library.  See below. */
 static int	bid(const void *, size_t);
-static int	init(struct archive *, const void *, size_t);
+static int	init(struct archive_read *, const void *, size_t);
 
 int
-archive_read_support_compression_bzip2(struct archive *a)
+archive_read_support_compression_bzip2(struct archive *_a)
 {
-	return (__archive_read_register_compression(a, bid, init));
+	struct archive_read *a = (struct archive_read *)_a;
+	if (__archive_read_register_compression(a, bid, init) != NULL)
+		return (ARCHIVE_OK);
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -129,12 +134,12 @@
 #ifndef HAVE_BZLIB_H
 
 /*
- * If we don't have bzlib on this system, we can't actually do the
- * decompression.  We can, however, still detect bzip2-compressed
- * archives and emit a useful message.
+ * If we don't have the library on this system, we can't actually do the
+ * decompression.  We can, however, still detect compressed archives
+ * and emit a useful message.
  */
 static int
-init(struct archive *a, const void *buff, size_t n)
+init(struct archive_read *a, const void *buff, size_t n)
 {
 	(void)a;	/* UNUSED */
 	(void)buff;	/* UNUSED */
@@ -152,19 +157,19 @@
  * Setup the callbacks.
  */
 static int
-init(struct archive *a, const void *buff, size_t n)
+init(struct archive_read *a, const void *buff, size_t n)
 {
 	struct private_data *state;
 	int ret;
 
-	a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
-	a->compression_name = "bzip2";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
+	a->archive.compression_name = "bzip2";
 
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for %s decompression",
-		    a->compression_name);
+		    a->archive.compression_name);
 		return (ARCHIVE_FATAL);
 	}
 	memset(state, 0, sizeof(*state));
@@ -176,9 +181,9 @@
 	state->stream.avail_out = state->uncompressed_buffer_size;
 
 	if (state->uncompressed_buffer == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate %s decompression buffers",
-		    a->compression_name);
+		    a->archive.compression_name);
 		free(state);
 		return (ARCHIVE_FATAL);
 	}
@@ -192,10 +197,10 @@
 	state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
 	state->stream.avail_in = n;
 
-	a->compression_read_ahead = read_ahead;
-	a->compression_read_consume = read_consume;
-	a->compression_skip = NULL; /* not supported */
-	a->compression_finish = finish;
+	a->decompressor->read_ahead = read_ahead;
+	a->decompressor->consume = read_consume;
+	a->decompressor->skip = NULL; /* not supported */
+	a->decompressor->finish = finish;
 
 	/* Initialize compression library. */
 	ret = BZ2_bzDecompressInit(&(state->stream),
@@ -210,30 +215,31 @@
 	}
 
 	if (ret == BZ_OK) {
-		a->compression_data = state;
+		a->decompressor->data = state;
 		return (ARCHIVE_OK);
 	}
 
 	/* Library setup failed: Clean up. */
-	archive_set_error(a, ARCHIVE_ERRNO_MISC,
-	    "Internal error initializing %s library", a->compression_name);
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Internal error initializing %s library",
+	    a->archive.compression_name);
 	free(state->uncompressed_buffer);
 	free(state);
 
 	/* Override the error message if we know what really went wrong. */
 	switch (ret) {
 	case BZ_PARAM_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid setup parameter");
 		break;
 	case BZ_MEM_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Internal error initializing compression library: "
 		    "out of memory");
 		break;
 	case BZ_CONFIG_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "mis-compiled library");
 		break;
@@ -247,15 +253,15 @@
  * as necessary.
  */
 static ssize_t
-read_ahead(struct archive *a, const void **p, size_t min)
+read_ahead(struct archive_read *a, const void **p, size_t min)
 {
 	struct private_data *state;
-	int read_avail, was_avail, ret;
+	size_t read_avail, was_avail;
+	int ret;
 
-	state = (struct private_data *)a->compression_data;
-	was_avail = -1;
+	state = (struct private_data *)a->decompressor->data;
 	if (!a->client_reader) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No read callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -272,13 +278,16 @@
 		    = state->uncompressed_buffer_size - read_avail;
 	}
 
-	while (was_avail < read_avail &&	/* Made some progress. */
-	    read_avail < (int)min &&		/* Haven't satisfied min. */
-	    read_avail < (int)state->uncompressed_buffer_size) { /* !full */
-		if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
-			return (ret);
+	while (read_avail < min &&		/* Haven't satisfied min. */
+	    read_avail < state->uncompressed_buffer_size) { /* !full */
 		was_avail = read_avail;
+		if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK)
+			return (ret);
+		if (ret == ARCHIVE_EOF)
+			break; /* Break on EOF even if we haven't met min. */
 		read_avail = state->stream.next_out - state->read_next;
+		if (was_avail == read_avail) /* No progress? */
+			break;
 	}
 
 	*p = state->read_next;
@@ -289,12 +298,12 @@
  * Mark a previously-returned block of data as read.
  */
 static ssize_t
-read_consume(struct archive *a, size_t n)
+read_consume(struct archive_read *a, size_t n)
 {
 	struct private_data *state;
 
-	state = (struct private_data *)a->compression_data;
-	a->file_position += n;
+	state = (struct private_data *)a->decompressor->data;
+	a->archive.file_position += n;
 	state->read_next += n;
 	if (state->read_next > state->stream.next_out)
 		__archive_errx(1, "Request to consume too many "
@@ -306,29 +315,27 @@
  * Clean up the decompressor.
  */
 static int
-finish(struct archive *a)
+finish(struct archive_read *a)
 {
 	struct private_data *state;
 	int ret;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->decompressor->data;
 	ret = ARCHIVE_OK;
 	switch (BZ2_bzDecompressEnd(&(state->stream))) {
 	case BZ_OK:
 		break;
 	default:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
-		    "Failed to clean up %s compressor", a->compression_name);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up %s compressor",
+		    a->archive.compression_name);
 		ret = ARCHIVE_FATAL;
 	}
 
 	free(state->uncompressed_buffer);
 	free(state);
 
-	a->compression_data = NULL;
-	if (a->client_closer != NULL)
-		(a->client_closer)(a, a->client_data);
-
+	a->decompressor->data = NULL;
 	return (ret);
 }
 
@@ -337,17 +344,22 @@
  * blocks as necessary.
  */
 static int
-drive_decompressor(struct archive *a, struct private_data *state)
+drive_decompressor(struct archive_read *a, struct private_data *state)
 {
 	ssize_t ret;
 	int decompressed, total_decompressed;
 	char *output;
+	const void *read_buf;
 
+	if (state->eof)
+		return (ARCHIVE_EOF);
 	total_decompressed = 0;
 	for (;;) {
 		if (state->stream.avail_in == 0) {
-			ret = (a->client_reader)(a, a->client_data,
-			    (const void **)&state->stream.next_in);
+			read_buf = state->stream.next_in;
+			ret = (a->client_reader)(&a->archive, a->client_data,
+			    &read_buf);
+			state->stream.next_in = (void *)(uintptr_t)read_buf;
 			if (ret < 0) {
 				/*
 				 * TODO: Find a better way to handle
@@ -356,12 +368,12 @@
 				goto fatal;
 			}
 			if (ret == 0  &&  total_decompressed == 0) {
-				archive_set_error(a, EIO,
+				archive_set_error(&a->archive, EIO,
 				    "Premature end of %s compressed data",
-				    a->compression_name);
+				    a->archive.compression_name);
 				return (ARCHIVE_FATAL);
 			}
-			a->raw_position += ret;
+			a->archive.raw_position += ret;
 			state->stream.avail_in = ret;
 		}
 
@@ -382,6 +394,7 @@
 					return (ARCHIVE_OK);
 				break;
 			case BZ_STREAM_END:	/* Found end of stream. */
+				state->eof = 1;
 				return (ARCHIVE_OK);
 			default:
 				/* Any other return value is an error. */
@@ -393,8 +406,8 @@
 
 	/* Return a fatal error. */
 fatal:
-	archive_set_error(a, ARCHIVE_ERRNO_MISC, "%s decompression failed",
-	    a->compression_name);
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "%s decompression failed", a->archive.compression_name);
 	return (ARCHIVE_FATAL);
 }
 
Index: COPYING
===================================================================
RCS file: /home/cvs/src/lib/libarchive/COPYING,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/COPYING -Llib/libarchive/COPYING -u -r1.1.1.2 -r1.2
--- lib/libarchive/COPYING
+++ lib/libarchive/COPYING
@@ -32,5 +32,5 @@
 automatically by GNU autoconf and GNU automake.  Those generated files
 are controlled by the relevant licenses.
 
-$FreeBSD: src/lib/libarchive/COPYING,v 1.1.8.2 2007/01/27 06:44:52 kientzle Exp $
+$FreeBSD: src/lib/libarchive/COPYING,v 1.3 2007/01/09 08:05:54 kientzle Exp $
 
--- /dev/null
+++ lib/libarchive/archive_write_set_format_ar.c
@@ -0,0 +1,528 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ar.c,v 1.3 2007/05/29 01:00:19 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+struct ar_w {
+	uint64_t	 entry_bytes_remaining;
+	uint64_t	 entry_padding;
+	int		 is_strtab;
+	int		 has_strtab;
+	char		*strtab;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int		 archive_write_set_format_ar(struct archive_write *);
+static int		 archive_write_ar_header(struct archive_write *,
+			     struct archive_entry *);
+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_entry(struct archive_write *);
+static const char	*basename(const char *path);
+static int		 format_octal(int64_t v, char *p, int s);
+static int		 format_decimal(int64_t v, char *p, int s);
+
+int
+archive_write_set_format_ar_bsd(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = archive_write_set_format_ar(a);
+	if (r == ARCHIVE_OK) {
+		a->archive_format = ARCHIVE_FORMAT_AR_BSD;
+		a->archive_format_name = "ar (BSD)";
+	}
+	return (r);
+}
+
+int
+archive_write_set_format_ar_svr4(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = archive_write_set_format_ar(a);
+	if (r == ARCHIVE_OK) {
+		a->archive_format = ARCHIVE_FORMAT_AR_GNU;
+		a->archive_format_name = "ar (GNU/SVR4)";
+	}
+	return (r);
+}
+
+/*
+ * Generic initialization.
+ */
+static int
+archive_write_set_format_ar(struct archive_write *a)
+{
+	struct ar_w *ar;
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
+
+	ar = (struct ar_w *)malloc(sizeof(*ar));
+	if (ar == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
+		return (ARCHIVE_FATAL);
+	}
+	memset(ar, 0, sizeof(*ar));
+	a->format_data = ar;
+
+	a->format_write_header = archive_write_ar_header;
+	a->format_write_data = archive_write_ar_data;
+	a->format_finish = NULL;
+	a->format_destroy = archive_write_ar_destroy;
+	a->format_finish_entry = archive_write_ar_finish_entry;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
+{
+	int ret, append_fn;
+	char buff[60];
+	char *ss, *se;
+	struct ar_w *ar;
+	const char *pathname;
+	const char *filename;
+
+	ret = 0;
+	append_fn = 0;
+	ar = (struct ar_w *)a->format_data;
+	ar->is_strtab = 0;
+	filename = NULL;
+
+	/*
+	 * Reject files with empty name.
+	 */
+	pathname = archive_entry_pathname(entry);
+	if (*pathname == '\0') {
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid filename");
+		return (ARCHIVE_WARN);
+	}
+
+	/*
+	 * If we are now at the beginning of the archive,
+	 * we need first write the ar global header.
+	 */
+	if (a->archive.file_position == 0)
+		(a->compressor.write)(a, "!<arch>\n", 8);
+
+	memset(buff, ' ', 60);
+	strncpy(&buff[AR_fmag_offset], "`\n", 2);
+
+	if (strcmp(pathname, "/") == 0 ) {
+		/* Entry is archive symbol table in GNU format */
+		buff[AR_name_offset] = '/';
+		goto stat;
+	}
+	if (strcmp(pathname, "__.SYMDEF") == 0) {
+		/* Entry is archive symbol table in BSD format */
+		strncpy(buff + AR_name_offset, "__.SYMDEF", 9);
+		goto stat;
+	}
+	if (strcmp(pathname, "//") == 0) {
+		/*
+		 * Entry is archive filename table, inform that we should
+		 * collect strtab in next _data call.
+		 */
+		ar->is_strtab = 1;
+		buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
+		/*
+		 * For archive string table, only ar_size filed should
+		 * be set.
+		 */
+		goto size;
+	}
+
+	/* 
+	 * Otherwise, entry is a normal archive member.
+	 * Strip leading paths from filenames, if any.
+	 */
+	if ((filename = basename(pathname)) == NULL) {
+		/* Reject filenames with trailing "/" */
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid filename");
+		return (ARCHIVE_WARN);
+	}
+
+	if (a->archive_format == ARCHIVE_FORMAT_AR_GNU) {
+		/*
+		 * SVR4/GNU variant use a "/" to mark then end of the filename,
+		 * make it possible to have embedded spaces in the filename.
+		 * So, the longest filename here (without extension) is
+		 * actually 15 bytes.
+		 */
+		if (strlen(filename) <= 15) {
+			strncpy(&buff[AR_name_offset], 
+			    filename, strlen(filename));
+			buff[AR_name_offset + strlen(filename)] = '/';
+		} else {
+			/*
+			 * For filename longer than 15 bytes, GNU variant
+			 * makes use of a string table and instead stores the
+			 * offset of the real filename to in the ar_name field.
+			 * The string table should have been written before.
+			 */
+			if (ar->has_strtab <= 0) {
+				archive_set_error(&a->archive, EINVAL,
+				    "Can't find string table");
+				return (ARCHIVE_WARN);
+			}
+
+			se = (char *)malloc(strlen(filename) + 3);
+			if (se == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate filename buffer");
+				return (ARCHIVE_FATAL);
+			}
+
+			strncpy(se, filename, strlen(filename));
+			strcpy(se + strlen(filename), "/\n");
+
+			ss = strstr(ar->strtab, se);
+			free(se);
+
+			if (ss == NULL) {
+				archive_set_error(&a->archive, EINVAL,
+				    "Invalid string table");
+				return (ARCHIVE_WARN);
+			}
+
+			/*
+			 * GNU variant puts "/" followed by digits into
+			 * ar_name field. These digits indicates the real
+			 * filename string's offset to the string table.
+			 */
+			buff[AR_name_offset] = '/';
+			if (format_decimal(ss - ar->strtab,
+			    buff + AR_name_offset + 1,
+			    AR_name_size - 1)) {
+				archive_set_error(&a->archive, ERANGE,
+				    "string table offset too large");
+				return (ARCHIVE_WARN);
+			}
+		}
+	} else if (a->archive_format == ARCHIVE_FORMAT_AR_BSD) {
+		/*
+		 * BSD variant: for any file name which is more than
+		 * 16 chars or contains one or more embedded space(s), the
+		 * string "#1/" followed by the ASCII length of the name is
+		 * put into the ar_name field. The file size (stored in the
+		 * ar_size field) is incremented by the length of the name.
+		 * The name is then written immediately following the
+		 * archive header.
+		 */
+		if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
+			strncpy(&buff[AR_name_offset], filename, strlen(filename));
+			buff[AR_name_offset + strlen(filename)] = ' ';
+		}
+		else {
+			strncpy(buff + AR_name_offset, "#1/", 3);
+			if (format_decimal(strlen(filename),
+			    buff + AR_name_offset + 3,
+			    AR_name_size - 3)) {
+				archive_set_error(&a->archive, ERANGE,
+				    "File name too long");
+				return (ARCHIVE_WARN);
+			}
+			append_fn = 1;
+			archive_entry_set_size(entry,
+			    archive_entry_size(entry) + strlen(filename));
+		}
+	}
+
+stat:
+	if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File modification time too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric user ID too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric group ID too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric mode too large");
+		return (ARCHIVE_WARN);
+	}
+	/*
+	 * Sanity Check: A non-pseudo archive member should always be
+	 * a regular file.
+	 */
+	if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Regular file required for non-pseudo member");
+		return (ARCHIVE_WARN);
+	}
+
+size:
+	if (format_decimal(archive_entry_size(entry), buff + AR_size_offset,
+	    AR_size_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File size out of range");
+		return (ARCHIVE_WARN);
+	}
+
+	ret = (a->compressor.write)(a, buff, 60);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	ar->entry_bytes_remaining = archive_entry_size(entry);
+	ar->entry_padding = ar->entry_bytes_remaining % 2;
+
+	if (append_fn > 0) {
+		ret = (a->compressor.write)(a, filename, strlen(filename));
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		ar->entry_bytes_remaining -= strlen(filename);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct ar_w *ar;
+	int ret;
+
+	ar = (struct ar_w *)a->format_data;
+	if (s > ar->entry_bytes_remaining)
+		s = ar->entry_bytes_remaining;
+
+	if (ar->is_strtab > 0) {
+		if (ar->has_strtab > 0) {
+			archive_set_error(&a->archive, EINVAL,
+			    "More than one string tables exist");
+			return (ARCHIVE_WARN);
+		}
+
+		ar->strtab = (char *)malloc(s);
+		if (ar->strtab == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate strtab buffer");
+			return (ARCHIVE_FATAL);
+		}
+		strncpy(ar->strtab, buff, s);
+		ar->has_strtab = 1;
+	}
+
+	ret = (a->compressor.write)(a, buff, s);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	ar->entry_bytes_remaining -= s;
+	return (s);
+}
+
+static int
+archive_write_ar_destroy(struct archive_write *a)
+{
+	struct ar_w *ar;
+
+	ar = (struct ar_w *)a->format_data;
+
+	if (ar->has_strtab > 0) {
+		free(ar->strtab);
+		ar->strtab = NULL;
+	}
+
+	free(ar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_finish_entry(struct archive_write *a)
+{
+	struct ar_w *ar;
+	int ret;
+
+	ar = (struct ar_w *)a->format_data;
+
+	if (ar->entry_bytes_remaining != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Entry remaining bytes larger than 0");
+		return (ARCHIVE_WARN);
+	}
+
+	if (ar->entry_padding == 0) {
+		return (ARCHIVE_OK);
+	}
+
+	if (ar->entry_padding != 1) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Padding wrong size: %d should be 1 or 0",
+		    ar->entry_padding);
+		return (ARCHIVE_WARN);
+	}
+
+	ret = (a->compressor.write)(a, "\n", 1);
+	return (ret);
+}
+
+/*
+ * Format a number into the specified field using base-8.
+ * NB: This version is slightly different from the one in
+ * _ustar.c
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+	int len;
+	char *h;
+
+	len = s;
+	h = p;
+
+	/* Octal values can't be negative, so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;		/* Start at the end and work backwards. */
+	do {
+		*--p = (char)('0' + (v & 7));
+		v >>= 3;
+	} while (--s > 0 && v > 0);
+
+	if (v == 0) {
+		memmove(h, p, len - s);
+		p = h + len - s;
+		while (s-- > 0)
+			*p++ = ' ';
+		return (0);
+	}
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '7';
+
+	return (-1);
+}
+
+/*
+ * Format a number into the specified field using base-10.
+ */
+static int
+format_decimal(int64_t v, char *p, int s)
+{
+	int len;
+	char *h;
+
+	len = s;
+	h = p;
+
+	/* Negative values in ar header are meaningless , so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;
+	do {
+		*--p = (char)('0' + (v % 10));
+		v /= 10;
+	} while (--s > 0 && v > 0);
+
+	if (v == 0) {
+		memmove(h, p, len - s);
+		p = h + len - s;
+		while (s-- > 0)
+			*p++ = ' ';
+		return (0);
+	}
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '9';
+
+	return (-1);
+}
+
+static const char *
+basename(const char *path)
+{
+	const char *endp, *startp;
+
+	endp = path + strlen(path) - 1;
+	/*
+	 * For filename with trailing slash(es), we return
+	 * NULL indicating an error.
+	 */
+	if (*endp == '/')
+		return (NULL);
+
+	/* Find the start of the base */
+	startp = endp;
+	while (startp > path && *(startp - 1) != '/')
+		startp--;
+	
+	return (startp);
+}
Index: config_freebsd.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/config_freebsd.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/config_freebsd.h -Llib/libarchive/config_freebsd.h -u -r1.1.1.1 -r1.2
--- lib/libarchive/config_freebsd.h
+++ lib/libarchive/config_freebsd.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.2.2.1 2007/01/27 06:44:53 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/config_freebsd.h,v 1.5 2007/05/29 01:00:20 kientzle Exp $
  */
 
 /* FreeBSD 5.0 and later have ACL support. */
@@ -37,7 +37,12 @@
 
 #define	HAVE_BZLIB_H 1
 #define	HAVE_CHFLAGS 1
+#define	HAVE_DECL_INT64_MAX 1
+#define	HAVE_DECL_INT64_MIN 1
+#define	HAVE_DECL_SIZE_MAX 1
 #define	HAVE_DECL_STRERROR_R 1
+#define	HAVE_DECL_UINT32_MAX 1
+#define	HAVE_DECL_UINT64_MAX 1
 #define	HAVE_EFTYPE 1
 #define	HAVE_EILSEQ 1
 #define	HAVE_ERRNO_H 1
@@ -60,7 +65,10 @@
 #define	HAVE_MEMSET 1
 #define	HAVE_MKDIR 1
 #define	HAVE_MKFIFO 1
+#define	HAVE_POLL 1
+#define	HAVE_POLL_H 1
 #define	HAVE_PWD_H 1
+#define	HAVE_SELECT 1
 #define	HAVE_STDINT_H 1
 #define	HAVE_STDLIB_H 1
 #define	HAVE_STRCHR 1
@@ -72,15 +80,24 @@
 #define	HAVE_STRRCHR 1
 #define	HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
 #define	HAVE_STRUCT_STAT_ST_RDEV 1
+#define	HAVE_STRUCT_TM_TM_GMTOFF 1
 #define	HAVE_SYS_ACL_H 1
 #define	HAVE_SYS_IOCTL_H 1
+#define	HAVE_SYS_SELECT_H 1
 #define	HAVE_SYS_STAT_H 1
 #define	HAVE_SYS_TIME_H 1
 #define	HAVE_SYS_TYPES_H 1
 #define	HAVE_SYS_WAIT_H 1
 #define	HAVE_TIMEGM 1
 #define	HAVE_UNISTD_H 1
+#define	HAVE_UTIME 1
+#define	HAVE_UTIMES 1
+#define	HAVE_UTIME_H 1
 #define	HAVE_WCHAR_H 1
+#define	HAVE_WCSCPY 1
+#define	HAVE_WCSLEN 1
+#define	HAVE_WMEMCMP 1
+#define	HAVE_WMEMCPY 1
 #define	HAVE_ZLIB_H 1
 #define	STDC_HEADERS 1
 #define	TIME_WITH_SYS_TIME 1
Index: archive_write_set_format_ustar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_ustar.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format_ustar.c -Llib/libarchive/archive_write_set_format_ustar.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format_ustar.c
+++ lib/libarchive/archive_write_set_format_ustar.c
@@ -24,18 +24,9 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.12.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_ustar.c,v 1.24 2007/06/11 05:17:30 kientzle Exp $");
+
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#else
-#ifdef MAJOR_IN_SYSMACROS
-#include <sys/sysmacros.h>
-#endif
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -50,11 +41,11 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
 struct ustar {
 	uint64_t	entry_bytes_remaining;
 	uint64_t	entry_padding;
-	char		written;
 };
 
 /*
@@ -150,38 +141,40 @@
 	0,0,0,0,0,0,0,0, 0,0,0,0
 };
 
-static ssize_t	archive_write_ustar_data(struct archive *a, const void *buff,
+static ssize_t	archive_write_ustar_data(struct archive_write *a, const void *buff,
 		    size_t s);
-static int	archive_write_ustar_finish(struct archive *);
-static int	archive_write_ustar_finish_entry(struct archive *);
-static int	archive_write_ustar_header(struct archive *,
+static int	archive_write_ustar_destroy(struct archive_write *);
+static int	archive_write_ustar_finish(struct archive_write *);
+static int	archive_write_ustar_finish_entry(struct archive_write *);
+static int	archive_write_ustar_header(struct archive_write *,
 		    struct archive_entry *entry);
 static int	format_256(int64_t, char *, int);
 static int	format_number(int64_t, char *, int size, int max, int strict);
 static int	format_octal(int64_t, char *, int);
-static int	write_nulls(struct archive *a, size_t);
+static int	write_nulls(struct archive_write *a, size_t);
 
 /*
  * Set output format to 'ustar' format.
  */
 int
-archive_write_set_format_ustar(struct archive *a)
+archive_write_set_format_ustar(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	struct ustar *ustar;
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_finish != NULL)
-		(a->format_finish)(a);
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
 
 	/* Basic internal sanity test. */
 	if (sizeof(template_header) != 512) {
-		archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header));
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %d should be 512", sizeof(template_header));
 		return (ARCHIVE_FATAL);
 	}
 
 	ustar = (struct ustar *)malloc(sizeof(*ustar));
 	if (ustar == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate ustar data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(ustar, 0, sizeof(*ustar));
@@ -191,6 +184,7 @@
 	a->format_write_header = archive_write_ustar_header;
 	a->format_write_data = archive_write_ustar_data;
 	a->format_finish = archive_write_ustar_finish;
+	a->format_destroy = archive_write_ustar_destroy;
 	a->format_finish_entry = archive_write_ustar_finish_entry;
 	a->archive_format = ARCHIVE_FORMAT_TAR_USTAR;
 	a->archive_format_name = "POSIX ustar";
@@ -198,25 +192,43 @@
 }
 
 static int
-archive_write_ustar_header(struct archive *a, struct archive_entry *entry)
+archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
 {
 	char buff[512];
 	int ret;
 	struct ustar *ustar;
 
 	ustar = (struct ustar *)a->format_data;
-	ustar->written = 1;
 
 	/* Only regular files (not hardlinks) have data. */
 	if (archive_entry_hardlink(entry) != NULL ||
 	    archive_entry_symlink(entry) != NULL ||
-	    !S_ISREG(archive_entry_mode(entry)))
+	    !(archive_entry_filetype(entry) == AE_IFREG))
 		archive_entry_set_size(entry, 0);
 
+	if (AE_IFDIR == archive_entry_mode(entry)) {
+		const char *p;
+		char *t;
+		/*
+		 * Ensure a trailing '/'.  Modify the entry so
+		 * the client sees the change.
+		 */
+		p = archive_entry_pathname(entry);
+		if (p[strlen(p) - 1] != '/') {
+			t = (char *)malloc(strlen(p) + 2);
+			if (t != NULL) {
+				strcpy(t, p);
+				strcat(t, "/");
+				archive_entry_copy_pathname(entry, t);
+				free(t);
+			}
+		}
+	}
+
 	ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1);
 	if (ret != ARCHIVE_OK)
 		return (ret);
-	ret = (a->compression_write)(a, buff, 512);
+	ret = (a->compressor.write)(a, buff, 512);
 	if (ret != ARCHIVE_OK)
 		return (ret);
 
@@ -236,14 +248,13 @@
  * This is exported so that other 'tar' formats can use it.
  */
 int
-__archive_write_format_header_ustar(struct archive *a, char h[512],
+__archive_write_format_header_ustar(struct archive_write *a, char h[512],
     struct archive_entry *entry, int tartype, int strict)
 {
 	unsigned int checksum;
 	int i, ret;
 	size_t copy_length;
 	const char *p, *pp;
-	const struct stat *st;
 	int mytartype;
 
 	ret = 0;
@@ -268,15 +279,25 @@
 		/* Store in two pieces, splitting at a '/'. */
 		p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/');
 		/*
+		 * If the separator we found is the first '/', find
+		 * the next one.  (This is a pathological case that
+		 * occurs for paths of exactly 101 bytes that start with
+		 * '/'; it occurs because the separating '/' is not
+		 * stored explicitly and the reconstruction assumes that
+		 * an empty prefix means there is no '/' separator.)
+		 */
+		if (p == pp)
+			p = strchr(p + 1, '/');
+		/*
 		 * If there is no path separator, or the prefix or
 		 * remaining name are too large, return an error.
 		 */
 		if (!p) {
-			archive_set_error(a, ENAMETOOLONG,
+			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Pathname too long");
 			ret = ARCHIVE_WARN;
 		} else if (p  > pp + USTAR_prefix_size) {
-			archive_set_error(a, ENAMETOOLONG,
+			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Pathname too long");
 			ret = ARCHIVE_WARN;
 		} else {
@@ -294,7 +315,7 @@
 	if (p != NULL && p[0] != '\0') {
 		copy_length = strlen(p);
 		if (copy_length > USTAR_linkname_size) {
-			archive_set_error(a, ENAMETOOLONG,
+			archive_set_error(&a->archive, ENAMETOOLONG,
 			    "Link contents too long");
 			ret = ARCHIVE_WARN;
 			copy_length = USTAR_linkname_size;
@@ -306,7 +327,7 @@
 	if (p != NULL && p[0] != '\0') {
 		copy_length = strlen(p);
 		if (copy_length > USTAR_uname_size) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Username too long");
 			ret = ARCHIVE_WARN;
 			copy_length = USTAR_uname_size;
@@ -318,7 +339,7 @@
 	if (p != NULL && p[0] != '\0') {
 		copy_length = strlen(p);
 		if (strlen(p) > USTAR_gname_size) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Group name too long");
 			ret = ARCHIVE_WARN;
 			copy_length = USTAR_gname_size;
@@ -326,45 +347,44 @@
 		memcpy(h + USTAR_gname_offset, p, copy_length);
 	}
 
-	st = archive_entry_stat(entry);
-
-	if (format_number(st->st_mode & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
-		archive_set_error(a, ERANGE, "Numeric mode too large");
+	if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE, "Numeric mode too large");
 		ret = ARCHIVE_WARN;
 	}
 
-	if (format_number(st->st_uid, h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
-		archive_set_error(a, ERANGE, "Numeric user ID too large");
+	if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE, "Numeric user ID too large");
 		ret = ARCHIVE_WARN;
 	}
 
-	if (format_number(st->st_gid, h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
-		archive_set_error(a, ERANGE, "Numeric group ID too large");
+	if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE, "Numeric group ID too large");
 		ret = ARCHIVE_WARN;
 	}
 
-	if (format_number(st->st_size, h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
-		archive_set_error(a, ERANGE, "File size out of range");
+	if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE, "File size out of range");
 		ret = ARCHIVE_WARN;
 	}
 
-	if (format_number(st->st_mtime, h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
-		archive_set_error(a, ERANGE,
+	if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
 		    "File modification time too large");
 		ret = ARCHIVE_WARN;
 	}
 
-	if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
-		if (format_number(major(st->st_rdev), h + USTAR_rdevmajor_offset,
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR) {
+		if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset,
 			USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) {
-			archive_set_error(a, ERANGE,
+			archive_set_error(&a->archive, ERANGE,
 			    "Major device number too large");
 			ret = ARCHIVE_WARN;
 		}
 
-		if (format_number(minor(st->st_rdev), h + USTAR_rdevminor_offset,
+		if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset,
 			USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) {
-			archive_set_error(a, ERANGE,
+			archive_set_error(&a->archive, ERANGE,
 			    "Minor device number too large");
 			ret = ARCHIVE_WARN;
 		}
@@ -375,22 +395,17 @@
 	} else if (mytartype >= 0) {
 		h[USTAR_typeflag_offset] = mytartype;
 	} else {
-		switch (st->st_mode & S_IFMT) {
-		case S_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
-		case S_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
-		case S_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
-		case S_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
-		case S_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
-		case S_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
-		case S_IFSOCK:
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "tar format cannot archive socket");
-			ret = ARCHIVE_WARN;
-			break;
+		switch (archive_entry_filetype(entry)) {
+		case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
+		case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
+		case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
+		case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
+		case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
+		case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
 		default:
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 			    "tar format cannot archive this (mode=0%lo)",
-			    (unsigned long)st->st_mode);
+			    (unsigned long)archive_entry_mode(entry));
 			ret = ARCHIVE_WARN;
 		}
 	}
@@ -486,27 +501,30 @@
 }
 
 static int
-archive_write_ustar_finish(struct archive *a)
+archive_write_ustar_finish(struct archive_write *a)
 {
-	struct ustar *ustar;
 	int r;
 
-	r = ARCHIVE_OK;
+	if (a->compressor.write == NULL)
+		return (ARCHIVE_OK);
+
+	r = write_nulls(a, 512*2);
+	return (r);
+}
+
+static int
+archive_write_ustar_destroy(struct archive_write *a)
+{
+	struct ustar *ustar;
+
 	ustar = (struct ustar *)a->format_data;
-	/*
-	 * Suppress end-of-archive if nothing else was ever written.
-	 * This fixes a problem where setting one format, then another
-	 * ends up writing a gratuitous end-of-archive marker.
-	 */
-	if (ustar->written && a->compression_write != NULL)
-		r = write_nulls(a, 512*2);
 	free(ustar);
 	a->format_data = NULL;
-	return (r);
+	return (ARCHIVE_OK);
 }
 
 static int
-archive_write_ustar_finish_entry(struct archive *a)
+archive_write_ustar_finish_entry(struct archive_write *a)
 {
 	struct ustar *ustar;
 	int ret;
@@ -519,13 +537,14 @@
 }
 
 static int
-write_nulls(struct archive *a, size_t padding)
+write_nulls(struct archive_write *a, size_t padding)
 {
-	int ret, to_write;
+	int ret;
+	size_t to_write;
 
 	while (padding > 0) {
 		to_write = padding < a->null_length ? padding : a->null_length;
-		ret = (a->compression_write)(a, a->nulls, to_write);
+		ret = (a->compressor.write)(a, a->nulls, to_write);
 		if (ret != ARCHIVE_OK)
 			return (ret);
 		padding -= to_write;
@@ -534,7 +553,7 @@
 }
 
 static ssize_t
-archive_write_ustar_data(struct archive *a, const void *buff, size_t s)
+archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
 {
 	struct ustar *ustar;
 	int ret;
@@ -542,7 +561,7 @@
 	ustar = (struct ustar *)a->format_data;
 	if (s > ustar->entry_bytes_remaining)
 		s = ustar->entry_bytes_remaining;
-	ret = (a->compression_write)(a, buff, s);
+	ret = (a->compressor.write)(a, buff, s);
 	ustar->entry_bytes_remaining -= s;
 	if (ret != ARCHIVE_OK)
 		return (ret);
Index: archive_write_open_fd.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_open_fd.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_open_fd.c -Llib/libarchive/archive_write_open_fd.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_open_fd.c
+++ lib/libarchive/archive_write_open_fd.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_fd.c,v 1.4.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_fd.c,v 1.9 2007/01/09 08:05:56 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
Index: archive_util.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_util.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_util.c -Llib/libarchive/archive_util.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_util.c
+++ lib/libarchive/archive_util.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.9.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_util.c,v 1.15 2007/07/06 15:36:38 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -51,6 +51,12 @@
 	return (ARCHIVE_API_VERSION);
 }
 
+int
+archive_version_stamp(void)
+{
+	return (ARCHIVE_VERSION_STAMP);
+}
+
 const char *
 archive_version(void)
 {
@@ -118,6 +124,12 @@
 	return (a->file_position);
 }
 
+void
+archive_clear_error(struct archive *a)
+{
+	archive_string_empty(&a->error_string);
+	a->error = NULL;
+}
 
 void
 archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
@@ -156,6 +168,15 @@
 }
 
 void
+archive_copy_error(struct archive *dest, struct archive *src)
+{
+	dest->archive_error_number = src->archive_error_number;
+
+	archive_string_copy(&dest->error_string, &src->error_string);
+	dest->error = dest->error_string.s;
+}
+
+void
 __archive_errx(int retvalue, const char *msg)
 {
 	static const char *msg1 = "Fatal Internal Error in libarchive: ";
Index: archive_entry.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_entry.c -Llib/libarchive/archive_entry.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_entry.c
+++ lib/libarchive/archive_entry.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.29.2.2 2007/01/27 06:44:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.44 2007/07/15 19:10:34 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -34,6 +34,9 @@
 #endif
 #ifdef MAJOR_IN_MKDEV
 #include <sys/mkdev.h>
+# if !defined makedev && (defined mkdev || defined _WIN32 || defined __WIN32__)
+#  define makedev mkdev
+# endif
 #else
 #ifdef MAJOR_IN_SYSMACROS
 #include <sys/sysmacros.h>
@@ -45,6 +48,12 @@
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>	/* for Linux file flags */
+#endif
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>	/* for Linux file flags */
+#endif
 #include <stddef.h>
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
@@ -53,67 +62,18 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-
-/* Obtain suitable wide-character manipulation functions. */
 #ifdef HAVE_WCHAR_H
 #include <wchar.h>
-#else
-static size_t wcslen(const wchar_t *s)
-{
-	const wchar_t *p = s;
-	while (*p != L'\0')
-		++p;
-	return p - s;
-}
-static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
-{
-	wchar_t *dest = s1;
-	while ((*s1 = *s2) != L'\0')
-		++s1, ++s2;
-	return dest;
-}
-#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
-/* Good enough for simple equality testing, but not for sorting. */
-#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
 #endif
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_entry_private.h"
 
 #undef max
 #define	max(a, b)	((a)>(b)?(a):(b))
 
-/*
- * Handle wide character (i.e., Unicode) and non-wide character
- * strings transparently.
- *
- */
-
-struct aes {
-	const char *aes_mbs;
-	char *aes_mbs_alloc;
-	const wchar_t *aes_wcs;
-	wchar_t *aes_wcs_alloc;
-};
-
-struct ae_acl {
-	struct ae_acl *next;
-	int	type;			/* E.g., access or default */
-	int	tag;			/* E.g., user/group/other/mask */
-	int	permset;		/* r/w/x bits */
-	int	id;			/* uid/gid for user/group */
-	struct aes name;		/* uname/gname */
-};
-
-struct ae_xattr {
-	struct ae_xattr *next;
-
-	char	*name;
-	void	*value;
-	size_t	size;
-};
-
 static void	aes_clean(struct aes *);
 static void	aes_copy(struct aes *dest, struct aes *src);
 static const char *	aes_get_mbs(struct aes *);
@@ -122,6 +82,7 @@
 static void	aes_copy_mbs(struct aes *, const char *mbs);
 /* static void	aes_set_wcs(struct aes *, const wchar_t *wcs); */
 static void	aes_copy_wcs(struct aes *, const wchar_t *wcs);
+static void	aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
 
 static char *	 ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
 static const wchar_t	*ae_wcstofflags(const wchar_t *stringp,
@@ -134,63 +95,42 @@
 		    int type, int permset, int tag);
 static struct ae_acl *acl_new_entry(struct archive_entry *entry,
 		    int type, int permset, int tag, int id);
+static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
 static void	next_field_w(const wchar_t **wp, const wchar_t **start,
 		    const wchar_t **end, wchar_t *sep);
 static int	prefix_w(const wchar_t *start, const wchar_t *end,
 		    const wchar_t *test);
+static void
+archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
+		    int permset, int tag, int id, const wchar_t *name, size_t);
 
 
-/*
- * Description of an archive entry.
- *
- * Basically, this is a "struct stat" with a few text fields added in.
- *
- * TODO: Add "comment", "charset", and possibly other entries
- * that are supported by "pax interchange" format.  However, GNU, ustar,
- * cpio, and other variants don't support these features, so they're not an
- * excruciatingly high priority right now.
- *
- * TODO: "pax interchange" format allows essentially arbitrary
- * key/value attributes to be attached to any entry.  Supporting
- * such extensions may make this library useful for special
- * applications (e.g., a package manager could attach special
- * package-management attributes to each entry).  There are tricky
- * API issues involved, so this is not going to happen until
- * there's a real demand for it.
- *
- * TODO: Design a good API for handling sparse files.
- */
-struct archive_entry {
-	/*
-	 * Note that ae_stat.st_mode & S_IFMT  can be  0!
-	 *
-	 * This occurs when the actual file type of the object is not
-	 * in the archive.  For example, 'tar' archives store
-	 * hardlinks without marking the type of the underlying
-	 * object.
-	 */
-	struct stat ae_stat;
-
-	/*
-	 * Use aes here so that we get transparent mbs<->wcs conversions.
-	 */
-	struct aes ae_fflags_text;	/* Text fflags per fflagstostr(3) */
-	unsigned long ae_fflags_set;		/* Bitmap fflags */
-	unsigned long ae_fflags_clear;
-	struct aes ae_gname;		/* Name of owning group */
-	struct aes ae_hardlink;	/* Name of target for hardlink */
-	struct aes ae_pathname;	/* Name of entry */
-	struct aes ae_symlink;		/* symlink contents */
-	struct aes ae_uname;		/* Name of owner */
-
-	struct ae_acl	*acl_head;
-	struct ae_acl	*acl_p;
-	int		 acl_state;	/* See acl_next for details. */
-	wchar_t		*acl_text_w;
+#ifndef HAVE_WCSCPY
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+	wchar_t *dest = s1;
+	while ((*s1 = *s2) != L'\0')
+		++s1, ++s2;
+	return dest;
+}
+#endif
+#ifndef HAVE_WCSLEN
+static size_t wcslen(const wchar_t *s)
+{
+	const wchar_t *p = s;
+	while (*p != L'\0')
+		++p;
+	return p - s;
+}
+#endif
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+#ifndef HAVE_WMEMCPY
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+#endif
 
-	struct ae_xattr *xattr_head;
-	struct ae_xattr *xattr_p;
-};
 
 static void
 aes_clean(struct aes *aes)
@@ -239,7 +179,8 @@
 		 * chars encode to no more than 3 bytes.  There must
 		 * be a better way... XXX
 		 */
-		int mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+		size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+
 		aes->aes_mbs_alloc = (char *)malloc(mbs_length);
 		aes->aes_mbs = aes->aes_mbs_alloc;
 		if (aes->aes_mbs == NULL)
@@ -260,7 +201,8 @@
 		 * No single byte will be more than one wide character,
 		 * so this length estimate will always be big enough.
 		 */
-		int wcs_length = strlen(aes->aes_mbs);
+		size_t wcs_length = strlen(aes->aes_mbs);
+
 		aes->aes_wcs_alloc
 		    = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
 		aes->aes_wcs = aes->aes_wcs_alloc;
@@ -326,6 +268,12 @@
 static void
 aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
 {
+	aes_copy_wcs_len(aes, wcs, wcslen(wcs));
+}
+
+static void
+aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
+{
 	if (aes->aes_mbs_alloc) {
 		free(aes->aes_mbs_alloc);
 		aes->aes_mbs_alloc = NULL;
@@ -335,10 +283,11 @@
 		aes->aes_wcs_alloc = NULL;
 	}
 	aes->aes_mbs = NULL;
-	aes->aes_wcs_alloc = (wchar_t *)malloc((wcslen(wcs) + 1) * sizeof(wchar_t));
+	aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
 	if (aes->aes_wcs_alloc == NULL)
 		__archive_errx(1, "No memory for aes_copy_wcs()");
-	wcscpy(aes->aes_wcs_alloc, wcs);
+	wmemcpy(aes->aes_wcs_alloc, wcs, len);
+	aes->aes_wcs_alloc[len] = L'\0';
 	aes->aes_wcs = aes->aes_wcs_alloc;
 }
 
@@ -353,6 +302,7 @@
 	aes_clean(&entry->ae_uname);
 	archive_entry_acl_clear(entry);
 	archive_entry_xattr_clear(entry);
+	free(entry->stat);
 	memset(entry, 0, sizeof(*entry));
 	return entry;
 }
@@ -361,6 +311,8 @@
 archive_entry_clone(struct archive_entry *entry)
 {
 	struct archive_entry *entry2;
+	struct ae_acl *ap, *ap2;
+	struct ae_xattr *xp;
 
 	/* Allocate new structure and copy over all of the fields. */
 	entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
@@ -378,8 +330,24 @@
 	aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
 	aes_copy(&entry2->ae_uname, &entry->ae_uname);
 
-	/* XXX TODO: Copy ACL data over as well. XXX */
-	/* XXX TODO: Copy xattr data over as well. XXX */
+	/* Copy ACL data over. */
+	ap = entry->acl_head;
+	while (ap != NULL) {
+		ap2 = acl_new_entry(entry2,
+		    ap->type, ap->permset, ap->tag, ap->id);
+		if (ap2 != NULL)
+			aes_copy(&ap2->name, &ap->name);
+		ap = ap->next;
+	}
+
+	/* Copy xattr data over. */
+	xp = entry->xattr_head;
+	while (xp != NULL) {
+		archive_entry_xattr_add_entry(entry2,
+		    xp->name, xp->value, xp->size);
+		xp = xp->next;
+	}
+
 	return (entry2);
 }
 
@@ -409,33 +377,59 @@
 time_t
 archive_entry_atime(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_atime);
+	return (entry->ae_stat.aest_atime);
 }
 
 long
 archive_entry_atime_nsec(struct archive_entry *entry)
 {
-	(void)entry; /* entry can be unused here. */
-	return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat));
+	return (entry->ae_stat.aest_atime_nsec);
 }
 
 time_t
 archive_entry_ctime(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_ctime);
+	return (entry->ae_stat.aest_ctime);
 }
 
 long
 archive_entry_ctime_nsec(struct archive_entry *entry)
 {
-	(void)entry; /* entry can be unused here. */
-	return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat));
+	return (entry->ae_stat.aest_ctime_nsec);
 }
 
 dev_t
 archive_entry_dev(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_dev);
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return makedev(entry->ae_stat.aest_devmajor,
+		    entry->ae_stat.aest_devminor);
+	else
+		return (entry->ae_stat.aest_dev);
+}
+
+dev_t
+archive_entry_devmajor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return (entry->ae_stat.aest_devmajor);
+	else
+		return major(entry->ae_stat.aest_dev);
+}
+
+dev_t
+archive_entry_devminor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return (entry->ae_stat.aest_devminor);
+	else
+		return minor(entry->ae_stat.aest_dev);
+}
+
+mode_t
+archive_entry_filetype(struct archive_entry *entry)
+{
+	return (AE_IFMT & entry->ae_stat.aest_mode);
 }
 
 void
@@ -481,7 +475,7 @@
 gid_t
 archive_entry_gid(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_gid);
+	return (entry->ae_stat.aest_gid);
 }
 
 const char *
@@ -511,26 +505,31 @@
 ino_t
 archive_entry_ino(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_ino);
+	return (entry->ae_stat.aest_ino);
 }
 
 mode_t
 archive_entry_mode(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_mode);
+	return (entry->ae_stat.aest_mode);
 }
 
 time_t
 archive_entry_mtime(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_mtime);
+	return (entry->ae_stat.aest_mtime);
 }
 
 long
 archive_entry_mtime_nsec(struct archive_entry *entry)
 {
-	(void)entry; /* entry can be unused here. */
-	return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat));
+	return (entry->ae_stat.aest_mtime_nsec);
+}
+
+unsigned int
+archive_entry_nlink(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_nlink);
 }
 
 const char *
@@ -548,31 +547,35 @@
 dev_t
 archive_entry_rdev(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_rdev);
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return makedev(entry->ae_stat.aest_rdevmajor,
+		    entry->ae_stat.aest_rdevminor);
+	else
+		return (entry->ae_stat.aest_rdev);
 }
 
 dev_t
 archive_entry_rdevmajor(struct archive_entry *entry)
 {
-	return (major(entry->ae_stat.st_rdev));
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return (entry->ae_stat.aest_rdevmajor);
+	else
+		return major(entry->ae_stat.aest_rdev);
 }
 
 dev_t
 archive_entry_rdevminor(struct archive_entry *entry)
 {
-	return (minor(entry->ae_stat.st_rdev));
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return (entry->ae_stat.aest_rdevminor);
+	else
+		return minor(entry->ae_stat.aest_rdev);
 }
 
 int64_t
 archive_entry_size(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_size);
-}
-
-const struct stat *
-archive_entry_stat(struct archive_entry *entry)
-{
-	return (&entry->ae_stat);
+	return (entry->ae_stat.aest_size);
 }
 
 const char *
@@ -590,7 +593,7 @@
 uid_t
 archive_entry_uid(struct archive_entry *entry)
 {
-	return (entry->ae_stat.st_uid);
+	return (entry->ae_stat.aest_uid);
 }
 
 const char *
@@ -609,14 +612,12 @@
  * Functions to set archive_entry properties.
  */
 
-/*
- * Note "copy" not "set" here.  The "set" functions that accept a pointer
- * only store the pointer; they don't copy the underlying object.
- */
 void
-archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
 {
-	entry->ae_stat = *st;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_mode &= ~AE_IFMT;
+	entry->ae_stat.aest_mode |= AE_IFMT & type;
 }
 
 void
@@ -640,7 +641,8 @@
 void
 archive_entry_set_gid(struct archive_entry *entry, gid_t g)
 {
-	entry->ae_stat.st_gid = g;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_gid = g;
 }
 
 void
@@ -650,12 +652,25 @@
 }
 
 void
+archive_entry_copy_gname(struct archive_entry *entry, const char *name)
+{
+	aes_copy_mbs(&entry->ae_gname, name);
+}
+
+void
 archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
 {
 	aes_copy_wcs(&entry->ae_gname, name);
 }
 
 void
+archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_ino = ino;
+}
+
+void
 archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
 {
 	aes_set_mbs(&entry->ae_hardlink, target);
@@ -676,15 +691,41 @@
 void
 archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
 {
-	entry->ae_stat.st_atime = t;
-	ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns);
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_atime = t;
+	entry->ae_stat.aest_atime_nsec = ns;
 }
 
 void
 archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
 {
-	entry->ae_stat.st_ctime = t;
-	ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns);
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_ctime = t;
+	entry->ae_stat.aest_ctime_nsec = ns;
+}
+
+void
+archive_entry_set_dev(struct archive_entry *entry, dev_t d)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_dev_is_broken_down = 0;
+	entry->ae_stat.aest_dev = d;
+}
+
+void
+archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_dev_is_broken_down = 1;
+	entry->ae_stat.aest_devmajor = m;
+}
+
+void
+archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_dev_is_broken_down = 1;
+	entry->ae_stat.aest_devminor = m;
 }
 
 /* Set symlink if symlink is already set, else set hardlink. */
@@ -701,14 +742,23 @@
 void
 archive_entry_set_mode(struct archive_entry *entry, mode_t m)
 {
-	entry->ae_stat.st_mode = m;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_mode = m;
 }
 
 void
 archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
 {
-	entry->ae_stat.st_mtime = m;
-	ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns);
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_mtime = m;
+	entry->ae_stat.aest_mtime_nsec = ns;
+}
+
+void
+archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_nlink = nlink;
 }
 
 void
@@ -730,27 +780,34 @@
 }
 
 void
-archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
 {
-	dev_t d;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev = m;
+	entry->ae_stat.aest_rdev_is_broken_down = 0;
+}
 
-	d = entry->ae_stat.st_rdev;
-	entry->ae_stat.st_rdev = makedev(major(m), minor(d));
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdevmajor = m;
 }
 
 void
 archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
 {
-	dev_t d;
-
-	d = entry->ae_stat.st_rdev;
-	entry->ae_stat.st_rdev = makedev(major(d), minor(m));
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdevminor = m;
 }
 
 void
 archive_entry_set_size(struct archive_entry *entry, int64_t s)
 {
-	entry->ae_stat.st_size = s;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_size = s;
 }
 
 void
@@ -760,6 +817,12 @@
 }
 
 void
+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
+{
+	aes_copy_mbs(&entry->ae_symlink, linkname);
+}
+
+void
 archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
 {
 	aes_copy_wcs(&entry->ae_symlink, linkname);
@@ -768,7 +831,8 @@
 void
 archive_entry_set_uid(struct archive_entry *entry, uid_t u)
 {
-	entry->ae_stat.st_uid = u;
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_uid = u;
 }
 
 void
@@ -778,6 +842,12 @@
 }
 
 void
+archive_entry_copy_uname(struct archive_entry *entry, const char *name)
+{
+	aes_copy_mbs(&entry->ae_uname, name);
+}
+
+void
 archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
 {
 	aes_copy_wcs(&entry->ae_uname, name);
@@ -840,6 +910,13 @@
 archive_entry_acl_add_entry_w(struct archive_entry *entry,
     int type, int permset, int tag, int id, const wchar_t *name)
 {
+	archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name));
+}
+
+void
+archive_entry_acl_add_entry_w_len(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
+{
 	struct ae_acl *ap;
 
 	if (acl_special(entry, type, permset, tag) == 0)
@@ -849,8 +926,8 @@
 		/* XXX Error XXX */
 		return;
 	}
-	if (name != NULL  &&  *name != L'\0')
-		aes_copy_wcs(&ap->name, name);
+	if (name != NULL  &&  *name != L'\0' && len > 0)
+		aes_copy_wcs_len(&ap->name, name, len);
 	else
 		aes_clean(&ap->name);
 }
@@ -865,16 +942,16 @@
 	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
 		switch (tag) {
 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			entry->ae_stat.st_mode &= ~0700;
-			entry->ae_stat.st_mode |= (permset & 7) << 6;
+			entry->ae_stat.aest_mode &= ~0700;
+			entry->ae_stat.aest_mode |= (permset & 7) << 6;
 			return (0);
 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			entry->ae_stat.st_mode &= ~0070;
-			entry->ae_stat.st_mode |= (permset & 7) << 3;
+			entry->ae_stat.aest_mode &= ~0070;
+			entry->ae_stat.aest_mode |= (permset & 7) << 3;
 			return (0);
 		case ARCHIVE_ENTRY_ACL_OTHER:
-			entry->ae_stat.st_mode &= ~0007;
-			entry->ae_stat.st_mode |= permset & 7;
+			entry->ae_stat.aest_mode &= ~0007;
+			entry->ae_stat.aest_mode |= permset & 7;
 			return (0);
 		}
 	}
@@ -990,7 +1067,7 @@
 	/*
 	 * The acl_state is either zero (no entries available), -1
 	 * (reading from list), or an entry type (retrieve that type
-	 * from ae_stat.st_mode).
+	 * from ae_stat.aest_mode).
 	 */
 	if (entry->acl_state == 0)
 		return (ARCHIVE_WARN);
@@ -999,19 +1076,19 @@
 	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 		switch (entry->acl_state) {
 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			*permset = (entry->ae_stat.st_mode >> 6) & 7;
+			*permset = (entry->ae_stat.aest_mode >> 6) & 7;
 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
 			entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 			return (ARCHIVE_OK);
 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			*permset = (entry->ae_stat.st_mode >> 3) & 7;
+			*permset = (entry->ae_stat.aest_mode >> 3) & 7;
 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
 			entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
 			return (ARCHIVE_OK);
 		case ARCHIVE_ENTRY_ACL_OTHER:
-			*permset = entry->ae_stat.st_mode & 7;
+			*permset = entry->ae_stat.aest_mode & 7;
 			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
 			*tag = ARCHIVE_ENTRY_ACL_OTHER;
 			entry->acl_state = -1;
@@ -1026,7 +1103,7 @@
 		entry->acl_p = entry->acl_p->next;
 	if (entry->acl_p == NULL) {
 		entry->acl_state = 0;
-		return (ARCHIVE_WARN);
+		return (ARCHIVE_EOF); /* End of ACL entries. */
 	}
 	*type = entry->acl_p->type;
 	*permset = entry->acl_p->permset;
@@ -1073,6 +1150,8 @@
 			wname = aes_get_wcs(&ap->name);
 			if (wname != NULL)
 				length += wcslen(wname);
+			else
+				length += sizeof(uid_t) * 3 + 1;
 			length ++; /* colon */
 			length += 3; /* rwx */
 			length += 1; /* colon */
@@ -1098,13 +1177,13 @@
 	count = 0;
 	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
-		    entry->ae_stat.st_mode & 0700, -1);
+		    entry->ae_stat.aest_mode & 0700, -1);
 		*wp++ = ',';
 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
-		    entry->ae_stat.st_mode & 0070, -1);
+		    entry->ae_stat.aest_mode & 0070, -1);
 		*wp++ = ',';
 		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
-		    entry->ae_stat.st_mode & 0007, -1);
+		    entry->ae_stat.aest_mode & 0007, -1);
 		count += 3;
 
 		ap = entry->acl_head;
@@ -1155,6 +1234,8 @@
 static void
 append_id_w(wchar_t **wp, int id)
 {
+	if (id < 0)
+		id = 0;
 	if (id > 9)
 		append_id_w(wp, id / 10);
 	*(*wp)++ = L"0123456789"[id % 10];
@@ -1172,14 +1253,14 @@
 	case ARCHIVE_ENTRY_ACL_USER_OBJ:
 		wname = NULL;
 		id = -1;
-		/* FALL THROUGH */
+		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_USER:
 		wcscpy(*wp, L"user");
 		break;
 	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
 		wname = NULL;
 		id = -1;
-		/* FALL THROUGH */
+		/* FALLTHROUGH */
 	case ARCHIVE_ENTRY_ACL_GROUP:
 		wcscpy(*wp, L"group");
 		break;
@@ -1199,6 +1280,10 @@
 	if (wname != NULL) {
 		wcscpy(*wp, wname);
 		*wp += wcslen(*wp);
+	} else if (tag == ARCHIVE_ENTRY_ACL_USER
+	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+		append_id_w(wp, id);
+		id = -1;
 	}
 	*(*wp)++ = L':';
 	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
@@ -1221,73 +1306,47 @@
 __archive_entry_acl_parse_w(struct archive_entry *entry,
     const wchar_t *text, int default_type)
 {
+	struct {
+		const wchar_t *start;
+		const wchar_t *end;
+	} field[4];
+
+	int fields;
 	int type, tag, permset, id;
-	const wchar_t *start, *end;
-	const wchar_t *name_start, *name_end;
+	const wchar_t *p;
 	wchar_t sep;
-	wchar_t *namebuff;
-	int namebuff_length;
-
-	name_start = name_end = NULL;
-	namebuff = NULL;
-	namebuff_length = 0;
 
 	while (text != NULL  &&  *text != L'\0') {
-		next_field_w(&text, &start, &end, &sep);
-		if (sep != L':')
-			goto fail;
-
 		/*
-		 * Solaris extension:  "defaultuser::rwx" is the
-		 * default ACL corresponding to "user::rwx", etc.
+		 * Parse the fields out of the next entry,
+		 * advance 'text' to start of next entry.
 		 */
-		if (end-start > 7  && wmemcmp(start, L"default", 7) == 0) {
-			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
-			start += 7;
-		} else
-			type = default_type;
-
-		if (prefix_w(start, end, L"user")) {
-			next_field_w(&text, &start, &end, &sep);
-			if (sep != L':')
-				goto fail;
-			if (end > start) {
-				tag = ARCHIVE_ENTRY_ACL_USER;
-				name_start = start;
-				name_end = end;
-			} else
-				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-		} else if (prefix_w(start, end, L"group")) {
-			next_field_w(&text, &start, &end, &sep);
-			if (sep != L':')
-				goto fail;
-			if (end > start) {
-				tag = ARCHIVE_ENTRY_ACL_GROUP;
-				name_start = start;
-				name_end = end;
-			} else
-				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-		} else if (prefix_w(start, end, L"other")) {
-			next_field_w(&text, &start, &end, &sep);
-			if (sep != L':')
-				goto fail;
-			if (end > start)
-				goto fail;
-			tag = ARCHIVE_ENTRY_ACL_OTHER;
-		} else if (prefix_w(start, end, L"mask")) {
+		fields = 0;
+		do {
+			const wchar_t *start, *end;
 			next_field_w(&text, &start, &end, &sep);
-			if (sep != L':')
-				goto fail;
-			if (end > start)
-				goto fail;
-			tag = ARCHIVE_ENTRY_ACL_MASK;
-		} else
-			goto fail;
+			if (fields < 4) {
+				field[fields].start = start;
+				field[fields].end = end;
+			}
+			++fields;
+		} while (sep == L':');
+
+		if (fields < 3)
+			return (ARCHIVE_WARN);
 
-		next_field_w(&text, &start, &end, &sep);
+		/* Check for a numeric ID in field 1 or 3. */
+		id = -1;
+		isint_w(field[1].start, field[1].end, &id);
+		/* Field 3 is optional. */
+		if (id == -1 && fields > 3)
+			isint_w(field[3].start, field[3].end, &id);
+
+		/* Parse the permissions from field 2. */
 		permset = 0;
-		while (start < end) {
-			switch (*start++) {
+		p = field[2].start;
+		while (p < field[2].end) {
+			switch (*p++) {
 			case 'r': case 'R':
 				permset |= ARCHIVE_ENTRY_ACL_READ;
 				break;
@@ -1300,71 +1359,47 @@
 			case '-':
 				break;
 			default:
-				goto fail;
+				return (ARCHIVE_WARN);
 			}
 		}
 
 		/*
-		 * Support star-compatible numeric UID/GID extension.
-		 * This extension adds a ":" followed by the numeric
-		 * ID so that "group:groupname:rwx", for example,
-		 * becomes "group:groupname:rwx:999", where 999 is the
-		 * numeric GID.  This extension makes it possible, for
-		 * example, to correctly restore ACLs on a system that
-		 * might have a damaged passwd file or be disconnected
-		 * from a central NIS server.  This extension is compatible
-		 * with POSIX.1e draft 17.
+		 * Solaris extension:  "defaultuser::rwx" is the
+		 * default ACL corresponding to "user::rwx", etc.
 		 */
-		if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER ||
-		    tag == ARCHIVE_ENTRY_ACL_GROUP)) {
-			next_field_w(&text, &start, &end, &sep);
-
-			id = 0;
-			while (start < end  && *start >= '0' && *start <= '9') {
-				if (id > (INT_MAX / 10))
-					id = INT_MAX;
-				else {
-					id *= 10;
-					id += *start - '0';
-					start++;
-				}
-			}
+		if (field[0].end-field[0].start > 7
+		    && wmemcmp(field[0].start, L"default", 7) == 0) {
+			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+			field[0].start += 7;
 		} else
-			id = -1; /* No id specified. */
+			type = default_type;
 
-		/* Skip any additional entries. */
-		while (sep == L':') {
-			next_field_w(&text, &start, &end, &sep);
-		}
+		if (prefix_w(field[0].start, field[0].end, L"user")) {
+			if (id != -1 || field[1].start < field[1].end)
+				tag = ARCHIVE_ENTRY_ACL_USER;
+			else
+				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
+			if (id != -1 || field[1].start < field[1].end)
+				tag = ARCHIVE_ENTRY_ACL_GROUP;
+			else
+				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
+			if (id != -1 || field[1].start < field[1].end)
+				return (ARCHIVE_WARN);
+			tag = ARCHIVE_ENTRY_ACL_OTHER;
+		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
+			if (id != -1 || field[1].start < field[1].end)
+				return (ARCHIVE_WARN);
+			tag = ARCHIVE_ENTRY_ACL_MASK;
+		} else
+			return (ARCHIVE_WARN);
 
 		/* Add entry to the internal list. */
-		if (name_end == name_start) {
-			archive_entry_acl_add_entry_w(entry, type, permset,
-			    tag, id, NULL);
-		} else {
-			if (namebuff_length <= name_end - name_start) {
-				if (namebuff != NULL)
-					free(namebuff);
-				namebuff_length = name_end - name_start + 256;
-				namebuff =
-				    (wchar_t *)malloc(namebuff_length * sizeof(wchar_t));
-				if (namebuff == NULL)
-					goto fail;
-			}
-			wmemcpy(namebuff, name_start, name_end - name_start);
-			namebuff[name_end - name_start] = L'\0';
-			archive_entry_acl_add_entry_w(entry, type,
-			    permset, tag, id, namebuff);
-		}
+		archive_entry_acl_add_entry_w_len(entry, type, permset,
+		    tag, id, field[1].start, field[1].end - field[1].start);
 	}
-	if (namebuff != NULL)
-		free(namebuff);
 	return (ARCHIVE_OK);
-
-fail:
-	if (namebuff != NULL)
-		free(namebuff);
-	return (ARCHIVE_WARN);
 }
 
 /*
@@ -1460,6 +1495,32 @@
  */
 
 /*
+ * Parse a string to a positive decimal integer.  Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint_w(const wchar_t *start, const wchar_t *end, int *result)
+{
+	int n = 0;
+	if (start >= end)
+		return (0);
+	while (start < end) {
+		if (*start < '0' || *start > '9')
+			return (0);
+		if (n > (INT_MAX / 10))
+			n = INT_MAX;
+		else {
+			n *= 10;
+			n += *start - '0';
+		}
+		start++;
+	}
+	*result = n;
+	return (1);
+}
+
+/*
  * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
  * to point to just after the separator.  *start points to the first
  * character of the matched text and *end just after the last
@@ -1496,6 +1557,10 @@
 		(*wp)++;
 }
 
+/*
+ * Return true if the characters [start...end) are a prefix of 'test'.
+ * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
+ */
 static int
 prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
 {
@@ -1629,7 +1694,7 @@
 	const char *sp;
 	unsigned long bits;
 	struct flag *flag;
-	int	length;
+	size_t	length;
 
 	bits = bitset | bitclear;
 	length = 0;
Index: archive_read_extract.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_extract.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_extract.c -Llib/libarchive/archive_read_extract.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_extract.c
+++ lib/libarchive/archive_read_extract.c
@@ -24,1645 +24,147 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.41.2.4 2007/03/06 08:03:17 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.59 2007/05/29 01:00:18 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
-#ifdef HAVE_SYS_ACL_H
-#include <sys/acl.h>
-#endif
-#ifdef HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#ifdef HAVE_EXT2FS_EXT2_FS_H
-#include <ext2fs/ext2_fs.h>	/* for Linux file flags */
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_GRP_H
-#include <grp.h>
-#endif
-#ifdef HAVE_LINUX_EXT2_FS_H
-#include <linux/ext2_fs.h>	/* for Linux file flags */
-#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 #include "archive.h"
-#include "archive_string.h"
-#include "archive_entry.h"
 #include "archive_private.h"
-
-struct fixup_entry {
-	struct fixup_entry	*next;
-	mode_t			 mode;
-	int64_t			 mtime;
-	int64_t			 atime;
-	unsigned long		 mtime_nanos;
-	unsigned long		 atime_nanos;
-	unsigned long		 fflags_set;
-	int			 fixup; /* bitmask of what needs fixing */
-	char			*name;
-};
-
-#define	FIXUP_MODE	1
-#define	FIXUP_TIMES	2
-#define	FIXUP_FFLAGS	4
-
-struct bucket {
-	char	*name;
-	int	 hash;
-	id_t	 id;
-};
+#include "archive_read_private.h"
+#include "archive_write_disk_private.h"
 
 struct extract {
-	mode_t			 umask;
-	mode_t			 default_dir_mode_initial;
-	mode_t			 default_dir_mode_final;
-	struct archive_string	 create_parent_dir;
-	struct fixup_entry	*fixup_list;
-	struct fixup_entry	*current_fixup;
-
-	struct bucket ucache[127];
-	struct bucket gcache[127];
+	struct archive *ad; /* archive_write_disk object */
 
-	/*
-	 * Cached stat data from disk for the current entry.
-	 * If this is valid, pst points to st.  Otherwise,
-	 * pst is null.
-	 */
-	struct stat		 st;
-	struct stat		*pst;
+	/* Progress function invoked during extract. */
+	void			(*extract_progress)(void *);
+	void			 *extract_progress_user_data;
 };
 
-/* Default mode for dirs created automatically (will be modified by umask). */
-#define DEFAULT_DIR_MODE 0777
-/*
- * Mode to use for newly-created dirs during extraction; the correct
- * mode will be set at the end of the extraction.
- */
-#define MINIMUM_DIR_MODE 0700
-#define MAXIMUM_DIR_MODE 0775
-
-static int	archive_extract_cleanup(struct archive *);
-static int	create_extract(struct archive *a);
-static int	extract_block_device(struct archive *,
-		    struct archive_entry *, int);
-static int	extract_char_device(struct archive *,
-		    struct archive_entry *, int);
-static int	extract_device(struct archive *,
-		    struct archive_entry *, int flags, mode_t mode);
-static int	extract_dir(struct archive *, struct archive_entry *, int);
-static int	extract_fifo(struct archive *, struct archive_entry *, int);
-static int	extract_file(struct archive *, struct archive_entry *, int);
-static int	extract_hard_link(struct archive *, struct archive_entry *, int);
-static int	extract_symlink(struct archive *, struct archive_entry *, int);
-static unsigned int	hash(const char *);
-static gid_t	lookup_gid(struct archive *, const char *uname, gid_t);
-static uid_t	lookup_uid(struct archive *, const char *uname, uid_t);
-static int	create_dir(struct archive *, const char *, int flags);
-static int	create_dir_mutable(struct archive *, char *, int flags);
-static int	create_dir_recursive(struct archive *, char *, int flags);
-static int	create_parent_dir(struct archive *, const char *, int flags);
-static int	create_parent_dir_mutable(struct archive *, char *, int flags);
-static int	restore_metadata(struct archive *, int fd,
-		    struct archive_entry *, int flags);
-#ifdef HAVE_POSIX_ACL
-static int	set_acl(struct archive *, int fd, struct archive_entry *,
-		    acl_type_t, int archive_entry_acl_type, const char *tn);
-#endif
-static int	set_acls(struct archive *, int fd, struct archive_entry *);
-static int	set_xattrs(struct archive *, int fd, struct archive_entry *);
-static int	set_fflags(struct archive *, int fd, const char *name, mode_t,
-		    unsigned long fflags_set, unsigned long fflags_clear);
-static int	set_ownership(struct archive *, int fd, struct archive_entry *,
-		    int flags);
-static int	set_perm(struct archive *, int fd, struct archive_entry *,
-		    int mode, int flags);
-static int	set_time(struct archive *, int fd, struct archive_entry *, int);
-static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
-
+static int	archive_read_extract_cleanup(struct archive_read *);
+static int	copy_data(struct archive *ar, struct archive *aw);
+static struct extract *get_extract(struct archive_read *);
 
-/*
- * Extract this entry to disk.
- *
- * TODO: Validate hardlinks.  According to the standards, we're
- * supposed to check each extracted hardlink and squawk if it refers
- * to a file that we didn't restore.  I'm not entirely convinced this
- * is a good idea, but more importantly: Is there any way to validate
- * hardlinks without keeping a complete list of filenames from the
- * entire archive?? Ugh.
- *
- */
-int
-archive_read_extract(struct archive *a, struct archive_entry *entry, int flags)
+static struct extract *
+get_extract(struct archive_read *a)
 {
-	mode_t mode;
-	struct extract *extract;
-	int ret;
-	int restore_pwd;
-	char *original_filename;
-
+	/* If we haven't initialized, do it now. */
+	/* This also sets up a lot of global state. */
 	if (a->extract == NULL) {
-		ret = create_extract(a);
-		if (ret)
-			return (ret);
-	}
-	extract = a->extract;
-	extract->pst = NULL;
-	extract->current_fixup = NULL;
-	restore_pwd = -1;
-	original_filename = NULL;
-
-	/* The following is not possible without fchdir.  <sigh> */
-#ifdef HAVE_FCHDIR
-	/*
-	 * If pathname is longer than PATH_MAX, record starting directory
-	 * and chdir to a suitable intermediate dir.
-	 */
-	if (strlen(archive_entry_pathname(entry)) > PATH_MAX) {
-		char *intdir, *tail;
-
-		restore_pwd = open(".", O_RDONLY);
-		if (restore_pwd < 0) {
-				archive_set_error(a, errno,
-				    "Unable to restore long pathname");
-				return (ARCHIVE_WARN);
-		}
-
-		/*
-		 * Yes, the copy here is necessary because we edit
-		 * the pathname in-place to create intermediate dirnames.
-		 */
-		original_filename = strdup(archive_entry_pathname(entry));
-
-		/*
-		 * "intdir" points to the initial dir section we're going
-		 * to remove, "tail" points to the remainder of the path.
-		 */
-		intdir = tail = original_filename;
-		while (strlen(tail) > PATH_MAX) {
-			intdir = tail;
-
-			/* Locate a dir prefix shorter than PATH_MAX. */
-			tail = intdir + PATH_MAX - 8;
-			while (tail > intdir && *tail != '/')
-				tail--;
-			if (tail <= intdir) {
-				archive_set_error(a, EPERM,
-				    "Path element too long");
-				ret = ARCHIVE_WARN;
-				goto cleanup;
-			}
-
-			/* Create intdir and chdir to it. */
-			*tail = '\0'; /* Terminate dir portion */
-			ret = create_dir(a, intdir, flags);
-			if (ret == ARCHIVE_OK && chdir(intdir) != 0) {
-				archive_set_error(a, errno, "Couldn't chdir");
-				ret = ARCHIVE_WARN;
-			}
-			*tail = '/'; /* Restore the / we removed. */
-			if (ret != ARCHIVE_OK)
-				goto cleanup;
-			tail++;
-		}
-		archive_entry_set_pathname(entry, tail);
-	}
-#endif
-
-	if (stat(archive_entry_pathname(entry), &extract->st) == 0)
-		extract->pst = &extract->st;
-	extract->umask = umask(0); /* Set the umask to zero, record old one. */
-
-	if (extract->pst != NULL &&
-	    extract->pst->st_dev == a->skip_file_dev &&
-	    extract->pst->st_ino == a->skip_file_ino) {
-		archive_set_error(a, 0, "Refusing to overwrite archive");
-		ret = ARCHIVE_WARN;
-	} else if (archive_entry_hardlink(entry) != NULL)
-		ret = extract_hard_link(a, entry, flags);
-	else {
-		mode = archive_entry_mode(entry);
-		switch (mode & S_IFMT) {
-		default:
-			/* Fall through, as required by POSIX. */
-		case S_IFREG:
-			ret = extract_file(a, entry, flags);
-			break;
-		case S_IFLNK:	/* Symlink */
-			ret = extract_symlink(a, entry, flags);
-			break;
-		case S_IFCHR:
-			ret = extract_char_device(a, entry, flags);
-			break;
-		case S_IFBLK:
-			ret = extract_block_device(a, entry, flags);
-			break;
-		case S_IFDIR:
-			ret = extract_dir(a, entry, flags);
-			break;
-		case S_IFIFO:
-			ret = extract_fifo(a, entry, flags);
-			break;
+		a->extract = (struct extract *)malloc(sizeof(*a->extract));
+		if (a->extract == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Can't extract");
+			return (NULL);
+		}
+		a->extract->ad = archive_write_disk_new();
+		if (a->extract->ad == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Can't extract");
+			return (NULL);
 		}
+		archive_write_disk_set_standard_lookup(a->extract->ad);
+		a->cleanup_archive_extract = archive_read_extract_cleanup;
 	}
-
-	umask(extract->umask); /* Restore umask. */
-
-cleanup:
-#ifdef HAVE_FCHDIR
-	/* If we changed directory above, restore it here. */
-	if (restore_pwd >= 0 && original_filename != NULL) {
-		fchdir(restore_pwd);
-		close(restore_pwd);
-		archive_entry_copy_pathname(entry, original_filename);
-		free(original_filename);
-	}
-#endif
-
-	return (ret);
+	return (a->extract);
 }
 
-
-static int
-create_extract(struct archive *a)
+int
+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct extract *extract;
+	int r, r2;
 
-	extract = (struct extract *)malloc(sizeof(*extract));
-	if (extract == NULL) {
-		archive_set_error(a, ENOMEM, "Can't extract");
+	extract = get_extract(a);
+	if (extract == NULL)
 		return (ARCHIVE_FATAL);
-	}
-	a->cleanup_archive_extract = archive_extract_cleanup;
-	memset(extract, 0, sizeof(*extract));
-	umask(extract->umask = umask(0)); /* Read the current umask. */
-	/* Final permission for default dirs. */
-	extract->default_dir_mode_final
-	    = DEFAULT_DIR_MODE & ~extract->umask;
-	/* Temporary permission for default dirs during extract. */
-	extract->default_dir_mode_initial
-	    = extract->default_dir_mode_final;
-	extract->default_dir_mode_initial |= MINIMUM_DIR_MODE;
-	extract->default_dir_mode_initial &= MAXIMUM_DIR_MODE;
-	/* If the two permissions above are different, then
-	 * the "final" permissions will be applied in the
-	 * post-extract fixup pass. */
-	a->extract = extract;
-	return (ARCHIVE_OK);
-}
-
-/*
- * Cleanup function for archive_extract.  Mostly, this involves processing
- * the fixup list, which is used to address a number of problems:
- *   * Dir permissions might prevent us from restoring a file in that
- *     dir, so we restore the dir 0700 first, then correct the
- *     mode at the end.
- *   * Similarly, the act of restoring a file touches the directory
- *     and changes the timestamp on the dir, so we have to touch-up dir
- *     timestamps at the end as well.
- *   * Some file flags can interfere with the restore by, for example,
- *     preventing the creation of hardlinks to those files.
- *
- * Note that tar/cpio do not require that archives be in a particular
- * order; there is no way to know when the last file has been restored
- * within a directory, so there's no way to optimize the memory usage
- * here by fixing up the directory any earlier than the
- * end-of-archive.
- *
- * XXX TODO: Directory ACLs should be restored here, for the same
- * reason we set directory perms here. XXX
- *
- * Registering this function (rather than calling it explicitly by
- * name from archive_read_finish) reduces static link pollution, since
- * applications that don't use this API won't get this file linked in.
- */
-static int
-archive_extract_cleanup(struct archive *a)
-{
-	struct fixup_entry *next, *p;
-	struct extract *extract;
-
-	/* Sort dir list so directories are fixed up in depth-first order. */
-	extract = a->extract;
-	p = sort_dir_list(extract->fixup_list);
-
-	while (p != NULL) {
-		extract->pst = NULL; /* Mark stat cache as out-of-date. */
-		if (p->fixup & FIXUP_TIMES) {
-			struct timeval times[2];
-			times[1].tv_sec = p->mtime;
-			times[1].tv_usec = p->mtime_nanos / 1000;
-			times[0].tv_sec = p->atime;
-			times[0].tv_usec = p->atime_nanos / 1000;
-			utimes(p->name, times);
-		}
-		if (p->fixup & FIXUP_MODE)
-			chmod(p->name, p->mode);
-
-		if (p->fixup & FIXUP_FFLAGS)
-			set_fflags(a, -1, p->name, p->mode, p->fflags_set, 0);
-
-		next = p->next;
-		free(p->name);
-		free(p);
-		p = next;
-	}
-	extract->fixup_list = NULL;
-	archive_string_free(&extract->create_parent_dir);
-	free(a->extract);
-	a->extract = NULL;
-	return (ARCHIVE_OK);
-}
-
-/*
- * Simple O(n log n) merge sort to order the fixup list.  In
- * particular, we want to restore dir timestamps depth-first.
- */
-static struct fixup_entry *
-sort_dir_list(struct fixup_entry *p)
-{
-	struct fixup_entry *a, *b, *t;
-
-	if (p == NULL)
-		return (NULL);
-	/* A one-item list is already sorted. */
-	if (p->next == NULL)
-		return (p);
-
-	/* Step 1: split the list. */
-	t = p;
-	a = p->next->next;
-	while (a != NULL) {
-		/* Step a twice, t once. */
-		a = a->next;
-		if (a != NULL)
-			a = a->next;
-		t = t->next;
-	}
-	/* Now, t is at the mid-point, so break the list here. */
-	b = t->next;
-	t->next = NULL;
-	a = p;
-
-	/* Step 2: Recursively sort the two sub-lists. */
-	a = sort_dir_list(a);
-	b = sort_dir_list(b);
-
-	/* Step 3: Merge the returned lists. */
-	/* Pick the first element for the merged list. */
-	if (strcmp(a->name, b->name) > 0) {
-		t = p = a;
-		a = a->next;
-	} else {
-		t = p = b;
-		b = b->next;
-	}
-
-	/* Always put the later element on the list first. */
-	while (a != NULL && b != NULL) {
-		if (strcmp(a->name, b->name) > 0) {
-			t->next = a;
-			a = a->next;
-		} else {
-			t->next = b;
-			b = b->next;
-		}
-		t = t->next;
-	}
-
-	/* Only one list is non-empty, so just splice it on. */
-	if (a != NULL)
-		t->next = a;
-	if (b != NULL)
-		t->next = b;
-
-	return (p);
-}
-
-/*
- * Returns a new, initialized fixup entry.
- *
- * TODO: Reduce the memory requirements for this list by using a tree
- * structure rather than a simple list of names.
- */
-static struct fixup_entry *
-new_fixup(struct archive *a, const char *pathname)
-{
-	struct extract *extract;
-	struct fixup_entry *fe;
-
-	extract = a->extract;
-	fe = (struct fixup_entry *)malloc(sizeof(struct fixup_entry));
-	if (fe == NULL)
-		return (NULL);
-	fe->next = extract->fixup_list;
-	extract->fixup_list = fe;
-	fe->fixup = 0;
-	fe->name = strdup(pathname);
-	return (fe);
-}
-
-/*
- * Returns a fixup structure for the current entry.
- */
-static struct fixup_entry *
-current_fixup(struct archive *a, const char *pathname)
-{
-	struct extract *extract;
-
-	extract = a->extract;
-	if (extract->current_fixup == NULL)
-		extract->current_fixup = new_fixup(a, pathname);
-	return (extract->current_fixup);
-}
-
-static int
-extract_file(struct archive *a, struct archive_entry *entry, int flags)
-{
-	struct extract *extract;
-	const char *name;
-	mode_t mode;
-	int fd, r, r2;
 
+	/* Set up for this particular entry. */
 	extract = a->extract;
-	name = archive_entry_pathname(entry);
-	mode = archive_entry_mode(entry) & 0777;
-	r = ARCHIVE_OK;
-
-	/*
-	 * If we're not supposed to overwrite pre-existing files,
-	 * use O_EXCL.  Otherwise, use O_TRUNC.
-	 */
-	if (flags & (ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_NO_OVERWRITE))
-		fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
+	archive_write_disk_set_options(a->extract->ad, flags);
+	archive_write_disk_set_skip_file(a->extract->ad,
+	    a->skip_file_dev, a->skip_file_ino);
+	r = archive_write_header(a->extract->ad, entry);
+	if (r < ARCHIVE_WARN)
+		r = ARCHIVE_WARN;
+	if (r != ARCHIVE_OK)
+		/* If _write_header failed, copy the error. */
+ 		archive_copy_error(&a->archive, extract->ad);
 	else
-		fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode);
-
-	/* Try removing a pre-existing file. */
-	if (fd < 0 && !(flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
-		unlink(name);
-		fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
-	}
-
-	/* Might be a non-existent parent dir; try fixing that. */
-	if (fd < 0) {
-		create_parent_dir(a, name, flags);
-		fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode);
-	}
-	if (fd < 0) {
-		archive_set_error(a, errno, "Can't open '%s'", name);
-		return (ARCHIVE_WARN);
-	}
-	r = archive_read_data_into_fd(a, fd);
-	extract->pst = NULL; /* Cached stat data no longer valid. */
-	r2 = restore_metadata(a, fd, entry, flags);
-	close(fd);
-	return (err_combine(r, r2));
-}
-
-static int
-extract_dir(struct archive *a, struct archive_entry *entry, int flags)
-{
-	struct extract *extract;
-	struct fixup_entry *fe;
-	char *path, *p;
-	mode_t restore_mode, final_mode;
-
-	extract = a->extract;
-	extract->pst = NULL; /* Invalidate cached stat data. */
-
-	/* Copy path to mutable storage. */
-	archive_strcpy(&(extract->create_parent_dir),
-	    archive_entry_pathname(entry));
-	path = extract->create_parent_dir.s;
-
-	if (*path == '\0') {
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
-		    "Invalid empty pathname");
-		return (ARCHIVE_WARN);
-	}
-
-	/* Deal with any troublesome trailing path elements. */
-	/* TODO: Someday, generalize this to remove '//' or '/./' from
-	 * the middle of paths.  But, it should not compress '..' from
-	 * the middle of paths.  It's a feature that restoring
-	 * "a/../b" creates both 'a' and 'b' directories. */
-	for (;;) {
-		/* Locate last element. */
-		p = strrchr(path, '/');
-		if (p != NULL)
-			p++;
-		else
-			p = path;
-		/* Trim trailing '/' unless that's the entire path. */
-		if (p[0] == '\0' && p - 1 > path) {
-			p[-1] = '\0';
-			continue;
-		}
-		/* Trim trailing '.' unless that's the entire path. */
-		if (p > path && p[0] == '.' && p[1] == '\0') {
-			p[0] = '\0';
-			continue;
-		}
-		/* Just exit on trailing '..'. */
-		if (p[0] == '.' && p[1] == '.' && p[2] == '\0') {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			    "Can't restore directory '..'");
-			return (ARCHIVE_WARN);
-		}
-		break;
-	}
-
-	final_mode = archive_entry_mode(entry) &
-	    (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
-	if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
-		final_mode &= ~extract->umask;
-	/* Constrain the permissions in effect during the restore. */
-	restore_mode = final_mode;
-	restore_mode |= MINIMUM_DIR_MODE;
-	restore_mode &= MAXIMUM_DIR_MODE;
-
-	if (mkdir(path, restore_mode) == 0)
-		goto success;
-
-	if (extract->pst == NULL && stat(path, &extract->st) == 0)
-		extract->pst = &extract->st;
-
-	if (extract->pst != NULL) {
-		extract->pst = &extract->st;
-		/* If dir already exists, don't reset permissions. */
-		if (S_ISDIR(extract->pst->st_mode))
-			return (ARCHIVE_OK);
-		/* It exists but isn't a dir. */
-		if ((flags & ARCHIVE_EXTRACT_UNLINK))
-			unlink(path);
-	} else {
-		/* Doesn't already exist; try building the parent path. */
-		if (create_parent_dir_mutable(a, path, flags) != ARCHIVE_OK)
-			return (ARCHIVE_WARN);
-	}
-
-	/* One final attempt to create the dir. */
-	if (mkdir(path, restore_mode) != 0) {
-		archive_set_error(a, errno, "Can't create directory");
-		return (ARCHIVE_WARN);
-	}
-
-success:
-	/* Add this dir to the fixup list. */
-	if (final_mode != restore_mode) {
-		fe = current_fixup(a, path);
-		fe->fixup |= FIXUP_MODE;
-		fe->mode = final_mode;
-	}
-	if (flags & ARCHIVE_EXTRACT_TIME) {
-		fe = current_fixup(a, path);
-		fe->fixup |= FIXUP_TIMES;
-		fe->mtime = archive_entry_mtime(entry);
-		fe->mtime_nanos = archive_entry_mtime_nsec(entry);
-		fe->atime = archive_entry_atime(entry);
-		fe->atime_nanos = archive_entry_atime_nsec(entry);
-	}
-	return (restore_metadata(a, -1, entry, flags));
-}
-
-
-/*
- * Create the parent of the specified path.  Copy the provided
- * path into mutable storage first.
- */
-static int
-create_parent_dir(struct archive *a, const char *path, int flags)
-{
-	int r;
-
-	/* Copy path to mutable storage. */
-	archive_strcpy(&(a->extract->create_parent_dir), path);
-	r = create_parent_dir_mutable(a, a->extract->create_parent_dir.s, flags);
-	return (r);
-}
-
-/*
- * Like create_parent_dir, but creates the dir actually requested, not
- * the parent.
- */
-static int
-create_dir(struct archive *a, const char *path, int flags)
-{
-	int r;
-	/* Copy path to mutable storage. */
-	archive_strcpy(&(a->extract->create_parent_dir), path);
-	r = create_dir_mutable(a, a->extract->create_parent_dir.s, flags);
-	return (r);
-}
-
-/*
- * Create the parent directory of the specified path, assuming path
- * is already in mutable storage.
- */
-static int
-create_parent_dir_mutable(struct archive *a, char *path, int flags)
-{
-	char *slash;
-	int r;
-
-	/* Remove tail element to obtain parent name. */
-	slash = strrchr(path, '/');
-	if (slash == NULL)
-		return (ARCHIVE_OK);
-	*slash = '\0';
-	r = create_dir_mutable(a, path, flags);
-	*slash = '/';
-	return (r);
-}
-
-/*
- * Create the specified dir, assuming path is already in
- * mutable storage.
- */
-static int
-create_dir_mutable(struct archive *a, char *path, int flags)
-{
-	int r;
-
-	r = create_dir_recursive(a, path, flags);
-	return (r);
-}
-
-/*
- * Create the specified dir, recursing to create parents as necessary.
- *
- * Returns ARCHIVE_OK if the path exists when we're done here.
- * Otherwise, returns ARCHIVE_WARN.
- */
-static int
-create_dir_recursive(struct archive *a, char *path, int flags)
-{
-	struct stat st;
-	struct extract *extract;
-	struct fixup_entry *le;
-	char *slash, *base;
-	int r;
-
-	extract = a->extract;
-	r = ARCHIVE_OK;
-
-	/* Check for special names and just skip them. */
-	slash = strrchr(path, '/');
-	base = strrchr(path, '/');
-	if (slash == NULL)
-		base = path;
-	else
-		base = slash + 1;
-
-	if (base[0] == '\0' ||
-	    (base[0] == '.' && base[1] == '\0') ||
-	    (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
-		/* Don't bother trying to create null path, '.', or '..'. */
-		if (slash != NULL) {
-			*slash = '\0';
-			r = create_dir_recursive(a, path, flags);
-			*slash = '/';
-			return (r);
-		}
-		return (ARCHIVE_OK);
-	}
-
-	/*
-	 * Yes, this should be stat() and not lstat().  Using lstat()
-	 * here loses the ability to extract through symlinks.  Also note
-	 * that this should not use the extract->st cache.
-	 */
-	if (stat(path, &st) == 0) {
-		if (S_ISDIR(st.st_mode))
-			return (ARCHIVE_OK);
-		if ((flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
-			archive_set_error(a, EEXIST,
-			    "Can't create directory '%s'", path);
-			return (ARCHIVE_WARN);
-		}
-		if (unlink(path) != 0) {
-			archive_set_error(a, errno,
-			    "Can't create directory '%s': "
-			    "Conflicting file cannot be removed");
-			return (ARCHIVE_WARN);
-		}
-	} else if (errno != ENOENT && errno != ENOTDIR) {
-		/* Stat failed? */
-		archive_set_error(a, errno, "Can't test directory '%s'", path);
-		return (ARCHIVE_WARN);
-	} else if (slash != NULL) {
-		*slash = '\0';
-		r = create_dir_recursive(a, path, flags);
-		*slash = '/';
-		if (r != ARCHIVE_OK)
-			return (r);
-	}
-
-	if (mkdir(path, extract->default_dir_mode_initial) == 0) {
-		if (extract->default_dir_mode_initial
-		    != extract->default_dir_mode_final) {
-			le = new_fixup(a, path);
-			le->fixup |= FIXUP_MODE;
-			le->mode = extract->default_dir_mode_final;
-		}
-		return (ARCHIVE_OK);
-	}
-
-	/*
-	 * Without the following check, a/b/../b/c/d fails at the
-	 * second visit to 'b', so 'd' can't be created.  Note that we
-	 * don't add it to the fixup list here, as it's already been
-	 * added.
-	 */
-	if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
-		return (ARCHIVE_OK);
-
-	archive_set_error(a, errno, "Failed to create dir '%s'", path);
-	return (ARCHIVE_WARN);
-}
-
-static int
-extract_hard_link(struct archive *a, struct archive_entry *entry, int flags)
-{
-	struct extract *extract;
-	int r;
-	const char *pathname;
-	const char *linkname;
-
-	extract = a->extract;
-	pathname = archive_entry_pathname(entry);
-	linkname = archive_entry_hardlink(entry);
-
-	/* Just remove any pre-existing file with this name. */
-	if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
-		unlink(pathname);
-
-	r = link(linkname, pathname);
-	extract->pst = NULL; /* Invalidate cached stat data. */
-
-	if (r != 0) {
-		/* Might be a non-existent parent dir; try fixing that. */
-		create_parent_dir(a, pathname, flags);
-		r = link(linkname, pathname);
-	}
-
-	if (r != 0) {
-		/* XXX Better error message here XXX */
-		archive_set_error(a, errno,
-		    "Can't restore hardlink to '%s'", linkname);
-		return (ARCHIVE_WARN);
-	}
-
-	/* Set ownership, time, permission information. */
-	r = restore_metadata(a, -1, entry, flags);
-	return (r);
-}
-
-static int
-extract_symlink(struct archive *a, struct archive_entry *entry, int flags)
-{
-	struct extract *extract;
-	int r;
-	const char *pathname;
-	const char *linkname;
-
-	extract = a->extract;
-	pathname = archive_entry_pathname(entry);
-	linkname = archive_entry_symlink(entry);
-
-	/* Just remove any pre-existing file with this name. */
-	if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
-		unlink(pathname);
-
-	r = symlink(linkname, pathname);
-	extract->pst = NULL; /* Invalidate cached stat data. */
-
-	if (r != 0) {
-		/* Might be a non-existent parent dir; try fixing that. */
-		create_parent_dir(a, pathname, flags);
-		r = symlink(linkname, pathname);
-	}
-
-	if (r != 0) {
-		/* XXX Better error message here XXX */
-		archive_set_error(a, errno,
-		    "Can't restore symlink to '%s'", linkname);
-		return (ARCHIVE_WARN);
-	}
-
-	r = restore_metadata(a, -1, entry, flags);
+		/* Otherwise, pour data into the entry. */
+		r = copy_data(_a, a->extract->ad);
+	r2 = archive_write_finish_entry(a->extract->ad);
+	if (r2 < ARCHIVE_WARN)
+		r2 = ARCHIVE_WARN;
+	/* Use the first message. */
+	if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
+ 		archive_copy_error(&a->archive, extract->ad);
+	/* Use the worst error return. */
+	if (r2 < r)
+		r = r2;
 	return (r);
 }
 
-static int
-extract_device(struct archive *a, struct archive_entry *entry,
-    int flags, mode_t mode)
+void
+archive_read_extract_set_progress_callback(struct archive *_a,
+    void (*progress_func)(void *), void *user_data)
 {
-	struct extract *extract;
-	int r;
-
-	extract = a->extract;
-	/* Just remove any pre-existing file with this name. */
-	if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
-		unlink(archive_entry_pathname(entry));
-
-	r = mknod(archive_entry_pathname(entry), mode,
-	    archive_entry_rdev(entry));
-	extract->pst = NULL; /* Invalidate cached stat data. */
-
-	/* Might be a non-existent parent dir; try fixing that. */
-	if (r != 0 && errno == ENOENT) {
-		create_parent_dir(a, archive_entry_pathname(entry), flags);
-		r = mknod(archive_entry_pathname(entry), mode,
-		    archive_entry_rdev(entry));
+	struct archive_read *a = (struct archive_read *)_a;
+	struct extract *extract = get_extract(a);
+	if (extract != NULL) {
+		extract->extract_progress = progress_func;
+		extract->extract_progress_user_data = user_data;
 	}
-
-	if (r != 0) {
-		archive_set_error(a, errno, "Can't restore device node");
-		return (ARCHIVE_WARN);
-	}
-
-	r = restore_metadata(a, -1, entry, flags);
-	return (r);
 }
 
 static int
-extract_char_device(struct archive *a, struct archive_entry *entry, int flags)
+copy_data(struct archive *ar, struct archive *aw)
 {
-	mode_t mode;
-
-	mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFCHR;
-	return (extract_device(a, entry, flags, mode));
-}
-
-static int
-extract_block_device(struct archive *a, struct archive_entry *entry, int flags)
-{
-	mode_t mode;
-
-	mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFBLK;
-	return (extract_device(a, entry, flags, mode));
-}
-
-static int
-extract_fifo(struct archive *a, struct archive_entry *entry, int flags)
-{
-	struct extract *extract;
 	int r;
+	const void *buff;
+	size_t size;
+	off_t offset;
 
-	extract = a->extract;
-	/* Just remove any pre-existing file with this name. */
-	if (!(flags & ARCHIVE_EXTRACT_NO_OVERWRITE))
-		unlink(archive_entry_pathname(entry));
-
-	r = mkfifo(archive_entry_pathname(entry),
-	    archive_entry_mode(entry));
-	extract->pst = NULL; /* Invalidate cached stat data. */
-
-	/* Might be a non-existent parent dir; try fixing that. */
-	if (r != 0 && errno == ENOENT) {
-		create_parent_dir(a, archive_entry_pathname(entry), flags);
-		r = mkfifo(archive_entry_pathname(entry),
-		    archive_entry_mode(entry));
-	}
-
-	if (r != 0) {
-		archive_set_error(a, errno, "Can't restore fifo");
-		return (ARCHIVE_WARN);
-	}
-
-	r = restore_metadata(a, -1, entry, flags);
-	return (r);
-}
-
-static int
-restore_metadata(struct archive *a, int fd, struct archive_entry *entry, int flags)
-{
-	int r, r2;
-
-	r = set_ownership(a, fd, entry, flags);
-	r2 = set_time(a, fd, entry, flags);
-	r = err_combine(r, r2);
-	r2 = set_perm(a, fd, entry, archive_entry_mode(entry), flags);
-	return (err_combine(r, r2));
-}
-
-static int
-set_ownership(struct archive *a, int fd,
-    struct archive_entry *entry, int flags)
-{
-	uid_t uid;
-	gid_t gid;
-
-	/* Not changed. */
-	if ((flags & ARCHIVE_EXTRACT_OWNER) == 0)
-		return (ARCHIVE_OK);
-
-	uid = lookup_uid(a, archive_entry_uname(entry),
-	    archive_entry_uid(entry));
-	gid = lookup_gid(a, archive_entry_gname(entry),
-	    archive_entry_gid(entry));
-
-	/* If we know we can't change it, don't bother trying. */
-	if (a->user_uid != 0  &&  a->user_uid != uid)
-		return (ARCHIVE_OK);
-
-#ifdef HAVE_FCHOWN
-	if (fd >= 0 && fchown(fd, uid, gid) == 0)
-		return (ARCHIVE_OK);
-#endif
-
-#ifdef HAVE_LCHOWN
-	if (lchown(archive_entry_pathname(entry), uid, gid))
-#else
-	if (!S_ISLNK(archive_entry_mode(entry))
-	    && chown(archive_entry_pathname(entry), uid, gid) != 0)
-#endif
-	{
-		archive_set_error(a, errno,
-		    "Can't set user=%d/group=%d for %s", uid, gid,
-		    archive_entry_pathname(entry));
-		return (ARCHIVE_WARN);
-	}
-	return (ARCHIVE_OK);
-}
-
-static int
-set_time(struct archive *a, int fd, struct archive_entry *entry, int flags)
-{
-	const struct stat *st;
-	struct timeval times[2];
-
-	(void)a; /* UNUSED */
-	st = archive_entry_stat(entry);
-
-	if ((flags & ARCHIVE_EXTRACT_TIME) == 0)
-		return (ARCHIVE_OK);
-	/* It's a waste of time to mess with dir timestamps here. */
-	if (S_ISDIR(archive_entry_mode(entry)))
-		return (ARCHIVE_OK);
-
-	times[1].tv_sec = st->st_mtime;
-	times[1].tv_usec = ARCHIVE_STAT_MTIME_NANOS(st) / 1000;
-
-	times[0].tv_sec = st->st_atime;
-	times[0].tv_usec = ARCHIVE_STAT_ATIME_NANOS(st) / 1000;
-
-#ifdef HAVE_FUTIMES
-	if (fd >= 0 && futimes(fd, times) == 0)
-		return (ARCHIVE_OK);
-#endif
-
-#ifdef HAVE_LUTIMES
-	if (lutimes(archive_entry_pathname(entry), times) != 0) {
-#else
-	if ((archive_entry_mode(entry) & S_IFMT) != S_IFLNK &&
-	    utimes(archive_entry_pathname(entry), times) != 0) {
-#endif
-		archive_set_error(a, errno, "Can't update time for %s",
-		    archive_entry_pathname(entry));
-		return (ARCHIVE_WARN);
-	}
-
-	/*
-	 * Note: POSIX does not provide a portable way to restore ctime.
-	 * (Apart from resetting the system clock, which is distasteful.)
-	 * So, any restoration of ctime will necessarily be OS-specific.
-	 */
-
-	/* XXX TODO: Can FreeBSD restore ctime? XXX */
-
-	return (ARCHIVE_OK);
-}
-
-static int
-set_perm(struct archive *a, int fd, struct archive_entry *entry,
-    int mode, int flags)
-{
-	struct extract *extract;
-	struct fixup_entry *le;
-	const char *name;
-	unsigned long	 set, clear;
-	int		 r;
-	int		 critical_flags;
-
-	extract = a->extract;
-
-	/* Obey umask unless ARCHIVE_EXTRACT_PERM. */
-	if ((flags & ARCHIVE_EXTRACT_PERM) == 0)
-		mode &= ~extract->umask; /* Enforce umask. */
-	name = archive_entry_pathname(entry);
-
-	if (mode & (S_ISUID | S_ISGID)) {
-		if (extract->pst != NULL) {
-			/* Already have stat() data available. */
-#ifdef HAVE_FSTAT
-		} else if (fd >= 0 && fstat(fd, &extract->st) == 0) {
-			extract->pst = &extract->st;
-#endif
-		} else if (stat(name, &extract->st) == 0) {
-			extract->pst = &extract->st;
-		} else {
-			archive_set_error(a, errno,
-			    "Couldn't stat file");
-			return (ARCHIVE_WARN);
-		}
-
-		/*
-		 * TODO: Use the uid/gid looked up in set_ownership
-		 * above rather than the uid/gid stored in the entry.
-		 */
-		if (extract->pst->st_uid != archive_entry_uid(entry))
-			mode &= ~ S_ISUID;
-		if (extract->pst->st_gid != archive_entry_gid(entry))
-			mode &= ~ S_ISGID;
-	}
-
-	if (S_ISLNK(archive_entry_mode(entry))) {
-#ifdef HAVE_LCHMOD
-		/*
-		 * If this is a symlink, use lchmod().  If the
-		 * platform doesn't support lchmod(), just skip it as
-		 * permissions on symlinks are actually ignored on
-		 * most platforms.
-		 */
-		if (lchmod(name, mode) != 0) {
-			archive_set_error(a, errno, "Can't set permissions");
-			return (ARCHIVE_WARN);
-		}
-#endif
-	} else if (!S_ISDIR(archive_entry_mode(entry))) {
-		/*
-		 * If it's not a symlink and not a dir, then use
-		 * fchmod() or chmod(), depending on whether we have
-		 * an fd.  Dirs get their perms set during the
-		 * post-extract fixup, which is handled elsewhere.
-		 */
-#ifdef HAVE_FCHMOD
-		if (fd >= 0) {
-			if (fchmod(fd, mode) != 0) {
-				archive_set_error(a, errno,
-				    "Can't set permissions");
-				return (ARCHIVE_WARN);
-			}
-		} else
-#endif
-			/* If this platform lacks fchmod(), then
-			 * we'll just use chmod(). */
-			if (chmod(name, mode) != 0) {
-				archive_set_error(a, errno,
-				    "Can't set permissions");
-				return (ARCHIVE_WARN);
-			}
-	}
-
-	if (flags & ARCHIVE_EXTRACT_ACL) {
-		r = set_acls(a, fd, entry);
+	for (;;) {
+		r = archive_read_data_block(ar, &buff, &size, &offset);
+		if (r == ARCHIVE_EOF)
+			return (ARCHIVE_OK);
 		if (r != ARCHIVE_OK)
 			return (r);
-	}
-
-	if (flags & ARCHIVE_EXTRACT_XATTR) {
-		r = set_xattrs(a, fd, entry);
-		if (r != ARCHIVE_OK)
+		r = archive_write_data_block(aw, buff, size, offset);
+		if (r < ARCHIVE_WARN)
+			r = ARCHIVE_WARN;
+		if (r != ARCHIVE_OK) {
+			archive_set_error(ar, archive_errno(aw),
+			    "%s", archive_error_string(aw));
 			return (r);
-	}
-
-	/*
-	 * Make 'critical_flags' hold all file flags that can't be
-	 * immediately restored.  For example, on BSD systems,
-	 * SF_IMMUTABLE prevents hardlinks from being created, so
-	 * should not be set until after any hardlinks are created.  To
-	 * preserve some semblance of portability, this uses #ifdef
-	 * extensively.  Ugly, but it works.
-	 *
-	 * Yes, Virginia, this does create a security race.  It's mitigated
-	 * somewhat by the practice of creating dirs 0700 until the extract
-	 * is done, but it would be nice if we could do more than that.
-	 * People restoring critical file systems should be wary of
-	 * other programs that might try to muck with files as they're
-	 * being restored.
-	 */
-	/* Hopefully, the compiler will optimize this mess into a constant. */
-	critical_flags = 0;
-#ifdef SF_IMMUTABLE
-	critical_flags |= SF_IMMUTABLE;
-#endif
-#ifdef UF_IMMUTABLE
-	critical_flags |= UF_IMMUTABLE;
-#endif
-#ifdef SF_APPEND
-	critical_flags |= SF_APPEND;
-#endif
-#ifdef UF_APPEND
-	critical_flags |= UF_APPEND;
-#endif
-#ifdef EXT2_APPEND_FL
-	critical_flags |= EXT2_APPEND_FL;
-#endif
-#ifdef EXT2_IMMUTABLE_FL
-	critical_flags |= EXT2_IMMUTABLE_FL;
-#endif
-
-	if (flags & ARCHIVE_EXTRACT_FFLAGS) {
-		archive_entry_fflags(entry, &set, &clear);
-
-		/*
-		 * The first test encourages the compiler to eliminate
-		 * all of this if it's not necessary.
-		 */
-		if ((critical_flags != 0)  &&  (set & critical_flags)) {
-			le = current_fixup(a, archive_entry_pathname(entry));
-			le->fixup |= FIXUP_FFLAGS;
-			le->fflags_set = set;
-			/* Store the mode if it's not already there. */
-			if ((le->fixup & FIXUP_MODE) == 0)
-				le->mode = mode;
-		} else {
-			r = set_fflags(a, fd, archive_entry_pathname(entry),
-			    mode, set, clear);
-			if (r != ARCHIVE_OK)
-				return (r);
 		}
 	}
-	return (ARCHIVE_OK);
 }
 
-
-#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux)
-static int
-set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
-    unsigned long set, unsigned long clear)
-{
-	struct extract *extract;
-
-	extract = a->extract;
-	if (set == 0  && clear == 0)
-		return (ARCHIVE_OK);
-
-	(void)mode; /* UNUSED */
-	/*
-	 * XXX Is the stat here really necessary?  Or can I just use
-	 * the 'set' flags directly?  In particular, I'm not sure
-	 * about the correct approach if we're overwriting an existing
-	 * file that already has flags on it. XXX
-	 */
-	if (extract->pst != NULL) {
-		/* Already have stat() data available. */
-	} else if (fd >= 0 && fstat(fd, &extract->st) == 0)
-		extract->pst = &extract->st;
-	else if (stat(name, &extract->st) == 0)
-		extract->pst = &extract->st;
-	else {
-		archive_set_error(a, errno,
-		    "Couldn't stat file");
-		return (ARCHIVE_WARN);
-	}
-
-	extract->st.st_flags &= ~clear;
-	extract->st.st_flags |= set;
-#ifdef HAVE_FCHFLAGS
-	/* If platform has fchflags() and we were given an fd, use it. */
-	if (fd >= 0 && fchflags(fd, extract->st.st_flags) == 0)
-		return (ARCHIVE_OK);
-#endif
-	/*
-	 * If we can't use the fd to set the flags, we'll use the
-	 * pathname to set flags.  We prefer lchflags() but will use
-	 * chflags() if we must.
-	 */
-#ifdef HAVE_LCHFLAGS
-	if (lchflags(name, extract->st.st_flags) == 0)
-		return (ARCHIVE_OK);
-#elif defined(HAVE_CHFLAGS)
-	if (chflags(name, extract->st.st_flags) == 0)
-		return (ARCHIVE_OK);
-#endif
-	archive_set_error(a, errno,
-	    "Failed to set file flags");
-	return (ARCHIVE_WARN);
-}
-
-#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS)
-
-/*
- * Linux has flags too, but uses ioctl() to access them instead of
- * having a separate chflags() system call.
- */
-static int
-set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
-    unsigned long set, unsigned long clear)
-{
-	struct extract *extract;
-	int		 ret;
-	int		 myfd = fd;
-	unsigned long newflags, oldflags;
-	unsigned long sf_mask = 0;
-
-	extract = a->extract;
-	if (set == 0  && clear == 0)
-		return (ARCHIVE_OK);
-	/* Only regular files and dirs can have flags. */
-	if (!S_ISREG(mode) && !S_ISDIR(mode))
-		return (ARCHIVE_OK);
-
-	/* If we weren't given an fd, open it ourselves. */
-	if (myfd < 0)
-		myfd = open(name, O_RDONLY|O_NONBLOCK);
-	if (myfd < 0)
-		return (ARCHIVE_OK);
-
-	/*
-	 * Linux has no define for the flags that are only settable by
-	 * the root user.  This code may seem a little complex, but
-	 * there seem to be some Linux systems that lack these
-	 * defines. (?)  The code below degrades reasonably gracefully
-	 * if sf_mask is incomplete.
-	 */
-#ifdef EXT2_IMMUTABLE_FL
-	sf_mask |= EXT2_IMMUTABLE_FL;
-#endif
-#ifdef EXT2_APPEND_FL
-	sf_mask |= EXT2_APPEND_FL;
-#endif
-	/*
-	 * XXX As above, this would be way simpler if we didn't have
-	 * to read the current flags from disk. XXX
-	 */
-	ret = ARCHIVE_OK;
-	/* Try setting the flags as given. */
-	if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
-		newflags = (oldflags & ~clear) | set;
-		if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
-			goto cleanup;
-		if (errno != EPERM)
-			goto fail;
-	}
-	/* If we couldn't set all the flags, try again with a subset. */
-	if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
-		newflags &= ~sf_mask;
-		oldflags &= sf_mask;
-		newflags |= oldflags;
-		if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
-			goto cleanup;
-	}
-	/* We couldn't set the flags, so report the failure. */
-fail:
-	archive_set_error(a, errno,
-	    "Failed to set file flags");
-	ret = ARCHIVE_WARN;
-cleanup:
-	if (fd < 0)
-		close(myfd);
-	return (ret);
-}
-
-#else /* Not HAVE_CHFLAGS && Not __linux */
-
-/*
- * Of course, some systems have neither BSD chflags() nor Linux' flags
- * support through ioctl().
- */
-static int
-set_fflags(struct archive *a, int fd, const char *name, mode_t mode,
-    unsigned long set, unsigned long clear)
-{
-	(void)a;
-	(void)fd;
-	(void)name;
-	(void)mode;
-	(void)set;
-	(void)clear;
-	return (ARCHIVE_OK);
-}
-
-#endif /* __linux */
-
-#ifndef HAVE_POSIX_ACL
-/* Default empty function body to satisfy mainline code. */
-static int
-set_acls(struct archive *a, int fd, struct archive_entry *entry)
-{
-	(void)a;
-	(void)fd;
-	(void)entry;
-
-	return (ARCHIVE_OK);
-}
-
-#else
-
-/*
- * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
- */
-static int
-set_acls(struct archive *a, int fd, struct archive_entry *entry)
-{
-	int		 ret;
-
-	ret = set_acl(a, fd, entry, ACL_TYPE_ACCESS,
-	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
-	if (ret != ARCHIVE_OK)
-		return (ret);
-	ret = set_acl(a, fd, entry, ACL_TYPE_DEFAULT,
-	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
-	return (ret);
-}
-
-
-static int
-set_acl(struct archive *a, int fd, struct archive_entry *entry,
-    acl_type_t acl_type, int ae_requested_type, const char *tname)
-{
-	acl_t		 acl;
-	acl_entry_t	 acl_entry;
-	acl_permset_t	 acl_permset;
-	int		 ret;
-	int		 ae_type, ae_permset, ae_tag, ae_id;
-	uid_t		 ae_uid;
-	gid_t		 ae_gid;
-	const char	*ae_name;
-	int		 entries;
-	const char	*name;
-
-	ret = ARCHIVE_OK;
-	entries = archive_entry_acl_reset(entry, ae_requested_type);
-	if (entries == 0)
-		return (ARCHIVE_OK);
-	acl = acl_init(entries);
-	while (archive_entry_acl_next(entry, ae_requested_type, &ae_type,
-		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
-		acl_create_entry(&acl, &acl_entry);
-
-		switch (ae_tag) {
-		case ARCHIVE_ENTRY_ACL_USER:
-			acl_set_tag_type(acl_entry, ACL_USER);
-			ae_uid = lookup_uid(a, ae_name, ae_id);
-			acl_set_qualifier(acl_entry, &ae_uid);
-			break;
-		case ARCHIVE_ENTRY_ACL_GROUP:
-			acl_set_tag_type(acl_entry, ACL_GROUP);
-			ae_gid = lookup_gid(a, ae_name, ae_id);
-			acl_set_qualifier(acl_entry, &ae_gid);
-			break;
-		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
-			break;
-		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
-			break;
-		case ARCHIVE_ENTRY_ACL_MASK:
-			acl_set_tag_type(acl_entry, ACL_MASK);
-			break;
-		case ARCHIVE_ENTRY_ACL_OTHER:
-			acl_set_tag_type(acl_entry, ACL_OTHER);
-			break;
-		default:
-			/* XXX */
-			break;
-		}
-
-		acl_get_permset(acl_entry, &acl_permset);
-		acl_clear_perms(acl_permset);
-		if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
-			acl_add_perm(acl_permset, ACL_EXECUTE);
-		if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
-			acl_add_perm(acl_permset, ACL_WRITE);
-		if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
-			acl_add_perm(acl_permset, ACL_READ);
-	}
-
-	name = archive_entry_pathname(entry);
-
-	/* Try restoring the ACL through 'fd' if we can. */
-#if HAVE_ACL_SET_FD
-	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
-		ret = ARCHIVE_OK;
-	else
-#else
-#if HAVE_ACL_SET_FD_NP
-	if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
-		ret = ARCHIVE_OK;
-	else
-#endif
-#endif
-	if (acl_set_file(name, acl_type, acl) != 0) {
-		archive_set_error(a, errno, "Failed to set %s acl", tname);
-		ret = ARCHIVE_WARN;
-	}
-	acl_free(acl);
-	return (ret);
-}
-#endif
-
-#if HAVE_LSETXATTR
 /*
- * Restore extended attributes -  Linux implementation
+ * Cleanup function for archive_extract.
  */
 static int
-set_xattrs(struct archive *a, int fd, struct archive_entry *entry)
+archive_read_extract_cleanup(struct archive_read *a)
 {
-	static int warning_done = 0;
 	int ret = ARCHIVE_OK;
-	int i = archive_entry_xattr_reset(entry);
 
-	while (i--) {
-		const char *name;
-		const void *value;
-		size_t size;
-		archive_entry_xattr_next(entry, &name, &value, &size);
-		if (name != NULL &&
-				strncmp(name, "xfsroot.", 8) != 0 &&
-				strncmp(name, "system.", 7) != 0) {
-			int e;
-#if HAVE_FSETXATTR
-			if (fd >= 0)
-				e = fsetxattr(fd, name, value, size, 0);
-			else
+#if ARCHIVE_API_VERSION > 1
+	ret =
 #endif
-			{
-				e = lsetxattr(archive_entry_pathname(entry),
-				    name, value, size, 0);
-			}
-			if (e == -1) {
-				if (errno == ENOTSUP) {
-					if (!warning_done) {
-						warning_done = 1;
-						archive_set_error(a, errno,
-						    "Cannot restore extended "
-						    "attributes on this file "
-						    "system");
-					}
-				} else
-					archive_set_error(a, errno,
-					    "Failed to set extended attribute");
-				ret = ARCHIVE_WARN;
-			}
-		} else {
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Invalid extended attribute encountered");
-			ret = ARCHIVE_WARN;
-		}
-	}
+	    archive_write_finish(a->extract->ad);
+	free(a->extract);
+	a->extract = NULL;
 	return (ret);
 }
-#else
-/*
- * Restore extended attributes - stub implementation for unsupported systems
- */
-static int
-set_xattrs(struct archive *a, int fd, struct archive_entry *entry)
-{
-	static int warning_done = 0;
-	(void)a; /* UNUSED */
-	(void)fd; /* UNUSED */
-
-	/* If there aren't any extended attributes, then it's okay not
-	 * to extract them, otherwise, issue a single warning. */
-	if (archive_entry_xattr_count(entry) != 0 && !warning_done) {
-		warning_done = 1;
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Cannot restore extended attributes on this system");
-		return (ARCHIVE_WARN);
-	}
-	/* Warning was already emitted; suppress further warnings. */
-	return (ARCHIVE_OK);
-}
-#endif
-
-/*
- * The following routines do some basic caching of uname/gname
- * lookups.  All such lookups go through these routines, including ACL
- * conversions.  Even a small cache here provides an enormous speedup,
- * especially on systems using NIS, LDAP, or a similar networked
- * directory system.
- *
- * TODO: Provide an API for clients to override these routines.
- */
-static gid_t
-lookup_gid(struct archive *a, const char *gname, gid_t gid)
-{
-	struct group	*grent;
-	struct extract *extract;
-	int h;
-	struct bucket *b;
-	int cache_size;
-
-	extract = a->extract;
-	cache_size = sizeof(extract->gcache) / sizeof(extract->gcache[0]);
-
-	/* If no gname, just use the gid provided. */
-	if (gname == NULL || *gname == '\0')
-		return (gid);
-
-	/* Try to find gname in the cache. */
-	h = hash(gname);
-	b = &extract->gcache[h % cache_size ];
-	if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
-		return ((gid_t)b->id);
-
-	/* Free the cache slot for a new entry. */
-	if (b->name != NULL)
-		free(b->name);
-	b->name = strdup(gname);
-	/* Note: If strdup fails, that's okay; we just won't cache. */
-	b->hash = h;
-	grent = getgrnam(gname);
-	if (grent != NULL)
-		gid = grent->gr_gid;
-	b->id = gid;
-
-	return (gid);
-}
-
-static uid_t
-lookup_uid(struct archive *a, const char *uname, uid_t uid)
-{
-	struct passwd	*pwent;
-	struct extract *extract;
-	int h;
-	struct bucket *b;
-	int cache_size;
-
-	extract = a->extract;
-	cache_size = sizeof(extract->ucache) / sizeof(extract->ucache[0]);
-
-	/* If no uname, just use the uid provided. */
-	if (uname == NULL || *uname == '\0')
-		return (uid);
-
-	/* Try to find uname in the cache. */
-	h = hash(uname);
-	b = &extract->ucache[h % cache_size ];
-	if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
-		return ((uid_t)b->id);
-
-	/* Free the cache slot for a new entry. */
-	if (b->name != NULL)
-		free(b->name);
-	b->name = strdup(uname);
-	/* Note: If strdup fails, that's okay; we just won't cache. */
-	b->hash = h;
-	pwent = getpwnam(uname);
-	if (pwent != NULL)
-		uid = pwent->pw_uid;
-	b->id = uid;
-
-	return (uid);
-}
-
-static unsigned int
-hash(const char *p)
-{
-	/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
-	   as used by ELF for hashing function names. */
-	unsigned g, h = 0;
-	while (*p != '\0') {
-		h = ( h << 4 ) + *p++;
-		if (( g = h & 0xF0000000 )) {
-			h ^= g >> 24;
-			h &= 0x0FFFFFFF;
-		}
-	}
-	return h;
-}
-
-void
-archive_read_extract_set_progress_callback(struct archive *a,
-    void (*progress_func)(void *), void *user_data)
-{
-	a->extract_progress = progress_func;
-	a->extract_progress_user_data = user_data;
-}
Index: archive_util.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_util.3,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_util.3 -Llib/libarchive/archive_util.3 -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_util.3
+++ lib/libarchive/archive_util.3
@@ -22,14 +22,16 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_util.3,v 1.3.2.2 2007/01/27 06:44:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_util.3,v 1.7 2007/05/29 01:00:19 kientzle Exp $
 .\"
 .Dd January 8, 2005
 .Dt archive_util 3
 .Os
 .Sh NAME
+.Nm archive_clear_error ,
 .Nm archive_compression ,
 .Nm archive_compression_name ,
+.Nm archive_copy_error ,
 .Nm archive_errno ,
 .Nm archive_error_string ,
 .Nm archive_format ,
@@ -38,10 +40,14 @@
 .Nd libarchive utility functions
 .Sh SYNOPSIS
 .In archive.h
+.Ft void
+.Fn archive_clear_error "struct archive *"
 .Ft int
 .Fn archive_compression "struct archive *"
 .Ft const char *
 .Fn archive_compression_name "struct archive *"
+.Ft void
+.Fn archive_copy_error "struct archive *" "struct archive *"
 .Ft int
 .Fn archive_errno "struct archive *"
 .Ft const char *
@@ -59,12 +65,17 @@
 .Xr libarchive 3
 library.
 .Bl -tag -compact -width indent
+.It Fn archive_clear_error
+Clears any error information left over from a previous call.
+Not generally used in client code.
 .It Fn archive_compression
 Returns a numeric code indicating the current compression.
 This value is set by
 .Fn archive_read_open .
 .It Fn archive_compression_name
 Returns a text description of the current compression suitable for display.
+.It Fn archive_copy_error
+Copies error information from one archive to another.
 .It Fn archive_errno
 Returns a numeric error code (see
 .Xr errno 2 )
--- /dev/null
+++ lib/libarchive/libarchive_internals.3
@@ -0,0 +1,376 @@
+.\" Copyright (c) 2003-2007 Tim Kientzle
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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/libarchive_internals.3,v 1.1 2007/05/29 01:00:20 kientzle Exp $
+.\"
+.Dd April 16, 2007
+.Dt LIBARCHIVE 3
+.Os
+.Sh NAME
+.Nm libarchive_internals
+.Nd description of libarchive internal interfaces
+.Sh OVERVIEW
+The
+.Nm libarchive
+library provides a flexible interface for reading and writing
+streaming archive files such as tar and cpio.
+Internally, it follows a modular layered design that should
+make it easy to add new archive and compression formats.
+.Sh GENERAL ARCHITECTURE
+Externally, libarchive exposes most operations through an
+opaque, object-style interface.
+The
+.Xr archive_entry 1
+objects store information about a single filesystem object.
+The rest of the library provides facilities to write
+.Xr archive_entry 1
+objects to archive files,
+read them from archive files,
+and write them to disk.
+(There are plans to add a facility to read
+.Xr archive_entry 1
+objects from disk as well.)
+.Pp
+The read and write APIs each have four layers: a public API
+layer, a format layer that understands the archive file format,
+a compression layer, and an I/O layer.
+The I/O layer is completely exposed to clients who can replace
+it entirely with their own functions.
+.Pp
+In order to provide as much consistency as possible for clients,
+some public functions are virtualized.
+Eventually, it should be possible for clients to open
+an archive or disk writer, and then use a single set of
+code to select and write entries, regardless of the target.
+.Sh READ ARCHITECTURE
+From the outside, clients use the
+.Xr archive_read 3
+API to manipulate an
+.Nm archive
+object to read entries and bodies from an archive stream.
+Internally, the
+.Nm archive
+object is cast to an
+.Nm archive_read
+object, which holds all read-specific data.
+The API has four layers:
+The lowest layer is the I/O layer.
+This layer can be overridden by clients, but most clients use
+the packaged I/O callbacks provided, for example, by
+.Xr archive_read_open_memory 3 ,
+and
+.Xr archive_read_open_fd 3 .
+The compression layer calls the I/O layer to
+read bytes and decompresses them for the format layer.
+The format layer unpacks a stream of uncompressed bytes and
+creates
+.Nm archive_entry
+objects from the incoming data.
+The API layer tracks overall state
+(for example, it prevents clients from reading data before reading a header)
+and invokes the format and compression layer operations
+through registered function pointers.
+In particular, the API layer drives the format-detection process:
+When opening the archive, it reads an initial block of data
+and offers it to each registered compression handler.
+The one with the highest bid is initialized with the first block.
+Similarly, the format handlers are polled to see which handler
+is the best for each header request.
+(Note that a single file can have entries handled by different
+format handlers;
+this allows a simple handler for a generic version of a format
+with more complex handlers implemented independently for
+extended sub-formats.)
+.Ss I/O Layer and Client Callbacks
+The read API goes to some lengths to be nice to clients.
+As a result, there are few restrictions on the behavior of
+the client callbacks.
+.Pp
+The client read callback is expected to provide a block
+of data on each call.
+A zero-length return does indicate end of file, but otherwise
+blocks may be as small as one byte or as large as the entire file.
+In particular, blocks may be of different sizes.
+.Pp
+The client skip callback returns the number of bytes actually
+skipped, which may be much smaller than the skip requested.
+The only requirement is that the skip not be larger.
+The skip callback must never be invoked with a negative value.
+.Pp
+Keep in mind that not all clients are reading from disk:
+clients reading from networks may provide different-sized
+blocks on every request and cannot skip at all;
+advanced clients may use
+.Xr mmap 2
+to read the entire file into memory at once and return the
+entire file to libarchive as a single block;
+other clients may begin asynchronous I/O operations for the
+next block on each request.
+.Ss Decompresssion Layer
+The decompression layer not only handles decompression,
+it also buffers data so that the format handlers see a
+much nicer I/O model.
+The decompression API is a two stage peek/consume model.
+A read_ahead request specifies a minimum read amount;
+the decompression layer must provide a pointer to at least
+that much data.
+If more data is immediately available, it should return more:
+the format layer handles bulk data reads by asking for a minimum
+of one byte and then copying as much data as is available.
+.Pp
+A subsequent call to the
+.Fn consume
+function advances the read pointer.
+Note that data returned from a
+.Fn read_ahead
+call is guaranteed to remain in place until
+the next call to
+.Fn read_ahead .
+Intervening calls to
+.Fn consume
+should not cause the data to move.
+.Pp
+Skip requests must always be handled exactly.
+Decompression handlers that cannot seek forward should
+not register a skip handler;
+the API layer fills in a generic skip handler that reads and discards data.
+.Pp
+A decompression handler has a specific lifecycle:
+.Bl -tag -compact -width indent
+.It Registration/Configuration
+When the client invokes the public support function,
+the decompression handler invokes the internal
+.Fn __archive_read_register_compression
+function to provide bid and initialization functions.
+This function returns
+.Cm NULL
+on error or else a pointer to a
+.Cm struct decompressor_t .
+This structure contains a
+.Va void * config
+slot that can be used for storing any customization information.
+.It Bid
+The bid function is invoked with a pointer and size of a block of data.
+The decompressor can access its config data
+through the
+.Va decompressor
+element of the
+.Cm archive_read
+object.
+The bid function is otherwise stateless.
+In particular, it must not perform any I/O operations.
+.Pp
+The value returned by the bid function indicates its suitability
+for handling this data stream.
+A bid of zero will ensure that this decompressor is never invoked.
+Return zero if magic number checks fail.
+Otherwise, your initial implementation should return the number of bits
+actually checked.
+For example, if you verify two full bytes and three bits of another
+byte, bid 19.
+Note that the initial block may be very short;
+be careful to only inspect the data you are given.
+(The current decompressors require two bytes for correct bidding.)
+.It Initialize
+The winning bidder will have its init function called.
+This function should initialize the remaining slots of the
+.Va struct decompressor_t
+object pointed to by the
+.Va decompressor
+element of the
+.Va archive_read
+object.
+In particular, it should allocate any working data it needs
+in the
+.Va data
+slot of that structure.
+The init function is called with the block of data that
+was used for tasting.
+At this point, the decompressor is responsible for all I/O
+requests to the client callbacks.
+The decompressor is free to read more data as and when
+necessary.
+.It Satisfy I/O requests
+The format handler will invoke the
+.Va read_ahead ,
+.Va consume ,
+and
+.Va skip
+functions as needed.
+.It Finish
+The finish method is called only once when the archive is closed.
+It should release anything stored in the
+.Va data
+and
+.Va config
+slots of the
+.Va decompressor
+object.
+It should not invoke the client close callback.
+.El
+.Ss Format Layer
+The read formats have a similar lifecycle to the decompression handlers:
+.Bl -tag -compact -width indent
+.It Registration
+Allocate your private data and initialize your pointers.
+.It Bid
+Formats bid by invoking the
+.Fn read_ahead
+decompression method but not calling the
+.Fn consume
+method.
+This allows each bidder to look ahead in the input stream.
+Bidders should not look further ahead than necessary, as long
+look aheads put pressure on the compression layer to buffer
+lots of data.
+Most formats only require a few hundred bytes of look ahead;
+look aheads of a few kilobytes are reasonable.
+(The ISO9660 reader sometimes looks ahead by 48k, which
+should be considered an upper limit.)
+Note that the bidder is invoked for every entry.
+For many formats, this is inappropriate; if you can only bid at
+the beginning of the file, store your bid value and check that
+each time your bid function is called.
+For example, the ISO9660 reader initializes a
+.Va bid
+value to -1 at registration time;
+each time the bid function is called, the bid value is returned
+immediately if it is zero or larger.
+.It Read header
+The header read is usually the most complex part of any format.
+There are a few strategies worth mentioning:
+For formats such as tar or cpio, reading and parsing the header is
+straightforward since headers alternate with data.
+For formats that store all header data at the beginning of the file,
+the first header read request may have to read all headers into
+memory and store that data, sorted by the location of the file
+data.
+Subsequent header read requests will skip forward to the
+beginning of the file data and return the corresponding header.
+.It Read Data
+The read data interface supports sparse files; this requires that
+each call return a block of data specifying the file offset and
+size.
+This may require you to carefully track the location so that you
+can return accurate file offsets for each read.
+Remember that the decompressor will return as much data as it has.
+Generally, you will want to request one byte,
+examine the return value to see how much data is available, and
+possibly trim that to the amount you can use.
+You should invoke consume for each block just before you return it.
+.It Skip All Data
+The skip data call should skip over all file data and trailing padding.
+This is called automatically by the API layer just before each
+header read.
+It is also called in response to the client calling the public
+.Fn data_skip
+function.
+.It Cleanup
+On cleanup, the format should release all of its allocated memory.
+.El
+.Ss API Layer
+XXX to do XXX
+.Sh WRITE ARCHITECTURE
+The write API has a similar set of four layers:
+an API layer, a format layer, a compression layer, and an I/O layer.
+The registration here is much simpler because only
+one format and one compression can be registered at a time.
+.Ss I/O Layer and Client Callbacks
+XXX To be written XXX
+.Ss Compression Layer
+XXX To be written XXX
+.Ss Format Layer
+XXX To be written XXX
+.Ss API Layer
+XXX To be written XXX
+.Sh WRITE_DISK ARCHITECTURE
+The write_disk API is intended to look just like the write API
+to clients.
+Since it does not handle multiple formats or compression, it
+is not layered internally.
+.Sh GENERAL SERVICES
+The
+.Nm archive_read ,
+.Nm archive_write ,
+and
+.Nm archive_write_disk
+objects all contain an initial
+.Nm archive
+object which provides common support for a set of standard services.
+(Recall that ANSI/ISO C90 guarantees that you can cast freely between
+a pointer to a structure and a pointer to the first element of that
+structure.)
+The
+.Nm archive
+object has a magic value that indicates which API this object
+is associated with,
+slots for storing error information,
+and function pointers for virtualized API functions.
+.Sh MISCELLANEOUS NOTES
+Connecting existing archiving libraries into libarchive is generally
+quite difficult.
+In particular, many existing libraries strongly assume that you
+are reading from a file; they seek forwards and backwards as necessary
+to locate various pieces of information.
+In contrast, libarchive never seeks backwards in its input, which
+sometimes requires very different approaches.
+.Pp
+For example, libarchive's ISO9660 support operates very differently
+from most ISO9660 readers.
+The libarchive support utilizes a work-queue design that
+keeps a list of known entries sorted by their location in the input.
+Whenever libarchive's ISO9660 implementation is asked for the next
+header, checks this list to find the next item on the disk.
+Directories are parsed when they are encountered and new
+items are added to the list.
+This design relies heavily on the ISO9660 image being optimized so that
+directories always occur earlier on the disk than the files they
+describe.
+.Pp
+Depending on the specific format, such approaches may not be possible.
+The ZIP format specification, for example, allows archivers to store
+key information only at the end of the file.
+In theory, it is possible to create ZIP archives that cannot
+be read without seeking.
+Fortunately, such archives are very rare, and libarchive can read
+most ZIP archives, though it cannot always extract as much information
+as a dedicated ZIP program.
+.Sh SEE ALSO
+.Xr archive 3 ,
+.Xr archive_entry 3 ,
+.Xr archive_read 3 ,
+.Xr archive_write 3 ,
+.Xr archive_write_disk 3
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle at acm.org .
+.Sh BUGS
Index: archive_read_support_format_zip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_zip.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_format_zip.c -Llib/libarchive/archive_read_support_format_zip.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_format_zip.c
+++ lib/libarchive/archive_read_support_format_zip.c
@@ -24,11 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.5.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_zip.c,v 1.14 2007/07/15 19:13:59 kientzle Exp $");
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -44,15 +41,16 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 struct zip {
 	/* entry_bytes_remaining is the number of bytes we expect. */
-	off_t			entry_bytes_remaining;
-	off_t			entry_offset;
+	int64_t			entry_bytes_remaining;
+	int64_t			entry_offset;
 
 	/* These count the number of bytes actually read for the entry. */
-	off_t			entry_compressed_bytes_read;
-	off_t			entry_uncompressed_bytes_read;
+	int64_t			entry_compressed_bytes_read;
+	int64_t			entry_uncompressed_bytes_read;
 
 	unsigned		version;
 	unsigned		system;
@@ -74,13 +72,14 @@
 	long			crc32;
 	ssize_t			filename_length;
 	ssize_t			extra_length;
-	off_t			uncompressed_size;
-	off_t			compressed_size;
+	int64_t			uncompressed_size;
+	int64_t			compressed_size;
 
 	unsigned char 		*uncompressed_buffer;
 	size_t 			uncompressed_buffer_size;
 #ifdef HAVE_ZLIB_H
 	z_stream		stream;
+	char			stream_valid;
 #endif
 
 	struct archive_string	pathname;
@@ -115,36 +114,37 @@
 	"deflation"
 };
 
-static int	archive_read_format_zip_bid(struct archive *);
-static int	archive_read_format_zip_cleanup(struct archive *);
-static int	archive_read_format_zip_read_data(struct archive *,
+static int	archive_read_format_zip_bid(struct archive_read *);
+static int	archive_read_format_zip_cleanup(struct archive_read *);
+static int	archive_read_format_zip_read_data(struct archive_read *,
 		    const void **, size_t *, off_t *);
-static int	archive_read_format_zip_read_data_skip(struct archive *a);
-static int	archive_read_format_zip_read_header(struct archive *,
+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 *a, const void **buff,
+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 *a, const void **buff,
+static int	zip_read_data_none(struct archive_read *a, const void **buff,
 		    size_t *size, off_t *offset);
-static int	zip_read_file_header(struct archive *a,
+static int	zip_read_file_header(struct archive_read *a,
 		    struct archive_entry *entry, struct zip *zip);
 static time_t	zip_time(const char *);
 static void process_extra(const void* extra, struct zip* zip);
 
 int
-archive_read_support_format_zip(struct archive *a)
+archive_read_support_format_zip(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct zip *zip;
 	int r;
 
 	zip = (struct zip *)malloc(sizeof(*zip));
 	if (zip == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate zip data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(zip, 0, sizeof(*zip));
@@ -164,17 +164,17 @@
 
 
 static int
-archive_read_format_zip_bid(struct archive *a)
+archive_read_format_zip_bid(struct archive_read *a)
 {
 	int bytes_read;
 	int bid = 0;
 	const void *h;
 	const char *p;
 
-	if (a->archive_format == ARCHIVE_FORMAT_ZIP)
+	if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP)
 		bid += 1;
 
-	bytes_read = (a->compression_read_ahead)(a, &h, 4);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
 	if (bytes_read < 4)
 	    return (-1);
 	p = (const char *)h;
@@ -194,7 +194,7 @@
 }
 
 static int
-archive_read_format_zip_read_header(struct archive *a,
+archive_read_format_zip_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
 	int bytes_read;
@@ -202,23 +202,23 @@
 	const char *signature;
 	struct zip *zip;
 
-	a->archive_format = ARCHIVE_FORMAT_ZIP;
-	if (a->archive_format_name == NULL)
-		a->archive_format_name = "ZIP";
+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "ZIP";
 
-	zip = (struct zip *)*(a->pformat_data);
+	zip = (struct zip *)(a->format->data);
 	zip->decompress_init = 0;
 	zip->end_of_entry = 0;
 	zip->end_of_entry_cleanup = 0;
 	zip->entry_uncompressed_bytes_read = 0;
 	zip->entry_compressed_bytes_read = 0;
-	bytes_read = (a->compression_read_ahead)(a, &h, 4);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 4);
 	if (bytes_read < 4)
 		return (ARCHIVE_FATAL);
 
 	signature = (const char *)h;
 	if (signature[0] != 'P' || signature[1] != 'K') {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Bad ZIP file");
 		return (ARCHIVE_FATAL);
 	}
@@ -243,30 +243,29 @@
 		 * We should never encounter this record here;
 		 * see ZIP_LENGTH_AT_END handling below for details.
 		 */
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Bad ZIP file: Unexpected end-of-entry record");
 		return (ARCHIVE_FATAL);
 	}
 
-	archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 	    "Damaged ZIP file or unsupported format variant (%d,%d)",
 	    signature[2], signature[3]);
 	return (ARCHIVE_FATAL);
 }
 
 int
-zip_read_file_header(struct archive *a, struct archive_entry *entry,
+zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
     struct zip *zip)
 {
 	const struct zip_file_header *p;
 	const void *h;
 	int bytes_read;
-	struct stat st;
 
 	bytes_read =
-	    (a->compression_read_ahead)(a, &h, sizeof(struct zip_file_header));
+	    (a->decompressor->read_ahead)(a, &h, sizeof(struct zip_file_header));
 	if (bytes_read < (int)sizeof(struct zip_file_header)) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
@@ -293,46 +292,45 @@
 	zip->uncompressed_size = u4(p->uncompressed_size);
 	zip->compressed_size = u4(p->compressed_size);
 
-	(a->compression_read_consume)(a, sizeof(struct zip_file_header));
+	(a->decompressor->consume)(a, sizeof(struct zip_file_header));
 
 
 	/* Read the filename. */
-	bytes_read = (a->compression_read_ahead)(a, &h, zip->filename_length);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, zip->filename_length);
 	if (bytes_read < zip->filename_length) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
-	archive_string_ensure(&zip->pathname, zip->filename_length);
+	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);
-	(a->compression_read_consume)(a, zip->filename_length);
+	(a->decompressor->consume)(a, zip->filename_length);
 	archive_entry_set_pathname(entry, zip->pathname.s);
 
 	if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/')
-		zip->mode = S_IFDIR | 0777;
+		zip->mode = AE_IFDIR | 0777;
 	else
-		zip->mode = S_IFREG | 0777;
+		zip->mode = AE_IFREG | 0777;
 
 	/* Read the extra data. */
-	bytes_read = (a->compression_read_ahead)(a, &h, zip->extra_length);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, zip->extra_length);
 	if (bytes_read < zip->extra_length) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
 	process_extra(h, zip);
-	(a->compression_read_consume)(a, zip->extra_length);
+	(a->decompressor->consume)(a, zip->extra_length);
 
 	/* Populate some additional entry fields: */
-	memset(&st, 0, sizeof(st));
-	st.st_mode = zip->mode;
-	st.st_uid = zip->uid;
-	st.st_gid = zip->gid;
-	st.st_mtime = zip->mtime;
-	st.st_ctime = zip->ctime;
-	st.st_atime = zip->atime;
-	st.st_size = zip->uncompressed_size;
-	archive_entry_copy_stat(entry, &st);
+	archive_entry_set_mode(entry, zip->mode);
+	archive_entry_set_uid(entry, zip->uid);
+	archive_entry_set_gid(entry, zip->gid);
+	archive_entry_set_mtime(entry, zip->mtime, 0);
+	archive_entry_set_ctime(entry, zip->ctime, 0);
+	archive_entry_set_atime(entry, zip->atime, 0);
+	archive_entry_set_size(entry, zip->uncompressed_size);
 
 	zip->entry_bytes_remaining = zip->compressed_size;
 	zip->entry_offset = 0;
@@ -341,7 +339,7 @@
 	sprintf(zip->format_name, "ZIP %d.%d (%s)",
 	    zip->version / 10, zip->version % 10,
 	    zip->compression_name);
-	a->archive_format_name = zip->format_name;
+	a->archive.archive_format_name = zip->format_name;
 
 	return (ARCHIVE_OK);
 }
@@ -368,13 +366,13 @@
 }
 
 static int
-archive_read_format_zip_read_data(struct archive *a,
+archive_read_format_zip_read_data(struct archive_read *a,
     const void **buff, size_t *size, off_t *offset)
 {
 	int r;
 	struct zip *zip;
 
-	zip = (struct zip *)*(a->pformat_data);
+	zip = (struct zip *)(a->format->data);
 
 	/*
 	 * If we hit end-of-entry last time, clean up and return
@@ -386,9 +384,9 @@
 				const void *h;
 				const char *p;
 				int bytes_read =
-				    (a->compression_read_ahead)(a, &h, 16);
+				    (a->decompressor->read_ahead)(a, &h, 16);
 				if (bytes_read < 16) {
-					archive_set_error(a,
+					archive_set_error(&a->archive,
 					    ARCHIVE_ERRNO_FILE_FORMAT,
 					    "Truncated ZIP end-of-file record");
 					return (ARCHIVE_FATAL);
@@ -397,24 +395,26 @@
 				zip->crc32 = i4(p + 4);
 				zip->compressed_size = u4(p + 8);
 				zip->uncompressed_size = u4(p + 12);
-				bytes_read = (a->compression_read_consume)(a, 16);
+				bytes_read = (a->decompressor->consume)(a, 16);
 			}
 
 			/* Check file size, CRC against these values. */
 			if (zip->compressed_size != zip->entry_compressed_bytes_read) {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 				    "ZIP compressed data is wrong size");
 				return (ARCHIVE_WARN);
 			}
-			if (zip->uncompressed_size != zip->entry_uncompressed_bytes_read) {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			/* Size field only stores the lower 32 bits of the actual size. */
+			if ((zip->uncompressed_size & UINT32_MAX)
+			    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 				    "ZIP uncompressed data is wrong size");
 				return (ARCHIVE_WARN);
 			}
 /* TODO: Compute CRC. */
 /*
 			if (zip->crc32 != zip->entry_crc32_calculated) {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 				    "ZIP data CRC error");
 				return (ARCHIVE_WARN);
 			}
@@ -437,7 +437,7 @@
 		*size = 0;
 		*offset = 0;
 		/* Return a warning. */
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Unsupported ZIP compression method (%s)",
 		    zip->compression_name);
 		if (zip->flags & ZIP_LENGTH_AT_END) {
@@ -472,13 +472,13 @@
  * zip->end_of_entry if it consumes all of the data.
  */
 static int
-zip_read_data_none(struct archive *a, const void **buff,
+zip_read_data_none(struct archive_read *a, const void **buff,
     size_t *size, off_t *offset)
 {
 	struct zip *zip;
 	ssize_t bytes_avail;
 
-	zip = (struct zip *)*(a->pformat_data);
+	zip = (struct zip *)(a->format->data);
 
 	if (zip->entry_bytes_remaining == 0) {
 		*buff = NULL;
@@ -493,15 +493,15 @@
 	 * available bytes; asking for more than that forces the
 	 * decompressor to combine reads by copying data.
 	 */
-	bytes_avail = (a->compression_read_ahead)(a, buff, 1);
+	bytes_avail = (a->decompressor->read_ahead)(a, buff, 1);
 	if (bytes_avail <= 0) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file data");
 		return (ARCHIVE_FATAL);
 	}
 	if (bytes_avail > zip->entry_bytes_remaining)
 		bytes_avail = zip->entry_bytes_remaining;
-	(a->compression_read_consume)(a, bytes_avail);
+	(a->decompressor->consume)(a, bytes_avail);
 	*size = bytes_avail;
 	*offset = zip->entry_offset;
 	zip->entry_offset += *size;
@@ -513,7 +513,7 @@
 
 #ifdef HAVE_ZLIB_H
 static int
-zip_read_data_deflate(struct archive *a, const void **buff,
+zip_read_data_deflate(struct archive_read *a, const void **buff,
     size_t *size, off_t *offset)
 {
 	struct zip *zip;
@@ -521,7 +521,7 @@
 	const void *compressed_buff;
 	int r;
 
-	zip = (struct zip *)*(a->pformat_data);
+	zip = (struct zip *)(a->format->data);
 
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (zip->uncompressed_buffer == NULL) {
@@ -529,7 +529,7 @@
 		zip->uncompressed_buffer
 		    = (unsigned char *)malloc(zip->uncompressed_buffer_size);
 		if (zip->uncompressed_buffer == NULL) {
-			archive_set_error(a, ENOMEM,
+			archive_set_error(&a->archive, ENOMEM,
 			    "No memory for ZIP decompression");
 			return (ARCHIVE_FATAL);
 		}
@@ -537,13 +537,19 @@
 
 	/* If we haven't yet read any data, initialize the decompressor. */
 	if (!zip->decompress_init) {
-		r = inflateInit2(&zip->stream,
-		    -15 /* Don't check for zlib header */);
+		if (zip->stream_valid)
+			r = inflateReset(&zip->stream);
+		else
+			r = inflateInit2(&zip->stream,
+			    -15 /* Don't check for zlib header */);
 		if (r != Z_OK) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Can't initialize ZIP decompression.");
 			return (ARCHIVE_FATAL);
 		}
+		/* Stream structure has been set up. */
+		zip->stream_valid = 1;
+		/* We've initialized decompression for this stream. */
 		zip->decompress_init = 1;
 	}
 
@@ -553,9 +559,9 @@
 	 * available bytes; asking for more than that forces the
 	 * decompressor to combine reads by copying data.
 	 */
-	bytes_avail = (a->compression_read_ahead)(a, &compressed_buff, 1);
+	bytes_avail = (a->decompressor->read_ahead)(a, &compressed_buff, 1);
 	if (bytes_avail <= 0) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file body");
 		return (ARCHIVE_FATAL);
 	}
@@ -581,18 +587,18 @@
 		zip->end_of_entry = 1;
 		break;
 	case Z_MEM_ERROR:
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Out of memory for ZIP decompression");
 		return (ARCHIVE_FATAL);
 	default:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "ZIP decompression failed (%d)", r);
 		return (ARCHIVE_FATAL);
 	}
 
 	/* Consume as much as the compressor actually used. */
 	bytes_avail = zip->stream.total_in;
-	(a->compression_read_consume)(a, bytes_avail);
+	(a->decompressor->consume)(a, bytes_avail);
 	zip->entry_bytes_remaining -= bytes_avail;
 	zip->entry_compressed_bytes_read += bytes_avail;
 
@@ -605,26 +611,26 @@
 }
 #else
 static int
-zip_read_data_deflate(struct archive *a, const void **buff,
+zip_read_data_deflate(struct archive_read *a, const void **buff,
     size_t *size, off_t *offset)
 {
 	*buff = NULL;
 	*size = 0;
 	*offset = 0;
-	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 	    "libarchive compiled without deflate support (no libz)");
 	return (ARCHIVE_FATAL);
 }
 #endif
 
 static int
-archive_read_format_zip_read_data_skip(struct archive *a)
+archive_read_format_zip_read_data_skip(struct archive_read *a)
 {
 	struct zip *zip;
 	const void *buff = NULL;
 	ssize_t bytes_avail;
 
-	zip = (struct zip *)*(a->pformat_data);
+	zip = (struct zip *)(a->format->data);
 
 	/*
 	 * If the length is at the end, we have no choice but
@@ -646,15 +652,16 @@
 	 * compressed data much more quickly.
 	 */
 	while (zip->entry_bytes_remaining > 0) {
-		bytes_avail = (a->compression_read_ahead)(a, &buff, 1);
+		bytes_avail = (a->decompressor->read_ahead)(a, &buff, 1);
 		if (bytes_avail <= 0) {
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Truncated ZIP file body");
 			return (ARCHIVE_FATAL);
 		}
 		if (bytes_avail > zip->entry_bytes_remaining)
 			bytes_avail = zip->entry_bytes_remaining;
-		(a->compression_read_consume)(a, bytes_avail);
+		(a->decompressor->consume)(a, bytes_avail);
 		zip->entry_bytes_remaining -= bytes_avail;
 	}
 	/* This entry is finished and done. */
@@ -663,17 +670,20 @@
 }
 
 static int
-archive_read_format_zip_cleanup(struct archive *a)
+archive_read_format_zip_cleanup(struct archive_read *a)
 {
 	struct zip *zip;
 
-	zip = (struct zip *)*(a->pformat_data);
-	if (zip->uncompressed_buffer != NULL)
-		free(zip->uncompressed_buffer);
+	zip = (struct zip *)(a->format->data);
+#ifdef HAVE_ZLIB_H
+	if (zip->stream_valid)
+		inflateEnd(&zip->stream);
+#endif
+	free(zip->uncompressed_buffer);
 	archive_string_free(&(zip->pathname));
 	archive_string_free(&(zip->extra));
 	free(zip);
-	*(a->pformat_data) = NULL;
+	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
 }
 
Index: archive_read.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read.c -Llib/libarchive/archive_read.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read.c
+++ lib/libarchive/archive_read.c
@@ -32,7 +32,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.15.2.6 2007/02/14 08:29:35 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.35 2007/05/29 01:00:18 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -51,9 +51,11 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
-static int	choose_decompressor(struct archive *, const void*, size_t);
-static int	choose_format(struct archive *);
+static void	choose_decompressor(struct archive_read *, const void*, size_t);
+static int	choose_format(struct archive_read *);
+static off_t	dummy_skip(struct archive_read *, off_t);
 
 /*
  * Allocate, initialize and return a struct archive object.
@@ -61,44 +63,45 @@
 struct archive *
 archive_read_new(void)
 {
-	struct archive	*a;
+	struct archive_read *a;
 	unsigned char	*nulls;
 
-	a = (struct archive *)malloc(sizeof(*a));
+	a = (struct archive_read *)malloc(sizeof(*a));
 	if (a == NULL)
 		return (NULL);
 	memset(a, 0, sizeof(*a));
-
-	a->user_uid = geteuid();
-	a->magic = ARCHIVE_READ_MAGIC;
+	a->archive.magic = ARCHIVE_READ_MAGIC;
 	a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
 
 	a->null_length = 1024;
 	nulls = (unsigned char *)malloc(a->null_length);
 	if (nulls == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate archive object 'nulls' element");
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate archive object 'nulls' element");
 		free(a);
 		return (NULL);
 	}
 	memset(nulls, 0, a->null_length);
 	a->nulls = nulls;
 
-	a->state = ARCHIVE_STATE_NEW;
+	a->archive.state = ARCHIVE_STATE_NEW;
 	a->entry = archive_entry_new();
 
 	/* We always support uncompressed archives. */
-	archive_read_support_compression_none((struct archive*)a);
+	archive_read_support_compression_none(&a->archive);
 
-	return (a);
+	return (&a->archive);
 }
 
 /*
  * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
  */
 void
-archive_read_extract_set_skip_file(struct archive *a, dev_t d, ino_t i)
+archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i)
 {
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file");
+	struct archive_read *a = (struct archive_read *)_a;
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
+	    "archive_read_extract_set_skip_file");
 	a->skip_file_dev = d;
 	a->skip_file_ino = i;
 }
@@ -119,18 +122,18 @@
 }
 
 int
-archive_read_open2(struct archive *a, void *client_data,
+archive_read_open2(struct archive *_a, void *client_data,
     archive_open_callback *client_opener,
     archive_read_callback *client_reader,
     archive_skip_callback *client_skipper,
     archive_close_callback *client_closer)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	const void *buffer;
 	ssize_t bytes_read;
-	int high_bidder;
 	int e;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open");
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open");
 
 	if (client_reader == NULL)
 		__archive_errx(1,
@@ -150,22 +153,22 @@
 
 	/* Open data source. */
 	if (client_opener != NULL) {
-		e =(client_opener)(a, client_data);
+		e =(client_opener)(&a->archive, client_data);
 		if (e != 0) {
 			/* If the open failed, call the closer to clean up. */
 			if (client_closer)
-				(client_closer)(a, client_data);
+				(client_closer)(&a->archive, client_data);
 			return (e);
 		}
 	}
 
-	/* Read first block now for format detection. */
-	bytes_read = (client_reader)(a, client_data, &buffer);
+	/* Read first block now for compress format detection. */
+	bytes_read = (client_reader)(&a->archive, client_data, &buffer);
 
 	if (bytes_read < 0) {
 		/* If the first read fails, close before returning error. */
 		if (client_closer)
-			(client_closer)(a, client_data);
+			(client_closer)(&a->archive, client_data);
 		/* client_reader should have already set error information. */
 		return (ARCHIVE_FATAL);
 	}
@@ -178,15 +181,22 @@
 	a->client_data = client_data;
 
 	/* Select a decompression routine. */
-	high_bidder = choose_decompressor(a, buffer, bytes_read);
-	if (high_bidder < 0)
+	choose_decompressor(a, buffer, (size_t)bytes_read);
+	if (a->decompressor == NULL)
 		return (ARCHIVE_FATAL);
 
 	/* Initialize decompression routine with the first block of data. */
-	e = (a->decompressors[high_bidder].init)(a, buffer, bytes_read);
+	e = (a->decompressor->init)(a, buffer, (size_t)bytes_read);
 
 	if (e == ARCHIVE_OK)
-		a->state = ARCHIVE_STATE_HEADER;
+		a->archive.state = ARCHIVE_STATE_HEADER;
+
+	/*
+	 * If the decompressor didn't register a skip function, provide a
+	 * dummy compression-layer skip function.
+	 */
+	if (a->decompressor->skip == NULL)
+		a->decompressor->skip = dummy_skip;
 
 	return (e);
 }
@@ -195,32 +205,37 @@
  * Allow each registered decompression routine to bid on whether it
  * wants to handle this stream.  Return index of winning bidder.
  */
-static int
-choose_decompressor(struct archive *a, const void *buffer, size_t bytes_read)
+static void
+choose_decompressor(struct archive_read *a,
+    const void *buffer, size_t bytes_read)
 {
-	int decompression_slots, i, bid, best_bid, best_bid_slot;
+	int decompression_slots, i, bid, best_bid;
+	struct decompressor_t *decompressor, *best_decompressor;
 
 	decompression_slots = sizeof(a->decompressors) /
 	    sizeof(a->decompressors[0]);
 
-	best_bid = -1;
-	best_bid_slot = -1;
+	best_bid = 0;
+	a->decompressor = NULL;
+	best_decompressor = NULL;
 
+	decompressor = a->decompressors;
 	for (i = 0; i < decompression_slots; i++) {
-		if (a->decompressors[i].bid) {
-			bid = (a->decompressors[i].bid)(buffer, bytes_read);
-			if ((bid > best_bid) || (best_bid_slot < 0)) {
+		if (decompressor->bid) {
+			bid = (decompressor->bid)(buffer, bytes_read);
+			if (bid > best_bid || best_decompressor == NULL) {
 				best_bid = bid;
-				best_bid_slot = i;
+				best_decompressor = decompressor;
 			}
 		}
+		decompressor ++;
 	}
 
 	/*
 	 * There were no bidders; this is a serious programmer error
 	 * and demands a quick and definitive abort.
 	 */
-	if (best_bid_slot < 0)
+	if (best_decompressor == NULL)
 		__archive_errx(1, "No decompressors were registered; you "
 		    "must call at least one "
 		    "archive_read_support_compression_XXX function in order "
@@ -231,40 +246,75 @@
 	 * support this stream.
 	 */
 	if (best_bid < 1) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Unrecognized archive format");
-		return (ARCHIVE_FATAL);
+		return;
 	}
 
-	return (best_bid_slot);
+	/* Record the best decompressor for this stream. */
+	a->decompressor = best_decompressor;
+}
+
+/*
+ * Dummy skip function, for use if the compression layer doesn't provide
+ * one: This code just reads data and discards it.
+ */
+static off_t
+dummy_skip(struct archive_read * a, off_t request)
+{
+	const void * dummy_buffer;
+	ssize_t bytes_read;
+	off_t bytes_skipped;
+
+	for (bytes_skipped = 0; request > 0;) {
+		bytes_read = (a->decompressor->read_ahead)(a, &dummy_buffer, 1);
+		if (bytes_read < 0)
+			return (bytes_read);
+		if (bytes_read == 0) {
+			/* Premature EOF. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated input file (need to skip %jd bytes)",
+			    (intmax_t)request);
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_read > request)
+			bytes_read = (ssize_t)request;
+		(a->decompressor->consume)(a, (size_t)bytes_read);
+		request -= bytes_read;
+		bytes_skipped += bytes_read;
+	}
+
+	return (bytes_skipped);
 }
 
 /*
  * Read header of next entry.
  */
 int
-archive_read_next_header(struct archive *a, struct archive_entry **entryp)
+archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct archive_entry *entry;
 	int slot, ret;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC,
-	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header");
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_next_header");
 
 	*entryp = NULL;
 	entry = a->entry;
 	archive_entry_clear(entry);
-	archive_string_empty(&a->error_string);
+	archive_clear_error(&a->archive);
 
 	/*
 	 * If client didn't consume entire data, skip any remainder
 	 * (This is especially important for GNU incremental directories.)
 	 */
-	if (a->state == ARCHIVE_STATE_DATA) {
-		ret = archive_read_data_skip(a);
+	if (a->archive.state == ARCHIVE_STATE_DATA) {
+		ret = archive_read_data_skip(&a->archive);
 		if (ret == ARCHIVE_EOF) {
-			archive_set_error(a, EIO, "Premature end-of-file.");
-			a->state = ARCHIVE_STATE_FATAL;
+			archive_set_error(&a->archive, EIO, "Premature end-of-file.");
+			a->archive.state = ARCHIVE_STATE_FATAL;
 			return (ARCHIVE_FATAL);
 		}
 		if (ret != ARCHIVE_OK)
@@ -272,15 +322,14 @@
 	}
 
 	/* Record start-of-header. */
-	a->header_position = a->file_position;
+	a->header_position = a->archive.file_position;
 
 	slot = choose_format(a);
 	if (slot < 0) {
-		a->state = ARCHIVE_STATE_FATAL;
+		a->archive.state = ARCHIVE_STATE_FATAL;
 		return (ARCHIVE_FATAL);
 	}
 	a->format = &(a->formats[slot]);
-	a->pformat_data = &(a->format->format_data);
 	ret = (a->format->read_header)(a, entry);
 
 	/*
@@ -290,18 +339,18 @@
 	 */
 	switch (ret) {
 	case ARCHIVE_EOF:
-		a->state = ARCHIVE_STATE_EOF;
+		a->archive.state = ARCHIVE_STATE_EOF;
 		break;
 	case ARCHIVE_OK:
-		a->state = ARCHIVE_STATE_DATA;
+		a->archive.state = ARCHIVE_STATE_DATA;
 		break;
 	case ARCHIVE_WARN:
-		a->state = ARCHIVE_STATE_DATA;
+		a->archive.state = ARCHIVE_STATE_DATA;
 		break;
 	case ARCHIVE_RETRY:
 		break;
 	case ARCHIVE_FATAL:
-		a->state = ARCHIVE_STATE_FATAL;
+		a->archive.state = ARCHIVE_STATE_FATAL;
 		break;
 	}
 
@@ -316,7 +365,7 @@
  * the next entry.  Return index of winning bidder.
  */
 static int
-choose_format(struct archive *a)
+choose_format(struct archive_read *a)
 {
 	int slots;
 	int i;
@@ -331,7 +380,6 @@
 	a->format = &(a->formats[0]);
 	for (i = 0; i < slots; i++, a->format++) {
 		if (a->format->bid) {
-			a->pformat_data = &(a->format->format_data);
 			bid = (a->format->bid)(a);
 			if (bid == ARCHIVE_FATAL)
 				return (ARCHIVE_FATAL);
@@ -356,7 +404,7 @@
 	 * can't support this stream.
 	 */
 	if (best_bid < 1) {
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Unrecognized archive format");
 		return (ARCHIVE_FATAL);
 	}
@@ -369,8 +417,11 @@
  * the last header started.
  */
 int64_t
-archive_read_header_position(struct archive *a)
+archive_read_header_position(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_header_position");
 	return (a->header_position);
 }
 
@@ -386,9 +437,11 @@
  * to read a single entry body.
  */
 ssize_t
-archive_read_data(struct archive *a, void *buff, size_t s)
+archive_read_data(struct archive *_a, void *buff, size_t s)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	char	*dest;
+	const void *read_buf;
 	size_t	 bytes_read;
 	size_t	 len;
 	int	 r;
@@ -397,11 +450,11 @@
 	dest = (char *)buff;
 
 	while (s > 0) {
-		if (a->read_data_remaining <= 0) {
-			r = archive_read_data_block(a,
-			    (const void **)&a->read_data_block,
-			    &a->read_data_remaining,
-			    &a->read_data_offset);
+		if (a->read_data_remaining == 0) {
+			read_buf = a->read_data_block;
+			r = archive_read_data_block(&a->archive, &read_buf,
+			    &a->read_data_remaining, &a->read_data_offset);
+			a->read_data_block = read_buf;
 			if (r == ARCHIVE_EOF)
 				return (bytes_read);
 			/*
@@ -414,7 +467,7 @@
 		}
 
 		if (a->read_data_offset < a->read_data_output_offset) {
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Encountered out-of-order sparse blocks");
 			return (ARCHIVE_RETRY);
 		}
@@ -455,23 +508,42 @@
 	return (bytes_read);
 }
 
+#if ARCHIVE_API_VERSION < 3
+/*
+ * Obsolete function provided for compatibility only.  Note that the API
+ * of this function doesn't allow the caller to detect if the remaining
+ * data from the archive entry is shorter than the buffer provided, or
+ * even if an error occurred while reading data.
+ */
+int
+archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
+{
+
+	archive_read_data(a, d, len);
+	return (ARCHIVE_OK);
+}
+#endif
+
 /*
  * Skip over all remaining data in this entry.
  */
 int
-archive_read_data_skip(struct archive *a)
+archive_read_data_skip(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	int r;
 	const void *buff;
 	size_t size;
 	off_t offset;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_skip");
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_skip");
 
 	if (a->format->read_data_skip != NULL)
 		r = (a->format->read_data_skip)(a);
 	else {
-		while ((r = archive_read_data_block(a, &buff, &size, &offset))
+		while ((r = archive_read_data_block(&a->archive,
+			    &buff, &size, &offset))
 		    == ARCHIVE_OK)
 			;
 	}
@@ -479,7 +551,7 @@
 	if (r == ARCHIVE_EOF)
 		r = ARCHIVE_OK;
 
-	a->state = ARCHIVE_STATE_HEADER;
+	a->archive.state = ARCHIVE_STATE_HEADER;
 	return (r);
 }
 
@@ -492,13 +564,15 @@
  * the end of entry is encountered.
  */
 int
-archive_read_data_block(struct archive *a,
+archive_read_data_block(struct archive *_a,
     const void **buff, size_t *size, off_t *offset)
 {
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block");
+	struct archive_read *a = (struct archive_read *)_a;
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_block");
 
 	if (a->format->read_data == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "Internal error: "
 		    "No format_read_data_block function registered");
 		return (ARCHIVE_FATAL);
@@ -515,22 +589,35 @@
  * initialization.
  */
 int
-archive_read_close(struct archive *a)
+archive_read_close(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+	size_t i, n;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close");
-	a->state = ARCHIVE_STATE_CLOSED;
+	__archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_close");
+	a->archive.state = ARCHIVE_STATE_CLOSED;
 
 	/* Call cleanup functions registered by optional components. */
 	if (a->cleanup_archive_extract != NULL)
 		r = (a->cleanup_archive_extract)(a);
 
-	/* TODO: Finish the format processing. */
+	/* TODO: Clean up the formatters. */
 
-	/* Close the input machinery. */
-	if (a->compression_finish != NULL) {
-		r1 = (a->compression_finish)(a);
+	/* Clean up the decompressors. */
+	n = sizeof(a->decompressors)/sizeof(a->decompressors[0]);
+	for (i = 0; i < n; i++) {
+		if (a->decompressors[i].finish != NULL) {
+			r1 = (a->decompressors[i].finish)(a);
+			if (r1 < r)
+				r = r1;
+		}
+	}
+
+	/* Close the client stream. */
+	if (a->client_closer != NULL) {
+		r1 = ((a->client_closer)(&a->archive, a->client_data));
 		if (r1 < r)
 			r = r1;
 	}
@@ -541,31 +628,42 @@
 /*
  * Release memory and other resources.
  */
+#if ARCHIVE_API_VERSION > 1
+int
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
 void
-archive_read_finish(struct archive *a)
+#endif
+archive_read_finish(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	int i;
 	int slots;
+	int r = ARCHIVE_OK;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_finish");
-	if (a->state != ARCHIVE_STATE_CLOSED)
-		archive_read_close(a);
+	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
+	    "archive_read_finish");
+	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+		r = archive_read_close(&a->archive);
 
 	/* Cleanup format-specific data. */
 	slots = sizeof(a->formats) / sizeof(a->formats[0]);
 	for (i = 0; i < slots; i++) {
-		a->pformat_data = &(a->formats[i].format_data);
+		a->format = &(a->formats[i]);
 		if (a->formats[i].cleanup)
 			(a->formats[i].cleanup)(a);
 	}
 
 	/* Casting a pointer to int allows us to remove 'const.' */
 	free((void *)(uintptr_t)(const void *)a->nulls);
-	archive_string_free(&a->error_string);
+	archive_string_free(&a->archive.error_string);
 	if (a->entry)
 		archive_entry_free(a->entry);
-	a->magic = 0;
+	a->archive.magic = 0;
 	free(a);
+#if ARCHIVE_API_VERSION > 1
+	return (r);
+#endif
 }
 
 /*
@@ -573,17 +671,19 @@
  * initialization functions.
  */
 int
-__archive_read_register_format(struct archive *a,
+__archive_read_register_format(struct archive_read *a,
     void *format_data,
-    int (*bid)(struct archive *),
-    int (*read_header)(struct archive *, struct archive_entry *),
-    int (*read_data)(struct archive *, const void **, size_t *, off_t *),
-    int (*read_data_skip)(struct archive *),
-    int (*cleanup)(struct archive *))
+    int (*bid)(struct archive_read *),
+    int (*read_header)(struct archive_read *, struct archive_entry *),
+    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+    int (*read_data_skip)(struct archive_read *),
+    int (*cleanup)(struct archive_read *))
 {
 	int i, number_slots;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_format");
+	__archive_check_magic(&a->archive,
+	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "__archive_read_register_format");
 
 	number_slots = sizeof(a->formats) / sizeof(a->formats[0]);
 
@@ -596,7 +696,7 @@
 			a->formats[i].read_data = read_data;
 			a->formats[i].read_data_skip = read_data_skip;
 			a->formats[i].cleanup = cleanup;
-			a->formats[i].format_data = format_data;
+			a->formats[i].data = format_data;
 			return (ARCHIVE_OK);
 		}
 	}
@@ -609,27 +709,29 @@
  * Used internally by decompression routines to register their bid and
  * initialization functions.
  */
-int
-__archive_read_register_compression(struct archive *a,
+struct decompressor_t *
+__archive_read_register_compression(struct archive_read *a,
     int (*bid)(const void *, size_t),
-    int (*init)(struct archive *, const void *, size_t))
+    int (*init)(struct archive_read *, const void *, size_t))
 {
 	int i, number_slots;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "__archive_read_register_compression");
+	__archive_check_magic(&a->archive,
+	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "__archive_read_register_compression");
 
 	number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]);
 
 	for (i = 0; i < number_slots; i++) {
 		if (a->decompressors[i].bid == bid)
-			return (ARCHIVE_OK); /* We've already installed */
+			return (a->decompressors + i);
 		if (a->decompressors[i].bid == NULL) {
 			a->decompressors[i].bid = bid;
 			a->decompressors[i].init = init;
-			return (ARCHIVE_OK);
+			return (a->decompressors + i);
 		}
 	}
 
 	__archive_errx(1, "Not enough slots for compression registration");
-	return (ARCHIVE_FATAL); /* Never actually executed. */
+	return (NULL); /* Never actually executed. */
 }
Index: archive_private.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_private.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_private.h -Llib/libarchive/archive_private.h -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_private.h
+++ lib/libarchive/archive_private.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.18.2.4 2007/01/27 06:44:52 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_private.h,v 1.29 2007/04/02 00:15:45 kientzle Exp $
  */
 
 #ifndef ARCHIVE_PRIVATE_H_INCLUDED
@@ -33,6 +33,28 @@
 
 #define	ARCHIVE_WRITE_MAGIC	(0xb0c5c0deU)
 #define	ARCHIVE_READ_MAGIC	(0xdeb0c5U)
+#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
+
+#define	ARCHIVE_STATE_ANY	0xFFFFU
+#define	ARCHIVE_STATE_NEW	1U
+#define	ARCHIVE_STATE_HEADER	2U
+#define	ARCHIVE_STATE_DATA	4U
+#define ARCHIVE_STATE_DATA_END	8U
+#define	ARCHIVE_STATE_EOF	0x10U
+#define	ARCHIVE_STATE_CLOSED	0x20U
+#define	ARCHIVE_STATE_FATAL	0x8000U
+
+struct archive_vtable {
+	int	(*archive_write_close)(struct archive *);
+	int	(*archive_write_finish)(struct archive *);
+	int	(*archive_write_header)(struct archive *,
+	    struct archive_entry *);
+	int	(*archive_write_finish_entry)(struct archive *);
+	ssize_t	(*archive_write_data)(struct archive *,
+	    const void *, size_t);
+	ssize_t	(*archive_write_data_block)(struct archive *,
+	    const void *, size_t, off_t);
+};
 
 struct archive {
 	/*
@@ -41,205 +63,37 @@
 	 * ridiculous time, or the client passes us an invalid
 	 * pointer, these values allow me to catch that.
 	 */
-	unsigned	  magic;
-	unsigned	  state;
-
-	struct archive_entry	*entry;
-	uid_t		  user_uid;	/* UID of current user. */
-
-	/* Dev/ino of the archive being read/written. */
-	dev_t		  skip_file_dev;
-	ino_t		  skip_file_ino;
-
-	/* Utility:  Pointer to a block of nulls. */
-	const unsigned char	*nulls;
-	size_t			 null_length;
+	unsigned int	magic;
+	unsigned int	state;
 
 	/*
-	 * Used by archive_read_data() to track blocks and copy
-	 * data to client buffers, filling gaps with zero bytes.
+	 * Some public API functions depend on the "real" type of the
+	 * archive object.
 	 */
-	const char	 *read_data_block;
-	off_t		  read_data_offset;
-	off_t		  read_data_output_offset;
-	size_t		  read_data_remaining;
-
-	/* Callbacks to open/read/write/close archive stream. */
-	archive_open_callback	*client_opener;
-	archive_read_callback	*client_reader;
-	archive_skip_callback	*client_skipper;
-	archive_write_callback	*client_writer;
-	archive_close_callback	*client_closer;
-	void			*client_data;
+	struct archive_vtable *vtable;
 
-	/*
-	 * Blocking information.  Note that bytes_in_last_block is
-	 * misleadingly named; I should find a better name.  These
-	 * control the final output from all compressors, including
-	 * compression_none.
-	 */
-	int		  bytes_per_block;
-	int		  bytes_in_last_block;
+	int		  archive_format;
+	const char	 *archive_format_name;
 
-	/*
-	 * These control whether data within a gzip/bzip2 compressed
-	 * stream gets padded or not.  If pad_uncompressed is set,
-	 * the data will be padded to a full block before being
-	 * compressed.  The pad_uncompressed_byte determines the value
-	 * that will be used for padding.  Note that these have no
-	 * effect on compression "none."
-	 */
-	int		  pad_uncompressed;
-	int		  pad_uncompressed_byte; /* TODO: Support this. */
+	int	  compression_code;	/* Currently active compression. */
+	const char *compression_name;
 
 	/* Position in UNCOMPRESSED data stream. */
 	off_t		  file_position;
 	/* Position in COMPRESSED data stream. */
 	off_t		  raw_position;
-	/* File offset of beginning of most recently-read header. */
-	off_t		  header_position;
-
-	/*
-	 * Detection functions for decompression: bid functions are
-	 * given a block of data from the beginning of the stream and
-	 * can bid on whether or not they support the data stream.
-	 * General guideline: bid the number of bits that you actually
-	 * test, e.g., 16 if you test a 2-byte magic value.  The
-	 * highest bidder will have their init function invoked, which
-	 * can set up pointers to specific handlers.
-	 *
-	 * On write, the client just invokes an archive_write_set function
-	 * which sets up the data here directly.
-	 */
-	int	  compression_code;	/* Currently active compression. */
-	const char *compression_name;
-	struct {
-		int	(*bid)(const void *buff, size_t);
-		int	(*init)(struct archive *, const void *buff, size_t);
-	}	decompressors[4];
-	/* Read/write data stream (with compression). */
-	void	 *compression_data;		/* Data for (de)compressor. */
-	int	(*compression_init)(struct archive *);	/* Initialize. */
-	int	(*compression_finish)(struct archive *);
-	int	(*compression_write)(struct archive *, const void *, size_t);
-	/*
-	 * Read uses a peek/consume I/O model: the decompression code
-	 * returns a pointer to the requested block and advances the
-	 * file position only when requested by a consume call.  This
-	 * reduces copying and also simplifies look-ahead for format
-	 * detection.
-	 */
-	ssize_t	(*compression_read_ahead)(struct archive *,
-		    const void **, size_t request);
-	ssize_t	(*compression_read_consume)(struct archive *, size_t);
-	off_t (*compression_skip)(struct archive *, off_t);
-
-	/*
-	 * Format detection is mostly the same as compression
-	 * detection, with two significant differences: The bidders
-	 * use the read_ahead calls above to examine the stream rather
-	 * than having the supervisor hand them a block of data to
-	 * examine, and the auction is repeated for every header.
-	 * Winning bidders should set the archive_format and
-	 * archive_format_name appropriately.  Bid routines should
-	 * check archive_format and decline to bid if the format of
-	 * the last header was incompatible.
-	 *
-	 * Again, write support is considerably simpler because there's
-	 * no need for an auction.
-	 */
-	int		  archive_format;
-	const char	 *archive_format_name;
-
-	struct archive_format_descriptor {
-		int	(*bid)(struct archive *);
-		int	(*read_header)(struct archive *, struct archive_entry *);
-		int	(*read_data)(struct archive *, const void **, size_t *, off_t *);
-		int	(*read_data_skip)(struct archive *);
-		int	(*cleanup)(struct archive *);
-		void	 *format_data;	/* Format-specific data for readers. */
-	}	formats[8];
-	struct archive_format_descriptor	*format; /* Active format. */
-
-	/*
-	 * Storage for format-specific data.  Note that there can be
-	 * multiple format readers active at one time, so we need to
-	 * allow for multiple format readers to have their data
-	 * available.  The pformat_data slot here is the solution: on
-	 * read, it is guaranteed to always point to a void* variable
-	 * that the format can use.
-	 */
-	void	**pformat_data;		/* Pointer to current format_data. */
-	void	 *format_data;		/* Used by writers. */
-
-	/*
-	 * Pointers to format-specific functions for writing.  They're
-	 * initialized by archive_write_set_format_XXX() calls.
-	 */
-	int	(*format_init)(struct archive *); /* Only used on write. */
-	int	(*format_finish)(struct archive *);
-	int	(*format_finish_entry)(struct archive *);
-	int 	(*format_write_header)(struct archive *,
-		    struct archive_entry *);
-	ssize_t	(*format_write_data)(struct archive *,
-		    const void *buff, size_t);
-
-	/*
-	 * Various information needed by archive_extract.
-	 */
-	struct extract		 *extract;
-	void			(*extract_progress)(void *);
-	void			 *extract_progress_user_data;
-	int			(*cleanup_archive_extract)(struct archive *);
 
 	int		  archive_error_number;
 	const char	 *error;
 	struct archive_string	error_string;
 };
 
-
-#define	ARCHIVE_STATE_ANY	0xFFFFU
-#define	ARCHIVE_STATE_NEW	1U
-#define	ARCHIVE_STATE_HEADER	2U
-#define	ARCHIVE_STATE_DATA	4U
-#define	ARCHIVE_STATE_EOF	8U
-#define	ARCHIVE_STATE_CLOSED	0x10U
-#define	ARCHIVE_STATE_FATAL	0x8000U
-
 /* Check magic value and state; exit if it isn't valid. */
-void	__archive_check_magic(struct archive *, unsigned magic,
-	    unsigned state, const char *func);
-
-
-int	__archive_read_register_format(struct archive *a,
-	    void *format_data,
-	    int (*bid)(struct archive *),
-	    int (*read_header)(struct archive *, struct archive_entry *),
-	    int (*read_data)(struct archive *, const void **, size_t *, off_t *),
-	    int (*read_data_skip)(struct archive *),
-	    int (*cleanup)(struct archive *));
-
-int	__archive_read_register_compression(struct archive *a,
-	    int (*bid)(const void *, size_t),
-	    int (*init)(struct archive *, const void *,	size_t));
+void	__archive_check_magic(struct archive *, unsigned int magic,
+	    unsigned int state, const char *func);
 
 void	__archive_errx(int retvalue, const char *msg);
 
 #define	err_combine(a,b)	((a) < (b) ? (a) : (b))
 
-
-/*
- * Utility function to format a USTAR header into a buffer.  If
- * "strict" is set, this tries to create the absolutely most portable
- * version of a ustar header.  If "strict" is set to 0, then it will
- * relax certain requirements.
- *
- * Generally, format-specific declarations don't belong in this
- * header; this is a rare example of a function that is shared by
- * two very similar formats (ustar and pax).
- */
-int
-__archive_write_format_header_ustar(struct archive *, char buff[512],
-    struct archive_entry *, int tartype, int strict);
-
 #endif
Index: archive_write_set_format.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format.c -Llib/libarchive/archive_write_set_format.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format.c
+++ lib/libarchive/archive_write_set_format.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.2.8.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format.c,v 1.5 2007/06/22 05:47:00 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -42,6 +42,7 @@
 struct { int code; int (*setter)(struct archive *); } codes[] =
 {
 	{ ARCHIVE_FORMAT_CPIO,		archive_write_set_format_cpio },
+	{ ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,	archive_write_set_format_cpio_newc },
 	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio },
 	{ ARCHIVE_FORMAT_SHAR,		archive_write_set_format_shar },
 	{ ARCHIVE_FORMAT_SHAR_BASE,	archive_write_set_format_shar },
--- /dev/null
+++ lib/libarchive/archive_entry_stat.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_stat.c,v 1.1 2007/05/29 01:00:18 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+	struct stat *st;
+	if (entry->stat == NULL) {
+		entry->stat = malloc(sizeof(*st));
+		if (entry->stat == NULL)
+			return (NULL);
+		entry->stat_valid = 0;
+	}
+
+	/*
+	 * If none of the underlying fields have been changed, we
+	 * don't need to regenerate.  In theory, we could use a bitmap
+	 * here to flag only those items that have changed, but the
+	 * extra complexity probably isn't worth it.  It will be very
+	 * rare for anyone to change just one field then request a new
+	 * stat structure.
+	 */
+	if (entry->stat_valid)
+		return (entry->stat);
+
+	st = entry->stat;
+	/*
+	 * Use the public interfaces to extract items, so that
+	 * the appropriate conversions get invoked.
+	 */
+	st->st_atime = archive_entry_atime(entry);
+	st->st_ctime = archive_entry_ctime(entry);
+	st->st_mtime = archive_entry_mtime(entry);
+	st->st_dev = archive_entry_dev(entry);
+	st->st_gid = archive_entry_gid(entry);
+	st->st_uid = archive_entry_uid(entry);
+	st->st_ino = archive_entry_ino(entry);
+	st->st_nlink = archive_entry_nlink(entry);
+	st->st_rdev = archive_entry_rdev(entry);
+	st->st_size = archive_entry_size(entry);
+	st->st_mode = archive_entry_mode(entry);
+
+	/*
+	 * On systems that support high-res timestamps, copy that
+	 * information into struct stat.
+	 */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
+	st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
+	st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
+	st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
+	st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
+#endif
+
+	/*
+	 * TODO: On Linux, store 32 or 64 here depending on whether
+	 * the cached stat structure is a stat32 or a stat64.  This
+	 * will allow us to support both variants interchangably.
+	 */
+	entry->stat_valid = 1;
+
+	return (st);
+}
--- /dev/null
+++ lib/libarchive/archive_write_disk.c
@@ -0,0 +1,2098 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk.c,v 1.17 2007/09/21 04:52:42 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_EXT2FS_EXT2_FS_H
+#include <ext2fs/ext2_fs.h>	/* for Linux file flags */
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>	/* for Linux file flags */
+#endif
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>	/* for Linux file flags */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+struct fixup_entry {
+	struct fixup_entry	*next;
+	mode_t			 mode;
+	int64_t			 mtime;
+	int64_t			 atime;
+	unsigned long		 mtime_nanos;
+	unsigned long		 atime_nanos;
+	unsigned long		 fflags_set;
+	int			 fixup; /* bitmask of what needs fixing */
+	char			*name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file.  In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another.  For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot.  This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink.  Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define	TODO_MODE_FORCE		0x40000000
+#define	TODO_MODE_BASE		0x20000000
+#define	TODO_SUID		0x10000000
+#define	TODO_SUID_CHECK		0x08000000
+#define	TODO_SGID		0x04000000
+#define	TODO_SGID_CHECK		0x02000000
+#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME
+#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER
+#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS
+#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL
+#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR
+
+struct archive_write_disk {
+	struct archive	archive;
+
+	mode_t			 user_umask;
+	struct fixup_entry	*fixup_list;
+	struct fixup_entry	*current_fixup;
+	uid_t			 user_uid;
+	dev_t			 skip_file_dev;
+	ino_t			 skip_file_ino;
+
+	gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid);
+	void  (*cleanup_gid)(void *private);
+	void			*lookup_gid_data;
+	uid_t (*lookup_uid)(void *private, const char *gname, gid_t gid);
+	void  (*cleanup_uid)(void *private);
+	void			*lookup_uid_data;
+
+	/*
+	 * Full path of last file to satisfy symlink checks.
+	 */
+	struct archive_string	path_safe;
+
+	/*
+	 * Cached stat data from disk for the current entry.
+	 * If this is valid, pst points to st.  Otherwise,
+	 * pst is null.
+	 */
+	struct stat		 st;
+	struct stat		*pst;
+
+	/* Information about the object being restored right now. */
+	struct archive_entry	*entry; /* Entry being extracted. */
+	char			*name; /* Name of entry, possibly edited. */
+	struct archive_string	 _name_data; /* backing store for 'name' */
+	/* Tasks remaining for this object. */
+	int			 todo;
+	/* Tasks deferred until end-of-archive. */
+	int			 deferred;
+	/* Options requested by the client. */
+	int			 flags;
+	/* Handle for the file we're restoring. */
+	int			 fd;
+	/* Current offset for writing data to the file. */
+	off_t			 offset;
+	/* 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. */
+	mode_t			 mode;
+	/* UID/GID to use in restoring this entry. */
+	uid_t			 uid;
+	gid_t			 gid;
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicity-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define	DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps:  During the extraction, the permissions
+ * in the archive are modified to match the following limits.  During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define	MINIMUM_DIR_MODE 0700
+#define	MAXIMUM_DIR_MODE 0775
+
+static int	check_symlinks(struct archive_write_disk *);
+static int	create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname);
+#ifdef HAVE_FCHDIR
+static void	edit_deep_directories(struct archive_write_disk *ad);
+#endif
+static int	cleanup_pathname(struct archive_write_disk *);
+static int	create_dir(struct archive_write_disk *, char *);
+static int	create_parent_dir(struct archive_write_disk *, char *);
+static int	older(struct stat *, struct archive_entry *);
+static int	restore_entry(struct archive_write_disk *);
+#ifdef HAVE_POSIX_ACL
+static int	set_acl(struct archive_write_disk *, int fd, struct archive_entry *,
+		    acl_type_t, int archive_entry_acl_type, const char *tn);
+#endif
+static int	set_acls(struct archive_write_disk *);
+static int	set_xattrs(struct archive_write_disk *);
+static int	set_fflags(struct archive_write_disk *);
+static int	set_fflags_platform(struct archive_write_disk *, int fd,
+		    const char *name, mode_t mode,
+		    unsigned long fflags_set, unsigned long fflags_clear);
+static int	set_ownership(struct archive_write_disk *);
+static int	set_mode(struct archive_write_disk *, int mode);
+static int	set_time(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static gid_t	trivial_lookup_gid(void *, const char *, gid_t);
+static uid_t	trivial_lookup_uid(void *, const char *, uid_t);
+
+
+static struct archive_vtable *archive_write_disk_vtable(void);
+
+static int	_archive_write_close(struct archive *);
+static int	_archive_write_finish(struct archive *);
+static int	_archive_write_header(struct archive *, struct archive_entry *);
+static int	_archive_write_finish_entry(struct archive *);
+static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
+static ssize_t	_archive_write_data_block(struct archive *, const void *, size_t, off_t);
+
+static struct archive_vtable *
+archive_write_disk_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_write_close = _archive_write_close;
+		av.archive_write_finish = _archive_write_finish;
+		av.archive_write_header = _archive_write_header;
+		av.archive_write_finish_entry = _archive_write_finish_entry;
+		av.archive_write_data = _archive_write_data;
+		av.archive_write_data_block = _archive_write_data_block;
+	}
+	return (&av);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+	a->flags = flags;
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks.  According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore.  I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_header(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *fe;
+	int ret, r;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_header");
+	archive_clear_error(&a->archive);
+	if (a->archive.state & ARCHIVE_STATE_DATA) {
+		r = _archive_write_finish_entry(&a->archive);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	/* Set up for this particular entry. */
+	a->pst = NULL;
+	a->current_fixup = NULL;
+	a->deferred = 0;
+	if (a->entry) {
+		archive_entry_free(a->entry);
+		a->entry = NULL;
+	}
+	a->entry = archive_entry_clone(entry);
+	a->fd = -1;
+	a->offset = 0;
+	a->uid = a->user_uid;
+	a->mode = archive_entry_mode(a->entry);
+	archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
+	a->name = a->_name_data.s;
+	archive_clear_error(&a->archive);
+
+	/*
+	 * Clean up the requested path.  This is necessary for correct
+	 * dir restores; the dir restore logic otherwise gets messed
+	 * up by nonsense like "dir/.".
+	 */
+	ret = cleanup_pathname(a);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	/*
+	 * Set the umask to zero so we get predictable mode settings.
+	 * This gets done on every call to _write_header in case the
+	 * user edits their umask during the extraction for some
+	 * reason. This will be reset before we return.  Note that we
+	 * don't need to do this in _finish_entry, as the chmod(), etc,
+	 * system calls don't obey umask.
+	 */
+	a->user_umask = umask(0);
+	/* From here on, early exit requires "goto done" to clean up. */
+
+	/* Figure out what we need to do for this entry. */
+	a->todo = TODO_MODE_BASE;
+	if (a->flags & ARCHIVE_EXTRACT_PERM) {
+		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+		/*
+		 * SGID requires an extra "check" step because we
+		 * cannot easily predict the GID that the system will
+		 * assign.  (Different systems assign GIDs to files
+		 * based on a variety of criteria, including process
+		 * credentials and the gid of the enclosing
+		 * directory.)  We can only restore the SGID bit if
+		 * the file has the right GID, and we only know the
+		 * GID if we either set it (see set_ownership) or if
+		 * we've actually called stat() on the file after it
+		 * was restored.  Since there are several places at
+		 * which we might verify the GID, we need a TODO bit
+		 * to keep track.
+		 */
+		if (a->mode & S_ISGID)
+			a->todo |= TODO_SGID | TODO_SGID_CHECK;
+		/*
+		 * Verifying the SUID is simpler, but can still be
+		 * done in multiple ways, hence the separate "check" bit.
+		 */
+		if (a->mode & S_ISUID)
+			a->todo |= TODO_SUID | TODO_SUID_CHECK;
+	} else {
+		/*
+		 * User didn't request full permissions, so don't
+		 * restore SUID, SGID bits and obey umask.
+		 */
+		a->mode &= ~S_ISUID;
+		a->mode &= ~S_ISGID;
+		a->mode &= ~S_ISVTX;
+		a->mode &= ~a->user_umask;
+	}
+	if (a->flags & ARCHIVE_EXTRACT_OWNER)
+		a->todo |= TODO_OWNER;
+	if (a->flags & ARCHIVE_EXTRACT_TIME)
+		a->todo |= TODO_TIMES;
+	if (a->flags & ARCHIVE_EXTRACT_ACL)
+		a->todo |= TODO_ACLS;
+	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+		a->todo |= TODO_FFLAGS;
+	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+		ret = check_symlinks(a);
+		if (ret != ARCHIVE_OK)
+			goto done;
+	}
+#ifdef HAVE_FCHDIR
+	/* If path exceeds PATH_MAX, shorten the path. */
+	edit_deep_directories(a);
+#endif
+
+	ret = restore_entry(a);
+
+#ifdef HAVE_FCHDIR
+	/* If we changed directory above, restore it here. */
+	if (a->restore_pwd >= 0) {
+		fchdir(a->restore_pwd);
+		close(a->restore_pwd);
+		a->restore_pwd = -1;
+	}
+#endif
+
+	/*
+	 * Fixup uses the unedited pathname from archive_entry_pathname(),
+	 * because it is relative to the base dir and the edited path
+	 * might be relative to some intermediate dir as a result of the
+	 * deep restore logic.
+	 */
+	if (a->deferred & TODO_MODE) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		fe->fixup |= TODO_MODE_BASE;
+		fe->mode = a->mode;
+	}
+
+	if (a->deferred & TODO_TIMES) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		fe->fixup |= TODO_TIMES;
+		fe->mtime = archive_entry_mtime(entry);
+		fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+		fe->atime = archive_entry_atime(entry);
+		fe->atime_nanos = archive_entry_atime_nsec(entry);
+	}
+
+	if (a->deferred & TODO_FFLAGS) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		fe->fixup |= TODO_FFLAGS;
+		/* TODO: Complete this.. defer fflags from below. */
+	}
+
+	/* We've created the object and are ready to pour data into it. */
+	if (ret == ARCHIVE_OK)
+		a->archive.state = ARCHIVE_STATE_DATA;
+done:
+	/* Restore the user's umask before returning. */
+	umask(a->user_umask);
+
+	return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, dev_t d, ino_t i)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+	a->skip_file_dev = d;
+	a->skip_file_ino = i;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+_archive_write_data_block(struct archive *_a,
+    const void *buff, size_t size, off_t offset)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	ssize_t bytes_written = 0;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_disk_block");
+	if (a->fd < 0) {
+		archive_set_error(&a->archive, 0, "File not open");
+		return (ARCHIVE_WARN);
+	}
+	archive_clear_error(&a->archive);
+
+	/* Seek if necessary to the specified offset. */
+	if (offset != a->offset) {
+		if (lseek(a->fd, offset, SEEK_SET) < 0) {
+			archive_set_error(&a->archive, errno, "Seek failed");
+			return (ARCHIVE_WARN);
+		}
+		a->offset = offset;
+	}
+
+	/* Write the data. */
+	while (size > 0) {
+		bytes_written = write(a->fd, buff, size);
+		if (bytes_written < 0) {
+			archive_set_error(&a->archive, errno, "Write failed");
+			return (ARCHIVE_WARN);
+		}
+		size -= bytes_written;
+		a->offset += bytes_written;
+	}
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+_archive_write_data(struct archive *_a, const void *buff, size_t size)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	int r;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data");
+	if (a->fd < 0)
+		return (ARCHIVE_OK);
+
+	r = _archive_write_data_block(_a, buff, size, a->offset);
+	if (r < ARCHIVE_OK)
+		return (r);
+	return (size);
+}
+
+static int
+_archive_write_finish_entry(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	int ret = ARCHIVE_OK;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_finish_entry");
+	if (a->archive.state & ARCHIVE_STATE_HEADER)
+		return (ARCHIVE_OK);
+	archive_clear_error(&a->archive);
+
+	/* Restore metadata. */
+
+	/*
+	 * Look up the "real" UID only if we're going to need it.  We
+	 * need this for TODO_SGID because chown() requires both.
+	 */
+	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+		a->uid = a->lookup_uid(a->lookup_uid_data,
+		    archive_entry_uname(a->entry),
+		    archive_entry_uid(a->entry));
+	}
+	/* Look up the "real" GID only if we're going to need it. */
+	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+		a->gid = a->lookup_gid(a->lookup_gid_data,
+		    archive_entry_gname(a->entry),
+		    archive_entry_gid(a->entry));
+	 }
+	/*
+	 * If restoring ownership, do it before trying to restore suid/sgid
+	 * bits.  If we set the owner, we know what it is and can skip
+	 * a stat() call to examine the ownership of the file on disk.
+	 */
+	if (a->todo & TODO_OWNER)
+		ret = set_ownership(a);
+	if (a->todo & TODO_MODE) {
+		int r2 = set_mode(a, a->mode);
+		if (r2 < ret) ret = r2;
+	}
+	if (a->todo & TODO_TIMES) {
+		int r2 = set_time(a);
+		if (r2 < ret) ret = r2;
+	}
+	if (a->todo & TODO_ACLS) {
+		int r2 = set_acls(a);
+		if (r2 < ret) ret = r2;
+	}
+	if (a->todo & TODO_XATTR) {
+		int r2 = set_xattrs(a);
+		if (r2 < ret) ret = r2;
+	}
+	if (a->todo & TODO_FFLAGS) {
+		int r2 = set_fflags(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/* If there's an fd, we can close it now. */
+	if (a->fd >= 0) {
+		close(a->fd);
+		a->fd = -1;
+	}
+	/* If there's an entry, we can release it now. */
+	if (a->entry) {
+		archive_entry_free(a->entry);
+		a->entry = NULL;
+	}
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+    void *private_data,
+    gid_t (*lookup_gid)(void *private, const char *gname, gid_t gid),
+    void (*cleanup_gid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+	a->lookup_gid = lookup_gid;
+	a->cleanup_gid = cleanup_gid;
+	a->lookup_gid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+    void *private_data,
+    uid_t (*lookup_uid)(void *private, const char *uname, uid_t uid),
+    void (*cleanup_uid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+	a->lookup_uid = lookup_uid;
+	a->cleanup_uid = cleanup_uid;
+	a->lookup_uid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+	struct archive_write_disk *a;
+
+	a = (struct archive_write_disk *)malloc(sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	memset(a, 0, sizeof(*a));
+	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+	/* We're ready to write a header immediately. */
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	a->archive.vtable = archive_write_disk_vtable();
+	a->lookup_uid = trivial_lookup_uid;
+	a->lookup_gid = trivial_lookup_gid;
+	a->user_uid = geteuid();
+	if (archive_string_ensure(&a->path_safe, 512) == NULL) {
+		free(a);
+		return (NULL);
+	}
+	return (&a->archive);
+}
+
+
+/*
+ * If pathname is longer than PATH_MAX, chdir to a suitable
+ * intermediate dir and edit the path down to a shorter suffix.  Note
+ * that this routine never returns an error; if the chdir() attempt
+ * fails for any reason, we just go ahead with the long pathname.  The
+ * object creation is likely to fail, but any error will get handled
+ * at that time.
+ */
+#ifdef HAVE_FCHDIR
+static void
+edit_deep_directories(struct archive_write_disk *a)
+{
+	int ret;
+	char *tail = a->name;
+
+	a->restore_pwd = -1;
+
+	/* If path is short, avoid the open() below. */
+	if (strlen(tail) <= PATH_MAX)
+		return;
+
+	/* Try to record our starting dir. */
+	a->restore_pwd = open(".", O_RDONLY);
+	if (a->restore_pwd < 0)
+		return;
+
+	/* As long as the path is too long... */
+	while (strlen(tail) > PATH_MAX) {
+		/* Locate a dir prefix shorter than PATH_MAX. */
+		tail += PATH_MAX - 8;
+		while (tail > a->name && *tail != '/')
+			tail--;
+		/* Exit if we find a too-long path component. */
+		if (tail <= a->name)
+			return;
+		/* Create the intermediate dir and chdir to it. */
+		*tail = '\0'; /* Terminate dir portion */
+		ret = create_dir(a, a->name);
+		if (ret == ARCHIVE_OK && chdir(a->name) != 0)
+			ret = ARCHIVE_WARN;
+		*tail = '/'; /* Restore the / we removed. */
+		if (ret != ARCHIVE_OK)
+			return;
+		tail++;
+		/* The chdir() succeeded; we've now shortened the path. */
+		a->name = tail;
+	}
+	return;
+}
+#endif
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+	int ret = ARCHIVE_OK, en;
+
+	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+		if (unlink(a->name) == 0) {
+			/* We removed it, we're done. */
+		} else if (errno == ENOENT) {
+			/* File didn't exist, that's just as good. */
+		} else if (rmdir(a->name) == 0) {
+			/* It was a dir, but now it's gone. */
+		} else {
+			/* We tried, but couldn't get rid of it. */
+			archive_set_error(&a->archive, errno,
+			    "Could not unlink");
+			return(ARCHIVE_WARN);
+		}
+	}
+
+	/* Try creating it first; if this fails, we'll try to recover. */
+	en = create_filesystem_object(a);
+
+	if ((en == ENOTDIR || en == ENOENT)
+	    && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+		/* If the parent dir doesn't exist, try creating it. */
+		create_parent_dir(a, a->name);
+		/* Now try to create the object again. */
+		en = create_filesystem_object(a);
+	}
+
+	if ((en == EISDIR || en == EEXIST)
+	    && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+		/* If we're not overwriting, we're done. */
+		archive_set_error(&a->archive, en, "Already exists");
+		return (ARCHIVE_WARN);
+	}
+
+	/*
+	 * Some platforms return EISDIR if you call
+	 * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+	 * return EEXIST.  POSIX is ambiguous, requiring EISDIR
+	 * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+	 * on an existing item.
+	 */
+	if (en == EISDIR) {
+		/* A dir is in the way of a non-dir, rmdir it. */
+		if (rmdir(a->name) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't remove already-existing dir");
+			return (ARCHIVE_WARN);
+		}
+		/* Try again. */
+		en = create_filesystem_object(a);
+	} else if (en == EEXIST) {
+		/*
+		 * We know something is in the way, but we don't know what;
+		 * we need to find out before we go any further.
+		 */
+		if (lstat(a->name, &a->st) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't stat existing object");
+			return (ARCHIVE_WARN);
+		}
+
+		/* TODO: if it's a symlink... */
+
+		if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) {
+			if (!older(&(a->st), a->entry)) {
+				archive_set_error(&a->archive, 0,
+				    "File on disk is not older; skipping.");
+				return (ARCHIVE_FAILED);
+			}
+		}
+
+		/* If it's our archive, we're done. */
+		if (a->skip_file_dev > 0 &&
+		    a->skip_file_ino > 0 &&
+		    a->st.st_dev == a->skip_file_dev &&
+		    a->st.st_ino == a->skip_file_ino) {
+			archive_set_error(&a->archive, 0, "Refusing to overwrite archive");
+			return (ARCHIVE_FAILED);
+		}
+
+		if (!S_ISDIR(a->st.st_mode)) {
+			/* A non-dir is in the way, unlink it. */
+			if (unlink(a->name) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't unlink already-existing object");
+				return (ARCHIVE_WARN);
+			}
+			/* Try again. */
+			en = create_filesystem_object(a);
+		} else if (!S_ISDIR(a->mode)) {
+			/* A dir is in the way of a non-dir, rmdir it. */
+			if (rmdir(a->name) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't remove already-existing dir");
+				return (ARCHIVE_WARN);
+			}
+			/* Try again. */
+			en = create_filesystem_object(a);
+		} else {
+			/*
+			 * There's a dir in the way of a dir.  Don't
+			 * waste time with rmdir()/mkdir(), just fix
+			 * up the permissions on the existing dir.
+			 * Note that we don't change perms on existing
+			 * dirs unless _EXTRACT_PERM is specified.
+			 */
+			if ((a->mode != a->st.st_mode)
+			    && (a->todo & TODO_MODE_FORCE))
+				a->deferred |= (a->todo & TODO_MODE);
+			/* Ownership doesn't need deferred fixup. */
+			en = 0; /* Forget the EEXIST. */
+		}
+	}
+
+	if (en) {
+		/* Everything failed; give up here. */
+		archive_set_error(&a->archive, en, "Can't create '%s'", a->name);
+		return (ARCHIVE_WARN);
+	}
+
+	a->pst = NULL; /* Cached stat data no longer valid. */
+	return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call.   Note:  This function should only ever perform
+ * a single system call.
+ */
+int
+create_filesystem_object(struct archive_write_disk *a)
+{
+	/* Create the entry. */
+	const char *linkname;
+	mode_t final_mode, mode;
+	int r;
+
+	/* 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;
+	linkname = archive_entry_symlink(a->entry);
+	if (linkname != NULL)
+		return symlink(linkname, a->name) ? errno : 0;
+
+	/*
+	 * The remaining system calls all set permissions, so let's
+	 * try to take advantage of that to avoid an extra chmod()
+	 * call.  (Recall that umask is set to zero right now!)
+	 */
+
+	/* Mode we want for the final restored object (w/o file type bits). */
+	final_mode = a->mode & 07777;
+	/*
+	 * The mode that will actually be restored in this step.  Note
+	 * that SUID, SGID, etc, require additional work to ensure
+	 * security, so we never restore them at this point.
+	 */
+	mode = final_mode & 0777;
+
+	switch (a->mode & S_IFMT) {
+	default:
+		/* POSIX requires that we fall through here. */
+		/* FALLTHROUGH */
+	case S_IFREG:
+		a->fd = open(a->name,
+		    O_WRONLY | O_CREAT | O_EXCL, mode);
+		r = (a->fd < 0);
+		break;
+	case S_IFCHR:
+		r = mknod(a->name, mode | S_IFCHR,
+		    archive_entry_rdev(a->entry));
+		break;
+	case S_IFBLK:
+		r = mknod(a->name, mode | S_IFBLK,
+		    archive_entry_rdev(a->entry));
+		break;
+	case S_IFDIR:
+		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+		r = mkdir(a->name, mode);
+		if (r == 0) {
+			/* Defer setting dir times. */
+			a->deferred |= (a->todo & TODO_TIMES);
+			a->todo &= ~TODO_TIMES;
+			/* Never use an immediate chmod(). */
+			if (mode != final_mode)
+				a->deferred |= (a->todo & TODO_MODE);
+			a->todo &= ~TODO_MODE;
+		}
+		break;
+	case S_IFIFO:
+		r = mkfifo(a->name, mode);
+		break;
+	}
+
+	/* All the system calls above set errno on failure. */
+	if (r)
+		return (errno);
+
+	/* If we managed to set the final mode, we've avoided a chmod(). */
+	if (mode == final_mode)
+		a->todo &= ~TODO_MODE;
+	return (0);
+}
+
+/*
+ * Cleanup function for archive_extract.  Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ *   * Dir permissions might prevent us from restoring a file in that
+ *     dir, so we restore the dir with minimum 0700 permissions first,
+ *     then correct the mode at the end.
+ *   * Similarly, the act of restoring a file touches the directory
+ *     and changes the timestamp on the dir, so we have to touch-up dir
+ *     timestamps at the end as well.
+ *   * Some file flags can interfere with the restore by, for example,
+ *     preventing the creation of hardlinks to those files.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_close(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *next, *p;
+	int ret;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_close");
+	ret = _archive_write_finish_entry(&a->archive);
+
+	/* Sort dir list so directories are fixed up in depth-first order. */
+	p = sort_dir_list(a->fixup_list);
+
+	while (p != NULL) {
+		a->pst = NULL; /* Mark stat cache as out-of-date. */
+		if (p->fixup & TODO_TIMES) {
+#ifdef HAVE_UTIMES
+			/* {f,l,}utimes() are preferred, when available. */
+			struct timeval times[2];
+			times[1].tv_sec = p->mtime;
+			times[1].tv_usec = p->mtime_nanos / 1000;
+			times[0].tv_sec = p->atime;
+			times[0].tv_usec = p->atime_nanos / 1000;
+#ifdef HAVE_LUTIMES
+			lutimes(p->name, times);
+#else
+			utimes(p->name, times);
+#endif
+#else
+			/* utime() is more portable, but less precise. */
+			struct utimbuf times;
+			times.modtime = p->mtime;
+			times.actime = p->atime;
+
+			utime(p->name, &times);
+#endif
+		}
+		if (p->fixup & TODO_MODE_BASE)
+			chmod(p->name, p->mode);
+
+		if (p->fixup & TODO_FFLAGS)
+			set_fflags_platform(a, -1, p->name,
+			    p->mode, p->fflags_set, 0);
+
+		next = p->next;
+		free(p->name);
+		free(p);
+		p = next;
+	}
+	a->fixup_list = NULL;
+	return (ret);
+}
+
+static int
+_archive_write_finish(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	int ret;
+	ret = _archive_write_close(&a->archive);
+	if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+		(a->cleanup_gid)(a->lookup_gid_data);
+	if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+		(a->cleanup_uid)(a->lookup_uid_data);
+	archive_string_free(&a->_name_data);
+	archive_string_free(&a->archive.error_string);
+	archive_string_free(&a->path_safe);
+	free(a);
+	return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list.  In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+	struct fixup_entry *a, *b, *t;
+
+	if (p == NULL)
+		return (NULL);
+	/* A one-item list is already sorted. */
+	if (p->next == NULL)
+		return (p);
+
+	/* Step 1: split the list. */
+	t = p;
+	a = p->next->next;
+	while (a != NULL) {
+		/* Step a twice, t once. */
+		a = a->next;
+		if (a != NULL)
+			a = a->next;
+		t = t->next;
+	}
+	/* Now, t is at the mid-point, so break the list here. */
+	b = t->next;
+	t->next = NULL;
+	a = p;
+
+	/* Step 2: Recursively sort the two sub-lists. */
+	a = sort_dir_list(a);
+	b = sort_dir_list(b);
+
+	/* Step 3: Merge the returned lists. */
+	/* Pick the first element for the merged list. */
+	if (strcmp(a->name, b->name) > 0) {
+		t = p = a;
+		a = a->next;
+	} else {
+		t = p = b;
+		b = b->next;
+	}
+
+	/* Always put the later element on the list first. */
+	while (a != NULL && b != NULL) {
+		if (strcmp(a->name, b->name) > 0) {
+			t->next = a;
+			a = a->next;
+		} else {
+			t->next = b;
+			b = b->next;
+		}
+		t = t->next;
+	}
+
+	/* Only one list is non-empty, so just splice it on. */
+	if (a != NULL)
+		t->next = a;
+	if (b != NULL)
+		t->next = b;
+
+	return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const char *pathname)
+{
+	struct fixup_entry *fe;
+
+	fe = (struct fixup_entry *)malloc(sizeof(struct fixup_entry));
+	if (fe == NULL)
+		return (NULL);
+	fe->next = a->fixup_list;
+	a->fixup_list = fe;
+	fe->fixup = 0;
+	fe->name = strdup(pathname);
+	return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const char *pathname)
+{
+	if (a->current_fixup == NULL)
+		a->current_fixup = new_fixup(a, pathname);
+	return (a->current_fixup);
+}
+
+/* TODO: Make this work. */
+/*
+ * TODO: The deep-directory support bypasses this; disable deep directory
+ * support if we're doing symlink checks.
+ */
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+	char *pn, *p;
+	char c;
+	int r;
+	struct stat st;
+
+	/*
+	 * Gaurd against symlink tricks.  Reject any archive entry whose
+	 * destination would be altered by a symlink.
+	 */
+	/* Whatever we checked last time doesn't need to be re-checked. */
+	pn = a->name;
+	p = a->path_safe.s;
+	while ((*pn != '\0') && (*p == *pn))
+		++p, ++pn;
+	c = pn[0];
+	/* Keep going until we've checked the entire name. */
+	while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) {
+		/* Skip the next path element. */
+		while (*pn != '\0' && *pn != '/')
+			++pn;
+		c = pn[0];
+		pn[0] = '\0';
+		/* Check that we haven't hit a symlink. */
+		r = lstat(a->name, &st);
+		if (r != 0) {
+			/* We've hit a dir that doesn't exist; stop now. */
+			if (errno == ENOENT)
+				break;
+		} else if (S_ISLNK(st.st_mode)) {
+			if (c == '\0') {
+				/*
+				 * Last element is symlink; remove it
+				 * so we can overwrite it with the
+				 * item being extracted.
+				 */
+				if (unlink(a->name)) {
+					archive_set_error(&a->archive, errno,
+					    "Could not remove symlink %s",
+					    a->name);
+					pn[0] = c;
+					return (ARCHIVE_WARN);
+				}
+				/*
+				 * Even if we did remove it, a warning
+				 * is in order.  The warning is silly,
+				 * though, if we're just replacing one
+				 * symlink with another symlink.
+				 */
+				if (!S_ISLNK(a->mode)) {
+					archive_set_error(&a->archive, 0,
+					    "Removing symlink %s",
+					    a->name);
+				}
+				/* Symlink gone.  No more problem! */
+				pn[0] = c;
+				return (0);
+			} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
+				/* User asked us to remove problems. */
+				if (unlink(a->name) != 0) {
+					archive_set_error(&a->archive, 0,
+					    "Cannot remove intervening symlink %s",
+					    a->name);
+					pn[0] = c;
+					return (ARCHIVE_WARN);
+				}
+			} else {
+				archive_set_error(&a->archive, 0,
+				    "Cannot extract through symlink %s",
+				    a->name);
+				pn[0] = c;
+				return (ARCHIVE_WARN);
+			}
+		}
+	}
+	pn[0] = c;
+	/* We've checked and/or cleaned the whole path, so remember it. */
+	archive_strcpy(&a->path_safe, a->name);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Canonicalize the pathname.  In particular, this strips duplicate
+ * '/' characters, '.' elements, and trailing '/'.  It also raises an
+ * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is
+ * set) any '..' in the path.
+ */
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+	char *dest, *src;
+	char separator = '\0';
+	int lastdotdot = 0; /* True if last elt copied was '..' */
+
+	dest = src = a->name;
+	if (*src == '\0') {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid empty pathname");
+		return (ARCHIVE_WARN);
+	}
+
+	/* Skip leading '/'. */
+	if (*src == '/')
+		separator = *src++;
+
+	/* Scan the pathname one element at a time. */
+	for (;;) {
+		/* src points to first char after '/' */
+		if (src[0] == '\0') {
+			break;
+		} else if (src[0] == '/') {
+			/* Found '//', ignore second one. */
+			src++;
+			continue;
+		} else if (src[0] == '.') {
+			if (src[1] == '\0') {
+				/* Ignore trailing '.' */
+				break;
+			} else if (src[1] == '/') {
+				/* Skip './'. */
+				src += 2;
+				continue;
+			} else if (src[1] == '.') {
+				if (src[2] == '/' || src[2] == '\0') {
+					/* Conditionally warn about '..' */
+					if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_MISC,
+						    "Path contains '..'");
+						return (ARCHIVE_WARN);
+					}
+					lastdotdot = 1;
+				} else
+					lastdotdot = 0;
+				/*
+				 * Note: Under no circumstances do we
+				 * remove '..' elements.  In
+				 * particular, restoring
+				 * '/foo/../bar/' should create the
+				 * 'foo' dir as a side-effect.
+				 */
+			} else
+				lastdotdot = 0;
+		} else
+			lastdotdot = 0;
+
+		/* Copy current element, including leading '/'. */
+		if (separator)
+			*dest++ = '/';
+		while (*src != '\0' && *src != '/') {
+			*dest++ = *src++;
+		}
+
+		if (*src == '\0')
+			break;
+
+		/* Skip '/' separator. */
+		separator = *src++;
+	}
+	/*
+	 * We've just copied zero or more path elements, not including the
+	 * final '/'.
+	 */
+	if (lastdotdot) {
+		/* Trailing '..' is always wrong. */
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Path contains trailing '..'");
+		return (ARCHIVE_WARN);
+	}
+	if (dest == a->name) {
+		/*
+		 * Nothing got copied.  The path must have been something
+		 * like '.' or '/' or './' or '/././././/./'.
+		 */
+		if (separator)
+			*dest++ = '/';
+		else
+			*dest++ = '.';
+	}
+	/* Terminate the result. */
+	*dest = '\0';
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, char *path)
+{
+	char *slash;
+	int r;
+
+	/* Remove tail element to obtain parent name. */
+	slash = strrchr(path, '/');
+	if (slash == NULL)
+		return (ARCHIVE_OK);
+	*slash = '\0';
+	r = create_dir(a, path);
+	*slash = '/';
+	return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_WARN.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, char *path)
+{
+	struct stat st;
+	struct fixup_entry *le;
+	char *slash, *base;
+	mode_t mode_final, mode;
+	int r;
+
+	r = ARCHIVE_OK;
+
+	/* Check for special names and just skip them. */
+	slash = strrchr(path, '/');
+	if (slash == NULL)
+		base = path;
+	else
+		base = slash + 1;
+
+	if (base[0] == '\0' ||
+	    (base[0] == '.' && base[1] == '\0') ||
+	    (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
+		/* Don't bother trying to create null path, '.', or '..'. */
+		if (slash != NULL) {
+			*slash = '\0';
+			r = create_dir(a, path);
+			*slash = '/';
+			return (r);
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Yes, this should be stat() and not lstat().  Using lstat()
+	 * here loses the ability to extract through symlinks.  Also note
+	 * that this should not use the a->st cache.
+	 */
+	if (stat(path, &st) == 0) {
+		if (S_ISDIR(st.st_mode))
+			return (ARCHIVE_OK);
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+			archive_set_error(&a->archive, EEXIST,
+			    "Can't create directory '%s'", path);
+			return (ARCHIVE_WARN);
+		}
+		if (unlink(path) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't create directory '%s': "
+			    "Conflicting file cannot be removed");
+			return (ARCHIVE_WARN);
+		}
+	} else if (errno != ENOENT && errno != ENOTDIR) {
+		/* Stat failed? */
+		archive_set_error(&a->archive, errno, "Can't test directory '%s'", path);
+		return (ARCHIVE_WARN);
+	} else if (slash != NULL) {
+		*slash = '\0';
+		r = create_dir(a, path);
+		*slash = '/';
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	/*
+	 * Mode we want for the final restored directory.  Per POSIX,
+	 * implicitly-created dirs must be created obeying the umask.
+	 * There's no mention whether this is different for privileged
+	 * restores (which the rest of this code handles by pretending
+	 * umask=0).  I've chosen here to always obey the user's umask for
+	 * implicit dirs, even if _EXTRACT_PERM was specified.
+	 */
+	mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+	/* Mode we want on disk during the restore process. */
+	mode = mode_final;
+	mode |= MINIMUM_DIR_MODE;
+	mode &= MAXIMUM_DIR_MODE;
+	if (mkdir(path, mode) == 0) {
+		if (mode != mode_final) {
+			le = new_fixup(a, path);
+			le->fixup |=TODO_MODE_BASE;
+			le->mode = mode_final;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Without the following check, a/b/../b/c/d fails at the
+	 * second visit to 'b', so 'd' can't be created.  Note that we
+	 * don't add it to the fixup list here, as it's already been
+	 * added.
+	 */
+	if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+		return (ARCHIVE_OK);
+
+	archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path);
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid bit based on the containing directory.  So
+ * we have to perform a chown syscall if we want to restore the SGID
+ * bit.  (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().)  Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+	/* If we know we can't change it, don't bother trying. */
+	if (a->user_uid != 0  &&  a->user_uid != a->uid) {
+		archive_set_error(&a->archive, errno,
+		    "Can't set UID=%d", a->uid);
+		return (ARCHIVE_WARN);
+	}
+
+#ifdef HAVE_FCHOWN
+	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0)
+		goto success;
+#endif
+
+#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;
+#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
+/*
+ * The utimes()-family functions provide high resolution and
+ * a way to set time on an fd or a symlink.  We prefer them
+ * when they're available.
+ */
+static int
+set_time(struct archive_write_disk *a)
+{
+	struct timeval times[2];
+
+	times[1].tv_sec = archive_entry_mtime(a->entry);
+	times[1].tv_usec = archive_entry_mtime_nsec(a->entry) / 1000;
+
+	times[0].tv_sec = archive_entry_atime(a->entry);
+	times[0].tv_usec = archive_entry_atime_nsec(a->entry) / 1000;
+
+#ifdef HAVE_FUTIMES
+	if (a->fd >= 0 && futimes(a->fd, times) == 0) {
+		return (ARCHIVE_OK);
+	}
+#endif
+
+#ifdef HAVE_LUTIMES
+	if (lutimes(a->name, times) != 0)
+#else
+	if (!S_ISLNK(a->mode) && utimes(a->name, times) != 0)
+#endif
+	{
+		archive_set_error(&a->archive, errno, "Can't update time for %s",
+		    a->name);
+		return (ARCHIVE_WARN);
+	}
+
+	/*
+	 * Note: POSIX does not provide a portable way to restore ctime.
+	 * (Apart from resetting the system clock, which is distasteful.)
+	 * So, any restoration of ctime will necessarily be OS-specific.
+	 */
+
+	/* XXX TODO: Can FreeBSD restore ctime? XXX */
+	return (ARCHIVE_OK);
+}
+#elif defined(HAVE_UTIME)
+/*
+ * utime() is an older, more standard interface that we'll use
+ * if utimes() isn't available.
+ */
+static int
+set_time(struct archive_write_disk *a)
+{
+	struct utimbuf times;
+
+	times.modtime = archive_entry_mtime(a->entry);
+	times.actime = archive_entry_atime(a->entry);
+	if (!S_ISLNK(a->mode) && utime(a->name, &times) != 0) {
+		archive_set_error(&a->archive, errno,
+		    "Can't update time for %s", a->name);
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+#else
+/* This platform doesn't give us a way to restore the time. */
+static int
+set_time(struct archive_write_disk *a)
+{
+	(void)a; /* UNUSED */
+	archive_set_error(&a->archive, errno,
+	    "Can't update time for %s", a->name);
+	return (ARCHIVE_WARN);
+}
+#endif
+
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+	int r = ARCHIVE_OK;
+	mode &= 07777; /* Strip off file type bits. */
+
+	if (a->todo & TODO_SGID_CHECK) {
+		/*
+		 * If we don't know the GID is right, we must stat()
+		 * to verify it.  We can't just check the GID of this
+		 * process, since systems sometimes set GID from
+		 * the enclosing dir or based on ACLs.
+		 */
+		if (a->pst != NULL) {
+			/* Already have stat() data available. */
+#ifdef HAVE_FSTAT
+		} else if (fd >= 0 && fstat(fd, &a->st) == 0) {
+			a->pst = &a->st;
+#endif
+		} else if (stat(a->name, &a->st) == 0) {
+			a->pst = &a->st;
+		} else {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't stat file");
+			return (ARCHIVE_WARN);
+		}
+		if (a->pst->st_gid != a->gid) {
+			mode &= ~ S_ISGID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				/*
+				 * This is only an error if you
+				 * requested owner restore.  If you
+				 * didn't, we'll try to restore
+				 * sgid/suid, but won't consider it a
+				 * problem if we can't.
+				 */
+				archive_set_error(&a->archive, -1,
+				    "Can't restore SGID bit");
+				r = ARCHIVE_WARN;
+			}
+		}
+		/* While we're here, double-check the UID. */
+		if (a->pst->st_uid != a->uid
+		    && (a->todo & TODO_SUID)) {
+			mode &= ~ S_ISUID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				archive_set_error(&a->archive, -1,
+				    "Can't restore SUID bit");
+				r = ARCHIVE_WARN;
+			}
+		}
+		a->todo &= ~TODO_SGID_CHECK;
+		a->todo &= ~TODO_SUID_CHECK;
+	} else if (a->todo & TODO_SUID_CHECK) {
+		/*
+		 * If we don't know the UID is right, we can just check
+		 * the user, since all systems set the file UID from
+		 * the process UID.
+		 */
+		if (a->user_uid != a->uid) {
+			mode &= ~ S_ISUID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				archive_set_error(&a->archive, -1,
+				    "Can't make file SUID");
+				r = ARCHIVE_WARN;
+			}
+		}
+		a->todo &= ~TODO_SUID_CHECK;
+	}
+
+	if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+		/*
+		 * If this is a symlink, use lchmod().  If the
+		 * platform doesn't support lchmod(), just skip it.  A
+		 * platform that doesn't provide a way to set
+		 * permissions on symlinks probably ignores
+		 * permissions on symlinks, so a failure here has no
+		 * impact.
+		 */
+		if (lchmod(a->name, mode) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't set permissions to 0%o", (int)mode);
+			r = ARCHIVE_WARN;
+		}
+#endif
+	} else if (!S_ISDIR(a->mode)) {
+		/*
+		 * If it's not a symlink and not a dir, then use
+		 * fchmod() or chmod(), depending on whether we have
+		 * an fd.  Dirs get their perms set during the
+		 * post-extract fixup, which is handled elsewhere.
+		 */
+#ifdef HAVE_FCHMOD
+		if (a->fd >= 0) {
+			if (fchmod(a->fd, mode) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't set permissions to 0%o", (int)mode);
+				r = ARCHIVE_WARN;
+			}
+		} else
+#endif
+			/* If this platform lacks fchmod(), then
+			 * we'll just use chmod(). */
+			if (chmod(a->name, mode) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't set permissions to 0%o", (int)mode);
+				r = ARCHIVE_WARN;
+			}
+	}
+	return (r);
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+	struct fixup_entry *le;
+	unsigned long	set, clear;
+	int		r;
+	int		critical_flags;
+	mode_t		mode = archive_entry_mode(a->entry);
+
+	/*
+	 * Make 'critical_flags' hold all file flags that can't be
+	 * immediately restored.  For example, on BSD systems,
+	 * SF_IMMUTABLE prevents hardlinks from being created, so
+	 * should not be set until after any hardlinks are created.  To
+	 * preserve some semblance of portability, this uses #ifdef
+	 * extensively.  Ugly, but it works.
+	 *
+	 * Yes, Virginia, this does create a security race.  It's mitigated
+	 * somewhat by the practice of creating dirs 0700 until the extract
+	 * is done, but it would be nice if we could do more than that.
+	 * People restoring critical file systems should be wary of
+	 * other programs that might try to muck with files as they're
+	 * being restored.
+	 */
+	/* Hopefully, the compiler will optimize this mess into a constant. */
+	critical_flags = 0;
+#ifdef SF_IMMUTABLE
+	critical_flags |= SF_IMMUTABLE;
+#endif
+#ifdef UF_IMMUTABLE
+	critical_flags |= UF_IMMUTABLE;
+#endif
+#ifdef SF_APPEND
+	critical_flags |= SF_APPEND;
+#endif
+#ifdef UF_APPEND
+	critical_flags |= UF_APPEND;
+#endif
+#ifdef EXT2_APPEND_FL
+	critical_flags |= EXT2_APPEND_FL;
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+	critical_flags |= EXT2_IMMUTABLE_FL;
+#endif
+
+	if (a->todo & TODO_FFLAGS) {
+		archive_entry_fflags(a->entry, &set, &clear);
+
+		/*
+		 * The first test encourages the compiler to eliminate
+		 * all of this if it's not necessary.
+		 */
+		if ((critical_flags != 0)  &&  (set & critical_flags)) {
+			le = current_fixup(a, a->name);
+			le->fixup |= TODO_FFLAGS;
+			le->fflags_set = set;
+			/* Store the mode if it's not already there. */
+			if ((le->fixup & TODO_MODE) == 0)
+				le->mode = mode;
+		} else {
+			r = set_fflags_platform(a, a->fd,
+			    a->name, mode, set, clear);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux)
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	(void)mode; /* UNUSED */
+	if (set == 0  && clear == 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * XXX Is the stat here really necessary?  Or can I just use
+	 * the 'set' flags directly?  In particular, I'm not sure
+	 * about the correct approach if we're overwriting an existing
+	 * file that already has flags on it. XXX
+	 */
+	if (fd >= 0 && fstat(fd, &a->st) == 0)
+		a->pst = &a->st;
+	else if (lstat(name, &a->st) == 0)
+		a->pst = &a->st;
+	else {
+		archive_set_error(&a->archive, errno,
+		    "Couldn't stat file");
+		return (ARCHIVE_WARN);
+	}
+
+	a->st.st_flags &= ~clear;
+	a->st.st_flags |= set;
+#ifdef HAVE_FCHFLAGS
+	/* If platform has fchflags() and we were given an fd, use it. */
+	if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#endif
+	/*
+	 * If we can't use the fd to set the flags, we'll use the
+	 * pathname to set flags.  We prefer lchflags() but will use
+	 * chflags() if we must.
+	 */
+#ifdef HAVE_LCHFLAGS
+	if (lchflags(name, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#elif defined(HAVE_CHFLAGS)
+	if (S_ISLNK(a->st.st_mode)) {
+		archive_set_error(&a->archive, errno,
+		    "Can't set file flags on symlink.");
+		return (ARCHIVE_WARN);
+	}
+	if (chflags(name, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#endif
+	archive_set_error(&a->archive, errno,
+	    "Failed to set file flags");
+	return (ARCHIVE_WARN);
+}
+
+#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS)
+
+/*
+ * Linux has flags too, but uses ioctl() to access them instead of
+ * having a separate chflags() system call.
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	int		 ret;
+	int		 myfd = fd;
+	unsigned long newflags, oldflags;
+	unsigned long sf_mask = 0;
+
+	if (set == 0  && clear == 0)
+		return (ARCHIVE_OK);
+	/* Only regular files and dirs can have flags. */
+	if (!S_ISREG(mode) && !S_ISDIR(mode))
+		return (ARCHIVE_OK);
+
+	/* If we weren't given an fd, open it ourselves. */
+	if (myfd < 0)
+		myfd = open(name, O_RDONLY|O_NONBLOCK);
+	if (myfd < 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * Linux has no define for the flags that are only settable by
+	 * the root user.  This code may seem a little complex, but
+	 * there seem to be some Linux systems that lack these
+	 * defines. (?)  The code below degrades reasonably gracefully
+	 * if sf_mask is incomplete.
+	 */
+#ifdef EXT2_IMMUTABLE_FL
+	sf_mask |= EXT2_IMMUTABLE_FL;
+#endif
+#ifdef EXT2_APPEND_FL
+	sf_mask |= EXT2_APPEND_FL;
+#endif
+	/*
+	 * XXX As above, this would be way simpler if we didn't have
+	 * to read the current flags from disk. XXX
+	 */
+	ret = ARCHIVE_OK;
+	/* Try setting the flags as given. */
+	if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
+		newflags = (oldflags & ~clear) | set;
+		if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+			goto cleanup;
+		if (errno != EPERM)
+			goto fail;
+	}
+	/* If we couldn't set all the flags, try again with a subset. */
+	if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
+		newflags &= ~sf_mask;
+		oldflags &= sf_mask;
+		newflags |= oldflags;
+		if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+			goto cleanup;
+	}
+	/* We couldn't set the flags, so report the failure. */
+fail:
+	archive_set_error(&a->archive, errno,
+	    "Failed to set file flags");
+	ret = ARCHIVE_WARN;
+cleanup:
+	if (fd < 0)
+		close(myfd);
+	return (ret);
+}
+
+#else /* Not HAVE_CHFLAGS && Not __linux */
+
+/*
+ * Of course, some systems have neither BSD chflags() nor Linux' flags
+ * support through ioctl().
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	(void)a; /* UNUSED */
+	(void)fd; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)mode; /* UNUSED */
+	(void)set; /* UNUSED */
+	(void)clear; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif /* __linux */
+
+#ifndef HAVE_POSIX_ACL
+/* Default empty function body to satisfy mainline code. */
+static int
+set_acls(struct archive_write_disk *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * XXX TODO: What about ACL types other than ACCESS and DEFAULT?
+ */
+static int
+set_acls(struct archive_write_disk *a)
+{
+	int		 ret;
+
+	ret = set_acl(a, a->fd, a->entry, ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	ret = set_acl(a, a->fd, a->entry, ACL_TYPE_DEFAULT,
+	    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+	return (ret);
+}
+
+
+static int
+set_acl(struct archive_write_disk *a, int fd, struct archive_entry *entry,
+    acl_type_t acl_type, int ae_requested_type, const char *tname)
+{
+	acl_t		 acl;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	const char	*name;
+
+	ret = ARCHIVE_OK;
+	entries = archive_entry_acl_reset(entry, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+	acl = acl_init(entries);
+	while (archive_entry_acl_next(entry, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		acl_create_entry(&acl, &acl_entry);
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			acl_set_tag_type(acl_entry, ACL_USER);
+			ae_uid = a->lookup_uid(a->lookup_uid_data,
+			    ae_name, ae_id);
+			acl_set_qualifier(acl_entry, &ae_uid);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			ae_gid = a->lookup_gid(a->lookup_gid_data,
+			    ae_name, ae_id);
+			acl_set_qualifier(acl_entry, &ae_gid);
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			acl_set_tag_type(acl_entry, ACL_MASK);
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl_set_tag_type(acl_entry, ACL_OTHER);
+			break;
+		default:
+			/* XXX */
+			break;
+		}
+
+		acl_get_permset(acl_entry, &acl_permset);
+		acl_clear_perms(acl_permset);
+		if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
+			acl_add_perm(acl_permset, ACL_EXECUTE);
+		if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
+			acl_add_perm(acl_permset, ACL_WRITE);
+		if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
+			acl_add_perm(acl_permset, ACL_READ);
+	}
+
+	name = archive_entry_pathname(entry);
+
+	/* Try restoring the ACL through 'fd' if we can. */
+#if HAVE_ACL_SET_FD
+	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0)
+		ret = ARCHIVE_OK;
+	else
+#else
+#if HAVE_ACL_SET_FD_NP
+	if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0)
+		ret = ARCHIVE_OK;
+	else
+#endif
+#endif
+	if (acl_set_file(name, acl_type, acl) != 0) {
+		archive_set_error(&a->archive, errno, "Failed to set %s acl", tname);
+		ret = ARCHIVE_WARN;
+	}
+	acl_free(acl);
+	return (ret);
+}
+#endif
+
+#if HAVE_LSETXATTR
+/*
+ * Restore extended attributes -  Linux implementation
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	struct archive_entry *entry = a->entry;
+	static int warning_done = 0;
+	int ret = ARCHIVE_OK;
+	int i = archive_entry_xattr_reset(entry);
+
+	while (i--) {
+		const char *name;
+		const void *value;
+		size_t size;
+		archive_entry_xattr_next(entry, &name, &value, &size);
+		if (name != NULL &&
+				strncmp(name, "xfsroot.", 8) != 0 &&
+				strncmp(name, "system.", 7) != 0) {
+			int e;
+#if HAVE_FSETXATTR
+			if (a->fd >= 0)
+				e = fsetxattr(a->fd, name, value, size, 0);
+			else
+#endif
+			{
+				e = lsetxattr(archive_entry_pathname(entry),
+				    name, value, size, 0);
+			}
+			if (e == -1) {
+				if (errno == ENOTSUP) {
+					if (!warning_done) {
+						warning_done = 1;
+						archive_set_error(&a->archive, errno,
+						    "Cannot restore extended "
+						    "attributes on this file "
+						    "system");
+					}
+				} else
+					archive_set_error(&a->archive, errno,
+					    "Failed to set extended attribute");
+				ret = ARCHIVE_WARN;
+			}
+		} else {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Invalid extended attribute encountered");
+			ret = ARCHIVE_WARN;
+		}
+	}
+	return (ret);
+}
+#else
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	static int warning_done = 0;
+
+	/* If there aren't any extended attributes, then it's okay not
+	 * to extract them, otherwise, issue a single warning. */
+	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+		warning_done = 1;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Cannot restore extended attributes on this system");
+		return (ARCHIVE_WARN);
+	}
+	/* Warning was already emitted; suppress further warnings. */
+	return (ARCHIVE_OK);
+}
+#endif
+
+
+/*
+ * Trivial implementations of gid/uid lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static gid_t
+trivial_lookup_gid(void *private_data, const char *gname, gid_t gid)
+{
+	(void)private_data; /* UNUSED */
+	(void)gname; /* UNUSED */
+	return (gid);
+}
+
+static uid_t
+trivial_lookup_uid(void *private_data, const char *uname, uid_t uid)
+{
+	(void)private_data; /* UNUSED */
+	(void)uname; /* UNUSED */
+	return (uid);
+}
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(struct stat *st, struct archive_entry *entry)
+{
+	/* First, test the seconds and return if we have a definite answer. */
+	/* Definitely older. */
+	if (st->st_mtime < archive_entry_mtime(entry))
+		return (1);
+	/* Definitely younger. */
+	if (st->st_mtime > archive_entry_mtime(entry))
+		return (0);
+	/* If this platform supports fractional seconds, try those. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	/* Definitely older. */
+	if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
+		return (1);
+	/* Definitely younger. */
+	if (st->st_mtimespec.tv_nsec > archive_entry_mtime_nsec(entry))
+		return (0);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	/* Definitely older. */
+	if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
+		return (1);
+	/* Definitely older. */
+	if (st->st_mtim.tv_nsec > archive_entry_mtime_nsec(entry))
+		return (0);
+#else
+	/* This system doesn't have high-res timestamps. */
+#endif
+	/* Same age, so not older. */
+	return (0);
+}
--- /dev/null
+++ lib/libarchive/archive_write_set_compression_program.c
@@ -0,0 +1,322 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.1 2007/05/29 01:00:19 kientzle Exp $");
+
+#ifdef HAVE_SYS_WAIT_H
+#  include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#  include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#include "filter_fork.h"
+
+struct private_data {
+	char		*description;
+	pid_t		 child;
+	int		 child_stdin, child_stdout;
+
+	char		*child_buf;
+	size_t		 child_buf_len, child_buf_avail;
+};
+
+static int	archive_compressor_program_finish(struct archive_write *);
+static int	archive_compressor_program_init(struct archive_write *);
+static int	archive_compressor_program_write(struct archive_write *,
+		    const void *, size_t);
+
+/*
+ * Allocate, initialize and return a archive object.
+ */
+int
+archive_write_set_compression_program(struct archive *_a, const char *cmd)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_compression_program");
+	a->compressor.init = &archive_compressor_program_init;
+	a->compressor.config = strdup(cmd);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_program_init(struct archive_write *a)
+{
+	int ret;
+	struct private_data *state;
+	static const char *prefix = "Program: ";
+	char *cmd = a->compressor.config;
+
+	if (a->client_opener != NULL) {
+		ret = (a->client_opener)(&a->archive, a->client_data);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+
+	state = (struct private_data *)malloc(sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data for compression");
+		return (ARCHIVE_FATAL);
+	}
+	memset(state, 0, sizeof(*state));
+
+	a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
+	state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
+	strcpy(state->description, prefix);
+	strcat(state->description, cmd);
+	a->archive.compression_name = state->description;
+
+	state->child_buf_len = a->bytes_per_block;
+	state->child_buf_avail = 0;
+	state->child_buf = malloc(state->child_buf_len);
+
+	if (state->child_buf == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data for compression buffer");
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	if ((state->child = __archive_create_child(cmd,
+		 &state->child_stdin, &state->child_stdout)) == -1) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Can't initialise filter");
+		free(state->child_buf);
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	a->compressor.write = archive_compressor_program_write;
+	a->compressor.finish = archive_compressor_program_finish;
+
+	a->compressor.data = state;
+	return (0);
+}
+
+static ssize_t
+child_write(struct archive_write *a, const char *buf, size_t buf_len)
+{
+	struct private_data *state = a->compressor.data;
+	ssize_t ret;
+
+	if (state->child_stdin == -1)
+		return (-1);
+
+	if (buf_len == 0)
+		return (-1);
+
+restart_write:
+	do {
+		ret = write(state->child_stdin, buf, buf_len);
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret > 0)
+		return (ret);
+	if (ret == 0) {
+		close(state->child_stdin);
+		state->child_stdin = -1;
+		fcntl(state->child_stdout, F_SETFL, 0);
+		return (0);
+	}
+	if (ret == -1 && errno != EAGAIN)
+		return (-1);
+
+	do {
+		ret = read(state->child_stdout,
+		    state->child_buf + state->child_buf_avail,
+		    state->child_buf_len - state->child_buf_avail);
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+		close(state->child_stdout);
+		state->child_stdout = -1;
+		fcntl(state->child_stdin, F_SETFL, 0);
+		goto restart_write;
+	}
+	if (ret == -1 && errno == EAGAIN) {
+		__archive_check_child(state->child_stdin, state->child_stdout);
+		goto restart_write;
+	}
+	if (ret == -1)
+		return (-1);
+
+	state->child_buf_avail += ret;
+
+	ret = (a->client_writer)(&a->archive, a->client_data,
+	    state->child_buf, state->child_buf_avail);
+	if (ret <= 0)
+		return (-1);
+
+	if ((size_t)ret < state->child_buf_avail) {
+		memmove(state->child_buf, state->child_buf + ret,
+		    state->child_buf_avail - ret);
+	}
+	state->child_buf_avail -= ret;
+	a->archive.raw_position += ret;
+	goto restart_write;
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_program_write(struct archive_write *a, const void *buff,
+    size_t length)
+{
+	struct private_data *state;
+	ssize_t ret;
+	const char *buf;
+
+	state = (struct private_data *)a->compressor.data;
+	if (a->client_writer == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "No write callback is registered?  "
+		    "This is probably an internal programming error.");
+		return (ARCHIVE_FATAL);
+	}
+
+	buf = buff;
+	while (length > 0) {
+		ret = child_write(a, buf, length);
+		if (ret == -1 || ret == 0) {
+			archive_set_error(&a->archive, EIO,
+			    "Can't write to filter");
+			return (ARCHIVE_FATAL);
+		}
+		length -= ret;
+		buf += ret;
+	}
+
+	a->archive.file_position += length;
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_program_finish(struct archive_write *a)
+{
+	int ret, status;
+	ssize_t bytes_read, bytes_written;
+	struct private_data *state;
+
+	state = (struct private_data *)a->compressor.data;
+	ret = 0;
+	if (a->client_writer == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "No write callback is registered?  "
+		    "This is probably an internal programming error.");
+		ret = ARCHIVE_FATAL;
+		goto cleanup;
+	}
+
+	/* XXX pad compressed data. */
+
+	close(state->child_stdin);
+	state->child_stdin = -1;
+	fcntl(state->child_stdout, F_SETFL, 0);
+
+	for (;;) {
+		do {
+			bytes_read = read(state->child_stdout,
+			    state->child_buf + state->child_buf_avail,
+			    state->child_buf_len - state->child_buf_avail);
+		} while (bytes_read == -1 && errno == EINTR);
+
+		if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
+			break;
+
+		if (bytes_read == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Read from filter failed unexpectedly.");
+			ret = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+		state->child_buf_avail += bytes_read;
+
+		bytes_written = (a->client_writer)(&a->archive, a->client_data,
+		    state->child_buf, state->child_buf_avail);
+		if (bytes_written <= 0) {
+			ret = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+		if ((size_t)bytes_written < state->child_buf_avail) {
+			memmove(state->child_buf,
+			    state->child_buf + bytes_written,
+			    state->child_buf_avail - bytes_written);
+		}
+		state->child_buf_avail -= bytes_written;
+		a->archive.raw_position += bytes_written;
+	}
+
+	/* XXX pad final compressed block. */
+
+cleanup:
+	/* Shut down the child. */
+	if (state->child_stdin != -1)
+		close(state->child_stdin);
+	if (state->child_stdout != -1)
+		close(state->child_stdout);
+	while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
+		continue;
+
+	if (status != 0) {
+		archive_set_error(&a->archive, EIO,
+		    "Filter exited with failure.");
+		ret = ARCHIVE_FATAL;
+	}
+
+	/* Release our configuration data. */
+	free(a->compressor.config);
+	a->compressor.config = NULL;
+
+	/* Release our private state data. */
+	free(state->child_buf);
+	free(state->description);
+	free(state);
+	return (ret);
+}
--- /dev/null
+++ lib/libarchive/filter_fork.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: src/lib/libarchive/filter_fork.c,v 1.1 2007/05/29 01:00:20 kientzle Exp $");
+
+#if defined(HAVE_POLL)
+#  if defined(HAVE_POLL_H)
+#    include <poll.h>
+#  endif
+#elif defined(HAVE_SELECT)
+#  if defined(HAVE_SYS_SELECT_H)
+#    include <sys/select.h>
+#  elif defined(HAVE_UNISTD_H)
+#    include <unistd.h>
+#  endif
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#include "filter_fork.h"
+
+pid_t
+__archive_create_child(const char *path, int *child_stdin, int *child_stdout)
+{
+	pid_t child;
+	int stdin_pipe[2], stdout_pipe[2], tmp;
+
+	if (pipe(stdin_pipe) == -1)
+		goto state_allocated;
+	if (stdin_pipe[0] == STDOUT_FILENO) {
+		if ((tmp = dup(stdin_pipe[0])) == -1)
+			goto stdin_opened;
+		close(stdin_pipe[0]);
+		stdin_pipe[0] = tmp;
+	}
+	if (pipe(stdout_pipe) == -1)
+		goto stdin_opened;
+	if (stdout_pipe[1] == STDIN_FILENO) {
+		if ((tmp = dup(stdout_pipe[1])) == -1)
+			goto stdout_opened;
+		close(stdout_pipe[1]);
+		stdout_pipe[1] = tmp;
+	}
+
+	switch ((child = vfork())) {
+	case -1:
+		goto stdout_opened;
+	case 0:
+		close(stdin_pipe[1]);
+		close(stdout_pipe[0]);
+		if (dup2(stdin_pipe[0], STDIN_FILENO) == -1)
+			_exit(254);
+		if (stdin_pipe[0] != STDIN_FILENO)
+			close(stdin_pipe[0]);
+		if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1)
+			_exit(254);
+		if (stdout_pipe[1] != STDOUT_FILENO)
+			close(stdout_pipe[1]);
+		execlp(path, path, (char *)NULL);
+		_exit(254);		
+	default:
+		close(stdin_pipe[0]);
+		close(stdout_pipe[1]);
+
+		*child_stdin = stdin_pipe[1];
+		fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
+		*child_stdout = stdout_pipe[0];
+		fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
+	}
+
+	return child;
+
+stdout_opened:
+	close(stdout_pipe[0]);
+	close(stdout_pipe[1]);
+stdin_opened:
+	close(stdin_pipe[0]);
+	close(stdin_pipe[1]);
+state_allocated:
+	return -1;
+}
+
+void
+__archive_check_child(int in, int out)
+{
+#if defined(HAVE_POLL)
+	struct pollfd fds[2];
+
+	fds[0].fd = in;
+	fds[0].events = POLLOUT;
+	fds[1].fd = out;
+	fds[1].events = POLLIN;
+
+	poll(fds, 2, -1); /* -1 == INFTIM, wait forever */
+#elif defined(HAVE_SELECT)
+	fd_set fds_in, fds_out, fds_error;
+
+	FD_ZERO(&fds_in);
+	FD_SET(out, &fds_in);
+	FD_ZERO(&fds_out);
+	FD_SET(in, &fds_out);
+	FD_ZERO(&fds_error);
+	FD_SET(in, &fds_error);
+	FD_SET(out, &fds_error);
+	select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
+#else
+	sleep(1);
+#endif
+}
Index: archive_write_set_compression_gzip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_gzip.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_compression_gzip.c -Llib/libarchive/archive_write_set_compression_gzip.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_compression_gzip.c
+++ lib/libarchive/archive_write_set_compression_gzip.c
@@ -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.10.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_gzip.c,v 1.14 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -46,6 +46,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
 struct private_data {
 	z_stream	 stream;
@@ -63,11 +64,11 @@
 #define	SET_NEXT_IN(st,src)					\
 	(st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
 
-static int	archive_compressor_gzip_finish(struct archive *);
-static int	archive_compressor_gzip_init(struct archive *);
-static int	archive_compressor_gzip_write(struct archive *, const void *,
-		    size_t);
-static int	drive_compressor(struct archive *, struct private_data *,
+static int	archive_compressor_gzip_finish(struct archive_write *);
+static int	archive_compressor_gzip_init(struct archive_write *);
+static int	archive_compressor_gzip_write(struct archive_write *,
+		    const void *, size_t);
+static int	drive_compressor(struct archive_write *, struct private_data *,
 		    int finishing);
 
 
@@ -75,12 +76,14 @@
  * Allocate, initialize and return a archive object.
  */
 int
-archive_write_set_compression_gzip(struct archive *a)
+archive_write_set_compression_gzip(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
-	a->compression_init = &archive_compressor_gzip_init;
-	a->compression_code = ARCHIVE_COMPRESSION_GZIP;
-	a->compression_name = "gzip";
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip");
+	a->compressor.init = &archive_compressor_gzip_init;
+	a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
+	a->archive.compression_name = "gzip";
 	return (ARCHIVE_OK);
 }
 
@@ -88,24 +91,24 @@
  * Setup callback.
  */
 static int
-archive_compressor_gzip_init(struct archive *a)
+archive_compressor_gzip_init(struct archive_write *a)
 {
 	int ret;
 	struct private_data *state;
 	time_t t;
 
-	a->compression_code = ARCHIVE_COMPRESSION_GZIP;
-	a->compression_name = "gzip";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
+	a->archive.compression_name = "gzip";
 
 	if (a->client_opener != NULL) {
-		ret = (a->client_opener)(a, a->client_data);
+		ret = (a->client_opener)(&a->archive, a->client_data);
 		if (ret != ARCHIVE_OK)
 			return (ret);
 	}
 
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for compression");
 		return (ARCHIVE_FATAL);
 	}
@@ -116,7 +119,7 @@
 	state->crc = crc32(0L, NULL, 0);
 
 	if (state->compressed == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for compression buffer");
 		free(state);
 		return (ARCHIVE_FATAL);
@@ -140,8 +143,8 @@
 	state->stream.next_out += 10;
 	state->stream.avail_out -= 10;
 
-	a->compression_write = archive_compressor_gzip_write;
-	a->compression_finish = archive_compressor_gzip_finish;
+	a->compressor.write = archive_compressor_gzip_write;
+	a->compressor.finish = archive_compressor_gzip_finish;
 
 	/* Initialize compression library. */
 	ret = deflateInit2(&(state->stream),
@@ -152,12 +155,12 @@
 	    Z_DEFAULT_STRATEGY);
 
 	if (ret == Z_OK) {
-		a->compression_data = state;
+		a->compressor.data = state;
 		return (0);
 	}
 
 	/* Library setup failed: clean up. */
-	archive_set_error(a, ARCHIVE_ERRNO_MISC, "Internal error "
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error "
 	    "initializing compression library");
 	free(state->compressed);
 	free(state);
@@ -165,16 +168,16 @@
 	/* Override the error message if we know what really went wrong. */
 	switch (ret) {
 	case Z_STREAM_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing "
 		    "compression library: invalid setup parameter");
 		break;
 	case Z_MEM_ERROR:
-		archive_set_error(a, ENOMEM, "Internal error initializing "
+		archive_set_error(&a->archive, ENOMEM, "Internal error initializing "
 		    "compression library");
 		break;
 	case Z_VERSION_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing "
 		    "compression library: invalid library version");
 		break;
@@ -187,15 +190,15 @@
  * Write data to the compressed stream.
  */
 static int
-archive_compressor_gzip_write(struct archive *a, const void *buff,
+archive_compressor_gzip_write(struct archive_write *a, const void *buff,
     size_t length)
 {
 	struct private_data *state;
 	int ret;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->compressor.data;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -211,7 +214,7 @@
 	if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK)
 		return (ret);
 
-	a->file_position += length;
+	a->archive.file_position += length;
 	return (ARCHIVE_OK);
 }
 
@@ -220,7 +223,7 @@
  * Finish the compression...
  */
 static int
-archive_compressor_gzip_finish(struct archive *a)
+archive_compressor_gzip_finish(struct archive_write *a)
 {
 	ssize_t block_length, target_block_length, bytes_written;
 	int ret;
@@ -228,10 +231,10 @@
 	unsigned tocopy;
 	unsigned char trailer[8];
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->compressor.data;
 	ret = 0;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?  "
 		    "This is probably an internal programming error.");
 		ret = ARCHIVE_FATAL;
@@ -280,13 +283,13 @@
 
 	/* If it overflowed, flush and start a new block. */
 	if (tocopy < 8) {
-		bytes_written = (a->client_writer)(a, a->client_data,
+		bytes_written = (a->client_writer)(&a->archive, a->client_data,
 		    state->compressed, state->compressed_buffer_size);
 		if (bytes_written <= 0) {
 			ret = ARCHIVE_FATAL;
 			goto cleanup;
 		}
-		a->raw_position += bytes_written;
+		a->archive.raw_position += bytes_written;
 		state->stream.next_out = state->compressed;
 		state->stream.avail_out = state->compressed_buffer_size;
 		memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy);
@@ -317,13 +320,13 @@
 	}
 
 	/* Write the last block */
-	bytes_written = (a->client_writer)(a, a->client_data,
+	bytes_written = (a->client_writer)(&a->archive, a->client_data,
 	    state->compressed, block_length);
 	if (bytes_written <= 0) {
 		ret = ARCHIVE_FATAL;
 		goto cleanup;
 	}
-	a->raw_position += bytes_written;
+	a->archive.raw_position += bytes_written;
 
 	/* Cleanup: shut down compressor, release memory, etc. */
 cleanup:
@@ -331,17 +334,12 @@
 	case Z_OK:
 		break;
 	default:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Failed to clean up compressor");
 		ret = ARCHIVE_FATAL;
 	}
 	free(state->compressed);
 	free(state);
-
-	/* Close the output */
-	if (a->client_closer != NULL)
-		(a->client_closer)(a, a->client_data);
-
 	return (ret);
 }
 
@@ -353,15 +351,16 @@
  * false) and the end-of-archive case (finishing == true).
  */
 static int
-drive_compressor(struct archive *a, struct private_data *state, int finishing)
+drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
 {
 	ssize_t bytes_written;
 	int ret;
 
 	for (;;) {
 		if (state->stream.avail_out == 0) {
-			bytes_written = (a->client_writer)(a, a->client_data,
-			    state->compressed, state->compressed_buffer_size);
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, state->compressed,
+			    state->compressed_buffer_size);
 			if (bytes_written <= 0) {
 				/* TODO: Handle this write failure */
 				return (ARCHIVE_FATAL);
@@ -372,7 +371,7 @@
 				    state->compressed + bytes_written,
 				    state->compressed_buffer_size - bytes_written);
 			}
-			a->raw_position += bytes_written;
+			a->archive.raw_position += bytes_written;
 			state->stream.next_out
 			    = state->compressed +
 			    state->compressed_buffer_size - bytes_written;
@@ -396,7 +395,7 @@
 			return (ARCHIVE_OK);
 		default:
 			/* Any other return value indicates an error. */
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "GZip compression failed");
 			return (ARCHIVE_FATAL);
 		}
--- /dev/null
+++ lib/libarchive/archive_write_private.h
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_write_private.h,v 1.2 2007/05/29 01:00:19 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+#define	ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_write {
+	struct archive	archive;
+
+	/* Dev/ino of the archive being written. */
+	dev_t		  skip_file_dev;
+	ino_t		  skip_file_ino;
+
+	/* Utility:  Pointer to a block of nulls. */
+	const unsigned char	*nulls;
+	size_t			 null_length;
+
+	/* Callbacks to open/read/write/close archive stream. */
+	archive_open_callback	*client_opener;
+	archive_write_callback	*client_writer;
+	archive_close_callback	*client_closer;
+	void			*client_data;
+
+	/*
+	 * Blocking information.  Note that bytes_in_last_block is
+	 * misleadingly named; I should find a better name.  These
+	 * control the final output from all compressors, including
+	 * compression_none.
+	 */
+	int		  bytes_per_block;
+	int		  bytes_in_last_block;
+
+	/*
+	 * These control whether data within a gzip/bzip2 compressed
+	 * stream gets padded or not.  If pad_uncompressed is set,
+	 * the data will be padded to a full block before being
+	 * compressed.  The pad_uncompressed_byte determines the value
+	 * that will be used for padding.  Note that these have no
+	 * effect on compression "none."
+	 */
+	int		  pad_uncompressed;
+	int		  pad_uncompressed_byte; /* TODO: Support this. */
+
+	/*
+	 * On write, the client just invokes an archive_write_set function
+	 * which sets up the data here directly.
+	 */
+	struct {
+		void	 *data;
+		void	 *config;
+		int	(*init)(struct archive_write *);
+		int	(*finish)(struct archive_write *);
+		int	(*write)(struct archive_write *, const void *, size_t);
+	} compressor;
+
+	/*
+	 * Again, write support is considerably simpler because there's
+	 * no need for an auction.
+	 */
+	int		  archive_format;
+	const char	 *archive_format_name;
+
+	/*
+	 * Pointers to format-specific functions for writing.  They're
+	 * initialized by archive_write_set_format_XXX() calls.
+	 */
+	void	 *format_data;
+	int	(*format_init)(struct archive_write *);
+	int	(*format_finish)(struct archive_write *);
+	int	(*format_destroy)(struct archive_write *);
+	int	(*format_finish_entry)(struct archive_write *);
+	int 	(*format_write_header)(struct archive_write *,
+		    struct archive_entry *);
+	ssize_t	(*format_write_data)(struct archive_write *,
+		    const void *buff, size_t);
+};
+
+/*
+ * Utility function to format a USTAR header into a buffer.  If
+ * "strict" is set, this tries to create the absolutely most portable
+ * version of a ustar header.  If "strict" is set to 0, then it will
+ * relax certain requirements.
+ *
+ * Generally, format-specific declarations don't belong in this
+ * header; this is a rare example of a function that is shared by
+ * two very similar formats (ustar and pax).
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *, char buff[512],
+    struct archive_entry *, int tartype, int strict);
+
+#endif
--- /dev/null
+++ lib/libarchive/archive_entry_private.h
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_entry_private.h,v 1.1 2007/05/29 01:00:18 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+#define	ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+
+/*
+ * Handle wide character (i.e., Unicode) and non-wide character
+ * strings transparently.
+ *
+ */
+
+struct aes {
+	const char *aes_mbs;
+	char *aes_mbs_alloc;
+	const wchar_t *aes_wcs;
+	wchar_t *aes_wcs_alloc;
+};
+
+struct ae_acl {
+	struct ae_acl *next;
+	int	type;			/* E.g., access or default */
+	int	tag;			/* E.g., user/group/other/mask */
+	int	permset;		/* r/w/x bits */
+	int	id;			/* uid/gid for user/group */
+	struct aes name;		/* uname/gname */
+};
+
+struct ae_xattr {
+	struct ae_xattr *next;
+
+	char	*name;
+	void	*value;
+	size_t	size;
+};
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format.  However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).  There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+	/*
+	 * Note that ae_stat.st_mode & AE_IFMT  can be  0!
+	 *
+	 * This occurs when the actual file type of the object is not
+	 * in the archive.  For example, 'tar' archives store
+	 * hardlinks without marking the type of the underlying
+	 * object.
+	 */
+
+	/*
+	 * Read archive_entry_copy_stat.c for an explanation of why I
+	 * don't just use "struct stat" instead of "struct aest" here
+	 * and why I have this odd pointer to a separately-allocated
+	 * struct stat.
+	 */
+	void *stat;
+	int  stat_valid; /* Set to 0 whenever a field in aest changes. */
+
+	struct aest {
+		int64_t		aest_atime;
+		uint32_t	aest_atime_nsec;
+		int64_t		aest_ctime;
+		uint32_t	aest_ctime_nsec;
+		int64_t		aest_mtime;
+		uint32_t	aest_mtime_nsec;
+		gid_t		aest_gid;
+		ino_t		aest_ino;
+		mode_t		aest_mode;
+		uint32_t	aest_nlink;
+		uint64_t	aest_size;
+		uid_t		aest_uid;
+		/*
+		 * Because converting between device codes and
+		 * major/minor values is platform-specific and
+		 * inherently a bit risky, we only do that conversion
+		 * lazily.  That way, we will do a better job of
+		 * preserving information in those cases where no
+		 * conversion is actually required.
+		 */
+		int		aest_dev_is_broken_down;
+		dev_t		aest_dev;
+		dev_t		aest_devmajor;
+		dev_t		aest_devminor;
+		int		aest_rdev_is_broken_down;
+		dev_t		aest_rdev;
+		dev_t		aest_rdevmajor;
+		dev_t		aest_rdevminor;
+	} ae_stat;
+
+
+
+	/*
+	 * Use aes here so that we get transparent mbs<->wcs conversions.
+	 */
+	struct aes ae_fflags_text;	/* Text fflags per fflagstostr(3) */
+	unsigned long ae_fflags_set;		/* Bitmap fflags */
+	unsigned long ae_fflags_clear;
+	struct aes ae_gname;		/* Name of owning group */
+	struct aes ae_hardlink;	/* Name of target for hardlink */
+	struct aes ae_pathname;	/* Name of entry */
+	struct aes ae_symlink;		/* symlink contents */
+	struct aes ae_uname;		/* Name of owner */
+
+	struct ae_acl	*acl_head;
+	struct ae_acl	*acl_p;
+	int		 acl_state;	/* See acl_next for details. */
+	wchar_t		*acl_text_w;
+
+	struct ae_xattr *xattr_head;
+	struct ae_xattr *xattr_p;
+};
+
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
Index: archive_read_open_fd.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_open_fd.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_open_fd.c -Llib/libarchive/archive_read_open_fd.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_open_fd.c
+++ lib/libarchive/archive_read_open_fd.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.3.8.3 2007/01/27 06:44:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.13 2007/06/26 03:06:48 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -47,6 +47,7 @@
 struct read_fd_data {
 	int	 fd;
 	size_t	 block_size;
+	char	 can_skip;
 	void	*buffer;
 };
 
@@ -77,6 +78,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	mine->fd = fd;
+	/* lseek() hardly ever works, so disable it by default.  See below. */
+	mine->can_skip = 0;
 	return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
 }
 
@@ -91,8 +94,18 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (S_ISREG(st.st_mode))
+	if (S_ISREG(st.st_mode)) {
 		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+		/*
+		 * Enabling skip here is a performance optimization for
+		 * anything that supports lseek().  On FreeBSD, only
+		 * regular files and raw disk devices support lseek() and
+		 * there's no portable way to determine if a device is
+		 * a raw disk device, so we only enable this optimization
+		 * for regular files.
+		 */
+		mine->can_skip = 1;
+	}
 	return (ARCHIVE_OK);
 }
 
@@ -121,8 +134,14 @@
 	struct read_fd_data *mine = (struct read_fd_data *)client_data;
 	off_t old_offset, new_offset;
 
+	if (!mine->can_skip)
+		return (0);
+
 	/* Reduce request to the next smallest multiple of block_size */
 	request = (request / mine->block_size) * mine->block_size;
+	if (request == 0)
+		return (0);
+
 	/*
 	 * Hurray for lazy evaluation: if the first lseek fails, the second
 	 * one will not be executed.
@@ -130,6 +149,9 @@
 	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
 	    ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
 	{
+		/* If seek failed once, it will probably fail again. */
+		mine->can_skip = 0;
+
 		if (errno == ESPIPE)
 		{
 			/*
Index: archive_entry.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_entry.h -Llib/libarchive/archive_entry.h -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_entry.h
+++ lib/libarchive/archive_entry.h
@@ -22,13 +22,15 @@
  * (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.16.2.2 2007/01/27 06:44:52 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_entry.h,v 1.23 2007/07/15 19:10:34 kientzle Exp $
  */
 
 #ifndef ARCHIVE_ENTRY_H_INCLUDED
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
+#include <sys/types.h>
 #include <stddef.h>  /* for wchar_t */
+#include <time.h>
 #include <unistd.h>
 
 #ifdef __cplusplus
@@ -55,6 +57,17 @@
 struct archive_entry;
 
 /*
+ * File-type constants.  These are returned from archive_entry_filetype().
+ */
+#define	AE_IFMT		0170000
+#define	AE_IFREG	0100000
+#define	AE_IFLNK	0120000
+#define	AE_IFCHR	0020000
+#define	AE_IFBLK	0060000
+#define	AE_IFDIR	0040000
+#define	AE_IFIFO	0010000
+
+/*
  * Basic object manipulation
  */
 
@@ -73,6 +86,9 @@
 time_t			 archive_entry_ctime(struct archive_entry *);
 long			 archive_entry_ctime_nsec(struct archive_entry *);
 dev_t			 archive_entry_dev(struct archive_entry *);
+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);
 const char		*archive_entry_fflags_text(struct archive_entry *);
@@ -85,13 +101,13 @@
 mode_t			 archive_entry_mode(struct archive_entry *);
 time_t			 archive_entry_mtime(struct archive_entry *);
 long			 archive_entry_mtime_nsec(struct archive_entry *);
+unsigned int		 archive_entry_nlink(struct archive_entry *);
 const char		*archive_entry_pathname(struct archive_entry *);
 const wchar_t		*archive_entry_pathname_w(struct archive_entry *);
 dev_t			 archive_entry_rdev(struct archive_entry *);
 dev_t			 archive_entry_rdevmajor(struct archive_entry *);
 dev_t			 archive_entry_rdevminor(struct archive_entry *);
 int64_t			 archive_entry_size(struct archive_entry *);
-const struct stat	*archive_entry_stat(struct archive_entry *);
 const char		*archive_entry_symlink(struct archive_entry *);
 const wchar_t		*archive_entry_symlink_w(struct archive_entry *);
 uid_t			 archive_entry_uid(struct archive_entry *);
@@ -105,9 +121,12 @@
  * In contrast, 'copy' functions do copy the object pointed to.
  */
 
-void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
 void	archive_entry_set_atime(struct archive_entry *, time_t, long);
 void	archive_entry_set_ctime(struct archive_entry *, time_t, long);
+void	archive_entry_set_dev(struct archive_entry *, dev_t);
+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);
 /* Returns pointer to start of first invalid token, or NULL if none. */
@@ -116,26 +135,44 @@
 	    const wchar_t *);
 void	archive_entry_set_gid(struct archive_entry *, gid_t);
 void	archive_entry_set_gname(struct archive_entry *, const char *);
+void	archive_entry_copy_gname(struct archive_entry *, const char *);
 void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
 void	archive_entry_set_hardlink(struct archive_entry *, const char *);
 void	archive_entry_copy_hardlink(struct archive_entry *, const char *);
 void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+void	archive_entry_set_ino(struct archive_entry *, unsigned long);
 void	archive_entry_set_link(struct archive_entry *, const char *);
 void	archive_entry_set_mode(struct archive_entry *, mode_t);
 void	archive_entry_set_mtime(struct archive_entry *, time_t, long);
+void	archive_entry_set_nlink(struct archive_entry *, unsigned int);
 void	archive_entry_set_pathname(struct archive_entry *, const char *);
 void	archive_entry_copy_pathname(struct archive_entry *, const char *);
 void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+void	archive_entry_set_rdev(struct archive_entry *, dev_t);
 void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
 void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);
 void	archive_entry_set_size(struct archive_entry *, int64_t);
 void	archive_entry_set_symlink(struct archive_entry *, const char *);
+void	archive_entry_copy_symlink(struct archive_entry *, const char *);
 void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
 void	archive_entry_set_uid(struct archive_entry *, uid_t);
 void	archive_entry_set_uname(struct archive_entry *, const char *);
+void	archive_entry_copy_uname(struct archive_entry *, const char *);
 void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
 
 /*
+ * Routines to bulk copy fields to/from a platform-native "struct
+ * stat."  Libarchive used to just store a struct stat inside of each
+ * archive_entry object, but this created issues when trying to
+ * manipulate archives on systems different than the ones they were
+ * created on.
+ *
+ * TODO: On Linux, provide both stat32 and stat64 versions of these functions.
+ */
+const struct stat	*archive_entry_stat(struct archive_entry *);
+void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
+/*
  * ACL routines.  This used to simply store and return text-format ACL
  * strings, but that proved insufficient for a number of reasons:
  *   = clients need control over uname/uid and gname/gid mappings
Index: archive_entry.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_entry.3,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_entry.3 -Llib/libarchive/archive_entry.3 -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_entry.3
+++ lib/libarchive/archive_entry.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.9.8.3 2007/01/27 06:44:52 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.15 2007/07/15 19:10:34 kientzle Exp $
 .\"
 .Dd December 15, 2003
 .Dt archive_entry 3
@@ -41,14 +41,20 @@
 .Nm archive_entry_clear ,
 .Nm archive_entry_clone ,
 .Nm archive_entry_copy_fflags_text_w ,
+.Nm archive_entry_copy_gname ,
 .Nm archive_entry_copy_gname_w ,
 .Nm archive_entry_copy_hardlink ,
 .Nm archive_entry_copy_hardlink_w ,
 .Nm archive_entry_copy_pathname_w ,
 .Nm archive_entry_copy_stat ,
+.Nm archive_entry_copy_symlink ,
 .Nm archive_entry_copy_symlink_w ,
+.Nm archive_entry_copy_uname ,
 .Nm archive_entry_copy_uname_w ,
 .Nm archive_entry_dev ,
+.Nm archive_entry_devmajor ,
+.Nm archive_entry_devminor ,
+.Nm archive_entry_filetype ,
 .Nm archive_entry_fflags ,
 .Nm archive_entry_fflags_text ,
 .Nm archive_entry_free ,
@@ -59,12 +65,19 @@
 .Nm archive_entry_mode ,
 .Nm archive_entry_mtime ,
 .Nm archive_entry_mtime_nsec ,
+.Nm archive_entry_nlink ,
 .Nm archive_entry_new ,
 .Nm archive_entry_pathname ,
 .Nm archive_entry_pathname_w ,
 .Nm archive_entry_rdev ,
 .Nm archive_entry_rdevmajor ,
 .Nm archive_entry_rdevminor ,
+.Nm archive_entry_set_atime ,
+.Nm archive_entry_set_ctime ,
+.Nm archive_entry_set_dev ,
+.Nm archive_entry_set_devmajor ,
+.Nm archive_entry_set_devminor ,
+.Nm archive_entry_set_filetype ,
 .Nm archive_entry_set_fflags ,
 .Nm archive_entry_set_gid ,
 .Nm archive_entry_set_gname ,
@@ -114,6 +127,8 @@
 .Ft const wchar_t *
 .Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
 .Ft void
+.Fn archive_entry_copy_gname "struct archive_entry *" "const char *"
+.Ft void
 .Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *"
 .Ft void
 .Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *"
@@ -124,11 +139,21 @@
 .Ft void
 .Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
 .Ft void
+.Fn archive_entry_copy_symlink "struct archive_entry *" "const char *"
+.Ft void
 .Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *"
 .Ft void
+.Fn archive_entry_copy_uname "struct archive_entry *" "const char *"
+.Ft void
 .Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
 .Ft dev_t
 .Fn archive_entry_dev "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_devmajor "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_devminor "struct archive_entry *"
+.Ft mode_t
+.Fn archive_entry_filetype "struct archive_entry *"
 .Ft void
 .Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear"
 .Ft const char *
@@ -147,6 +172,8 @@
 .Fn archive_entry_mtime "struct archive_entry *"
 .Ft long
 .Fn archive_entry_mtime_nsec "struct archive_entry *"
+.Ft unsigned int
+.Fn archive_entry_nlink "struct archive_entry *"
 .Ft struct archive_entry *
 .Fn archive_entry_new "void"
 .Ft const char *
@@ -160,6 +187,14 @@
 .Ft dev_t
 .Fn archive_entry_rdevminor "struct archive_entry *"
 .Ft void
+.Fn archive_entry_set_dev "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_devmajor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_devminor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
+.Ft void
 .Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear"
 .Ft void
 .Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
@@ -168,14 +203,20 @@
 .Ft void
 .Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
 .Ft void
+.Fn archive_entry_set_ino "struct archive_entry *" "unsigned long"
+.Ft void
 .Fn archive_entry_set_link "struct archive_entry *" "const char *"
 .Ft void
 .Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
 .Ft void
 .Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
 .Ft void
+.Fn archive_entry_set_nlink "struct archive_entry *" "unsigned int"
+.Ft void
 .Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
 .Ft void
+.Fn archive_entry_set_rdev "struct archive_entry *" "dev_t"
+.Ft void
 .Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
 .Ft void
 .Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"
Index: archive_write_set_compression_bzip2.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_bzip2.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_compression_bzip2.c -Llib/libarchive/archive_write_set_compression_bzip2.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_compression_bzip2.c
+++ lib/libarchive/archive_write_set_compression_bzip2.c
@@ -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.8.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_bzip2.c,v 1.12 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -46,6 +46,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
 struct private_data {
 	bz_stream	 stream;
@@ -62,23 +63,23 @@
 #define	SET_NEXT_IN(st,src)					\
 	(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
 
-static int	archive_compressor_bzip2_finish(struct archive *);
-static int	archive_compressor_bzip2_init(struct archive *);
-static int	archive_compressor_bzip2_write(struct archive *, const void *,
-		    size_t);
-static int	drive_compressor(struct archive *, struct private_data *,
+static int	archive_compressor_bzip2_finish(struct archive_write *);
+static int	archive_compressor_bzip2_init(struct archive_write *);
+static int	archive_compressor_bzip2_write(struct archive_write *,
+		    const void *, size_t);
+static int	drive_compressor(struct archive_write *, struct private_data *,
 		    int finishing);
 
 /*
  * Allocate, initialize and return an archive object.
  */
 int
-archive_write_set_compression_bzip2(struct archive *a)
+archive_write_set_compression_bzip2(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
-	a->compression_init = &archive_compressor_bzip2_init;
-	a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
-	a->compression_name = "bzip2";
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2");
+	a->compressor.init = &archive_compressor_bzip2_init;
 	return (ARCHIVE_OK);
 }
 
@@ -86,23 +87,23 @@
  * Setup callback.
  */
 static int
-archive_compressor_bzip2_init(struct archive *a)
+archive_compressor_bzip2_init(struct archive_write *a)
 {
 	int ret;
 	struct private_data *state;
 
-	a->compression_code = ARCHIVE_COMPRESSION_BZIP2;
-	a->compression_name = "bzip2";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
+	a->archive.compression_name = "bzip2";
 
 	if (a->client_opener != NULL) {
-		ret = (a->client_opener)(a, a->client_data);
+		ret = (a->client_opener)(&a->archive, a->client_data);
 		if (ret != 0)
 			return (ret);
 	}
 
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for compression");
 		return (ARCHIVE_FATAL);
 	}
@@ -112,7 +113,7 @@
 	state->compressed = (char *)malloc(state->compressed_buffer_size);
 
 	if (state->compressed == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for compression buffer");
 		free(state);
 		return (ARCHIVE_FATAL);
@@ -120,18 +121,18 @@
 
 	state->stream.next_out = state->compressed;
 	state->stream.avail_out = state->compressed_buffer_size;
-	a->compression_write = archive_compressor_bzip2_write;
-	a->compression_finish = archive_compressor_bzip2_finish;
+	a->compressor.write = archive_compressor_bzip2_write;
+	a->compressor.finish = archive_compressor_bzip2_finish;
 
 	/* Initialize compression library */
 	ret = BZ2_bzCompressInit(&(state->stream), 9, 0, 30);
 	if (ret == BZ_OK) {
-		a->compression_data = state;
+		a->compressor.data = state;
 		return (ARCHIVE_OK);
 	}
 
 	/* Library setup failed: clean up. */
-	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 	    "Internal error initializing compression library");
 	free(state->compressed);
 	free(state);
@@ -139,17 +140,17 @@
 	/* Override the error message if we know what really went wrong. */
 	switch (ret) {
 	case BZ_PARAM_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid setup parameter");
 		break;
 	case BZ_MEM_ERROR:
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Internal error initializing compression library: "
 		    "out of memory");
 		break;
 	case BZ_CONFIG_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "mis-compiled library");
 		break;
@@ -165,14 +166,14 @@
  * Returns ARCHIVE_OK if all data written, error otherwise.
  */
 static int
-archive_compressor_bzip2_write(struct archive *a, const void *buff,
+archive_compressor_bzip2_write(struct archive_write *a, const void *buff,
     size_t length)
 {
 	struct private_data *state;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->compressor.data;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -186,7 +187,7 @@
 	state->stream.avail_in = length;
 	if (drive_compressor(a, state, 0))
 		return (ARCHIVE_FATAL);
-	a->file_position += length;
+	a->archive.file_position += length;
 	return (ARCHIVE_OK);
 }
 
@@ -195,7 +196,7 @@
  * Finish the compression.
  */
 static int
-archive_compressor_bzip2_finish(struct archive *a)
+archive_compressor_bzip2_finish(struct archive_write *a)
 {
 	ssize_t block_length;
 	int ret;
@@ -204,10 +205,10 @@
 	ssize_t bytes_written;
 	unsigned tocopy;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->compressor.data;
 	ret = ARCHIVE_OK;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?\n"
 		    "This is probably an internal programming error.");
 		ret = ARCHIVE_FATAL;
@@ -257,14 +258,14 @@
 	}
 
 	/* Write the last block */
-	bytes_written = (a->client_writer)(a, a->client_data,
+	bytes_written = (a->client_writer)(&a->archive, a->client_data,
 	    state->compressed, block_length);
 
 	/* TODO: Handle short write of final block. */
 	if (bytes_written <= 0)
 		ret = ARCHIVE_FATAL;
 	else {
-		a->raw_position += ret;
+		a->archive.raw_position += ret;
 		ret = ARCHIVE_OK;
 	}
 
@@ -274,18 +275,13 @@
 	case BZ_OK:
 		break;
 	default:
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "Failed to clean up compressor");
 		ret = ARCHIVE_FATAL;
 	}
 
 	free(state->compressed);
 	free(state);
-
-	/* Close the output */
-	if (a->client_closer != NULL)
-		(a->client_closer)(a, a->client_data);
-
 	return (ret);
 }
 
@@ -297,15 +293,16 @@
  * false) and the end-of-archive case (finishing == true).
  */
 static int
-drive_compressor(struct archive *a, struct private_data *state, int finishing)
+drive_compressor(struct archive_write *a, struct private_data *state, int finishing)
 {
 	ssize_t	bytes_written;
 	int ret;
 
 	for (;;) {
 		if (state->stream.avail_out == 0) {
-			bytes_written = (a->client_writer)(a, a->client_data,
-			    state->compressed, state->compressed_buffer_size);
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, state->compressed,
+			    state->compressed_buffer_size);
 			if (bytes_written <= 0) {
 				/* TODO: Handle this write failure */
 				return (ARCHIVE_FATAL);
@@ -317,7 +314,7 @@
 				    state->compressed_buffer_size - bytes_written);
 			}
 
-			a->raw_position += bytes_written;
+			a->archive.raw_position += bytes_written;
 			state->stream.next_out = state->compressed +
 			    state->compressed_buffer_size - bytes_written;
 			state->stream.avail_out = bytes_written;
@@ -340,7 +337,8 @@
 			return (ARCHIVE_OK);
 		default:
 			/* Any other return value indicates an error */
-			archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
 			    "Bzip2 compression failed");
 			return (ARCHIVE_FATAL);
 		}
Index: archive_read_support_compression_gzip.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_gzip.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_compression_gzip.c -Llib/libarchive/archive_read_support_compression_gzip.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_compression_gzip.c
+++ lib/libarchive/archive_read_support_compression_gzip.c
@@ -25,7 +25,7 @@
 
 #include "archive_platform.h"
 
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.9.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
 
 
 #ifdef HAVE_ERRNO_H
@@ -46,6 +46,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 #ifdef HAVE_ZLIB_H
 struct private_data {
@@ -56,22 +57,26 @@
 	int64_t		 total_out;
 	unsigned long	 crc;
 	char		 header_done;
+	char		 eof; /* True = found end of compressed data. */
 };
 
-static int	finish(struct archive *);
-static ssize_t	read_ahead(struct archive *, const void **, size_t);
-static ssize_t	read_consume(struct archive *, size_t);
-static int	drive_decompressor(struct archive *a, struct private_data *);
+static int	finish(struct archive_read *);
+static ssize_t	read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t	read_consume(struct archive_read *, size_t);
+static int	drive_decompressor(struct archive_read *a, struct private_data *);
 #endif
 
-/* These two functions are defined even if we lack zlib.  See below. */
+/* These two functions are defined even if we lack the library.  See below. */
 static int	bid(const void *, size_t);
-static int	init(struct archive *, const void *, size_t);
+static int	init(struct archive_read *, const void *, size_t);
 
 int
-archive_read_support_compression_gzip(struct archive *a)
+archive_read_support_compression_gzip(struct archive *_a)
 {
-	return (__archive_read_register_compression(a, bid, init));
+	struct archive_read *a = (struct archive_read *)_a;
+	if (__archive_read_register_compression(a, bid, init) != NULL)
+		return (ARCHIVE_OK);
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -130,12 +135,12 @@
 #ifndef HAVE_ZLIB_H
 
 /*
- * If we don't have zlib on this system, we can't actually do the
- * decompression.  We can, however, still detect gzip-compressed
- * archives and emit a useful message.
+ * If we don't have the library on this system, we can't actually do the
+ * decompression.  We can, however, still detect compressed archives
+ * and emit a useful message.
  */
 static int
-init(struct archive *a, const void *buff, size_t n)
+init(struct archive_read *a, const void *buff, size_t n)
 {
 	(void)a;	/* UNUSED */
 	(void)buff;	/* UNUSED */
@@ -153,19 +158,19 @@
  * Setup the callbacks.
  */
 static int
-init(struct archive *a, const void *buff, size_t n)
+init(struct archive_read *a, const void *buff, size_t n)
 {
 	struct private_data *state;
 	int ret;
 
-	a->compression_code = ARCHIVE_COMPRESSION_GZIP;
-	a->compression_name = "gzip";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
+	a->archive.compression_name = "gzip";
 
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for %s decompression",
-		    a->compression_name);
+		    a->archive.compression_name);
 		return (ARCHIVE_FATAL);
 	}
 	memset(state, 0, sizeof(*state));
@@ -180,9 +185,9 @@
 	state->stream.avail_out = state->uncompressed_buffer_size;
 
 	if (state->uncompressed_buffer == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate %s decompression buffers",
-		    a->compression_name);
+		    a->archive.compression_name);
 		free(state);
 		return (ARCHIVE_FATAL);
 	}
@@ -196,10 +201,10 @@
 	state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
 	state->stream.avail_in = n;
 
-	a->compression_read_ahead = read_ahead;
-	a->compression_read_consume = read_consume;
-	a->compression_skip = NULL; /* not supported */
-	a->compression_finish = finish;
+	a->decompressor->read_ahead = read_ahead;
+	a->decompressor->consume = read_consume;
+	a->decompressor->skip = NULL; /* not supported */
+	a->decompressor->finish = finish;
 
 	/*
 	 * TODO: Do I need to parse the gzip header before calling
@@ -215,30 +220,31 @@
 	ret = inflateInit2(&(state->stream),
 	    -15 /* Don't check for zlib header */);
 	if (ret == Z_OK) {
-		a->compression_data = state;
+		a->decompressor->data = state;
 		return (ARCHIVE_OK);
 	}
 
 	/* Library setup failed: Clean up. */
-	archive_set_error(a, ARCHIVE_ERRNO_MISC,
-	    "Internal error initializing %s library", a->compression_name);
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Internal error initializing %s library",
+	    a->archive.compression_name);
 	free(state->uncompressed_buffer);
 	free(state);
 
 	/* Override the error message if we know what really went wrong. */
 	switch (ret) {
 	case Z_STREAM_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid setup parameter");
 		break;
 	case Z_MEM_ERROR:
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Internal error initializing compression library: "
 		    "out of memory");
 		break;
 	case Z_VERSION_ERROR:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Internal error initializing compression library: "
 		    "invalid library version");
 		break;
@@ -252,15 +258,15 @@
  * as necessary.
  */
 static ssize_t
-read_ahead(struct archive *a, const void **p, size_t min)
+read_ahead(struct archive_read *a, const void **p, size_t min)
 {
 	struct private_data *state;
-	int read_avail, was_avail, ret;
+	size_t read_avail, was_avail;
+	int ret;
 
-	state = (struct private_data *)a->compression_data;
-	was_avail = -1;
+	state = (struct private_data *)a->decompressor->data;
 	if (!a->client_reader) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No read callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -277,13 +283,16 @@
 		    = state->uncompressed_buffer_size - read_avail;
 	}
 
-	while (was_avail < read_avail &&	/* Made some progress. */
-	    read_avail < (int)min &&		/* Haven't satisfied min. */
-	    read_avail < (int)state->uncompressed_buffer_size) { /* !full */
-		if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
-			return (ret);
+	while (read_avail < min &&		/* Haven't satisfied min. */
+	    read_avail < state->uncompressed_buffer_size) { /* !full */
 		was_avail = read_avail;
+		if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK)
+			return (ret);
+		if (ret == ARCHIVE_EOF)
+			break; /* Break on EOF even if we haven't met min. */
 		read_avail = state->stream.next_out - state->read_next;
+		if (was_avail == read_avail) /* No progress? */
+			break;
 	}
 
 	*p = state->read_next;
@@ -294,12 +303,12 @@
  * Mark a previously-returned block of data as read.
  */
 static ssize_t
-read_consume(struct archive *a, size_t n)
+read_consume(struct archive_read *a, size_t n)
 {
 	struct private_data *state;
 
-	state = (struct private_data *)a->compression_data;
-	a->file_position += n;
+	state = (struct private_data *)a->decompressor->data;
+	a->archive.file_position += n;
 	state->read_next += n;
 	if (state->read_next > state->stream.next_out)
 		__archive_errx(1, "Request to consume too many "
@@ -311,29 +320,27 @@
  * Clean up the decompressor.
  */
 static int
-finish(struct archive *a)
+finish(struct archive_read *a)
 {
 	struct private_data *state;
 	int ret;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->decompressor->data;
 	ret = ARCHIVE_OK;
 	switch (inflateEnd(&(state->stream))) {
 	case Z_OK:
 		break;
 	default:
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
-		    "Failed to clean up %s compressor", a->compression_name);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up %s compressor",
+		    a->archive.compression_name);
 		ret = ARCHIVE_FATAL;
 	}
 
 	free(state->uncompressed_buffer);
 	free(state);
 
-	a->compression_data = NULL;
-	if (a->client_closer != NULL)
-		(a->client_closer)(a, a->client_data);
-
+	a->decompressor->data = NULL;
 	return (ret);
 }
 
@@ -342,22 +349,27 @@
  * blocks as necessary.
  */
 static int
-drive_decompressor(struct archive *a, struct private_data *state)
+drive_decompressor(struct archive_read *a, struct private_data *state)
 {
 	ssize_t ret;
-	int decompressed, total_decompressed;
+	size_t decompressed, total_decompressed;
 	int count, flags, header_state;
 	unsigned char *output;
 	unsigned char b;
+	const void *read_buf;
 
+	if (state->eof)
+		return (ARCHIVE_EOF);
 	flags = 0;
 	count = 0;
 	header_state = 0;
 	total_decompressed = 0;
 	for (;;) {
 		if (state->stream.avail_in == 0) {
-			ret = (a->client_reader)(a, a->client_data,
-			    (const void **)&state->stream.next_in);
+			read_buf = state->stream.next_in;
+			ret = (a->client_reader)(&a->archive, a->client_data,
+			    &read_buf);
+			state->stream.next_in = (unsigned char *)(uintptr_t)read_buf;
 			if (ret < 0) {
 				/*
 				 * TODO: Find a better way to handle
@@ -366,12 +378,12 @@
 				goto fatal;
 			}
 			if (ret == 0  &&  total_decompressed == 0) {
-				archive_set_error(a, EIO,
+				archive_set_error(&a->archive, EIO,
 				    "Premature end of %s compressed data",
-				    a->compression_name);
+				    a->archive.compression_name);
 				return (ARCHIVE_FATAL);
 			}
-			a->raw_position += ret;
+			a->archive.raw_position += ret;
 			state->stream.avail_in = ret;
 		}
 
@@ -517,12 +529,10 @@
 				 * TODO: Verify gzip trailer
 				 * (uncompressed length and CRC).
 				 */
+				state->eof = 1;
 				return (ARCHIVE_OK);
 			default:
 				/* Any other return value is an error. */
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
-				    "gzip decompression failed (%s)",
-				    state->stream.msg);
 				goto fatal;
 			}
 		}
@@ -531,8 +541,8 @@
 
 	/* Return a fatal error. */
 fatal:
-	archive_set_error(a, ARCHIVE_ERRNO_MISC, "%s decompression failed",
-	    a->compression_name);
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "%s decompression failed", a->archive.compression_name);
 	return (ARCHIVE_FATAL);
 }
 
Index: archive_write_set_format_shar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_shar.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format_shar.c -Llib/libarchive/archive_write_set_format_shar.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format_shar.c
+++ lib/libarchive/archive_write_set_format_shar.c
@@ -24,11 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.11.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_shar.c,v 1.18 2007/05/29 01:00:19 kientzle Exp $");
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -44,6 +41,7 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
 struct shar {
 	int			 dump;
@@ -60,19 +58,20 @@
 	struct archive_string	 work;
 };
 
-static int	archive_write_shar_finish(struct archive *);
-static int	archive_write_shar_header(struct archive *,
+static int	archive_write_shar_finish(struct archive_write *);
+static int	archive_write_shar_destroy(struct archive_write *);
+static int	archive_write_shar_header(struct archive_write *,
 		    struct archive_entry *);
-static ssize_t	archive_write_shar_data_sed(struct archive *,
+static ssize_t	archive_write_shar_data_sed(struct archive_write *,
 		    const void * buff, size_t);
-static ssize_t	archive_write_shar_data_uuencode(struct archive *,
+static ssize_t	archive_write_shar_data_uuencode(struct archive_write *,
 		    const void * buff, size_t);
-static int	archive_write_shar_finish_entry(struct archive *);
-static int	shar_printf(struct archive *, const char *fmt, ...);
+static int	archive_write_shar_finish_entry(struct archive_write *);
+static int	shar_printf(struct archive_write *, const char *fmt, ...);
 static void	uuencode_group(struct shar *);
 
 static int
-shar_printf(struct archive *a, const char *fmt, ...)
+shar_printf(struct archive_write *a, const char *fmt, ...)
 {
 	struct shar *shar;
 	va_list ap;
@@ -82,7 +81,7 @@
 	va_start(ap, fmt);
 	archive_string_empty(&(shar->work));
 	archive_string_vsprintf(&(shar->work), fmt, ap);
-	ret = ((a->compression_write)(a, shar->work.s, strlen(shar->work.s)));
+	ret = ((a->compressor.write)(a, shar->work.s, strlen(shar->work.s)));
 	va_end(ap);
 	return (ret);
 }
@@ -91,17 +90,18 @@
  * Set output format to 'shar' format.
  */
 int
-archive_write_set_format_shar(struct archive *a)
+archive_write_set_format_shar(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	struct shar *shar;
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_finish != NULL)
-		(a->format_finish)(a);
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
 
 	shar = (struct shar *)malloc(sizeof(*shar));
 	if (shar == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate shar data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(shar, 0, sizeof(*shar));
@@ -110,6 +110,7 @@
 	a->pad_uncompressed = 0;
 	a->format_write_header = archive_write_shar_header;
 	a->format_finish = archive_write_shar_finish;
+	a->format_destroy = archive_write_shar_destroy;
 	a->format_write_data = archive_write_shar_data_sed;
 	a->format_finish_entry = archive_write_shar_finish_entry;
 	a->archive_format = ARCHIVE_FORMAT_SHAR_BASE;
@@ -124,11 +125,12 @@
  * and other extended file information.
  */
 int
-archive_write_set_format_shar_dump(struct archive *a)
+archive_write_set_format_shar_dump(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	struct shar *shar;
 
-	archive_write_set_format_shar(a);
+	archive_write_set_format_shar(&a->archive);
 	shar = (struct shar *)a->format_data;
 	shar->dump = 1;
 	a->format_write_data = archive_write_shar_data_uuencode;
@@ -138,13 +140,12 @@
 }
 
 static int
-archive_write_shar_header(struct archive *a, struct archive_entry *entry)
+archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
 {
 	const char *linkname;
 	const char *name;
 	char *p, *pp;
 	struct shar *shar;
-	const struct stat *st;
 	int ret;
 
 	shar = (struct shar *)a->format_data;
@@ -163,22 +164,21 @@
 		archive_entry_free(shar->entry);
 	shar->entry = archive_entry_clone(entry);
 	name = archive_entry_pathname(entry);
-	st = archive_entry_stat(entry);
 
 	/* Handle some preparatory issues. */
-	switch(st->st_mode & S_IFMT) {
-	case S_IFREG:
+	switch(archive_entry_filetype(entry)) {
+	case AE_IFREG:
 		/* Only regular files have non-zero size. */
 		break;
-	case S_IFDIR:
+	case AE_IFDIR:
 		archive_entry_set_size(entry, 0);
 		/* Don't bother trying to recreate '.' */
 		if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
 			return (ARCHIVE_OK);
 		break;
-	case S_IFIFO:
-	case S_IFCHR:
-	case S_IFBLK:
+	case AE_IFIFO:
+	case AE_IFCHR:
+	case AE_IFBLK:
 		/* All other file types have zero size in the archive. */
 		archive_entry_set_size(entry, 0);
 		break;
@@ -186,7 +186,7 @@
 		archive_entry_set_size(entry, 0);
 		if (archive_entry_hardlink(entry) == NULL &&
 		    archive_entry_symlink(entry) == NULL) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "shar format cannot archive this");
 			return (ARCHIVE_WARN);
 		}
@@ -197,7 +197,7 @@
 	if (ret != ARCHIVE_OK)
 		return (ret);
 
-	if (!S_ISDIR(st->st_mode)) {
+	if (archive_entry_filetype(entry) != AE_IFDIR) {
 		/* Try to create the dir. */
 		p = strdup(name);
 		pp = strrchr(p, '/');
@@ -208,11 +208,14 @@
 			/* Try to avoid a lot of redundant mkdir commands. */
 			if (strcmp(p, ".") == 0) {
 				/* Don't try to "mkdir ." */
+				free(p);
 			} else if (shar->last_dir == NULL) {
 				ret = shar_printf(a,
 				    "mkdir -p %s > /dev/null 2>&1\n", p);
-				if (ret != ARCHIVE_OK)
+				if (ret != ARCHIVE_OK) {
+					free(p);
 					return (ret);
+				}
 				shar->last_dir = p;
 			} else if (strcmp(p, shar->last_dir) == 0) {
 				/* We've already created this exact dir. */
@@ -224,11 +227,15 @@
 			} else {
 				ret = shar_printf(a,
 				    "mkdir -p %s > /dev/null 2>&1\n", p);
-				if (ret != ARCHIVE_OK)
+				if (ret != ARCHIVE_OK) {
+					free(p);
 					return (ret);
+				}
 				free(shar->last_dir);
 				shar->last_dir = p;
 			}
+		} else {
+			free(p);
 		}
 	}
 
@@ -243,8 +250,8 @@
 		if (ret != ARCHIVE_OK)
 			return (ret);
 	} else {
-		switch(st->st_mode & S_IFMT) {
-		case S_IFREG:
+		switch(archive_entry_filetype(entry)) {
+		case AE_IFREG:
 			if (archive_entry_size(entry) == 0) {
 				/* More portable than "touch." */
 				ret = shar_printf(a, "test -e \"%s\" || :> \"%s\"\n", name, name);
@@ -275,7 +282,7 @@
 				shar->outbytes = 0;
 			}
 			break;
-		case S_IFDIR:
+		case AE_IFDIR:
 			ret = shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n",
 			    name);
 			if (ret != ARCHIVE_OK)
@@ -294,19 +301,19 @@
 			 * up at end of archive.
 			 */
 			break;
-		case S_IFIFO:
+		case AE_IFIFO:
 			ret = shar_printf(a, "mkfifo %s\n", name);
 			if (ret != ARCHIVE_OK)
 				return (ret);
 			break;
-		case S_IFCHR:
+		case AE_IFCHR:
 			ret = shar_printf(a, "mknod %s c %d %d\n", name,
 			    archive_entry_rdevmajor(entry),
 			    archive_entry_rdevminor(entry));
 			if (ret != ARCHIVE_OK)
 				return (ret);
 			break;
-		case S_IFBLK:
+		case AE_IFBLK:
 			ret = shar_printf(a, "mknod %s b %d %d\n", name,
 			    archive_entry_rdevmajor(entry),
 			    archive_entry_rdevminor(entry));
@@ -323,7 +330,7 @@
 
 /* XXX TODO: This could be more efficient XXX */
 static ssize_t
-archive_write_shar_data_sed(struct archive *a, const void *buff, size_t n)
+archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
 {
 	struct shar *shar;
 	const char *src;
@@ -347,7 +354,7 @@
 		shar->outbuff[shar->outpos++] = *src++;
 
 		if (shar->outpos > sizeof(shar->outbuff) - 2) {
-			ret = (a->compression_write)(a, shar->outbuff,
+			ret = (a->compressor.write)(a, shar->outbuff,
 			    shar->outpos);
 			if (ret != ARCHIVE_OK)
 				return (ret);
@@ -356,7 +363,7 @@
 	}
 
 	if (shar->outpos > 0)
-		ret = (a->compression_write)(a, shar->outbuff, shar->outpos);
+		ret = (a->compressor.write)(a, shar->outbuff, shar->outpos);
 	if (ret != ARCHIVE_OK)
 		return (ret);
 	return (written);
@@ -387,7 +394,7 @@
 }
 
 static ssize_t
-archive_write_shar_data_uuencode(struct archive *a, const void *buff,
+archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
     size_t length)
 {
 	struct shar *shar;
@@ -419,7 +426,7 @@
 }
 
 static int
-archive_write_shar_finish_entry(struct archive *a)
+archive_write_shar_finish_entry(struct archive_write *a)
 {
 	const char *g, *p, *u;
 	struct shar *shar;
@@ -504,7 +511,7 @@
 }
 
 static int
-archive_write_shar_finish(struct archive *a)
+archive_write_shar_finish(struct archive_write *a)
 {
 	struct shar *shar;
 	int ret;
@@ -527,12 +534,21 @@
 		if (ret != ARCHIVE_OK)
 			return (ret);
 		/* Shar output is never padded. */
-		archive_write_set_bytes_in_last_block(a, 1);
+		archive_write_set_bytes_in_last_block(&a->archive, 1);
 		/*
 		 * TODO: shar should also suppress padding of
 		 * uncompressed data within gzip/bzip2 streams.
 		 */
 	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_destroy(struct archive_write *a)
+{
+	struct shar *shar;
+
+	shar = (struct shar *)a->format_data;
 	if (shar->entry != NULL)
 		archive_entry_free(shar->entry);
 	if (shar->last_dir != NULL)
Index: archive_read_open_file.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_open_file.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_open_file.c -Llib/libarchive/archive_read_open_file.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_open_file.c
+++ lib/libarchive/archive_read_open_file.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_file.c,v 1.8.2.3 2007/01/27 06:44:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_file.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -51,6 +51,7 @@
 	FILE    *f;
 	size_t	 block_size;
 	void	*buffer;
+	char	 can_skip;
 };
 
 static int	file_close(struct archive *, void *);
@@ -80,6 +81,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	mine->f = f;
+	/* Suppress skip by default. See below. */
+	mine->can_skip = 0;
 	return (archive_read_open2(a, mine, file_open, file_read,
 		    file_skip, file_close));
 }
@@ -95,8 +98,11 @@
 	 * it's not a file.  (FILE * objects can wrap many kinds
 	 * of I/O streams.)
 	 */
-	if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode))
+	if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
 		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+		/* Enable the seek optimization for regular files. */
+		mine->can_skip = 1;
+	}
 
 	return (ARCHIVE_OK);
 }
@@ -125,21 +131,25 @@
 {
 	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
 
+	(void)a; /* UNUSED */
+
 	/*
-	 * Note: the 'fd' and 'filename' versions round the request
-	 * down to a multiple of the block size to ensure proper
-	 * operation on block-oriented media such as tapes.  But stdio
-	 * doesn't work with such media (it doesn't ensure blocking),
-	 * so we don't need to bother.
+	 * If we can't skip, return 0 as the amount we did step and
+	 * the caller will work around by reading and discarding.
 	 */
+	if (!mine->can_skip)
+		return (0);
+	if (request == 0)
+		return (0);
+
 #if HAVE_FSEEKO
 	if (fseeko(mine->f, request, SEEK_CUR) != 0)
 #else
 	if (fseek(mine->f, request, SEEK_CUR) != 0)
 #endif
 	{
-		archive_set_error(a, errno, "Error skipping forward");
-		return (ARCHIVE_FATAL);
+		mine->can_skip = 0;
+		return (0);
 	}
 	return (request);
 }
--- /dev/null
+++ lib/libarchive/archive_read_private.h
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_read_private.h,v 1.3 2007/05/29 01:00:18 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
+#define	ARCHIVE_READ_PRIVATE_H_INCLUDED
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_read {
+	struct archive	archive;
+
+	struct archive_entry	*entry;
+
+	/* Dev/ino of the archive being read/written. */
+	dev_t		  skip_file_dev;
+	ino_t		  skip_file_ino;
+
+	/* Utility:  Pointer to a block of nulls. */
+	const unsigned char	*nulls;
+	size_t			 null_length;
+
+	/*
+	 * Used by archive_read_data() to track blocks and copy
+	 * data to client buffers, filling gaps with zero bytes.
+	 */
+	const char	 *read_data_block;
+	off_t		  read_data_offset;
+	off_t		  read_data_output_offset;
+	size_t		  read_data_remaining;
+
+	/* Callbacks to open/read/write/close archive stream. */
+	archive_open_callback	*client_opener;
+	archive_read_callback	*client_reader;
+	archive_skip_callback	*client_skipper;
+	archive_write_callback	*client_writer;
+	archive_close_callback	*client_closer;
+	void			*client_data;
+
+	/*
+	 * Blocking information.  Note that bytes_in_last_block is
+	 * misleadingly named; I should find a better name.  These
+	 * control the final output from all compressors, including
+	 * compression_none.
+	 */
+	int		  bytes_per_block;
+	int		  bytes_in_last_block;
+
+	/*
+	 * These control whether data within a gzip/bzip2 compressed
+	 * stream gets padded or not.  If pad_uncompressed is set,
+	 * the data will be padded to a full block before being
+	 * compressed.  The pad_uncompressed_byte determines the value
+	 * that will be used for padding.  Note that these have no
+	 * effect on compression "none."
+	 */
+	int		  pad_uncompressed;
+	int		  pad_uncompressed_byte; /* TODO: Support this. */
+
+	/* File offset of beginning of most recently-read header. */
+	off_t		  header_position;
+
+	/*
+	 * Decompressors have a very specific lifecycle:
+	 *    public setup function initializes a slot in this table
+	 *    'config' holds minimal configuration data
+	 *    bid() examines a block of data and returns a bid [1]
+	 *    init() is called for successful bidder
+	 *    'data' is initialized by init()
+	 *    read() returns a pointer to the next block of data
+	 *    consume() indicates how much data is used
+	 *    skip() ignores bytes of data
+	 *    finish() cleans up and frees 'data' and 'config'
+	 *
+	 * [1] General guideline: bid the number of bits that you actually
+	 * test, e.g., 16 if you test a 2-byte magic value.
+	 */
+	struct decompressor_t {
+		void *config;
+		void *data;
+		int	(*bid)(const void *buff, size_t);
+		int	(*init)(struct archive_read *,
+			    const void *buff, size_t);
+		int	(*finish)(struct archive_read *);
+		ssize_t	(*read_ahead)(struct archive_read *,
+			    const void **, size_t);
+		ssize_t	(*consume)(struct archive_read *, size_t);
+		off_t	(*skip)(struct archive_read *, off_t);
+	}	decompressors[4];
+
+	/* Pointer to current decompressor. */
+	struct decompressor_t *decompressor;
+
+	/*
+	 * Format detection is mostly the same as compression
+	 * detection, with two significant differences: The bidders
+	 * use the read_ahead calls above to examine the stream rather
+	 * than having the supervisor hand them a block of data to
+	 * examine, and the auction is repeated for every header.
+	 * Winning bidders should set the archive_format and
+	 * archive_format_name appropriately.  Bid routines should
+	 * check archive_format and decline to bid if the format of
+	 * the last header was incompatible.
+	 *
+	 * Again, write support is considerably simpler because there's
+	 * no need for an auction.
+	 */
+
+	struct archive_format_descriptor {
+		void	 *data;
+		int	(*bid)(struct archive_read *);
+		int	(*read_header)(struct archive_read *, struct archive_entry *);
+		int	(*read_data)(struct archive_read *, const void **, size_t *, off_t *);
+		int	(*read_data_skip)(struct archive_read *);
+		int	(*cleanup)(struct archive_read *);
+	}	formats[8];
+	struct archive_format_descriptor	*format; /* Active format. */
+
+	/*
+	 * Pointers to format-specific functions for writing.  They're
+	 * initialized by archive_write_set_format_XXX() calls.
+	 */
+	int	(*format_init)(struct archive *); /* Only used on write. */
+	int	(*format_finish)(struct archive *);
+	int	(*format_finish_entry)(struct archive *);
+	int 	(*format_write_header)(struct archive *,
+		    struct archive_entry *);
+	ssize_t	(*format_write_data)(struct archive *,
+		    const void *buff, size_t);
+
+	/*
+	 * Various information needed by archive_extract.
+	 */
+	struct extract		 *extract;
+	int			(*cleanup_archive_extract)(struct archive_read *);
+};
+
+int	__archive_read_register_format(struct archive_read *a,
+	    void *format_data,
+	    int (*bid)(struct archive_read *),
+	    int (*read_header)(struct archive_read *, struct archive_entry *),
+	    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+	    int (*read_data_skip)(struct archive_read *),
+	    int (*cleanup)(struct archive_read *));
+
+struct decompressor_t
+	*__archive_read_register_compression(struct archive_read *a,
+	    int (*bid)(const void *, size_t),
+	    int (*init)(struct archive_read *, const void *, size_t));
+
+#endif
--- /dev/null
+++ lib/libarchive/filter_fork.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/filter_fork.h,v 1.1 2007/05/29 01:00:20 kientzle Exp $
+ */
+
+#ifndef FILTER_FORK_H
+#define FILTER_FORK_H
+
+pid_t
+__archive_create_child(const char *path, int *child_stdin, int *child_stdout);
+
+void
+__archive_check_child(int in, int out);
+
+#endif
Index: archive_write_set_format_cpio.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_cpio.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format_cpio.c -Llib/libarchive/archive_write_set_format_cpio.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format_cpio.c
+++ lib/libarchive/archive_write_set_format_cpio.c
@@ -24,11 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.5.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio.c,v 1.11 2007/05/29 01:00:19 kientzle Exp $");
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -43,12 +40,14 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
-static ssize_t	archive_write_cpio_data(struct archive *, const void *buff,
-		    size_t s);
-static int	archive_write_cpio_finish(struct archive *);
-static int	archive_write_cpio_finish_entry(struct archive *);
-static int	archive_write_cpio_header(struct archive *,
+static ssize_t	archive_write_cpio_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_cpio_finish(struct archive_write *);
+static int	archive_write_cpio_destroy(struct archive_write *);
+static int	archive_write_cpio_finish_entry(struct archive_write *);
+static int	archive_write_cpio_header(struct archive_write *,
 		    struct archive_entry *);
 static int	format_octal(int64_t, void *, int);
 static int64_t	format_octal_recursive(int64_t, char *, int);
@@ -75,17 +74,18 @@
  * Set output format to 'cpio' format.
  */
 int
-archive_write_set_format_cpio(struct archive *a)
+archive_write_set_format_cpio(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	struct cpio *cpio;
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_finish != NULL)
-		(a->format_finish)(a);
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
 
 	cpio = (struct cpio *)malloc(sizeof(*cpio));
 	if (cpio == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate cpio data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(cpio, 0, sizeof(*cpio));
@@ -96,18 +96,18 @@
 	a->format_write_data = archive_write_cpio_data;
 	a->format_finish_entry = archive_write_cpio_finish_entry;
 	a->format_finish = archive_write_cpio_finish;
+	a->format_destroy = archive_write_cpio_destroy;
 	a->archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
 	a->archive_format_name = "POSIX cpio";
 	return (ARCHIVE_OK);
 }
 
 static int
-archive_write_cpio_header(struct archive *a, struct archive_entry *entry)
+archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
 {
 	struct cpio *cpio;
 	const char *p, *path;
 	int pathlength, ret;
-	const struct stat	*st;
 	struct cpio_header	 h;
 
 	cpio = (struct cpio *)a->format_data;
@@ -115,31 +115,31 @@
 
 	path = archive_entry_pathname(entry);
 	pathlength = strlen(path) + 1; /* Include trailing null. */
-	st = archive_entry_stat(entry);
 
 	memset(&h, 0, sizeof(h));
 	format_octal(070707, &h.c_magic, sizeof(h.c_magic));
-	format_octal(st->st_dev, &h.c_dev, sizeof(h.c_dev));
+	format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev));
 	/*
 	 * TODO: Generate artificial inode numbers rather than just
 	 * re-using the ones off the disk.  That way, the 18-bit c_ino
 	 * field only limits the number of files in the archive.
 	 */
-	if (st->st_ino > 0777777) {
-		archive_set_error(a, ERANGE, "large inode number truncated");
+	if (archive_entry_ino(entry) > 0777777) {
+		archive_set_error(&a->archive, ERANGE, "large inode number truncated");
 		ret = ARCHIVE_WARN;
 	}
 
-	format_octal(st->st_ino & 0777777, &h.c_ino, sizeof(h.c_ino));
-	format_octal(st->st_mode, &h.c_mode, sizeof(h.c_mode));
-	format_octal(st->st_uid, &h.c_uid, sizeof(h.c_uid));
-	format_octal(st->st_gid, &h.c_gid, sizeof(h.c_gid));
-	format_octal(st->st_nlink, &h.c_nlink, sizeof(h.c_nlink));
-	if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode))
-	    format_octal(st->st_rdev, &h.c_rdev, sizeof(h.c_rdev));
+	format_octal(archive_entry_ino(entry) & 0777777, &h.c_ino, sizeof(h.c_ino));
+	format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
+	format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
+	format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
+	format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR)
+	    format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev));
 	else
 	    format_octal(0, &h.c_rdev, sizeof(h.c_rdev));
-	format_octal(st->st_mtime, &h.c_mtime, sizeof(h.c_mtime));
+	format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
 	format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize));
 
 	/* Symlinks get the link written as the body of the entry. */
@@ -147,27 +147,27 @@
 	if (p != NULL  &&  *p != '\0')
 		format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
 	else
-		format_octal(st->st_size, &h.c_filesize, sizeof(h.c_filesize));
+		format_octal(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize));
 
-	ret = (a->compression_write)(a, &h, sizeof(h));
+	ret = (a->compressor.write)(a, &h, sizeof(h));
 	if (ret != ARCHIVE_OK)
 		return (ARCHIVE_FATAL);
 
-	ret = (a->compression_write)(a, path, pathlength);
+	ret = (a->compressor.write)(a, path, pathlength);
 	if (ret != ARCHIVE_OK)
 		return (ARCHIVE_FATAL);
 
-	cpio->entry_bytes_remaining = st->st_size;
+	cpio->entry_bytes_remaining = archive_entry_size(entry);
 
 	/* Write the symlink now. */
 	if (p != NULL  &&  *p != '\0')
-		ret = (a->compression_write)(a, p, strlen(p));
+		ret = (a->compressor.write)(a, p, strlen(p));
 
 	return (ret);
 }
 
 static ssize_t
-archive_write_cpio_data(struct archive *a, const void *buff, size_t s)
+archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)
 {
 	struct cpio *cpio;
 	int ret;
@@ -176,7 +176,7 @@
 	if (s > cpio->entry_bytes_remaining)
 		s = cpio->entry_bytes_remaining;
 
-	ret = (a->compression_write)(a, buff, s);
+	ret = (a->compressor.write)(a, buff, s);
 	cpio->entry_bytes_remaining -= s;
 	if (ret >= 0)
 		return (s);
@@ -215,29 +215,34 @@
 }
 
 static int
-archive_write_cpio_finish(struct archive *a)
+archive_write_cpio_finish(struct archive_write *a)
 {
 	struct cpio *cpio;
-	struct stat st;
 	int er;
 	struct archive_entry *trailer;
 
 	cpio = (struct cpio *)a->format_data;
 	trailer = archive_entry_new();
-	memset(&st, 0, sizeof(st));
-	st.st_nlink = 1;
-	archive_entry_copy_stat(trailer, &st);
+	archive_entry_set_nlink(trailer, 1);
 	archive_entry_set_pathname(trailer, "TRAILER!!!");
 	er = archive_write_cpio_header(a, trailer);
 	archive_entry_free(trailer);
+	return (er);
+}
+
+static int
+archive_write_cpio_destroy(struct archive_write *a)
+{
+	struct cpio *cpio;
 
+	cpio = (struct cpio *)a->format_data;
 	free(cpio);
 	a->format_data = NULL;
-	return (er);
+	return (ARCHIVE_OK);
 }
 
 static int
-archive_write_cpio_finish_entry(struct archive *a)
+archive_write_cpio_finish_entry(struct archive_write *a)
 {
 	struct cpio *cpio;
 	int to_write, ret;
@@ -247,7 +252,7 @@
 	while (cpio->entry_bytes_remaining > 0) {
 		to_write = cpio->entry_bytes_remaining < a->null_length ?
 		    cpio->entry_bytes_remaining : a->null_length;
-		ret = (a->compression_write)(a, a->nulls, to_write);
+		ret = (a->compressor.write)(a, a->nulls, to_write);
 		if (ret != ARCHIVE_OK)
 			return (ret);
 		cpio->entry_bytes_remaining -= to_write;
Index: README
===================================================================
RCS file: /home/cvs/src/lib/libarchive/README,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/README -Llib/libarchive/README -u -r1.1.1.1 -r1.2
--- lib/libarchive/README
+++ lib/libarchive/README
@@ -1,4 +1,4 @@
-$FreeBSD: src/lib/libarchive/README,v 1.4 2005/02/12 23:09:44 kientzle Exp $
+$FreeBSD: src/lib/libarchive/README,v 1.5 2007/03/03 07:37:35 kientzle Exp $
 
 libarchive: a library for reading and writing streaming archives
 
@@ -6,16 +6,16 @@
 
 Documentation:
  * libarchive.3 gives an overview of the library as a whole
- * archive_read.3 and archive_write.3 provide detailed calling
-   sequences for the read and write APIs
+ * archive_read.3, archive_write.3, and archive_write_disk.3 provide
+   detailed calling sequences for the read and write APIs
  * archive_entry.3 details the "struct archive_entry" utility class
  * libarchive-formats.5 documents the file formats supported by the library
  * tar.5 provides some detailed information about a variety of different
    "tar" formats.
 
 You should also read the copious comments in "archive.h" and the source
-code for the sample "bsdtar" program for more details.  Please let me know
-about any errors or omissions you find.
+code for the sample "bsdtar" and "minitar" programs for more details.
+Please let me know about any errors or omissions you find.
 
 Currently, the library automatically detects and reads the following:
   * gzip compression
@@ -84,8 +84,10 @@
    a block of data in memory and add it to a tar archive without
    first writing a temporary file.  You can also read an entry from
    an archive and write the data directly to a socket.  If you want
-   to read/write entries to disk, there are convenience functions to
-   make this especially easy.
+   to read/write entries to disk, the archive_write_disk interface
+   treats a directory as if it were an archive so you can copy
+   from archive->disk using the same code you use for archive->archive
+   transfers.
 
  * Note: "pax interchange format" is really an extended tar format,
    despite what the name says.
Index: archive_write_set_format_by_name.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_by_name.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format_by_name.c -Llib/libarchive/archive_write_set_format_by_name.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format_by_name.c
+++ lib/libarchive/archive_write_set_format_by_name.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.3.8.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_by_name.c,v 1.7 2007/06/22 05:47:00 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -44,7 +44,13 @@
 static
 struct { const char *name; int (*setter)(struct archive *); } names[] =
 {
+	{ "arbsd",	archive_write_set_format_ar_bsd },
+	{ "ar",		archive_write_set_format_ar_bsd },
+	{ "argnu",	archive_write_set_format_ar_svr4 },
+	{ "arsvr4",	archive_write_set_format_ar_svr4 },
 	{ "cpio",	archive_write_set_format_cpio },
+	{ "newc",	archive_write_set_format_cpio_newc },
+	{ "odc",	archive_write_set_format_cpio },
 	{ "pax",	archive_write_set_format_pax },
 	{ "posix",	archive_write_set_format_pax },
 	{ "shar",	archive_write_set_format_shar },
--- /dev/null
+++ lib/libarchive/archive_read_support_compression_program.c
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_program.c,v 1.2 2007/07/20 01:28:50 kientzle Exp $");
+
+#ifdef HAVE_SYS_WAIT_H
+#  include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#  include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#include "filter_fork.h"
+
+struct archive_decompress_program {
+	char		*description;
+	pid_t		 child;
+	int		 child_stdin, child_stdout;
+
+	char		*child_out_buf;
+	char		*child_out_buf_next;
+	size_t		 child_out_buf_len, child_out_buf_avail;
+
+	const char	*child_in_buf;
+	size_t		 child_in_buf_avail;
+};
+
+static int	archive_decompressor_program_bid(const void *, size_t);
+static int	archive_decompressor_program_finish(struct archive_read *);
+static int	archive_decompressor_program_init(struct archive_read *,
+		    const void *, size_t);
+static ssize_t	archive_decompressor_program_read_ahead(struct archive_read *,
+		    const void **, size_t);
+static ssize_t	archive_decompressor_program_read_consume(struct archive_read *,
+		    size_t);
+
+int
+archive_read_support_compression_program(struct archive *_a, const char *cmd)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct decompressor_t *decompressor;
+
+	if (cmd == NULL || *cmd == '\0')
+		return (ARCHIVE_WARN);
+
+	decompressor = __archive_read_register_compression(a,
+		    archive_decompressor_program_bid,
+		    archive_decompressor_program_init);
+	if (decompressor == NULL)
+		return (ARCHIVE_WARN);
+
+	decompressor->config = strdup(cmd);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * If the user used us to register, they must really want us to
+ * handle it, so this module always bids INT_MAX.
+ */
+static int
+archive_decompressor_program_bid(const void *buff, size_t len)
+{
+	(void)buff; /* UNUSED */
+	(void)len; /* UNUSED */
+
+	return (INT_MAX); /* Default: We'll take it. */
+}
+
+static ssize_t
+child_read(struct archive_read *a, char *buf, size_t buf_len)
+{
+	struct archive_decompress_program *state = a->decompressor->data;
+	ssize_t ret, requested;
+	const void *child_buf;
+
+	if (state->child_stdout == -1)
+		return (-1);
+
+	if (buf_len == 0)
+		return (-1);
+
+restart_read:
+	requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
+
+	do {
+		ret = read(state->child_stdout, buf, requested);
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret > 0)
+		return (ret);
+	if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+		close(state->child_stdout);
+		state->child_stdout = -1;
+		return (0);
+	}
+	if (ret == -1 && errno != EAGAIN)
+		return (-1);
+
+	if (state->child_in_buf_avail == 0) {
+		child_buf = state->child_in_buf;
+		ret = (a->client_reader)(&a->archive,
+		    a->client_data,&child_buf);
+		state->child_in_buf = (const char *)child_buf;
+
+		if (ret < 0) {
+			close(state->child_stdin);
+			state->child_stdin = -1;
+			fcntl(state->child_stdout, F_SETFL, 0);
+			return (-1);
+		}
+		if (ret == 0) {
+			close(state->child_stdin);
+			state->child_stdin = -1;
+			fcntl(state->child_stdout, F_SETFL, 0);
+			goto restart_read;
+		}
+		state->child_in_buf_avail = ret;
+	}
+
+	do {
+		ret = write(state->child_stdin, state->child_in_buf,
+		    state->child_in_buf_avail);
+	} while (ret == -1 && errno == EINTR);
+
+	if (ret > 0) {
+		state->child_in_buf += ret;
+		state->child_in_buf_avail -= ret;
+		goto restart_read;
+	} else if (ret == -1 && errno == EAGAIN) {
+		__archive_check_child(state->child_stdin, state->child_stdout);
+		goto restart_read;
+	} else if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+		close(state->child_stdin);
+		state->child_stdout = -1;
+		fcntl(state->child_stdout, F_SETFL, 0);
+		goto restart_read;
+	} else {
+		close(state->child_stdin);
+		state->child_stdin = -1;
+		fcntl(state->child_stdout, F_SETFL, 0);
+		return (-1);
+	}
+}
+
+static int
+archive_decompressor_program_init(struct archive_read *a, const void *buff, size_t n)
+{
+	struct archive_decompress_program	*state;
+	const char *cmd = a->decompressor->config;
+	const char *prefix = "Program: ";
+
+
+	state = (struct archive_decompress_program *)malloc(sizeof(*state));
+	if (!state) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate input data");
+		return (ARCHIVE_FATAL);
+	}
+
+	a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM;
+	state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1);
+	strcpy(state->description, prefix);
+	strcat(state->description, cmd);
+	a->archive.compression_name = state->description;
+
+	state->child_out_buf_next = state->child_out_buf = malloc(65536);
+	if (!state->child_out_buf) {
+		free(state);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate filter buffer");
+		return (ARCHIVE_FATAL);
+	}
+	state->child_out_buf_len = 65536;
+	state->child_out_buf_avail = 0;
+
+	state->child_in_buf = buff;
+	state->child_in_buf_avail = n;
+
+	if ((state->child = __archive_create_child(cmd,
+		 &state->child_stdin, &state->child_stdout)) == -1) {
+		free(state->child_out_buf);
+		free(state);
+		archive_set_error(&a->archive, EINVAL,
+		    "Can't initialise filter");
+		return (ARCHIVE_FATAL);
+	}
+
+	a->decompressor->data = state;
+	a->decompressor->read_ahead = archive_decompressor_program_read_ahead;
+	a->decompressor->consume = archive_decompressor_program_read_consume;
+	a->decompressor->skip = NULL;
+	a->decompressor->finish = archive_decompressor_program_finish;
+
+	/* XXX Check that we can read at least one byte? */
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_decompressor_program_read_ahead(struct archive_read *a, const void **buff,
+    size_t min)
+{
+	struct archive_decompress_program *state;
+	ssize_t bytes_read;
+
+	state = (struct archive_decompress_program *)a->decompressor->data;
+
+	if (min > state->child_out_buf_len)
+		min = state->child_out_buf_len;
+
+	while (state->child_stdout != -1 && min > state->child_out_buf_avail) {
+		if (state->child_out_buf != state->child_out_buf_next) {
+			memmove(state->child_out_buf, state->child_out_buf_next,
+			    state->child_out_buf_avail);
+			state->child_out_buf_next = state->child_out_buf;
+		}
+
+		bytes_read = child_read(a,
+		    state->child_out_buf + state->child_out_buf_avail,
+		    state->child_out_buf_len - state->child_out_buf_avail);
+		if (bytes_read == -1)
+			return (-1);
+		if (bytes_read == 0)
+			break;
+		state->child_out_buf_avail += bytes_read;
+		a->archive.raw_position += bytes_read;
+	}
+
+	*buff = state->child_out_buf_next;
+	return (state->child_out_buf_avail);
+}
+
+static ssize_t
+archive_decompressor_program_read_consume(struct archive_read *a, size_t request)
+{
+	struct archive_decompress_program *state;
+
+	state = (struct archive_decompress_program *)a->decompressor->data;
+
+	state->child_out_buf_next += request;
+	state->child_out_buf_avail -= request;
+
+	a->archive.file_position += request;
+	return (request);
+}
+
+static int
+archive_decompressor_program_finish(struct archive_read *a)
+{
+	struct archive_decompress_program	*state;
+	int status;
+
+	state = (struct archive_decompress_program *)a->decompressor->data;
+
+	/* Release our configuration data. */
+	free(a->decompressor->config);
+	a->decompressor->config = NULL;
+
+	/* Shut down the child. */
+	if (state->child_stdin != -1)
+		close(state->child_stdin);
+	if (state->child_stdout != -1)
+		close(state->child_stdout);
+	while (waitpid(state->child, &status, 0) == -1 && errno == EINTR)
+		continue;
+
+	/* Release our private data. */
+	free(state->child_out_buf);
+	free(state->description);
+	free(state);
+	a->decompressor->data = NULL;
+
+	return (ARCHIVE_OK);
+}
Index: archive.h.in
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive.h.in,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive.h.in -Llib/libarchive/archive.h.in -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive.h.in
+++ lib/libarchive/archive.h.in
@@ -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.23.2.5 2007/02/14 08:29:35 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.46 2007/07/06 15:36:37 kientzle Exp $
  */
 
 #ifndef ARCHIVE_H_INCLUDED
@@ -30,43 +30,88 @@
 
 /*
  * This header file corresponds to:
- *   Library version @VERSION@
+ *   Library version @ARCHIVE_VERSION@
  *   Shared library version @SHLIB_MAJOR@
  */
 
 #include <sys/types.h>  /* Linux requires this for off_t */
 @ARCHIVE_H_INCLUDE_INTTYPES_H@
 #include <stdio.h> /* For FILE * */
+#ifndef _WIN32
 #include <unistd.h>  /* For ssize_t and size_t */
+#else
+typedef long ssize_t;
+typedef unsigned int uid_t;
+typedef unsigned int gid_t;
+typedef unsigned short mode_t;
+#endif
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * Each of the version identifiers comes as a macro and a function.
+ * The macro identifies the installed header; the function identifies
+ * the library version (which may not be the same if you're using a
+ * dynamically-linked version of the library).
+ */
 
 /*
- * If ARCHIVE_API_VERSION != archive_api_version(), then the library you
- * were linked with is using an incompatible API to the one you were
- * compiled with.  This is almost certainly a fatal problem.
- *
- * ARCHIVE_API_FEATURE is incremented with each significant feature
- * addition, so you can test (at compile or run time) if a particular
- * feature is implemented.  It's no big deal if ARCHIVE_API_FEATURE !=
- * archive_api_feature(), as long as both are high enough to include
- * the features you're relying on.  Specific values of FEATURE are
- * documented here:
+ * Textual name/version of the library, useful for version displays.
+ */
+#define	ARCHIVE_LIBRARY_VERSION	"libarchive @ARCHIVE_VERSION@"
+const char *	archive_version(void);
+
+/*
+ * Major version number: If ARCHIVE_API_VERSION !=
+ * archive_api_version(), then the library you were linked with is
+ * using an incompatible API to the one you were compiled with.  This
+ * is almost certainly a fatal problem.
+ */
+#define	ARCHIVE_API_VERSION	@ARCHIVE_API_MAJOR@
+int		archive_api_version(void);
+
+/*
+ * Minor version number: ARCHIVE_API_FEATURE is incremented with each
+ * significant feature addition, so you can test (at compile or run
+ * time) if a particular feature is implemented.  It's no big deal if
+ * ARCHIVE_API_FEATURE != archive_api_feature(), as long as both are
+ * high enough to include the features you're relying on.  Specific
+ * values of FEATURE are documented here:
  *
  *    1 - Version tests are available.
  *    2 - archive_{read,write}_close available separately from _finish.
  *    3 - open_memory, open_memory2, open_FILE, open_fd available
+ *    5 - archive_write_disk interface available
+ *
+ * Unfortunately, this count resets whenever ARCHIVE_API_VERSION changes,
+ * making it awkward to use in practice.  For that reason, it is deprecated
+ * in favor of the more-accurate version stamp below.  It will eventually
+ * be removed.
  */
-#define	ARCHIVE_API_VERSION	@ARCHIVE_API_MAJOR@
-int		archive_api_version(void);
 #define	ARCHIVE_API_FEATURE	@ARCHIVE_API_MINOR@
 int		archive_api_feature(void);
-/* Textual name/version of the library. */
-#define	ARCHIVE_LIBRARY_VERSION	"libarchive @VERSION@"
-const char *	archive_version(void);
+
+/*
+ * The "version stamp" is a single integer that makes it easy to check
+ * the exact version: for version a.b.c, the version stamp is
+ * printf("%d%03d%03d",a,b,c).  For example, version 2.12.108 has
+ * version stamp 2012108.
+ *
+ * This was introduced with libarchive 1.9.0 in the libarchive 1.x family
+ * and libarchive 2.2.4 in the libarchive 2.x family.  The following
+ * may be useful if you really want to do feature detection for earlier
+ * libarchive versions:
+ *
+ * #ifndef ARCHIVE_VERSION_STAMP
+ * #define ARCHIVE_VERSION_STAMP 	\
+ *             (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
+ * #endif
+ */
+#define ARCHIVE_VERSION_STAMP	@ARCHIVE_VERSION_STAMP@
+int		archive_version_stamp(void);
+
 
 #define	ARCHIVE_BYTES_PER_RECORD	  512
 #define	ARCHIVE_DEFAULT_BYTES_PER_BLOCK	10240
@@ -84,6 +129,8 @@
 #define	ARCHIVE_OK	  0	/* Operation was successful. */
 #define	ARCHIVE_RETRY	(-10)	/* Retry might succeed. */
 #define	ARCHIVE_WARN	(-20)	/* Partial success. */
+/* For example, if write_header "fails", then you can't push data. */
+#define	ARCHIVE_FAILED	(-25)	/* Current operation cannot complete. */
 #define	ARCHIVE_FATAL	(-30)	/* No more operations are possible. */
 
 /*
@@ -131,6 +178,7 @@
 #define	ARCHIVE_COMPRESSION_GZIP	1
 #define	ARCHIVE_COMPRESSION_BZIP2	2
 #define	ARCHIVE_COMPRESSION_COMPRESS	3
+#define	ARCHIVE_COMPRESSION_PROGRAM	4
 
 /*
  * Codes returned by archive_format.
@@ -138,8 +186,17 @@
  * Top 16 bits identifies the format family (e.g., "tar"); lower
  * 16 bits indicate the variant.  This is updated by read_next_header.
  * Note that the lower 16 bits will often vary from entry to entry.
+ * In some cases, this variation occurs as libarchive learns more about
+ * the archive (for example, later entries might utilize extensions that
+ * weren't necessary earlier in the archive; in this case, libarchive
+ * will change the format code to indicate the extended format that
+ * was used).  In other cases, it's because different tools have
+ * modified the archive and so different parts of the archive
+ * actually have slightly different formts.  (Both tar and cpio store
+ * format codes in each entry, so it is quite possible for each
+ * entry to be in a different format.)
  */
-#define	ARCHIVE_FORMAT_BASE_MASK		0xff0000U
+#define	ARCHIVE_FORMAT_BASE_MASK		0xff0000
 #define	ARCHIVE_FORMAT_CPIO			0x10000
 #define	ARCHIVE_FORMAT_CPIO_POSIX		(ARCHIVE_FORMAT_CPIO | 1)
 #define	ARCHIVE_FORMAT_CPIO_BIN_LE		(ARCHIVE_FORMAT_CPIO | 2)
@@ -158,6 +215,9 @@
 #define	ARCHIVE_FORMAT_ISO9660_ROCKRIDGE	(ARCHIVE_FORMAT_ISO9660 | 1)
 #define	ARCHIVE_FORMAT_ZIP			0x50000
 #define	ARCHIVE_FORMAT_EMPTY			0x60000
+#define	ARCHIVE_FORMAT_AR			0x70000
+#define	ARCHIVE_FORMAT_AR_GNU			(ARCHIVE_FORMAT_AR | 1)
+#define	ARCHIVE_FORMAT_AR_BSD			(ARCHIVE_FORMAT_AR | 2)
 
 /*-
  * Basic outline for reading an archive:
@@ -185,8 +245,11 @@
 int		 archive_read_support_compression_compress(struct archive *);
 int		 archive_read_support_compression_gzip(struct archive *);
 int		 archive_read_support_compression_none(struct archive *);
+int		 archive_read_support_compression_program(struct archive *,
+		     const char *command);
 
 int		 archive_read_support_format_all(struct archive *);
+int		 archive_read_support_format_ar(struct archive *);
 int		 archive_read_support_format_cpio(struct archive *);
 int		 archive_read_support_format_empty(struct archive *);
 int		 archive_read_support_format_gnutar(struct archive *);
@@ -274,15 +337,32 @@
  */
 
 /* The "flags" argument selects optional behavior, 'OR' the flags you want. */
-/* TODO: The 'Default' comments here are not quite correct; clean this up. */
-#define	ARCHIVE_EXTRACT_OWNER	(1) /* Default: owner/group not restored */
-#define	ARCHIVE_EXTRACT_PERM	(2) /* Default: restore perm only for reg file*/
-#define	ARCHIVE_EXTRACT_TIME	(4) /* Default: mod time not restored */
-#define	ARCHIVE_EXTRACT_NO_OVERWRITE (8) /* Default: Replace files on disk */
-#define	ARCHIVE_EXTRACT_UNLINK	(16) /* Default: don't unlink existing files */
-#define	ARCHIVE_EXTRACT_ACL	(32) /* Default: don't restore ACLs */
-#define	ARCHIVE_EXTRACT_FFLAGS	(64) /* Default: don't restore fflags */
-#define	ARCHIVE_EXTRACT_XATTR   (128) /* Default: don't restore xattrs */
+
+/* Default: Do not try to set owner/group. */
+#define	ARCHIVE_EXTRACT_OWNER	(1)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define	ARCHIVE_EXTRACT_PERM	(2)
+/* Default: Do not restore mtime/atime. */
+#define	ARCHIVE_EXTRACT_TIME	(4)
+/* Default: Replace existing files. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE (8)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define	ARCHIVE_EXTRACT_UNLINK	(16)
+/* Default: Do not restore ACLs. */
+#define	ARCHIVE_EXTRACT_ACL	(32)
+/* Default: Do not restore fflags. */
+#define	ARCHIVE_EXTRACT_FFLAGS	(64)
+/* Default: Do not restore xattrs. */
+#define	ARCHIVE_EXTRACT_XATTR   (128)
+/* Default: Do not try to guard against extracts redirected by symlinks. */
+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
+#define	ARCHIVE_EXTRACT_SECURE_SYMLINKS (256)
+/* Default: Do not reject entries with '..' as path elements. */
+#define	ARCHIVE_EXTRACT_SECURE_NODOTDOT (512)
+/* Default: Create parent directories as needed. */
+#define	ARCHIVE_EXTRACT_NO_AUTODIR (1024)
+/* Default: Overwrite files, even if one on disk is newer. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (2048)
 
 int		 archive_read_extract(struct archive *, struct archive_entry *,
 		     int flags);
@@ -298,7 +378,13 @@
 int		 archive_read_close(struct archive *);
 /* Release all resources and destroy the object. */
 /* Note that archive_read_finish will call archive_read_close for you. */
+#if ARCHIVE_API_VERSION > 1
+int		 archive_read_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Erroneously declared to return void in libarchive 1.x */
 void		 archive_read_finish(struct archive *);
+#endif
 
 /*-
  * To create an archive:
@@ -331,12 +417,17 @@
 int		 archive_write_set_compression_bzip2(struct archive *);
 int		 archive_write_set_compression_gzip(struct archive *);
 int		 archive_write_set_compression_none(struct archive *);
+int		 archive_write_set_compression_program(struct archive *,
+		     const char *cmd);
 /* A convenience function to set the format based on the code or name. */
 int		 archive_write_set_format(struct archive *, int format_code);
 int		 archive_write_set_format_by_name(struct archive *,
 		     const char *name);
 /* To minimize link pollution, use one or more of the following. */
+int		 archive_write_set_format_ar_bsd(struct archive *);
+int		 archive_write_set_format_ar_svr4(struct archive *);
 int		 archive_write_set_format_cpio(struct archive *);
+int		 archive_write_set_format_cpio_newc(struct archive *);
 /* TODO: int archive_write_set_format_old_tar(struct archive *); */
 int		 archive_write_set_format_pax(struct archive *);
 int		 archive_write_set_format_pax_restricted(struct archive *);
@@ -362,11 +453,76 @@
  */
 int		 archive_write_header(struct archive *,
 		     struct archive_entry *);
-/* TODO: should be ssize_t, but that might require .so version bump? */
+#if ARCHIVE_API_VERSION > 1
+ssize_t		 archive_write_data(struct archive *, const void *, size_t);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* This was erroneously declared to return "int" in libarchive 1.x. */
 int		 archive_write_data(struct archive *, const void *, size_t);
+#endif
+ssize_t		 archive_write_data_block(struct archive *, const void *, size_t, off_t);
 int		 archive_write_finish_entry(struct archive *);
 int		 archive_write_close(struct archive *);
+#if ARCHIVE_API_VERSION > 1
+int		 archive_write_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Return value was incorrect in libarchive 1.x. */
 void		 archive_write_finish(struct archive *);
+#endif
+
+/*-
+ * To create objects on disk:
+ *   1) Ask archive_write_disk_new for a new archive_write_disk object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to create the file/dir/etc on disk
+ *      - archive_write_data to write the entry data
+ *   4) archive_write_finish to cleanup the writer and release resources
+ *
+ * In particular, you can use this in conjunction with archive_read()
+ * to pull entries out of an archive and create them on disk.
+ */
+struct archive	*archive_write_disk_new(void);
+/* This file will not be overwritten. */
+int		 archive_write_disk_set_skip_file(struct archive *,
+		     dev_t, ino_t);
+/* Set flags to control how the next item gets created. */
+int		 archive_write_disk_set_options(struct archive *,
+		     int flags);
+/*
+ * The lookup functions are given uname/uid (or gname/gid) pairs and
+ * return a uid (gid) suitable for this system.  These are used for
+ * restoring ownership and for setting ACLs.  The default functions
+ * are naive, they just return the uid/gid.  These are small, so reasonable
+ * for applications that don't need to preserve ownership; they
+ * are probably also appropriate for applications that are doing
+ * same-system backup and restore.
+ */
+/*
+ * The "standard" lookup functions use common system calls to lookup
+ * the uname/gname, falling back to the uid/gid if the names can't be
+ * found.  They cache lookups and are reasonably fast, but can be very
+ * large, so they are not used unless you ask for them.  In
+ * particular, these match the specifications of POSIX "pax" and old
+ * POSIX "tar".
+ */
+int		 archive_write_disk_set_standard_lookup(struct archive *);
+/*
+ * If neither the default (naive) nor the standard (big) functions suit
+ * your needs, you can write your own and register them.  Be sure to
+ * include a cleanup function if you have allocated private data.
+ */
+int		 archive_write_disk_set_group_lookup(struct archive *,
+		     void *private_data,
+		     gid_t (*loookup)(void *, const char *gname, gid_t gid),
+		     void (*cleanup)(void *));
+int		 archive_write_disk_set_user_lookup(struct archive *,
+		     void *private_data,
+		     uid_t (*)(void *, const char *uname, uid_t uid),
+		     void (*cleanup)(void *));
 
 /*
  * Accessor functions to read/set various information in
@@ -383,7 +539,9 @@
 const char	*archive_error_string(struct archive *);
 const char	*archive_format_name(struct archive *);
 int		 archive_format(struct archive *);
+void		 archive_clear_error(struct archive *);
 void		 archive_set_error(struct archive *, int _err, const char *fmt, ...);
+void		 archive_copy_error(struct archive *dest, struct archive *src);
 
 #ifdef __cplusplus
 }
--- /dev/null
+++ lib/libarchive/archive_write_disk_private.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/archive_write_disk_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+
+struct archive_write_disk;
+
+#endif
Index: archive_read_data_into_fd.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_data_into_fd.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_data_into_fd.c -Llib/libarchive/archive_read_data_into_fd.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_data_into_fd.c
+++ lib/libarchive/archive_read_data_into_fd.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.8.2.2 2007/01/27 06:44:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.15 2007/04/02 00:21:46 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -50,8 +50,8 @@
 {
 	int r;
 	const void *buff;
-	size_t size;
-	ssize_t bytes_to_write, bytes_written, total_written;
+	size_t size, bytes_to_write;
+	ssize_t bytes_written, total_written;
 	off_t offset;
 	off_t output_offset;
 
@@ -80,8 +80,6 @@
 			total_written += bytes_written;
 			p += bytes_written;
 			size -= bytes_written;
-			if (a->extract_progress != NULL)
-				(*a->extract_progress)(a->extract_progress_user_data);
 		}
 	}
 
Index: archive_write_open_memory.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_open_memory.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/archive_write_open_memory.c -Llib/libarchive/archive_write_open_memory.c -u -r1.1.1.1 -r1.2
--- lib/libarchive/archive_write_open_memory.c
+++ lib/libarchive/archive_write_open_memory.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");
 
 #include <errno.h>
 #include <stdlib.h>
Index: archive_read_support_compression_compress.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_compress.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_compression_compress.c -Llib/libarchive/archive_read_support_compression_compress.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_compression_compress.c
+++ lib/libarchive/archive_read_support_compression_compress.c
@@ -64,7 +64,7 @@
 
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.3.2.3 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.10 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -81,6 +81,7 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 /*
  * Because LZW decompression is pretty simple, I've just implemented
@@ -133,17 +134,20 @@
 };
 
 static int	bid(const void *, size_t);
-static int	finish(struct archive *);
-static int	init(struct archive *, const void *, size_t);
-static ssize_t	read_ahead(struct archive *, const void **, size_t);
-static ssize_t	read_consume(struct archive *, size_t);
-static int	getbits(struct archive *, struct private_data *, int n);
-static int	next_code(struct archive *a, struct private_data *state);
+static int	finish(struct archive_read *);
+static int	init(struct archive_read *, const void *, size_t);
+static ssize_t	read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t	read_consume(struct archive_read *, size_t);
+static int	getbits(struct archive_read *, struct private_data *, int n);
+static int	next_code(struct archive_read *a, struct private_data *state);
 
 int
-archive_read_support_compression_compress(struct archive *a)
+archive_read_support_compression_compress(struct archive *_a)
 {
-	return (__archive_read_register_compression(a, bid, init));
+	struct archive_read *a = (struct archive_read *)_a;
+	if (__archive_read_register_compression(a, bid, init) != NULL)
+		return (ARCHIVE_OK);
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -187,36 +191,36 @@
  * Setup the callbacks.
  */
 static int
-init(struct archive *a, const void *buff, size_t n)
+init(struct archive_read *a, const void *buff, size_t n)
 {
 	struct private_data *state;
 	int code;
 
-	a->compression_code = ARCHIVE_COMPRESSION_COMPRESS;
-	a->compression_name = "compress (.Z)";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
+	a->archive.compression_name = "compress (.Z)";
 
-	a->compression_read_ahead = read_ahead;
-	a->compression_read_consume = read_consume;
-	a->compression_skip = NULL; /* not supported */
-	a->compression_finish = finish;
+	a->decompressor->read_ahead = read_ahead;
+	a->decompressor->consume = read_consume;
+	a->decompressor->skip = NULL; /* not supported */
+	a->decompressor->finish = finish;
 
 	state = (struct private_data *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for %s decompression",
-		    a->compression_name);
+		    a->archive.compression_name);
 		return (ARCHIVE_FATAL);
 	}
 	memset(state, 0, sizeof(*state));
-	a->compression_data = state;
+	a->decompressor->data = state;
 
 	state->uncompressed_buffer_size = 64 * 1024;
 	state->uncompressed_buffer = malloc(state->uncompressed_buffer_size);
 
 	if (state->uncompressed_buffer == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate %s decompression buffers",
-		    a->compression_name);
+		    a->archive.compression_name);
 		goto fatal;
 	}
 
@@ -235,7 +239,7 @@
 		 * blocks and gzip and compress are both enabled.
 		 * You can't distinguish gzip and compress only from
 		 * the first byte. */
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Compress signature did not match.");
 		goto fatal;
 	}
@@ -270,15 +274,15 @@
  * as necessary.
  */
 static ssize_t
-read_ahead(struct archive *a, const void **p, size_t min)
+read_ahead(struct archive_read *a, const void **p, size_t min)
 {
 	struct private_data *state;
-	int read_avail, was_avail, ret;
+	size_t read_avail;
+	int ret;
 
-	state = (struct private_data *)a->compression_data;
-	was_avail = -1;
+	state = (struct private_data *)a->decompressor->data;
 	if (!a->client_reader) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No read callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -286,14 +290,14 @@
 
 	read_avail = state->next_out - state->read_next;
 
-	if (read_avail < (int)min  &&  state->end_of_stream) {
+	if (read_avail < min  &&  state->end_of_stream) {
 		if (state->end_of_stream == ARCHIVE_EOF)
 			return (0);
 		else
 			return (-1);
 	}
 
-	if (read_avail < (int)min) {
+	if (read_avail < min) {
 		memmove(state->uncompressed_buffer, state->read_next,
 		    read_avail);
 		state->read_next = (unsigned char *)state->uncompressed_buffer;
@@ -301,7 +305,7 @@
 		state->avail_out
 		    = state->uncompressed_buffer_size - read_avail;
 
-		while (read_avail < (int)state->uncompressed_buffer_size
+		while (read_avail < state->uncompressed_buffer_size
 			&& !state->end_of_stream) {
 			if (state->stackp > state->stack) {
 				*state->next_out++ = *--state->stackp;
@@ -325,12 +329,12 @@
  * Mark a previously-returned block of data as read.
  */
 static ssize_t
-read_consume(struct archive *a, size_t n)
+read_consume(struct archive_read *a, size_t n)
 {
 	struct private_data *state;
 
-	state = (struct private_data *)a->compression_data;
-	a->file_position += n;
+	state = (struct private_data *)a->decompressor->data;
+	a->archive.file_position += n;
 	state->read_next += n;
 	if (state->read_next > state->next_out)
 		__archive_errx(1, "Request to consume too many "
@@ -342,12 +346,12 @@
  * Clean up the decompressor.
  */
 static int
-finish(struct archive *a)
+finish(struct archive_read *a)
 {
 	struct private_data *state;
 	int ret = ARCHIVE_OK;
 
-	state = (struct private_data *)a->compression_data;
+	state = (struct private_data *)a->decompressor->data;
 
 	if (state != NULL) {
 		if (state->uncompressed_buffer != NULL)
@@ -355,10 +359,7 @@
 		free(state);
 	}
 
-	a->compression_data = NULL;
-	if (a->client_closer != NULL)
-		ret = (a->client_closer)(a, a->client_data);
-
+	a->decompressor->data = NULL;
 	return (ret);
 }
 
@@ -368,7 +369,7 @@
  * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
  */
 static int
-next_code(struct archive *a, struct private_data *state)
+next_code(struct archive_read *a, struct private_data *state)
 {
 	int code, newcode;
 
@@ -412,7 +413,7 @@
 
 	if (code > state->free_ent) {
 		/* An invalid code is a fatal error. */
-		archive_set_error(a, -1, "Invalid compressed data");
+		archive_set_error(&a->archive, -1, "Invalid compressed data");
 		return (ARCHIVE_FATAL);
 	}
 
@@ -456,24 +457,26 @@
  * -1 indicates end of available data.
  */
 static int
-getbits(struct archive *a, struct private_data *state, int n)
+getbits(struct archive_read *a, struct private_data *state, int n)
 {
 	int code, ret;
 	static const int mask[] = {
 		0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
 		0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
 	};
-
+	const void *read_buf;
 
 	while (state->bits_avail < n) {
 		if (state->avail_in <= 0) {
-			ret = (a->client_reader)(a, a->client_data,
-			    (const void **)&state->next_in);
+			read_buf = state->next_in;
+			ret = (a->client_reader)(&a->archive, a->client_data,
+			    &read_buf);
+			state->next_in = read_buf;
 			if (ret < 0)
 				return (ARCHIVE_FATAL);
 			if (ret == 0)
 				return (ARCHIVE_EOF);
-			a->raw_position += ret;
+			a->archive.raw_position += ret;
 			state->avail_in = ret;
 		}
 		state->bit_buffer |= *state->next_in++ << state->bits_avail;
Index: archive_read.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read.3,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read.3 -Llib/libarchive/archive_read.3 -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read.3
+++ lib/libarchive/archive_read.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.20.2.4 2007/02/14 08:29:35 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.35 2007/09/19 16:37:45 kientzle Exp $
 .\"
 .Dd August 19, 2006
 .Dt archive_read 3
@@ -34,6 +34,7 @@
 .Nm archive_read_support_compression_compress ,
 .Nm archive_read_support_compression_gzip ,
 .Nm archive_read_support_compression_none ,
+.Nm archive_read_support_compression_program ,
 .Nm archive_read_support_format_all ,
 .Nm archive_read_support_format_cpio ,
 .Nm archive_read_support_format_empty ,
@@ -50,7 +51,9 @@
 .Nm archive_read_data ,
 .Nm archive_read_data_block ,
 .Nm archive_read_data_skip ,
+.\" #if ARCHIVE_API_VERSION < 3
 .Nm archive_read_data_into_buffer ,
+.\" #endif
 .Nm archive_read_data_into_fd ,
 .Nm archive_read_extract ,
 .Nm archive_read_extract_set_progress_callback ,
@@ -72,6 +75,8 @@
 .Ft int
 .Fn archive_read_support_compression_none "struct archive *"
 .Ft int
+.Fn archive_read_support_compression_program "struct archive *" "const char *cmd"
+.Ft int
 .Fn archive_read_support_format_all "struct archive *"
 .Ft int
 .Fn archive_read_support_format_cpio "struct archive *"
@@ -103,8 +108,10 @@
 .Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset"
 .Ft int
 .Fn archive_read_data_skip "struct archive *"
+.\" #if ARCHIVE_API_VERSION < 3
 .Ft int
 .Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len"
+.\" #endif
 .Ft int
 .Fn archive_read_data_into_fd "struct archive *" "int fd"
 .Ft int
@@ -113,7 +120,7 @@
 .Fn archive_read_extract_set_progress_callback "struct archive *" "void (*func)(void *)" "void *user_data"
 .Ft int
 .Fn archive_read_close "struct archive *"
-.Ft void
+.Ft int
 .Fn archive_read_finish "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for reading streaming archives.
@@ -138,6 +145,11 @@
 For convenience,
 .Fn archive_read_support_compression_all
 enables all available decompression code.
+.It Fn archive_read_support_compression_program
+Data is fed through the specified external program before being dearchived.
+Note that this disables automatic detection of the compression format,
+so it makes no sense to specify this in conjunction with any other
+decompression option.
 .It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_empty , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip
 Enables support---including auto-detection code---for the
 specified archive format.
@@ -148,7 +160,7 @@
 For convenience,
 .Fn archive_read_support_format_all
 enables support for all available formats.
-Note that there is no default.
+Only empty archives are supported by default.
 .It Fn archive_read_open
 The same as
 .Fn archive_read_open2 ,
@@ -224,67 +236,38 @@
 A convenience function that repeatedly calls
 .Fn archive_read_data_block
 to skip all of the data for this archive entry.
+.\" #if ARCHIVE_API_VERSION < 3
 .It Fn archive_read_data_into_buffer
-A convenience function that repeatedly calls
-.Fn archive_read_data_block
-to copy the entire entry into the client-supplied buffer.
-Note that the client is responsible for sizing the buffer appropriately.
+This function is deprecated and will be removed.
+Use
+.Fn archive_read_data
+instead.
+.\" #endif
 .It Fn archive_read_data_into_fd
 A convenience function that repeatedly calls
 .Fn archive_read_data_block
 to copy the entire entry to the provided file descriptor.
-.It Fn archive_read_extract_set_skip_file
-This function records the device and inode numbers
-of a file that should not be restored.
-This is a convenience that prevents
+.It Fn archive_read_extract , Fn archive_read_extract_set_skip_file
+A convenience function that wraps the corresponding
+.Xr archive_write_disk 3
+interfaces.
+The first call to
 .Fn archive_read_extract
-from restoring a file over the archive itself.
-.It Fn archive_read_extract
-A convenience function that recreates the specified object on
-disk and reads the entry data into that object.
-The filename, permissions, and other critical information
-are taken from the provided
-.Va archive_entry
-object.
+creates a restore object using
+.Xr archive_write_disk_new 3
+and
+.Xr archive_write_disk_set_standard_lookup 3 ,
+then transparently invokes
+.Xr archive_write_disk_set_options 3 ,
+.Xr archive_write_header 3 ,
+.Xr archive_write_data 3 ,
+and
+.Xr archive_write_finish_entry 3
+to create the entry on disk and copy data into it.
 The
 .Va flags
-argument modifies how the object is recreated.
-It consists of a bitwise OR of one or more of the following values:
-.Bl -tag -compact -width "indent"
-.It Cm ARCHIVE_EXTRACT_OWNER
-The user and group IDs should be set on the restored file.
-By default, the user and group IDs are not restored.
-.It Cm ARCHIVE_EXTRACT_PERM
-The permissions (mode bits) should be restored for all objects.
-By default, permissions are only restored for regular files.
-.It Cm ARCHIVE_EXTRACT_TIME
-The timestamps (mtime, ctime, and atime) should be restored.
-By default, they are ignored.
-Note that restoring of atime is not currently supported.
-.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
-Existing files on disk will not be overwritten.
-By default, existing regular files are truncated and overwritten;
-existing directories will have their permissions updated;
-other pre-existing objects are unlinked and recreated from scratch.
-.It Cm ARCHIVE_EXTRACT_UNLINK
-Existing files on disk will be unlinked and recreated from scratch.
-By default, existing files are truncated and rewritten, but
-the file is not recreated.
-In particular, the default behavior does not break existing hard links.
-.It Cm ARCHIVE_EXTRACT_ACL
-Attempt to restore ACLs.
-By default, extended ACLs are ignored.
-.It Cm ARCHIVE_EXTRACT_FFLAGS
-Attempt to restore extended file flags.
-By default, file flags are ignored.
-.El
-Note that not all attributes are set immediately;
-some attributes are cached in memory and written to disk only
-when the archive is closed.
-(For example, read-only directories are initially created
-writable so that files within those directories can be
-restored.
-The final permissions are set when the archive is closed.)
+argument is passed unmodified to
+.Xr archive_write_disk_set_options 3 .
 .It Fn archive_read_extract_set_progress_callback
 Sets a pointer to a user-defined callback that can be used
 for updating progress displays during extraction.
@@ -300,6 +283,12 @@
 Invokes
 .Fn archive_read_close
 if it was not invoked manually, then release all resources.
+Note: In libarchive 1.x, this function was declared to return
+.Ft void ,
+which made it impossible to detect certain errors when
+.Fn archive_read_close
+was invoked implicitly from this function.
+The declaration is corrected beginning with libarchive 2.0.
 .El
 .Pp
 Note that the library determines most of the relevant information about
@@ -523,26 +512,6 @@
 library was written by
 .An Tim Kientzle Aq kientzle at acm.org .
 .Sh BUGS
-Directories are actually extracted in two distinct phases.
-Directories are created during
-.Fn archive_read_extract ,
-but final permissions are not set until
-.Fn archive_read_close .
-This separation is necessary to correctly handle borderline
-cases such as a non-writable directory containing
-files, but can cause unexpected results.
-In particular, directory permissions are not fully
-restored until the archive is closed.
-If you use
-.Xr chdir 2
-to change the current directory between calls to
-.Fn archive_read_extract
-or before calling
-.Fn archive_read_close ,
-you may confuse the permission-setting logic with
-the result that directory permissions are restored
-incorrectly.
-.Pp
 Many traditional archiver programs treat
 empty files as valid empty archives.
 For example, many implementations of
Index: Makefile
===================================================================
RCS file: /home/cvs/src/lib/libarchive/Makefile,v
retrieving revision 1.2
retrieving revision 1.3
diff -Llib/libarchive/Makefile -Llib/libarchive/Makefile -u -r1.2 -r1.3
--- lib/libarchive/Makefile
+++ lib/libarchive/Makefile
@@ -1,26 +1,31 @@
-# $FreeBSD: src/lib/libarchive/Makefile,v 1.36.2.4 2007/02/14 08:29:35 kientzle Exp $
+# $FreeBSD: src/lib/libarchive/Makefile,v 1.77 2007/07/15 19:10:34 kientzle Exp $
+# $MidnightBSD$
 
 LIB=	archive
-#DPADD=	${LIBBZ2} ${LIBZ}
-#LDADD=	-lbz2 -lz
+DPADD=	${LIBBZ2} ${LIBZ}
+LDADD=	-lbz2 -lz
 
 # The libarchive version stamp.
 # Version is three numbers:
 #  Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR)
 #  Minor: Bumped when significant new features are added
 #  Revision: Bumped on any notable change
-VERSION= 1.3.1
+VERSION= 2.2.4
 
 ARCHIVE_API_MAJOR!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/\..*//'
 ARCHIVE_API_MINOR!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/[0-9]*\.//' -e 's/\..*//'
+ARCHIVE_API_REV!=	echo ${VERSION} | sed -e 's/[^0-9]/./g' -e 's/.*\.//'
+
+# Can't use /usr/bin/printf to format the version stamp here, because
+# that's not available during installworld.  Fortunately, awk is.
+ARCHIVE_VERSION_STAMP!=	echo ${ARCHIVE_API_MAJOR} ${ARCHIVE_API_MINOR} ${ARCHIVE_API_REV} | awk '{printf("%d%03d%03d",$$1,$$2,$$3)}'
+
 
-# FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system.
-# It has no real relation to the version number above.
-# libarchive in FreeBSD 6 uses SHLIB_MAJOR=2.
 SHLIB_MAJOR= 2
 
 CFLAGS+=	-DPACKAGE_NAME=\"lib${LIB}\"
 CFLAGS+=	-DPACKAGE_VERSION=\"${VERSION}\"
+CFLAGS+=	-DPLATFORM_CONFIG_H=\"config_freebsd.h\"
 CFLAGS+=	-I${.OBJDIR}
 
 WARNS?=	6
@@ -29,15 +34,16 @@
 INCS=	archive.h archive_entry.h
 
 # Build archive.h from archive.h.in by substituting version information.
-# Note: FreeBSD has inttypes.h, so enable that include in archive.h.in
+# Note: mbsd has inttypes.h, so enable that include in archive.h.in
 archive.h:	archive.h.in Makefile
-	cat ${.CURDIR}/archive.h.in |					\
-		sed 's/@VERSION@/${VERSION}/g' | 			\
-		sed 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g' |		\
-		sed 's/@ARCHIVE_API_MAJOR@/${ARCHIVE_API_MAJOR}/g' |	\
-		sed 's/@ARCHIVE_API_MINOR@/${ARCHIVE_API_MINOR}/g' |	\
-		sed 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h>  /* For int64_t */|g' |			\
-		cat > archive.h
+	cat ${.CURDIR}/archive.h.in | sed				\
+		-e 's/@ARCHIVE_VERSION@/${VERSION}/g'	 		\
+		-e 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g'			\
+		-e 's/@ARCHIVE_API_MAJOR@/${ARCHIVE_API_MAJOR}/g'	\
+		-e 's/@ARCHIVE_API_MINOR@/${ARCHIVE_API_MINOR}/g'	\
+		-e 's/@ARCHIVE_VERSION_STAMP@/${ARCHIVE_VERSION_STAMP}/g' \
+		-e 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h>  /* For int64_t */|g' \
+		> archive.h
 
 # archive.h needs to be cleaned
 CLEANFILES+=	archive.h
@@ -46,8 +52,9 @@
 SRCS=	archive.h					\
 	archive_check_magic.c				\
 	archive_entry.c					\
+	archive_entry_copy_stat.c			\
+	archive_entry_stat.c				\
 	archive_read.c					\
-	archive_read_data_into_buffer.c			\
 	archive_read_data_into_fd.c			\
 	archive_read_extract.c				\
 	archive_read_open_fd.c				\
@@ -59,7 +66,9 @@
 	archive_read_support_compression_compress.c	\
 	archive_read_support_compression_gzip.c		\
 	archive_read_support_compression_none.c		\
+	archive_read_support_compression_program.c	\
 	archive_read_support_format_all.c		\
+	archive_read_support_format_ar.c		\
 	archive_read_support_format_cpio.c		\
 	archive_read_support_format_empty.c		\
 	archive_read_support_format_iso9660.c		\
@@ -68,7 +77,10 @@
 	archive_string.c				\
 	archive_string_sprintf.c			\
 	archive_util.c					\
+	archive_virtual.c				\
 	archive_write.c					\
+	archive_write_disk.c				\
+	archive_write_disk_set_standard_lookup.c	\
 	archive_write_open_fd.c				\
 	archive_write_open_file.c			\
 	archive_write_open_filename.c			\
@@ -76,18 +88,23 @@
 	archive_write_set_compression_bzip2.c		\
 	archive_write_set_compression_gzip.c		\
 	archive_write_set_compression_none.c		\
+	archive_write_set_compression_program.c		\
 	archive_write_set_format.c			\
+	archive_write_set_format_ar.c			\
 	archive_write_set_format_by_name.c		\
 	archive_write_set_format_cpio.c			\
+	archive_write_set_format_cpio_newc.c		\
 	archive_write_set_format_pax.c			\
 	archive_write_set_format_shar.c			\
-	archive_write_set_format_ustar.c
+	archive_write_set_format_ustar.c		\
+	filter_fork.c
 
 # Man pages to be installed.
 MAN=	archive_entry.3					\
 	archive_read.3					\
 	archive_util.3					\
 	archive_write.3					\
+	archive_write_disk.3				\
 	libarchive.3					\
 	libarchive-formats.5				\
 	tar.5
@@ -104,12 +121,18 @@
 MLINKS+=	archive_entry.3 archive_entry_clear.3
 MLINKS+=	archive_entry.3 archive_entry_clone.3
 MLINKS+=	archive_entry.3 archive_entry_copy_fflags_text_w.3
+MLINKS+=	archive_entry.3 archive_entry_copy_gname.3
 MLINKS+=	archive_entry.3 archive_entry_copy_gname_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_hardlink_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_pathname_w.3
 MLINKS+=	archive_entry.3 archive_entry_copy_stat.3
 MLINKS+=	archive_entry.3 archive_entry_copy_symlink_w.3
+MLINKS+=	archive_entry.3 archive_entry_copy_uname.3
 MLINKS+=	archive_entry.3 archive_entry_copy_uname_w.3
+MLINKS+=	archive_entry.3 archive_entry_dev.3
+MLINKS+=	archive_entry.3 archive_entry_devmajor.3
+MLINKS+=	archive_entry.3 archive_entry_devminor.3
+MLINKS+=	archive_entry.3 archive_entry_filetype.3
 MLINKS+=	archive_entry.3 archive_entry_fflags.3
 MLINKS+=	archive_entry.3 archive_entry_fflags_text.3
 MLINKS+=	archive_entry.3 archive_entry_free.3
@@ -121,19 +144,28 @@
 MLINKS+=	archive_entry.3 archive_entry_mode.3
 MLINKS+=	archive_entry.3 archive_entry_mtime.3
 MLINKS+=	archive_entry.3 archive_entry_mtime_nsec.3
+MLINKS+=	archive_entry.3 archive_entry_nlink.3
 MLINKS+=	archive_entry.3 archive_entry_new.3
 MLINKS+=	archive_entry.3 archive_entry_pathname.3
 MLINKS+=	archive_entry.3 archive_entry_pathname_w.3
 MLINKS+=	archive_entry.3 archive_entry_rdev.3
 MLINKS+=	archive_entry.3 archive_entry_rdevmajor.3
 MLINKS+=	archive_entry.3 archive_entry_rdevminor.3
+MLINKS+=	archive_entry.3 archive_entry_set_atime.3
+MLINKS+=	archive_entry.3 archive_entry_set_ctime.3
+MLINKS+=	archive_entry.3 archive_entry_set_dev.3
+MLINKS+=	archive_entry.3 archive_entry_set_devmajor.3
+MLINKS+=	archive_entry.3 archive_entry_set_devminor.3
 MLINKS+=	archive_entry.3 archive_entry_set_fflags.3
 MLINKS+=	archive_entry.3 archive_entry_set_gid.3
 MLINKS+=	archive_entry.3 archive_entry_set_gname.3
 MLINKS+=	archive_entry.3 archive_entry_set_hardlink.3
 MLINKS+=	archive_entry.3 archive_entry_set_link.3
 MLINKS+=	archive_entry.3 archive_entry_set_mode.3
+MLINKS+=	archive_entry.3 archive_entry_set_mtime.3
+MLINKS+=	archive_entry.3 archive_entry_set_nlink.3
 MLINKS+=	archive_entry.3 archive_entry_set_pathname.3
+MLINKS+=	archive_entry.3 archive_entry_set_rdev.3
 MLINKS+=	archive_entry.3 archive_entry_set_rdevmajor.3
 MLINKS+=	archive_entry.3 archive_entry_set_rdevminor.3
 MLINKS+=	archive_entry.3 archive_entry_set_size.3
@@ -169,11 +201,13 @@
 MLINKS+=	archive_read.3 archive_read_support_compression_compress.3
 MLINKS+=	archive_read.3 archive_read_support_compression_gzip.3
 MLINKS+=	archive_read.3 archive_read_support_compression_none.3
+MLINKS+=	archive_read.3 archive_read_support_compression_program.3
 MLINKS+=	archive_read.3 archive_read_support_format_all.3
 MLINKS+=	archive_read.3 archive_read_support_format_cpio.3
 MLINKS+=	archive_read.3 archive_read_support_format_iso9660.3
 MLINKS+=	archive_read.3 archive_read_support_format_tar.3
 MLINKS+=	archive_read.3 archive_read_support_format_zip.3
+MLINKS+=	archive_util.3 archive_clear_error.3
 MLINKS+=	archive_util.3 archive_compression.3
 MLINKS+=	archive_util.3 archive_compression_name.3
 MLINKS+=	archive_util.3 archive_errno.3
@@ -199,9 +233,22 @@
 MLINKS+=	archive_write.3 archive_write_set_callbacks.3
 MLINKS+=	archive_write.3 archive_write_set_compression_bzip2.3
 MLINKS+=	archive_write.3 archive_write_set_compression_gzip.3
+MLINKS+=	archive_write.3 archive_write_set_compression_none.3
+MLINKS+=	archive_write.3 archive_write_set_compression_program.3
 MLINKS+=	archive_write.3 archive_write_set_format_pax.3
 MLINKS+=	archive_write.3 archive_write_set_format_shar.3
 MLINKS+=	archive_write.3 archive_write_set_format_ustar.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_new.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_set_group_lookup.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_set_options.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_set_skip_file.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_set_standard_lookup.3
+MLINKS+=	archive_write_disk.3 archive_write_disk_set_user_lookup.3
 MLINKS+=	libarchive.3 archive.3
 
+.PHONY: test
+
+test:
+	cd ${.CURDIR}/test && make test
+
 .include <bsd.lib.mk>
Index: tar.5
===================================================================
RCS file: /home/cvs/src/lib/libarchive/tar.5,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/tar.5 -Llib/libarchive/tar.5 -u -r1.1.1.2 -r1.2
--- lib/libarchive/tar.5
+++ lib/libarchive/tar.5
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.12.2.2 2007/01/27 06:44:54 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/tar.5,v 1.17 2007/01/09 08:05:56 kientzle Exp $
 .\"
 .Dd May 20, 2004
 .Dt TAR 5
Index: archive_read_support_compression_all.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_all.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_compression_all.c -Llib/libarchive/archive_read_support_compression_all.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_compression_all.c
+++ lib/libarchive/archive_read_support_compression_all.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.5.8.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.6 2007/01/09 08:05:55 kientzle Exp $");
 
 #include "archive.h"
 
Index: archive_read_open_filename.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_open_filename.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/archive_read_open_filename.c -Llib/libarchive/archive_read_open_filename.c -u -r1.1.1.1 -r1.2
--- lib/libarchive/archive_read_open_filename.c
+++ lib/libarchive/archive_read_open_filename.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.18.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.20 2007/06/26 03:06:48 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -52,6 +52,7 @@
 	size_t	 block_size;
 	void	*buffer;
 	mode_t	 st_mode;  /* Mode bits for opened file. */
+	char	 can_skip; /* This file supports skipping. */
 	char	 filename[1]; /* Must be last! */
 };
 
@@ -95,6 +96,8 @@
 	mine->block_size = block_size;
 	mine->buffer = NULL;
 	mine->fd = -1;
+	/* lseek() almost never works; disable it by default.  See below. */
+	mine->can_skip = 0;
 	return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
 }
 
@@ -121,8 +124,19 @@
 	if (fstat(mine->fd, &st) == 0) {
 		/* If we're reading a file from disk, ensure that we don't
 		   overwrite it with an extracted file. */
-		if (S_ISREG(st.st_mode))
+		if (S_ISREG(st.st_mode)) {
 			archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+			/*
+			 * Enabling skip here is a performance
+			 * optimization for anything that supports
+			 * lseek().  On FreeBSD, only regular files
+			 * and raw disk devices support lseek() and
+			 * there's no portable way to determine if a
+			 * device is a raw disk device, so we only
+			 * enable this optimization for regular files.
+			 */
+			mine->can_skip = 1;
+		}
 		/* Remember mode so close can decide whether to flush. */
 		mine->st_mode = st.st_mode;
 	} else {
@@ -165,8 +179,14 @@
 	struct read_file_data *mine = (struct read_file_data *)client_data;
 	off_t old_offset, new_offset;
 
+	if (!mine->can_skip) /* We can't skip, so ... */
+		return (0); /* ... skip zero bytes. */
+
 	/* Reduce request to the next smallest multiple of block_size */
 	request = (request / mine->block_size) * mine->block_size;
+	if (request == 0)
+		return (0);
+
 	/*
 	 * Hurray for lazy evaluation: if the first lseek fails, the second
 	 * one will not be executed.
@@ -174,6 +194,9 @@
 	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
 	    ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
 	{
+		/* If skip failed once, it will probably fail again. */
+		mine->can_skip = 0;
+
 		if (errno == ESPIPE)
 		{
 			/*
Index: archive_read_support_format_empty.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_empty.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_format_empty.c -Llib/libarchive/archive_read_support_format_empty.c -u -r1.1.1.1 -r1.2
--- lib/libarchive/archive_read_support_format_empty.c
+++ lib/libarchive/archive_read_support_format_empty.c
@@ -24,20 +24,22 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.1.2.1 2007/02/14 08:29:35 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.3 2007/05/29 01:00:19 kientzle Exp $");
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
-static int	archive_read_format_empty_bid(struct archive *);
-static int	archive_read_format_empty_read_data(struct archive *,
+static int	archive_read_format_empty_bid(struct archive_read *);
+static int	archive_read_format_empty_read_data(struct archive_read *,
 		    const void **, size_t *, off_t *);
-static int	archive_read_format_empty_read_header(struct archive *,
+static int	archive_read_format_empty_read_header(struct archive_read *,
 		    struct archive_entry *);
 int
-archive_read_support_format_empty(struct archive *a)
+archive_read_support_format_empty(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	int r;
 
 	r = __archive_read_register_format(a,
@@ -53,32 +55,32 @@
 
 
 static int
-archive_read_format_empty_bid(struct archive *a)
+archive_read_format_empty_bid(struct archive_read *a)
 {
 	int bytes_read;
 	const void *h;
 
-	bytes_read = (a->compression_read_ahead)(a, &h, 1);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 1);
 	if (bytes_read > 0)
 		return (-1);
 	return (1);
 }
 
 static int
-archive_read_format_empty_read_header(struct archive *a,
+archive_read_format_empty_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
 	(void)a; /* UNUSED */
 	(void)entry; /* UNUSED */
 
-	a->archive_format = ARCHIVE_FORMAT_EMPTY;
-	a->archive_format_name = "Empty file";
+	a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;
+	a->archive.archive_format_name = "Empty file";
 
 	return (ARCHIVE_EOF);
 }
 
 static int
-archive_read_format_empty_read_data(struct archive *a,
+archive_read_format_empty_read_data(struct archive_read *a,
     const void **buff, size_t *size, off_t *offset)
 {
 	(void)a; /* UNUSED */
Index: archive_read_open_memory.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_open_memory.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/archive_read_open_memory.c -Llib/libarchive/archive_read_open_memory.c -u -r1.1.1.1 -r1.2
--- lib/libarchive/archive_read_open_memory.c
+++ lib/libarchive/archive_read_open_memory.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.3.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $");
 
 #include <errno.h>
 #include <stdlib.h>
@@ -134,7 +134,7 @@
 	struct read_memory_data *mine = (struct read_memory_data *)client_data;
 
 	(void)a; /* UNUSED */
-	if (mine->buffer + skip > mine->end)
+	if ((off_t)skip > (off_t)(mine->end - mine->buffer))
 		skip = mine->end - mine->buffer;
 	/* Round down to block size. */
 	skip /= mine->read_size;
Index: archive_write_open_file.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_open_file.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_open_file.c -Llib/libarchive/archive_write_open_file.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_open_file.c
+++ lib/libarchive/archive_write_open_file.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.11.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
--- /dev/null
+++ lib/libarchive/archive_write_set_format_cpio_newc.c
@@ -0,0 +1,274 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_cpio_newc.c,v 1.1 2007/06/22 05:47:00 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+static ssize_t	archive_write_newc_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_newc_finish(struct archive_write *);
+static int	archive_write_newc_destroy(struct archive_write *);
+static int	archive_write_newc_finish_entry(struct archive_write *);
+static int	archive_write_newc_header(struct archive_write *,
+		    struct archive_entry *);
+static int	format_hex(int64_t, void *, int);
+static int64_t	format_hex_recursive(int64_t, char *, int);
+
+struct cpio {
+	uint64_t	  entry_bytes_remaining;
+	int		  padding;
+};
+
+struct cpio_header_newc {
+	char	c_magic[6];
+	char	c_ino[8];
+	char	c_mode[8];
+	char	c_uid[8];
+	char	c_gid[8];
+	char	c_nlink[8];
+	char	c_mtime[8];
+	char	c_filesize[8];
+	char	c_devmajor[8];
+	char	c_devminor[8];
+	char	c_rdevmajor[8];
+	char	c_rdevminor[8];
+	char	c_namesize[8];
+	char	c_checksum[8];
+};
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_newc(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct cpio *cpio;
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
+
+	cpio = (struct cpio *)malloc(sizeof(*cpio));
+	if (cpio == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+		return (ARCHIVE_FATAL);
+	}
+	memset(cpio, 0, sizeof(*cpio));
+	a->format_data = cpio;
+
+	a->pad_uncompressed = 1;
+	a->format_write_header = archive_write_newc_header;
+	a->format_write_data = archive_write_newc_data;
+	a->format_finish_entry = archive_write_newc_finish_entry;
+	a->format_finish = archive_write_newc_finish;
+	a->format_destroy = archive_write_newc_destroy;
+	a->archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+	a->archive_format_name = "SVR4 cpio nocrc";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct cpio *cpio;
+	const char *p, *path;
+	int pathlength, ret;
+	struct cpio_header_newc	 h;
+	int pad;
+
+	cpio = (struct cpio *)a->format_data;
+	ret = 0;
+
+	path = archive_entry_pathname(entry);
+	pathlength = strlen(path) + 1; /* Include trailing null. */
+
+	memset(&h, 0, sizeof(h));
+	format_hex(0x070701, &h.c_magic, sizeof(h.c_magic));
+	format_hex(archive_entry_devmajor(entry), &h.c_devmajor, sizeof(h.c_devmajor));
+	format_hex(archive_entry_devminor(entry), &h.c_devminor, sizeof(h.c_devminor));
+	if (archive_entry_ino(entry) > 0xffffffff) {
+		archive_set_error(&a->archive, ERANGE, "large inode number truncated");
+		ret = ARCHIVE_WARN;
+	}
+
+	format_hex(archive_entry_ino(entry) & 0xffffffff, &h.c_ino, sizeof(h.c_ino));
+	format_hex(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
+	format_hex(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
+	format_hex(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
+	format_hex(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR) {
+	    format_hex(archive_entry_rdevmajor(entry), &h.c_rdevmajor, sizeof(h.c_rdevmajor));
+	    format_hex(archive_entry_rdevminor(entry), &h.c_rdevminor, sizeof(h.c_rdevminor));
+	} else {
+	    format_hex(0, &h.c_rdevmajor, sizeof(h.c_rdevmajor));
+	    format_hex(0, &h.c_rdevminor, sizeof(h.c_rdevminor));
+	}
+	format_hex(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
+	format_hex(pathlength, &h.c_namesize, sizeof(h.c_namesize));
+	format_hex(0, &h.c_checksum, sizeof(h.c_checksum));
+
+	/* Symlinks get the link written as the body of the entry. */
+	p = archive_entry_symlink(entry);
+	if (p != NULL  &&  *p != '\0')
+		format_hex(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
+	else
+		format_hex(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize));
+
+	ret = (a->compressor.write)(a, &h, sizeof(h));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Pad pathname to even length. */
+	ret = (a->compressor.write)(a, path, pathlength);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	pad = 0x3 & - (pathlength + sizeof(struct cpio_header_newc));
+	if (pad)
+		ret = (a->compressor.write)(a, "\0\0\0", pad);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	cpio->entry_bytes_remaining = archive_entry_size(entry);
+	cpio->padding = 3 & (-cpio->entry_bytes_remaining);
+	/* Write the symlink now. */
+	if (p != NULL  &&  *p != '\0')
+		ret = (a->compressor.write)(a, p, strlen(p));
+
+	return (ret);
+}
+
+static ssize_t
+archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct cpio *cpio;
+	int ret;
+
+	cpio = (struct cpio *)a->format_data;
+	if (s > cpio->entry_bytes_remaining)
+		s = cpio->entry_bytes_remaining;
+
+	ret = (a->compressor.write)(a, buff, s);
+	cpio->entry_bytes_remaining -= s;
+	if (ret >= 0)
+		return (s);
+	else
+		return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_hex(int64_t v, void *p, int digits)
+{
+	int64_t	max;
+	int	ret;
+
+	max = (((int64_t)1) << (digits * 4)) - 1;
+	if (v >= 0  &&  v <= max) {
+	    format_hex_recursive(v, (char *)p, digits);
+	    ret = 0;
+	} else {
+	    format_hex_recursive(max, (char *)p, digits);
+	    ret = -1;
+	}
+	return (ret);
+}
+
+static int64_t
+format_hex_recursive(int64_t v, char *p, int s)
+{
+	if (s == 0)
+		return (v);
+	v = format_hex_recursive(v, p+1, s-1);
+	*p = "0123456789abcdef"[v & 0xf];
+	return (v >>= 4);
+}
+
+static int
+archive_write_newc_finish(struct archive_write *a)
+{
+	struct cpio *cpio;
+	int er;
+	struct archive_entry *trailer;
+
+	cpio = (struct cpio *)a->format_data;
+	trailer = archive_entry_new();
+	archive_entry_set_nlink(trailer, 1);
+	archive_entry_set_pathname(trailer, "TRAILER!!!");
+	er = archive_write_newc_header(a, trailer);
+	archive_entry_free(trailer);
+	return (er);
+}
+
+static int
+archive_write_newc_destroy(struct archive_write *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)a->format_data;
+	free(cpio);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_finish_entry(struct archive_write *a)
+{
+	struct cpio *cpio;
+	int to_write, ret;
+
+	cpio = (struct cpio *)a->format_data;
+	ret = ARCHIVE_OK;
+	while (cpio->entry_bytes_remaining > 0) {
+		to_write = cpio->entry_bytes_remaining < a->null_length ?
+		    cpio->entry_bytes_remaining : a->null_length;
+		ret = (a->compressor.write)(a, a->nulls, to_write);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		cpio->entry_bytes_remaining -= to_write;
+	}
+	ret = (a->compressor.write)(a, a->nulls, cpio->padding);
+	return (ret);
+}
Index: archive_check_magic.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_check_magic.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_check_magic.c -Llib/libarchive/archive_check_magic.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_check_magic.c
+++ lib/libarchive/archive_check_magic.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.5.2.1 2007/01/27 06:44:52 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.8 2007/04/02 00:15:45 kientzle Exp $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -72,9 +72,9 @@
 
 
 static void
-write_all_states(int states)
+write_all_states(unsigned int states)
 {
-	unsigned lowbit;
+	unsigned int lowbit;
 
 	/* A trick for computing the lowest set bit. */
 	while ((lowbit = states & (-states)) != 0) {
@@ -92,8 +92,8 @@
  * the libarchive API.
  */
 void
-__archive_check_magic(struct archive *a, unsigned magic, unsigned state,
-    const char *function)
+__archive_check_magic(struct archive *a, unsigned int magic,
+    unsigned int state, const char *function)
 {
 	if (a->magic != magic) {
 		errmsg("INTERNAL ERROR: Function ");
Index: archive_read_support_format_all.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_all.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_format_all.c -Llib/libarchive/archive_read_support_format_all.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_format_all.c
+++ lib/libarchive/archive_read_support_format_all.c
@@ -24,13 +24,14 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.6.2.2 2007/02/14 08:29:35 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.9 2007/04/07 05:54:23 kientzle Exp $");
 
 #include "archive.h"
 
 int
 archive_read_support_format_all(struct archive *a)
 {
+	archive_read_support_format_ar(a);
 	archive_read_support_format_cpio(a);
 	archive_read_support_format_empty(a);
 	archive_read_support_format_iso9660(a);
Index: archive_string.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_string.c -Llib/libarchive/archive_string.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_string.c
+++ lib/libarchive/archive_string.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.6.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_string.c,v 1.11 2007/07/15 19:13:59 kientzle Exp $");
 
 /*
  * Basic resizable string support, to simplify manipulating arbitrary-sized
@@ -44,7 +44,8 @@
 struct archive_string *
 __archive_string_append(struct archive_string *as, const char *p, size_t s)
 {
-	__archive_string_ensure(as, as->length + s + 1);
+	if (__archive_string_ensure(as, as->length + s + 1) == NULL)
+		__archive_errx(1, "Out of memory");
 	memcpy(as->s + as->length, p, s);
 	as->s[as->length + s] = 0;
 	as->length += s;
@@ -52,6 +53,16 @@
 }
 
 void
+__archive_string_copy(struct archive_string *dest, struct archive_string *src)
+{
+	if (__archive_string_ensure(dest, src->length + 1) == NULL)
+		__archive_errx(1, "Out of memory");
+	memcpy(dest->s, src->s, src->length);
+	dest->length = src->length;
+	dest->s[dest->length] = 0;
+}
+
+void
 __archive_string_free(struct archive_string *as)
 {
 	as->length = 0;
@@ -60,6 +71,7 @@
 		free(as->s);
 }
 
+/* Returns NULL on any allocation failure. */
 struct archive_string *
 __archive_string_ensure(struct archive_string *as, size_t s)
 {
@@ -71,10 +83,8 @@
 	while (as->buffer_length < s)
 		as->buffer_length *= 2;
 	as->s = (char *)realloc(as->s, as->buffer_length);
-	/* TODO: Return null instead and fix up all of our callers to
-	 * handle this correctly. */
 	if (as->s == NULL)
-		__archive_errx(1, "Out of memory");
+		return (NULL);
 	return (as);
 }
 
Index: archive_string_sprintf.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string_sprintf.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_string_sprintf.c -Llib/libarchive/archive_string_sprintf.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_string_sprintf.c
+++ lib/libarchive/archive_string_sprintf.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.7.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_string_sprintf.c,v 1.9 2007/07/15 19:13:59 kientzle Exp $");
 
 /*
  * The use of printf()-family functions can be troublesome
@@ -42,6 +42,7 @@
 #include <stdio.h>
 
 #include "archive_string.h"
+#include "archive_private.h"
 
 /*
  * Like 'vsprintf', but ensures the target is big enough, resizing if
@@ -56,7 +57,8 @@
 	uintmax_t u; /* Unsigned integer temp. */
 	const char *p, *p2;
 
-	__archive_string_ensure(as, 64);
+	if (__archive_string_ensure(as, 64) == NULL)
+		__archive_errx(1, "Out of memory");
 
 	if (fmt == NULL) {
 		as->s[0] = 0;
Index: archive_write_set_compression_none.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_compression_none.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_compression_none.c -Llib/libarchive/archive_write_set_compression_none.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_compression_none.c
+++ lib/libarchive/archive_write_set_compression_none.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.8.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_none.c,v 1.15 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -38,11 +38,12 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
-static int	archive_compressor_none_finish(struct archive *a);
-static int	archive_compressor_none_init(struct archive *);
-static int	archive_compressor_none_write(struct archive *, const void *,
-		    size_t);
+static int	archive_compressor_none_finish(struct archive_write *a);
+static int	archive_compressor_none_init(struct archive_write *);
+static int	archive_compressor_none_write(struct archive_write *,
+		    const void *, size_t);
 
 struct archive_none {
 	char	*buffer;
@@ -52,12 +53,12 @@
 };
 
 int
-archive_write_set_compression_none(struct archive *a)
+archive_write_set_compression_none(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
-	a->compression_init = &archive_compressor_none_init;
-	a->compression_code = ARCHIVE_COMPRESSION_NONE;
-	a->compression_name = "none";
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_compression_none");
+	a->compressor.init = &archive_compressor_none_init;
 	return (0);
 }
 
@@ -65,23 +66,23 @@
  * Setup callback.
  */
 static int
-archive_compressor_none_init(struct archive *a)
+archive_compressor_none_init(struct archive_write *a)
 {
 	int ret;
 	struct archive_none *state;
 
-	a->compression_code = ARCHIVE_COMPRESSION_NONE;
-	a->compression_name = "none";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
+	a->archive.compression_name = "none";
 
 	if (a->client_opener != NULL) {
-		ret = (a->client_opener)(a, a->client_data);
+		ret = (a->client_opener)(&a->archive, a->client_data);
 		if (ret != 0)
 			return (ret);
 	}
 
 	state = (struct archive_none *)malloc(sizeof(*state));
 	if (state == NULL) {
-		archive_set_error(a, ENOMEM,
+		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate data for output buffering");
 		return (ARCHIVE_FATAL);
 	}
@@ -91,7 +92,7 @@
 	if (state->buffer_size != 0) {
 		state->buffer = (char *)malloc(state->buffer_size);
 		if (state->buffer == NULL) {
-			archive_set_error(a, ENOMEM,
+			archive_set_error(&a->archive, ENOMEM,
 			    "Can't allocate output buffer");
 			free(state);
 			return (ARCHIVE_FATAL);
@@ -101,9 +102,9 @@
 	state->next = state->buffer;
 	state->avail = state->buffer_size;
 
-	a->compression_data = state;
-	a->compression_write = archive_compressor_none_write;
-	a->compression_finish = archive_compressor_none_finish;
+	a->compressor.data = state;
+	a->compressor.write = archive_compressor_none_write;
+	a->compressor.finish = archive_compressor_none_finish;
 	return (ARCHIVE_OK);
 }
 
@@ -111,7 +112,7 @@
  * Write data to the stream.
  */
 static int
-archive_compressor_none_write(struct archive *a, const void *vbuff,
+archive_compressor_none_write(struct archive_write *a, const void *vbuff,
     size_t length)
 {
 	const char *buff;
@@ -119,10 +120,10 @@
 	ssize_t bytes_written;
 	struct archive_none *state;
 
-	state = (struct archive_none *)a->compression_data;
+	state = (struct archive_none *)a->compressor.data;
 	buff = (const char *)vbuff;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -138,14 +139,15 @@
 	 */
 	if (state->buffer_size == 0) {
 		while (remaining > 0) {
-			bytes_written = (a->client_writer)(a, a->client_data,
-			    buff, remaining);
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, buff, remaining);
 			if (bytes_written <= 0)
 				return (ARCHIVE_FATAL);
+			a->archive.raw_position += bytes_written;
 			remaining -= bytes_written;
 			buff += bytes_written;
 		}
-		a->file_position += length;
+		a->archive.file_position += length;
 		return (ARCHIVE_OK);
 	}
 
@@ -155,12 +157,12 @@
 		 * output buffer.
 		 */
 		if (state->avail == 0) {
-			bytes_written = (a->client_writer)(a, a->client_data,
-			    state->buffer, state->buffer_size);
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, state->buffer, state->buffer_size);
 			if (bytes_written <= 0)
 				return (ARCHIVE_FATAL);
 			/* XXX TODO: if bytes_written < state->buffer_size */
-			a->raw_position += bytes_written;
+			a->archive.raw_position += bytes_written;
 			state->next = state->buffer;
 			state->avail = state->buffer_size;
 		}
@@ -174,7 +176,7 @@
 		buff += to_copy;
 		remaining -= to_copy;
 	}
-	a->file_position += length;
+	a->archive.file_position += length;
 	return (ARCHIVE_OK);
 }
 
@@ -183,7 +185,7 @@
  * Finish the compression.
  */
 static int
-archive_compressor_none_finish(struct archive *a)
+archive_compressor_none_finish(struct archive_write *a)
 {
 	ssize_t block_length;
 	ssize_t target_block_length;
@@ -192,10 +194,10 @@
 	int ret2;
 	struct archive_none *state;
 
-	state = (struct archive_none *)a->compression_data;
+	state = (struct archive_none *)a->compressor.data;
 	ret = ret2 = ARCHIVE_OK;
 	if (a->client_writer == NULL) {
-		archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
 		    "No write callback is registered?  "
 		    "This is probably an internal programming error.");
 		return (ARCHIVE_FATAL);
@@ -222,23 +224,19 @@
 			    target_block_length - block_length);
 			block_length = target_block_length;
 		}
-		bytes_written = (a->client_writer)(a, a->client_data,
-		    state->buffer, block_length);
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, state->buffer, block_length);
 		if (bytes_written <= 0)
 			ret = ARCHIVE_FATAL;
 		else {
-			a->raw_position += bytes_written;
+			a->archive.raw_position += bytes_written;
 			ret = ARCHIVE_OK;
 		}
 	}
-
-	/* Close the output */
-	if (a->client_closer != NULL)
-		ret2 = (a->client_closer)(a, a->client_data);
-
-	free(state->buffer);
+	if (state->buffer)
+		free(state->buffer);
 	free(state);
-	a->compression_data = NULL;
+	a->compressor.data = NULL;
 
-	return (ret != ARCHIVE_OK ? ret : ret2);
+	return (ret);
 }
Index: archive_string.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_string.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_string.h -Llib/libarchive/archive_string.h -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_string.h
+++ lib/libarchive/archive_string.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.6.2.1 2007/01/27 06:44:53 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_string.h,v 1.9 2007/05/29 01:00:19 kientzle Exp $
  *
  */
 
@@ -74,6 +74,12 @@
 struct archive_string *
 __archive_string_append(struct archive_string *as, const char *p, size_t s);
 
+/* Copy one archive_string to another */
+void
+__archive_string_copy(struct archive_string *dest, struct archive_string *src);
+#define archive_string_copy(dest, src) \
+	__archive_string_copy(dest, src)
+
 /* Ensure that the underlying buffer is at least as large as the request. */
 struct archive_string *
 __archive_string_ensure(struct archive_string *, size_t);
Index: archive_read_support_format_iso9660.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_iso9660.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_format_iso9660.c -Llib/libarchive/archive_read_support_format_iso9660.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_format_iso9660.c
+++ lib/libarchive/archive_read_support_format_iso9660.c
@@ -24,11 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.8.2.2 2007/01/27 06:44:53 kientzle Exp $");
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_iso9660.c,v 1.23 2007/05/29 01:00:19 kientzle Exp $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -42,13 +38,11 @@
 #include <string.h>
 #endif
 #include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 #include "archive_string.h"
 
 /*
@@ -222,12 +216,12 @@
 };
 
 static void	add_entry(struct iso9660 *iso9660, struct file_info *file);
-static int	archive_read_format_iso9660_bid(struct archive *);
-static int	archive_read_format_iso9660_cleanup(struct archive *);
-static int	archive_read_format_iso9660_read_data(struct archive *,
+static int	archive_read_format_iso9660_bid(struct archive_read *);
+static int	archive_read_format_iso9660_cleanup(struct archive_read *);
+static int	archive_read_format_iso9660_read_data(struct archive_read *,
 		    const void **, size_t *, off_t *);
-static int	archive_read_format_iso9660_read_data_skip(struct archive *);
-static int	archive_read_format_iso9660_read_header(struct archive *,
+static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);
+static int	archive_read_format_iso9660_read_header(struct archive_read *,
 		    struct archive_entry *);
 static const char *build_pathname(struct archive_string *, struct file_info *);
 static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);
@@ -236,7 +230,7 @@
 static time_t	isodate7(const unsigned char *);
 static int	isPVD(struct iso9660 *, const unsigned char *);
 static struct file_info *next_entry(struct iso9660 *);
-static int	next_entry_seek(struct archive *a, struct iso9660 *iso9660,
+static int	next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
 		    struct file_info **pfile);
 static struct file_info *
 		parse_file_info(struct iso9660 *iso9660,
@@ -248,14 +242,15 @@
 static unsigned	toi(const void *p, int n);
 
 int
-archive_read_support_format_iso9660(struct archive *a)
+archive_read_support_format_iso9660(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct iso9660 *iso9660;
 	int r;
 
 	iso9660 = (struct iso9660 *)malloc(sizeof(*iso9660));
 	if (iso9660 == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate iso9660 data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(iso9660, 0, sizeof(*iso9660));
@@ -279,14 +274,14 @@
 
 
 static int
-archive_read_format_iso9660_bid(struct archive *a)
+archive_read_format_iso9660_bid(struct archive_read *a)
 {
 	struct iso9660 *iso9660;
 	ssize_t bytes_read;
 	const void *h;
-	const char *p;
+	const unsigned char *p;
 
-	iso9660 = (struct iso9660 *)*(a->pformat_data);
+	iso9660 = (struct iso9660 *)(a->format->data);
 
 	if (iso9660->bid >= 0)
 		return (iso9660->bid);
@@ -296,10 +291,10 @@
 	 * 8 sectors of the volume descriptor table.  Of course,
 	 * if the I/O layer gives us more, we'll take it.
 	 */
-	bytes_read = (a->compression_read_ahead)(a, &h, 32768 + 8*2048);
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 32768 + 8*2048);
 	if (bytes_read < 32768 + 8*2048)
 	    return (iso9660->bid = -1);
-	p = (const char *)h;
+	p = (const unsigned char *)h;
 
 	/* Skip the reserved area. */
 	bytes_read -= 32768;
@@ -310,7 +305,7 @@
 		iso9660->bid = isPVD(iso9660, p);
 		if (iso9660->bid > 0)
 			return (iso9660->bid);
-		if (*p == '\xff') /* End-of-volume-descriptor marker. */
+		if (*p == '\177') /* End-of-volume-descriptor marker. */
 			break;
 	}
 
@@ -338,20 +333,19 @@
 }
 
 static int
-archive_read_format_iso9660_read_header(struct archive *a,
+archive_read_format_iso9660_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	struct stat st;
 	struct iso9660 *iso9660;
 	struct file_info *file;
 	ssize_t bytes_read;
 	int r;
 
-	iso9660 = (struct iso9660 *)*(a->pformat_data);
+	iso9660 = (struct iso9660 *)(a->format->data);
 
-	if (!a->archive_format) {
-		a->archive_format = ARCHIVE_FORMAT_ISO9660;
-		a->archive_format_name = "ISO9660";
+	if (!a->archive.archive_format) {
+		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+		a->archive.archive_format_name = "ISO9660";
 	}
 
 	/* Get the next entry that appears after the current offset. */
@@ -363,22 +357,20 @@
 	iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */
 
 	/* Set up the entry structure with information about this entry. */
-	memset(&st, 0, sizeof(st));
-	st.st_mode = file->mode;
-	st.st_uid = file->uid;
-	st.st_gid = file->gid;
-	st.st_nlink = file->nlinks;
-	st.st_ino = file->inode;
-	st.st_mtime = file->mtime;
-	st.st_ctime = file->ctime;
-	st.st_atime = file->atime;
-	st.st_size = iso9660->entry_bytes_remaining;
-	archive_entry_copy_stat(entry, &st);
+	archive_entry_set_mode(entry, file->mode);
+	archive_entry_set_uid(entry, file->uid);
+	archive_entry_set_gid(entry, file->gid);
+	archive_entry_set_nlink(entry, file->nlinks);
+	archive_entry_set_ino(entry, file->inode);
+	archive_entry_set_mtime(entry, file->mtime, 0);
+	archive_entry_set_ctime(entry, file->ctime, 0);
+	archive_entry_set_atime(entry, file->atime, 0);
+	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
 	archive_string_empty(&iso9660->pathname);
 	archive_entry_set_pathname(entry,
 	    build_pathname(&iso9660->pathname, file));
 	if (file->symlink.s != NULL)
-		archive_entry_set_symlink(entry, file->symlink.s);
+		archive_entry_copy_symlink(entry, file->symlink.s);
 
 	/* If this entry points to the same data as the previous
 	 * entry, convert this into a hardlink to that entry.
@@ -397,7 +389,7 @@
 	/* If the offset is before our current position, we can't
 	 * seek backwards to extract it, so issue a warning. */
 	if (file->offset < iso9660->current_position) {
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Ignoring out-of-order file");
 		iso9660->entry_bytes_remaining = 0;
 		iso9660->entry_sparse_offset = 0;
@@ -410,23 +402,23 @@
 	archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s);
 
 	/* If this is a directory, read in all of the entries right now. */
-	if (S_ISDIR(st.st_mode)) {
+	if (archive_entry_filetype(entry) == AE_IFDIR) {
 		while (iso9660->entry_bytes_remaining > 0) {
 			const void *block;
 			const unsigned char *p;
 			ssize_t step = iso9660->logical_block_size;
 			if (step > iso9660->entry_bytes_remaining)
 				step = iso9660->entry_bytes_remaining;
-			bytes_read = (a->compression_read_ahead)(a, &block, step);
+			bytes_read = (a->decompressor->read_ahead)(a, &block, step);
 			if (bytes_read < step) {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 	    "Failed to read full block when scanning ISO9660 directory list");
 				release_file(iso9660, file);
 				return (ARCHIVE_FATAL);
 			}
 			if (bytes_read > step)
 				bytes_read = step;
-			(a->compression_read_consume)(a, bytes_read);
+			(a->decompressor->consume)(a, bytes_read);
 			iso9660->current_position += bytes_read;
 			iso9660->entry_bytes_remaining -= bytes_read;
 			for (p = (const unsigned char *)block;
@@ -445,9 +437,9 @@
 				child = parse_file_info(iso9660, file, p);
 				add_entry(iso9660, child);
 				if (iso9660->seenRockridge) {
-					a->archive_format =
+					a->archive.archive_format =
 					    ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
-					a->archive_format_name =
+					a->archive.archive_format_name =
 					    "ISO9660 with Rockridge extensions";
 				}
 			}
@@ -459,7 +451,7 @@
 }
 
 static int
-archive_read_format_iso9660_read_data_skip(struct archive *a)
+archive_read_format_iso9660_read_data_skip(struct archive_read *a)
 {
 	/* Because read_next_header always does an explicit skip
 	 * to the next entry, we don't need to do anything here. */
@@ -468,13 +460,13 @@
 }
 
 static int
-archive_read_format_iso9660_read_data(struct archive *a,
+archive_read_format_iso9660_read_data(struct archive_read *a,
     const void **buff, size_t *size, off_t *offset)
 {
 	ssize_t bytes_read;
 	struct iso9660 *iso9660;
 
-	iso9660 = (struct iso9660 *)*(a->pformat_data);
+	iso9660 = (struct iso9660 *)(a->format->data);
 	if (iso9660->entry_bytes_remaining <= 0) {
 		*buff = NULL;
 		*size = 0;
@@ -482,9 +474,9 @@
 		return (ARCHIVE_EOF);
 	}
 
-	bytes_read = (a->compression_read_ahead)(a, buff, 1);
+	bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
 	if (bytes_read == 0)
-		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Truncated input file");
 	if (bytes_read <= 0)
 		return (ARCHIVE_FATAL);
@@ -495,23 +487,25 @@
 	iso9660->entry_sparse_offset += bytes_read;
 	iso9660->entry_bytes_remaining -= bytes_read;
 	iso9660->current_position += bytes_read;
-	(a->compression_read_consume)(a, bytes_read);
+	(a->decompressor->consume)(a, bytes_read);
 	return (ARCHIVE_OK);
 }
 
 static int
-archive_read_format_iso9660_cleanup(struct archive *a)
+archive_read_format_iso9660_cleanup(struct archive_read *a)
 {
 	struct iso9660 *iso9660;
 	struct file_info *file;
 
-	iso9660 = (struct iso9660 *)*(a->pformat_data);
+	iso9660 = (struct iso9660 *)(a->format->data);
 	while ((file = next_entry(iso9660)) != NULL)
 		release_file(iso9660, file);
 	archive_string_free(&iso9660->pathname);
 	archive_string_free(&iso9660->previous_pathname);
+	if (iso9660->pending_files)
+		free(iso9660->pending_files);
 	free(iso9660);
-	*(a->pformat_data) = NULL;
+	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
 }
 
@@ -552,9 +546,9 @@
 	file->name[name_len] = '\0';
 	flags = *(isodirrec + DR_flags_offset);
 	if (flags & 0x02)
-		file->mode = S_IFDIR | 0700;
+		file->mode = AE_IFDIR | 0700;
 	else
-		file->mode = S_IFREG | 0400;
+		file->mode = AE_IFREG | 0400;
 
 	/* Rockridge extensions overwrite information from above. */
 	{
@@ -889,7 +883,7 @@
 }
 
 static int
-next_entry_seek(struct archive *a, struct iso9660 *iso9660,
+next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
     struct file_info **pfile)
 {
 	struct file_info *file;
@@ -915,33 +909,13 @@
 			offset = file->offset;
 
 		/* Seek forward to the start of the entry. */
-		/* Use fast compression_skip if it's available. */
-		if (iso9660->current_position < offset
-		    && a->compression_skip != NULL) {
+		if (iso9660->current_position < offset) {
 			off_t step = offset - iso9660->current_position;
 			off_t bytes_read;
-			bytes_read = (a->compression_skip)(a, step);
-			iso9660->current_position += bytes_read;
-		}
-
-		/* Use a series of reads if compression_skip didn't
-		 * get us all the way there. */
-		while (iso9660->current_position < offset) {
-			ssize_t step = offset - iso9660->current_position;
-			ssize_t bytes_read;
-			const void *buff;
-
-			if (step > iso9660->logical_block_size)
-				step = iso9660->logical_block_size;
-			bytes_read = (a->compression_read_ahead)(a, &buff, step);
-			if (bytes_read <= 0) {
-				release_file(iso9660, file);
-				return (ARCHIVE_FATAL);
-			}
-			if (bytes_read > step)
-				bytes_read = step;
-			iso9660->current_position += bytes_read;
-			(a->compression_read_consume)(a, bytes_read);
+			bytes_read = (a->decompressor->skip)(a, step);
+			if (bytes_read < 0)
+				return (bytes_read);
+			iso9660->current_position = offset;
 		}
 
 		/* We found body of file; handle it now. */
@@ -957,13 +931,13 @@
 
 			file->ce_offset = 0;
 			file->ce_size = 0;
-			bytes_read = (a->compression_read_ahead)(a, &p, size);
+			bytes_read = (a->decompressor->read_ahead)(a, &p, size);
 			if (bytes_read > size)
 				bytes_read = size;
 			rr_start = (const unsigned char *)p;
 			parse_rockridge(iso9660, file, rr_start,
 			    rr_start + bytes_read);
-			(a->compression_read_consume)(a, bytes_read);
+			(a->decompressor->consume)(a, bytes_read);
 			iso9660->current_position += bytes_read;
 			add_entry(iso9660, file);
 		}
@@ -1071,34 +1045,37 @@
 {
 #if HAVE_TIMEGM
 	return (timegm(t));
-#else
+#elif HAVE_STRUCT_TM_TM_GMTOFF
 	/*
 	 * Unfortunately, timegm() isn't standard.  The standard
 	 * mktime() function is a close match, except that it uses
-	 * local timezone instead of GMT.  Close enough for now.
-	 * Note that it is not possible to emulate timegm() using
-	 * completely standard interfaces:
-	 *   * ANSI C90 does not even guarantee that time_t is
-	 *     an arithmetic type, so time adjustments can only be
-	 *     done by manipulating struct tm elements.  You cannot
-	 *     portably calculate time_t values.
-	 *   * POSIX does promise that time_t is an arithmetic type
-	 *     measured in seconds, so you can do time_t calculations
-	 *     while remaining POSIX-compliant.
-	 *   * Neither ANSI nor POSIX provides an easy way to measure
-	 *     the timezone offset, so you can't adjust mktime() to
-	 *     work like timegm().
-	 *   * POSIX does not promise that the epoch begins in 1970,
-	 *     so you can't write a portable timegm() function from
-	 *     scratch.
-	 * In practice, of course, mktime() is a reasonable approximation
-	 * and most POSIX systems do use seconds since 1970, so you
-	 * can roll your own and have it work on all but a few pretty
-	 * whacky systems.
+	 * local timezone instead of GMT.  You can compensate for
+	 * this by adding the timezone and DST offsets back in, at
+	 * the cost of two calls to mktime().
 	 */
-	time_t result = mktime(t);
-	/* TODO: Find a way to improve this approximation to timegm(). */
-	return result;
+	mktime(t); /* Normalize the time and get the TZ offset. */
+	t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/
+	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!).
+	 */
+	time_t ret;
+	char *tz;
+
+	tz = getenv("TZ");
+	setenv("TZ", "UTC 0", 1);
+	tzset();
+	ret = mktime(t);
+	if (tz)
+	    setenv("TZ", tz, 1);
+	else
+	    unsetenv("TZ");
+	tzset();
+	return ret;
 #endif
 }
 
Index: archive_write_open_filename.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_open_filename.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -Llib/libarchive/archive_write_open_filename.c -Llib/libarchive/archive_write_open_filename.c -u -r1.1.1.1 -r1.2
--- lib/libarchive/archive_write_open_filename.c
+++ lib/libarchive/archive_write_open_filename.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19.2.1 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_filename.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
Index: libarchive.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/libarchive.3,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/libarchive.3 -Llib/libarchive/libarchive.3 -u -r1.1.1.2 -r1.2
--- lib/libarchive/libarchive.3
+++ lib/libarchive/libarchive.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/libarchive.3,v 1.7.2.2 2007/01/27 06:44:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive.3,v 1.11 2007/01/09 08:05:56 kientzle Exp $
 .\"
 .Dd August 19, 2006
 .Dt LIBARCHIVE 3
Index: archive_write.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write.c -Llib/libarchive/archive_write.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write.c
+++ lib/libarchive/archive_write.c
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.15.2.3 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write.c,v 1.26 2007/05/29 01:00:19 kientzle Exp $");
 
 /*
  * This file contains the "essential" portions of the write API, that
@@ -55,6 +55,31 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
+
+static struct archive_vtable *archive_write_vtable(void);
+
+static int	_archive_write_close(struct archive *);
+static int	_archive_write_finish(struct archive *);
+static int	_archive_write_header(struct archive *, struct archive_entry *);
+static int	_archive_write_finish_entry(struct archive *);
+static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
+
+static struct archive_vtable *
+archive_write_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_write_close = _archive_write_close;
+		av.archive_write_finish = _archive_write_finish;
+		av.archive_write_header = _archive_write_header;
+		av.archive_write_finish_entry = _archive_write_finish_entry;
+		av.archive_write_data = _archive_write_data;
+	}
+	return (&av);
+}
 
 /*
  * Allocate, initialize and return an archive object.
@@ -62,19 +87,18 @@
 struct archive *
 archive_write_new(void)
 {
-	struct archive *a;
+	struct archive_write *a;
 	unsigned char *nulls;
 
-	a = (struct archive *)malloc(sizeof(*a));
+	a = (struct archive_write *)malloc(sizeof(*a));
 	if (a == NULL)
 		return (NULL);
 	memset(a, 0, sizeof(*a));
-	a->magic = ARCHIVE_WRITE_MAGIC;
-	a->user_uid = geteuid();
+	a->archive.magic = ARCHIVE_WRITE_MAGIC;
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->archive.vtable = archive_write_vtable();
 	a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
 	a->bytes_in_last_block = -1;	/* Default */
-	a->state = ARCHIVE_STATE_NEW;
-	a->pformat_data = &(a->format_data);
 
 	/* Initialize a block of nulls for padding purposes. */
 	a->null_length = 1024;
@@ -91,17 +115,19 @@
 	 * client to link in support for that format, even if they didn't
 	 * ever use it.
 	 */
-	archive_write_set_compression_none(a);
-	return (a);
+	archive_write_set_compression_none(&a->archive);
+	return (&a->archive);
 }
 
 /*
  * Set the block size.  Returns 0 if successful.
  */
 int
-archive_write_set_bytes_per_block(struct archive *a, int bytes_per_block)
+archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
 	a->bytes_per_block = bytes_per_block;
 	return (ARCHIVE_OK);
 }
@@ -110,9 +136,11 @@
  * Get the current block size.  -1 if it has never been set.
  */
 int
-archive_write_get_bytes_per_block(struct archive *a)
+archive_write_get_bytes_per_block(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
 	return (a->bytes_per_block);
 }
 
@@ -121,9 +149,11 @@
  * Returns 0 if successful.
  */
 int
-archive_write_set_bytes_in_last_block(struct archive *a, int bytes)
+archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
 	a->bytes_in_last_block = bytes;
 	return (ARCHIVE_OK);
 }
@@ -132,9 +162,11 @@
  * Return the value set above.  -1 indicates it has not been set.
  */
 int
-archive_write_get_bytes_in_last_block(struct archive *a)
+archive_write_get_bytes_in_last_block(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
 	return (a->bytes_in_last_block);
 }
 
@@ -144,9 +176,11 @@
  * an archive to itself recursively.
  */
 int
-archive_write_set_skip_file(struct archive *a, dev_t d, ino_t i)
+archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
 	a->skip_file_dev = d;
 	a->skip_file_ino = i;
 	return (ARCHIVE_OK);
@@ -157,21 +191,23 @@
  * Open the archive using the current settings.
  */
 int
-archive_write_open(struct archive *a, void *client_data,
+archive_write_open(struct archive *_a, void *client_data,
     archive_open_callback *opener, archive_write_callback *writer,
     archive_close_callback *closer)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	int ret;
 
 	ret = ARCHIVE_OK;
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open");
-	archive_string_empty(&a->error_string);
-	a->state = ARCHIVE_STATE_HEADER;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_open");
+	archive_clear_error(&a->archive);
+	a->archive.state = ARCHIVE_STATE_HEADER;
 	a->client_data = client_data;
 	a->client_writer = writer;
 	a->client_opener = opener;
 	a->client_closer = closer;
-	ret = (a->compression_init)(a);
+	ret = (a->compressor.init)(a);
 	if (a->format_init && ret == ARCHIVE_OK)
 		ret = (a->format_init)(a);
 	return (ret);
@@ -185,15 +221,17 @@
  * Don't assume we actually wrote anything or performed any non-trivial
  * initialization.
  */
-int
-archive_write_close(struct archive *a)
+static int
+_archive_write_close(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
 
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_close");
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_close");
 
 	/* Finish the last entry. */
-	if (a->state & ARCHIVE_STATE_DATA)
+	if (a->archive.state & ARCHIVE_STATE_DATA)
 		r = ((a->format_finish_entry)(a));
 
 	/* Finish off the archive. */
@@ -203,48 +241,68 @@
 			r = r1;
 	}
 
+	/* Release format resources. */
+	if (a->format_destroy != NULL) {
+		r1 = (a->format_destroy)(a);
+		if (r1 < r)
+			r = r1;
+	}
+
 	/* Finish the compression and close the stream. */
-	if (a->compression_finish != NULL) {
-		r1 = (a->compression_finish)(a);
+	if (a->compressor.finish != NULL) {
+		r1 = (a->compressor.finish)(a);
 		if (r1 < r)
 			r = r1;
 	}
 
-	a->state = ARCHIVE_STATE_CLOSED;
+	/* Close out the client stream. */
+	if (a->client_closer != NULL) {
+		r1 = (a->client_closer)(&a->archive, a->client_data);
+		if (r1 < r)
+			r = r1;
+	}
+
+	a->archive.state = ARCHIVE_STATE_CLOSED;
 	return (r);
 }
 
 /*
  * Destroy the archive structure.
  */
-void
-archive_write_finish(struct archive *a)
+static int
+_archive_write_finish(struct archive *_a)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_finish");
-	if (a->state != ARCHIVE_STATE_CLOSED)
-		archive_write_close(a);
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = ARCHIVE_OK;
+
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_finish");
+	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+		r = archive_write_close(&a->archive);
 
 	/* Release various dynamic buffers. */
 	free((void *)(uintptr_t)(const void *)a->nulls);
-	archive_string_free(&a->error_string);
-	a->magic = 0;
+	archive_string_free(&a->archive.error_string);
+	a->archive.magic = 0;
 	free(a);
+	return (r);
 }
 
 /*
  * Write the appropriate header.
  */
-int
-archive_write_header(struct archive *a, struct archive_entry *entry)
+static int
+_archive_write_header(struct archive *_a, struct archive_entry *entry)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	int ret, r2;
 
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
-	archive_string_empty(&a->error_string);
+	archive_clear_error(&a->archive);
 
 	/* In particular, "retry" and "fatal" get returned immediately. */
-	ret = archive_write_finish_entry(a);
+	ret = archive_write_finish_entry(&a->archive);
 	if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
 		return (ret);
 
@@ -252,8 +310,9 @@
 	    archive_entry_dev(entry) == a->skip_file_dev &&
 	    a->skip_file_ino != 0 &&
 	    archive_entry_ino(entry) == a->skip_file_ino) {
-		archive_set_error(a, 0, "Can't add archive to itself");
-		return (ARCHIVE_WARN);
+		archive_set_error(&a->archive, 0,
+		    "Can't add archive to itself");
+		return (ARCHIVE_FAILED);
 	}
 
 	/* Format and write header. */
@@ -261,33 +320,34 @@
 	if (r2 < ret)
 		ret = r2;
 
-	a->state = ARCHIVE_STATE_DATA;
+	a->archive.state = ARCHIVE_STATE_DATA;
 	return (ret);
 }
 
-int
-archive_write_finish_entry(struct archive * a)
+static int
+_archive_write_finish_entry(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	int ret = ARCHIVE_OK;
 
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
 	    "archive_write_finish_entry");
-	if (a->state & ARCHIVE_STATE_DATA)
+	if (a->archive.state & ARCHIVE_STATE_DATA)
 		ret = (a->format_finish_entry)(a);
-	a->state = ARCHIVE_STATE_HEADER;
+	a->archive.state = ARCHIVE_STATE_HEADER;
 	return (ret);
 }
 
 /*
  * Note that the compressor is responsible for blocking.
  */
-/* Should be "ssize_t", but that breaks the ABI.  <sigh> */
-int
-archive_write_data(struct archive *a, const void *buff, size_t s)
+static ssize_t
+_archive_write_data(struct archive *_a, const void *buff, size_t s)
 {
-	__archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+	struct archive_write *a = (struct archive_write *)_a;
+	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_DATA, "archive_write_data");
-	archive_string_empty(&a->error_string);
+	archive_clear_error(&a->archive);
 	return ((a->format_write_data)(a, buff, s));
 }
Index: libarchive-formats.5
===================================================================
RCS file: /home/cvs/src/lib/libarchive/libarchive-formats.5,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/libarchive-formats.5 -Llib/libarchive/libarchive-formats.5 -u -r1.1.1.2 -r1.2
--- lib/libarchive/libarchive-formats.5
+++ lib/libarchive/libarchive-formats.5
@@ -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.7.2.2 2007/01/27 06:44:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/libarchive-formats.5,v 1.14 2007/04/05 05:07:53 kientzle Exp $
 .\"
 .Dd April 27, 2004
 .Dt libarchive-formats 3
@@ -235,7 +235,20 @@
 .Dq deflate
 algorithm.
 Older zip compression algorithms are not supported.
+.Ss Archive (library) file format
+The Unix archive format (commonly created by the
+.Xr ar 1
+archiver) is a general-purpose format which is
+used almost exclusively for object files to be
+read by the link editor
+.Xr ld 1 .
+The ar format has never been standardised.
+There are two common variants:
+the GNU format derived from SVR4,
+and the BSD format, which first appeared in 4.4BSD.
+Libarchive provides read and write support for both variants.
 .Sh SEE ALSO
+.Xr ar 1 ,
 .Xr cpio 1 ,
 .Xr mkisofs 1 ,
 .Xr shar 1 ,
--- /dev/null
+++ lib/libarchive/archive_read_support_format_ar.c
@@ -0,0 +1,606 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_ar.c,v 1.6 2007/05/29 01:00:19 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct ar {
+	int	 bid;
+	off_t	 entry_bytes_remaining;
+	off_t	 entry_offset;
+	off_t	 entry_padding;
+	char	*strtab;
+	size_t	 strtab_size;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+#define isdigit(x)	(x) >= '0' && (x) <= '9'
+
+static int	archive_read_format_ar_bid(struct archive_read *a);
+static int	archive_read_format_ar_cleanup(struct archive_read *a);
+static int	archive_read_format_ar_read_data(struct archive_read *a,
+		    const void **buff, size_t *size, off_t *offset);
+static int	archive_read_format_ar_skip(struct archive_read *a);
+static int	archive_read_format_ar_read_header(struct archive_read *a,
+		    struct archive_entry *e);
+static uint64_t	ar_atol8(const char *p, unsigned char_cnt);
+static uint64_t	ar_atol10(const char *p, unsigned char_cnt);
+static int	ar_parse_gnu_filename_table(struct archive_read *, struct ar *,
+		    const void *, size_t);
+static int	ar_parse_common_header(struct ar *ar, struct archive_entry *,
+		    const char *h);
+
+int
+archive_read_support_format_ar(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct ar *ar;
+	int r;
+
+	ar = (struct ar *)malloc(sizeof(*ar));
+	if (ar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ar data");
+		return (ARCHIVE_FATAL);
+	}
+	memset(ar, 0, sizeof(*ar));
+	ar->bid = -1;
+	ar->strtab = NULL;
+
+	r = __archive_read_register_format(a,
+	    ar,
+	    archive_read_format_ar_bid,
+	    archive_read_format_ar_read_header,
+	    archive_read_format_ar_read_data,
+	    archive_read_format_ar_skip,
+	    archive_read_format_ar_cleanup);
+
+	if (r != ARCHIVE_OK) {
+		free(ar);
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_cleanup(struct archive_read *a)
+{
+	struct ar *ar;
+
+	ar = (struct ar *)(a->format->data);
+	if (ar->strtab)
+		free(ar->strtab);
+	free(ar);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_bid(struct archive_read *a)
+{
+	struct ar *ar;
+	ssize_t bytes_read;
+	const void *h;
+
+	if (a->archive.archive_format != 0 &&
+	    (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
+	    ARCHIVE_FORMAT_AR)
+		return(0);
+
+	ar = (struct ar *)(a->format->data);
+
+	if (ar->bid > 0)
+		return (ar->bid);
+
+	/*
+	 * Verify the 8-byte file signature.
+	 * TODO: Do we need to check more than this?
+	 */
+	bytes_read = (a->decompressor->read_ahead)(a, &h, 8);
+	if (bytes_read < 8)
+		return (-1);
+	if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
+		ar->bid = 64;
+		return (ar->bid);
+	}
+	return (-1);
+}
+
+static int
+archive_read_format_ar_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	char filename[AR_name_size + 1];
+	struct ar *ar;
+	uint64_t number; /* Used to hold parsed numbers before validation. */
+	ssize_t bytes_read;
+	size_t bsd_name_length, entry_size;
+	char *p;
+	const void *b;
+	const char *h;
+	int r;
+
+	ar = (struct ar*)(a->format->data);
+
+	if (a->archive.file_position == 0) {
+		/*
+		 * We are now at the beginning of the archive,
+		 * so we need first consume the ar global header.
+		 */
+		(a->decompressor->consume)(a, 8);
+		/* Set a default format code for now. */
+		a->archive.archive_format = ARCHIVE_FORMAT_AR;
+	}
+
+	/* Read the header for the next file entry. */
+	bytes_read = (a->decompressor->read_ahead)(a, &b, 60);
+	if (bytes_read < 60) {
+		/* Broken header. */
+		return (ARCHIVE_EOF);
+	}
+	(a->decompressor->consume)(a, 60);
+	h = (const char *)b;
+
+	/* Verify the magic signature on the file header. */
+	if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Consistency check failed");
+		return (ARCHIVE_WARN);
+	}
+
+	/* Copy filename into work buffer. */
+	strncpy(filename, h + AR_name_offset, AR_name_size);
+	filename[AR_name_size] = '\0';
+
+	/*
+	 * Guess the format variant based on the filename.
+	 */
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
+		/* We don't already know the variant, so let's guess. */
+		/*
+		 * Biggest clue is presence of '/': GNU starts special
+		 * filenames with '/', appends '/' as terminator to
+		 * non-special names, so anything with '/' should be
+		 * GNU except for BSD long filenames.
+		 */
+		if (strncmp(filename, "#1/", 3) == 0)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		else if (strchr(filename, '/') != NULL)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+		else if (strncmp(filename, "__.SYMDEF", 9) == 0)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		/*
+		 * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
+		 * if name exactly fills 16-byte field?  If so, we
+		 * can't assume entries without '/' are BSD. XXX
+		 */
+	}
+
+	/* Update format name from the code. */
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
+		a->archive.archive_format_name = "ar (GNU/SVR4)";
+	else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
+		a->archive.archive_format_name = "ar (BSD)";
+	else
+		a->archive.archive_format_name = "ar";
+
+	/*
+	 * Remove trailing spaces from the filename.  GNU and BSD
+	 * variants both pad filename area out with spaces.
+	 * This will only be wrong if GNU/SVR4 'ar' implementations
+	 * omit trailing '/' for 16-char filenames and we have
+	 * a 16-char filename that ends in ' '.
+	 */
+	p = filename + AR_name_size - 1;
+	while (p >= filename && *p == ' ') {
+		*p = '\0';
+		p--;
+	}
+
+	/*
+	 * Remove trailing slash unless first character is '/'.
+	 * (BSD entries never end in '/', so this will only trim
+	 * GNU-format entries.  GNU special entries start with '/'
+	 * and are not terminated in '/', so we don't trim anything
+	 * that starts with '/'.)
+	 */
+	if (filename[0] != '/' && *p == '/')
+		*p = '\0';
+
+	/*
+	 * '//' is the GNU filename table.
+	 * Later entries can refer to names in this table.
+	 */
+	if (strcmp(filename, "//") == 0) {
+		/* 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));
+		/* Get the size of the filename table. */
+		number = ar_atol10(h + AR_size_offset, AR_size_size);
+		if (number > SIZE_MAX) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Filename table too large");
+			return (ARCHIVE_FATAL);
+		}
+		entry_size = (size_t)number;
+		/* Read the filename table into memory. */
+		bytes_read = (a->decompressor->read_ahead)(a, &b, entry_size);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		if ((size_t)bytes_read < entry_size) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated input file");
+			return (ARCHIVE_FATAL);
+		}
+		/*
+		 * Don't consume the contents, so the client will
+		 * also get a shot at reading it.
+		 */
+
+		/* Parse the filename table. */
+		return (ar_parse_gnu_filename_table(a, ar, b, entry_size));
+	}
+
+	/*
+	 * GNU variant handles long filenames by storing /<number>
+	 * to indicate a name stored in the filename table.
+	 */
+	if (filename[0] == '/' && isdigit(filename[1])) {
+		number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
+		/*
+		 * If we can't look up the real name, warn and return
+		 * the entry with the wrong name.
+		 */
+		if (ar->strtab == NULL || number > ar->strtab_size) {
+			archive_set_error(&a->archive, EINVAL,
+			    "Can't find long filename for entry");
+			archive_entry_copy_pathname(entry, filename);
+			/* Parse the time, owner, mode, size fields. */
+			ar_parse_common_header(ar, entry, h);
+			return (ARCHIVE_WARN);
+		}
+
+		archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
+		/* Parse the time, owner, mode, size fields. */
+		return (ar_parse_common_header(ar, entry, h));
+	}
+
+	/*
+	 * BSD handles long filenames by storing "#1/" followed by the
+	 * length of filename as a decimal number, then prepends the
+	 * the filename to the file contents.
+	 */
+	if (strncmp(filename, "#1/", 3) == 0) {
+		/* Parse the time, owner, mode, size fields. */
+		/* This must occur before _read_ahead is called again. */
+		ar_parse_common_header(ar, entry, h);
+
+		/* Parse the size of the name, adjust the file size. */
+		number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
+		if ((off_t)number > ar->entry_bytes_remaining) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Bad input file size");
+			return (ARCHIVE_FATAL);
+		}
+		bsd_name_length = (size_t)number;
+		ar->entry_bytes_remaining -= bsd_name_length;
+		/* Adjust file size reported to client. */
+		archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+		/* Read the long name into memory. */
+		bytes_read = (a->decompressor->read_ahead)(a, &b, bsd_name_length);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		if ((size_t)bytes_read < bsd_name_length) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated input file");
+			return (ARCHIVE_FATAL);
+		}
+		(a->decompressor->consume)(a, bsd_name_length);
+
+		/* Store it in the entry. */
+		p = (char *)malloc(bsd_name_length + 1);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate fname buffer");
+			return (ARCHIVE_FATAL);
+		}
+		strncpy(p, b, bsd_name_length);
+		p[bsd_name_length] = '\0';
+		archive_entry_copy_pathname(entry, p);
+		free(p);
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * "/" is the SVR4/GNU archive symbol table.
+	 */
+	if (strcmp(filename, "/") == 0) {
+		archive_entry_copy_pathname(entry, "/");
+		/* 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));
+		return (r);
+	}
+
+	/*
+	 * "__.SYMDEF" is a BSD archive symbol table.
+	 */
+	if (strcmp(filename, "__.SYMDEF") == 0) {
+		archive_entry_copy_pathname(entry, filename);
+		/* Parse the time, owner, mode, size fields. */
+		return (ar_parse_common_header(ar, entry, h));
+	}
+
+	/*
+	 * Otherwise, this is a standard entry.  The filename
+	 * has already been trimmed as much as possible, based
+	 * on our current knowledge of the format.
+	 */
+	archive_entry_copy_pathname(entry, filename);
+	return (ar_parse_common_header(ar, entry, h));
+}
+
+static int
+ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
+    const char *h)
+{
+	uint64_t n;
+
+	/* Copy remaining header */
+	archive_entry_set_mtime(entry,
+	    (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
+	archive_entry_set_uid(entry,
+	    (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));
+	archive_entry_set_gid(entry,
+	    (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
+	archive_entry_set_mode(entry,
+	    (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
+	n = ar_atol10(h + AR_size_offset, AR_size_size);
+
+	ar->entry_offset = 0;
+	ar->entry_padding = n % 2;
+	archive_entry_set_size(entry, n);
+	ar->entry_bytes_remaining = n;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_read_data(struct archive_read *a,
+    const void **buff, size_t *size, off_t *offset)
+{
+	ssize_t bytes_read;
+	struct ar *ar;
+
+	ar = (struct ar *)(a->format->data);
+
+	if (ar->entry_bytes_remaining > 0) {
+		bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
+		if (bytes_read == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated ar archive");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		if (bytes_read > ar->entry_bytes_remaining)
+			bytes_read = (ssize_t)ar->entry_bytes_remaining;
+		*size = bytes_read;
+		*offset = ar->entry_offset;
+		ar->entry_offset += bytes_read;
+		ar->entry_bytes_remaining -= bytes_read;
+		(a->decompressor->consume)(a, (size_t)bytes_read);
+		return (ARCHIVE_OK);
+	} else {
+		while (ar->entry_padding > 0) {
+			bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
+			if (bytes_read <= 0)
+				return (ARCHIVE_FATAL);
+			if (bytes_read > ar->entry_padding)
+				bytes_read = (ssize_t)ar->entry_padding;
+			(a->decompressor->consume)(a, (size_t)bytes_read);
+			ar->entry_padding -= bytes_read;
+		}
+		*buff = NULL;
+		*size = 0;
+		*offset = ar->entry_offset;
+		return (ARCHIVE_EOF);
+	}
+}
+
+static int
+archive_read_format_ar_skip(struct archive_read *a)
+{
+	off_t bytes_skipped;
+	struct ar* ar;
+	int r = ARCHIVE_OK;
+	const void *b;		/* Dummy variables */
+	size_t s;
+	off_t o;
+
+	ar = (struct ar *)(a->format->data);
+	if (a->decompressor->skip == NULL) {
+		while (r == ARCHIVE_OK)
+			r = archive_read_format_ar_read_data(a, &b, &s, &o);
+		return (r);
+	}
+
+	bytes_skipped = (a->decompressor->skip)(a, ar->entry_bytes_remaining +
+	    ar->entry_padding);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	ar->entry_bytes_remaining = 0;
+	ar->entry_padding = 0;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+ar_parse_gnu_filename_table(struct archive_read *a, struct ar *ar,
+    const void *h, size_t size)
+{
+	char *p;
+
+	if (ar->strtab != NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "More than one string tables exist");
+		return (ARCHIVE_WARN);
+	}
+
+	if (size == 0) {
+		archive_set_error(&a->archive, EINVAL, "Invalid string table");
+		return (ARCHIVE_WARN);
+	}
+
+	ar->strtab_size = size;
+	ar->strtab = malloc(size);
+	if (ar->strtab == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate string table buffer");
+		return (ARCHIVE_FATAL);
+	}
+
+	(void)memcpy(ar->strtab, h, size);
+	for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
+		if (*p == '/') {
+			*p++ = '\0';
+			if (*p != '\n')
+				goto bad_string_table;
+			*p = '\0';
+		}
+	}
+	/*
+	 * Sanity check, last two chars must be `/\n' or '\n\n',
+	 * depending on whether the string table is padded by a '\n'
+	 * (string table produced by GNU ar always has a even size).
+	 */
+	if (p != ar->strtab + size && *p != '\n')
+		goto bad_string_table;
+
+	/* Enforce zero termination. */
+	ar->strtab[size - 1] = '\0';
+
+	return (ARCHIVE_OK);
+
+bad_string_table:
+	archive_set_error(&a->archive, EINVAL,
+	    "Invalid string table");
+	free(ar->strtab);
+	ar->strtab = NULL;
+	return (ARCHIVE_WARN);
+}
+
+static uint64_t
+ar_atol8(const char *p, unsigned char_cnt)
+{
+	uint64_t l, limit, last_digit_limit;
+	unsigned int digit, base;
+
+	base = 8;
+	limit = UINT64_MAX / base;
+	last_digit_limit = UINT64_MAX % base;
+
+	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+		p++;
+
+	l = 0;
+	digit = *p - '0';
+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {
+		if (l>limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++p - '0';
+	}
+	return (l);
+}
+
+static uint64_t
+ar_atol10(const char *p, unsigned char_cnt)
+{
+	uint64_t l, limit, last_digit_limit;
+	unsigned int base, digit;
+
+	base = 10;
+	limit = UINT64_MAX / base;
+	last_digit_limit = UINT64_MAX % base;
+
+	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+		p++;
+	l = 0;
+	digit = *p - '0';
+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {
+		if (l > limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++p - '0';
+	}
+	return (l);
+}
--- /dev/null
+++ lib/libarchive/archive_virtual.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_virtual.c,v 1.1 2007/03/03 07:37:36 kientzle Exp $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+int
+archive_write_close(struct archive *a)
+{
+	return ((a->vtable->archive_write_close)(a));
+}
+
+#if ARCHIVE_API_VERSION > 1
+int
+archive_write_finish(struct archive *a)
+{
+	return ((a->vtable->archive_write_finish)(a));
+}
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+void
+archive_write_finish(struct archive *a)
+{
+	(void)(a->vtable->archive_write_finish)(a);
+}
+#endif
+
+int
+archive_write_header(struct archive *a, struct archive_entry *entry)
+{
+	return ((a->vtable->archive_write_header)(a, entry));
+}
+
+int
+archive_write_finish_entry(struct archive *a)
+{
+	return ((a->vtable->archive_write_finish_entry)(a));
+}
+
+#if ARCHIVE_API_VERSION > 1
+ssize_t
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+int
+#endif
+archive_write_data(struct archive *a, const void *buff, size_t s)
+{
+	return ((a->vtable->archive_write_data)(a, buff, s));
+}
+
+ssize_t
+archive_write_data_block(struct archive *a, const void *buff, size_t s, off_t o)
+{
+	return ((a->vtable->archive_write_data_block)(a, buff, s, o));
+}
--- /dev/null
+++ lib/libarchive/archive_write_disk_set_standard_lookup.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_disk_set_standard_lookup.c,v 1.4 2007/05/29 01:00:19 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_write_disk_private.h"
+
+struct bucket {
+	char	*name;
+	int	 hash;
+	id_t	 id;
+};
+
+static const size_t cache_size = 127;
+static unsigned int	hash(const char *);
+static gid_t	lookup_gid(void *, const char *uname, gid_t);
+static uid_t	lookup_uid(void *, const char *uname, uid_t);
+static void	cleanup(void *);
+
+/*
+ * Installs functions that use getpwnam()/getgrnam()---along with
+ * a simple cache to accelerate such lookups---into the archive_write_disk
+ * object.  This is in a separate file because getpwnam()/getgrnam()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_write_disk.c) that just
+ * use the uid/gid without the lookup.  Or define your own custom functions
+ * if you prefer.
+ *
+ * TODO: Replace these hash tables with simpler move-to-front LRU
+ * lists with a bounded size (128 items?).  The hash is a bit faster,
+ * but has a bad pathology in which it thrashes a single bucket.  Even
+ * walking a list of 128 items is a lot faster than calling
+ * getpwnam()!
+ */
+int
+archive_write_disk_set_standard_lookup(struct archive *a)
+{
+	struct bucket *ucache = malloc(cache_size * sizeof(struct bucket));
+	struct bucket *gcache = malloc(cache_size * sizeof(struct bucket));
+	memset(ucache, 0, cache_size * sizeof(struct bucket));
+	memset(gcache, 0, cache_size * sizeof(struct bucket));
+	archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
+	archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
+	return (ARCHIVE_OK);
+}
+
+static gid_t
+lookup_gid(void *private_data, const char *gname, gid_t gid)
+{
+	int h;
+	struct bucket *b;
+	struct bucket *gcache = (struct bucket *)private_data;
+
+	/* If no gname, just use the gid provided. */
+	if (gname == NULL || *gname == '\0')
+		return (gid);
+
+	/* Try to find gname in the cache. */
+	h = hash(gname);
+	b = &gcache[h % cache_size ];
+	if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
+		return ((gid_t)b->id);
+
+	/* Free the cache slot for a new entry. */
+	if (b->name != NULL)
+		free(b->name);
+	b->name = strdup(gname);
+	/* Note: If strdup fails, that's okay; we just won't cache. */
+	b->hash = h;
+#if HAVE_GRP_H
+	{
+		struct group	*grent = getgrnam(gname);
+		if (grent != NULL)
+			gid = grent->gr_gid;
+	}
+#elif _WIN32
+	/* TODO: do a gname->gid lookup for Windows. */
+#endif
+	b->id = gid;
+
+	return (gid);
+}
+
+static uid_t
+lookup_uid(void *private_data, const char *uname, uid_t uid)
+{
+	int h;
+	struct bucket *b;
+	struct bucket *ucache = (struct bucket *)private_data;
+
+	/* If no uname, just use the uid provided. */
+	if (uname == NULL || *uname == '\0')
+		return (uid);
+
+	/* Try to find uname in the cache. */
+	h = hash(uname);
+	b = &ucache[h % cache_size ];
+	if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
+		return ((uid_t)b->id);
+
+	/* Free the cache slot for a new entry. */
+	if (b->name != NULL)
+		free(b->name);
+	b->name = strdup(uname);
+	/* Note: If strdup fails, that's okay; we just won't cache. */
+	b->hash = h;
+#if HAVE_PWD_H
+	{
+		struct passwd	*pwent = getpwnam(uname);
+		if (pwent != NULL)
+			uid = pwent->pw_uid;
+	}
+#elif _WIN32
+	/* TODO: do a uname->uid lookup for Windows. */
+#endif
+	b->id = uid;
+
+	return (uid);
+}
+
+static void
+cleanup(void *private)
+{
+	size_t i;
+	struct bucket *cache = (struct bucket *)private;
+
+	for (i = 0; i < cache_size; i++)
+		free(cache[i].name);
+	free(cache);
+}
+
+
+static unsigned int
+hash(const char *p)
+{
+	/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+	   as used by ELF for hashing function names. */
+	unsigned g, h = 0;
+	while (*p != '\0') {
+		h = ( h << 4 ) + *p++;
+		if (( g = h & 0xF0000000 )) {
+			h ^= g >> 24;
+			h &= 0x0FFFFFFF;
+		}
+	}
+	return h;
+}
Index: archive_platform.h
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_platform.h,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_platform.h -Llib/libarchive/archive_platform.h -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_platform.h
+++ lib/libarchive/archive_platform.h
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.16.2.2 2007/01/27 06:44:52 kientzle Exp $
+ * $FreeBSD: src/lib/libarchive/archive_platform.h,v 1.27 2007/05/29 01:00:18 kientzle Exp $
  */
 
 /*
@@ -36,15 +36,12 @@
 #ifndef ARCHIVE_PLATFORM_H_INCLUDED
 #define	ARCHIVE_PLATFORM_H_INCLUDED
 
-#if defined(HAVE_CONFIG_H)
+#if defined(PLATFORM_CONFIG_H)
+/* Use hand-built config.h in environments that need it. */
+#include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
 /* Most POSIX platforms use the 'configure' script to build config.h */
 #include "../config.h"
-#elif defined(__FreeBSD__)
-/* Building as part of FreeBSD system requires a pre-built config.h. */
-#include "config_freebsd.h"
-#elif defined(_WIN32)
-/* Win32 can't run the 'configure' script. */
-#include "config_windows.h"
 #else
 /* Warn if the library hasn't been (automatically or manually) configured. */
 #error Oops: No config.h and no pre-built configuration in archive_platform.h.
@@ -70,6 +67,23 @@
 #include <stdint.h>
 #endif
 
+/* Some platforms lack the standard *_MAX definitions. */
+#if !HAVE_DECL_SIZE_MAX
+#define	SIZE_MAX (~(size_t)0)
+#endif
+#if !HAVE_DECL_UINT32_MAX
+#define	UINT32_MAX (~(uint32_t)0)
+#endif
+#if !HAVE_DECL_UINT64_MAX
+#define	UINT64_MAX (~(uint64_t)0)
+#endif
+#if !HAVE_DECL_INT64_MAX
+#define	INT64_MAX ((int64_t)(UINT64_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT64_MIN
+#define	INT64_MIN ((int64_t)(~INT64_MAX))
+#endif
+
 /*
  * If this platform has <sys/acl.h>, acl_create(), acl_init(),
  * acl_set_file(), and ACL_USER, we assume it has the rest of the
@@ -108,33 +122,4 @@
 #define	ARCHIVE_ERRNO_MISC (-1)
 #endif
 
-/* Select the best way to set/get hi-res timestamps. */
-#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
-/* FreeBSD uses "timespec" members. */
-#define	ARCHIVE_STAT_ATIME_NANOS(st)	(st)->st_atimespec.tv_nsec
-#define	ARCHIVE_STAT_CTIME_NANOS(st)	(st)->st_ctimespec.tv_nsec
-#define	ARCHIVE_STAT_MTIME_NANOS(st)	(st)->st_mtimespec.tv_nsec
-#define	ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n)
-#define	ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n)
-#define	ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n)
-#else
-#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
-/* Linux uses "tim" members. */
-#define	ARCHIVE_STAT_ATIME_NANOS(pstat)	(pstat)->st_atim.tv_nsec
-#define	ARCHIVE_STAT_CTIME_NANOS(pstat)	(pstat)->st_ctim.tv_nsec
-#define	ARCHIVE_STAT_MTIME_NANOS(pstat)	(pstat)->st_mtim.tv_nsec
-#define	ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n)
-#define	ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n)
-#define	ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n)
-#else
-/* If we can't find a better way, just use stubs. */
-#define	ARCHIVE_STAT_ATIME_NANOS(pstat)	0
-#define	ARCHIVE_STAT_CTIME_NANOS(pstat)	0
-#define	ARCHIVE_STAT_MTIME_NANOS(pstat)	0
-#define	ARCHIVE_STAT_SET_ATIME_NANOS(st, n) ((void)(n))
-#define	ARCHIVE_STAT_SET_CTIME_NANOS(st, n) ((void)(n))
-#define	ARCHIVE_STAT_SET_MTIME_NANOS(st, n) ((void)(n))
-#endif
-#endif
-
 #endif /* !ARCHIVE_H_INCLUDED */
Index: archive_read_support_format_tar.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_format_tar.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -Llib/libarchive/archive_read_support_format_tar.c -Llib/libarchive/archive_read_support_format_tar.c -u -r1.2 -r1.3
--- lib/libarchive/archive_read_support_format_tar.c
+++ lib/libarchive/archive_read_support_format_tar.c
@@ -24,18 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.32.2.3 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_tar.c,v 1.61 2007/08/18 21:53:25 kientzle Exp $");
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#else
-#ifdef MAJOR_IN_SYSMACROS
-#include <sys/sysmacros.h>
-#endif
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -47,9 +37,6 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 /* Obtain suitable wide-character manipulation functions. */
 #ifdef HAVE_WCHAR_H
@@ -83,6 +70,9 @@
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
+
+#define tar_min(a,b) ((a) < (b) ? (a) : (b))
 
 /*
  * Layout of POSIX 'ustar' tar header.
@@ -162,90 +152,83 @@
 	struct archive_string	 longname;
 	struct archive_string	 pax_header;
 	struct archive_string	 pax_global;
+	struct archive_string	 line;
 	wchar_t 		*pax_entry;
 	size_t			 pax_entry_length;
 	int			 header_recursion_depth;
 	off_t			 entry_bytes_remaining;
 	off_t			 entry_offset;
 	off_t			 entry_padding;
+	off_t			 realsize;
 	struct sparse_block	*sparse_list;
+	struct sparse_block	*sparse_last;
+	int64_t			 sparse_offset;
+	int64_t			 sparse_numbytes;
+	int			 sparse_gnu_major;
+	int			 sparse_gnu_minor;
+	char			 sparse_gnu_pending;
 };
 
 static size_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 int	gnu_read_sparse_data(struct archive *, struct tar *,
+static void	 gnu_add_sparse_entry(struct tar *,
+		    off_t offset, off_t remaining);
+static void	gnu_clear_sparse_list(struct tar *);
+static int	gnu_sparse_old_read(struct archive_read *, struct tar *,
 		    const struct archive_entry_header_gnutar *header);
-static void	gnu_parse_sparse_data(struct archive *, struct tar *,
+static void	gnu_sparse_old_parse(struct tar *,
 		    const struct gnu_sparse *sparse, int length);
-static int	header_Solaris_ACL(struct archive *,  struct tar *,
-		    struct archive_entry *, struct stat *, const void *);
-static int	header_common(struct archive *,  struct tar *,
-		    struct archive_entry *, struct stat *, const void *);
-static int	header_old_tar(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *);
-static int	header_pax_extensions(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *);
-static int	header_pax_global(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	header_longlink(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	header_longname(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	header_volume(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	header_ustar(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	header_gnutar(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, const void *h);
-static int	archive_read_format_tar_bid(struct archive *);
-static int	archive_read_format_tar_cleanup(struct archive *);
-static int	archive_read_format_tar_read_data(struct archive *a,
+static int	gnu_sparse_01_parse(struct tar *, const wchar_t *);
+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 *);
+static int	header_common(struct archive_read *,  struct tar *,
+		    struct archive_entry *, const void *);
+static int	header_old_tar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *);
+static int	header_pax_extensions(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *);
+static int	header_pax_global(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_longlink(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_longname(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_volume(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_ustar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_gnutar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	archive_read_format_tar_bid(struct archive_read *);
+static int	archive_read_format_tar_cleanup(struct archive_read *);
+static int	archive_read_format_tar_read_data(struct archive_read *a,
 		    const void **buff, size_t *size, off_t *offset);
-static int	archive_read_format_tar_skip(struct archive *a);
-static int	archive_read_format_tar_read_header(struct archive *,
+static int	archive_read_format_tar_skip(struct archive_read *a);
+static int	archive_read_format_tar_read_header(struct archive_read *,
 		    struct archive_entry *);
-static int	checksum(struct archive *, const void *);
-static int 	pax_attribute(struct archive_entry *, struct stat *,
+static int	checksum(struct archive_read *, const void *);
+static int 	pax_attribute(struct tar *, struct archive_entry *,
 		    wchar_t *key, wchar_t *value);
-static int 	pax_header(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *, char *attr);
+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 int	read_body_to_string(struct archive *, struct tar *,
+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_atol256(const char *, unsigned);
 static int64_t	tar_atol8(const char *, unsigned);
-static int	tar_read_header(struct archive *, struct tar *,
-		    struct archive_entry *, struct stat *);
+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);
 
-/*
- * ANSI C99 defines constants for these, but not everyone supports
- * those constants, so I define a couple of static variables here and
- * compute the values.  These calculations should be portable to any
- * 2s-complement architecture.
- */
-#ifdef UINT64_MAX
-static const uint64_t max_uint64 = UINT64_MAX;
-#else
-static const uint64_t max_uint64 = ~(uint64_t)0;
-#endif
-#ifdef INT64_MAX
-static const int64_t max_int64 = INT64_MAX;
-#else
-static const int64_t max_int64 = (int64_t)((~(uint64_t)0) >> 1);
-#endif
-#ifdef INT64_MIN
-static const int64_t min_int64 = INT64_MIN;
-#else
-static const int64_t min_int64 = (int64_t)(~((~(uint64_t)0) >> 1));
-#endif
-
 int
 archive_read_support_format_gnutar(struct archive *a)
 {
@@ -254,14 +237,16 @@
 
 
 int
-archive_read_support_format_tar(struct archive *a)
+archive_read_support_format_tar(struct archive *_a)
 {
+	struct archive_read *a = (struct archive_read *)_a;
 	struct tar *tar;
 	int r;
 
 	tar = (struct tar *)malloc(sizeof(*tar));
 	if (tar == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate tar data");
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate tar data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(tar, 0, sizeof(*tar));
@@ -279,28 +264,29 @@
 }
 
 static int
-archive_read_format_tar_cleanup(struct archive *a)
+archive_read_format_tar_cleanup(struct archive_read *a)
 {
 	struct tar *tar;
 
-	tar = (struct tar *)*(a->pformat_data);
+	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_uname);
 	archive_string_free(&tar->entry_gname);
+	archive_string_free(&tar->line);
 	archive_string_free(&tar->pax_global);
 	archive_string_free(&tar->pax_header);
-	if (tar->pax_entry != NULL)
-		free(tar->pax_entry);
+	free(tar->pax_entry);
 	free(tar);
-	*(a->pformat_data) = NULL;
+	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
 }
 
 
 static int
-archive_read_format_tar_bid(struct archive *a)
+archive_read_format_tar_bid(struct archive_read *a)
 {
 	int bid;
 	ssize_t bytes_read;
@@ -311,8 +297,8 @@
 	 * If we're already reading a non-tar file, don't
 	 * bother to bid.
 	 */
-	if (a->archive_format != 0 &&
-	    (a->archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
+	if (a->archive.archive_format != 0 &&
+	    (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
 	    ARCHIVE_FORMAT_TAR)
 		return (0);
 	bid = 0;
@@ -321,13 +307,13 @@
 	 * If we're already reading a tar format, start the bid at 1 as
 	 * a failsafe.
 	 */
-	if ((a->archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
+	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->compression_read_ahead != NULL)
-		bytes_read = (a->compression_read_ahead)(a, &h, 512);
+	if (a->decompressor->read_ahead != NULL)
+		bytes_read = (a->decompressor->read_ahead)(a, &h, 512);
 	else
 		bytes_read = 0; /* Empty file. */
 	if (bytes_read < 0)
@@ -345,7 +331,7 @@
 		 * If we already know this is a tar archive,
 		 * then we have a problem.
 		 */
-		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated tar archive");
 		return (ARCHIVE_FATAL);
 	}
@@ -353,7 +339,7 @@
 	/* 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_format & ARCHIVE_FORMAT_BASE_MASK) ==
+		if ((a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) ==
 		    ARCHIVE_FORMAT_TAR)
 			return (512);
 		/* Empty archive? */
@@ -412,7 +398,7 @@
  * tar_read_header() function below.
  */
 static int
-archive_read_format_tar_read_header(struct archive *a,
+archive_read_format_tar_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
 	/*
@@ -431,26 +417,40 @@
 	 */
 	static int default_inode;
 	static int default_dev;
-	struct stat st;
 	struct tar *tar;
+	struct sparse_block *sp;
 	const char *p;
 	int r;
 	size_t l;
 
-	memset(&st, 0, sizeof(st));
 	/* Assign default device/inode values. */
-	st.st_dev = 1 + default_dev; /* Don't use zero. */
-	st.st_ino = ++default_inode; /* Don't use zero. */
+	archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
+	archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
 	/* Limit generated st_ino number to 16 bits. */
 	if (default_inode >= 0xffff) {
 		++default_dev;
 		default_inode = 0;
 	}
 
-	tar = (struct tar *)*(a->pformat_data);
+	tar = (struct tar *)(a->format->data);
 	tar->entry_offset = 0;
+	while (tar->sparse_list != NULL) {
+		sp = tar->sparse_list;
+		tar->sparse_list = sp->next;
+		free(sp);
+	}
+	tar->sparse_last = NULL;
 
-	r = tar_read_header(a, tar, entry, &st);
+	r = tar_read_header(a, tar, entry);
+
+	/*
+	 * "non-sparse" files are really just sparse files with
+	 * a single block.
+	 */
+	if (tar->sparse_list == NULL)
+		gnu_add_sparse_entry(tar, 0, tar->entry_bytes_remaining);
+
+	tar->realsize = archive_entry_size(entry);
 
 	if (r == ARCHIVE_OK) {
 		/*
@@ -460,108 +460,97 @@
 		 */
 		p = archive_entry_pathname(entry);
 		l = strlen(p);
-		if (S_ISREG(st.st_mode) && p[l-1] == '/') {
-			st.st_mode &= ~S_IFMT;
-			st.st_mode |= S_IFDIR;
-		}
-
-		/* Copy the final stat data into the entry. */
-		archive_entry_copy_stat(entry, &st);
+		if (archive_entry_filetype(entry) == AE_IFREG
+		    && p[l-1] == '/')
+			archive_entry_set_filetype(entry, AE_IFDIR);
 	}
 	return (r);
 }
 
 static int
-archive_read_format_tar_read_data(struct archive *a,
+archive_read_format_tar_read_data(struct archive_read *a,
     const void **buff, size_t *size, off_t *offset)
 {
 	ssize_t bytes_read;
 	struct tar *tar;
 	struct sparse_block *p;
 
-	tar = (struct tar *)*(a->pformat_data);
-	if (tar->sparse_list != NULL) {
-		/* Remove exhausted entries from sparse list. */
-		while (tar->sparse_list != NULL &&
-		    tar->sparse_list->remaining == 0) {
-			p = tar->sparse_list;
-			tar->sparse_list = p->next;
-			free(p);
-		}
-		if (tar->sparse_list == NULL) {
-			/* We exhausted the entire sparse list. */
-			tar->entry_bytes_remaining = 0;
+	tar = (struct tar *)(a->format->data);
+
+	if (tar->sparse_gnu_pending) {
+		if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) {
+			tar->sparse_gnu_pending = 0;
+			/* Read initial sparse map. */
+			bytes_read = gnu_sparse_10_read(a, tar);
+			tar->entry_bytes_remaining -= bytes_read;
+			if (bytes_read < 0)
+				return (bytes_read);
+		} else {
+			*size = 0;
+			*offset = 0;
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Unrecognized GNU sparse file format");
+			return (ARCHIVE_WARN);
 		}
+		tar->sparse_gnu_pending = 0;
 	}
 
-	if (tar->entry_bytes_remaining > 0) {
-		bytes_read = (a->compression_read_ahead)(a, buff, 1);
-		if (bytes_read == 0) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
-			    "Truncated tar archive");
-			return (ARCHIVE_FATAL);
-		}
-		if (bytes_read < 0)
+	/* Remove exhausted entries from sparse list. */
+	while (tar->sparse_list != NULL &&
+	    tar->sparse_list->remaining == 0) {
+		p = tar->sparse_list;
+		tar->sparse_list = p->next;
+		free(p);
+	}
+
+	/* If we're at end of file, return EOF. */
+	if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) {
+		if ((a->decompressor->skip)(a, tar->entry_padding) < 0)
 			return (ARCHIVE_FATAL);
-		if (bytes_read > tar->entry_bytes_remaining)
-			bytes_read = tar->entry_bytes_remaining;
-		if (tar->sparse_list != NULL) {
-			/* Don't read more than is available in the
-			 * current sparse block. */
-			if (tar->sparse_list->remaining < bytes_read)
-				bytes_read = tar->sparse_list->remaining;
-			tar->entry_offset = tar->sparse_list->offset;
-			tar->sparse_list->remaining -= bytes_read;
-			tar->sparse_list->offset += bytes_read;
-		}
-		*size = bytes_read;
-		*offset = tar->entry_offset;
-		tar->entry_offset += bytes_read;
-		tar->entry_bytes_remaining -= bytes_read;
-		(a->compression_read_consume)(a, bytes_read);
-		return (ARCHIVE_OK);
-	} else {
-		while (tar->entry_padding > 0) {
-			bytes_read = (a->compression_read_ahead)(a, buff, 1);
-			if (bytes_read <= 0)
-				return (ARCHIVE_FATAL);
-			if (bytes_read > tar->entry_padding)
-				bytes_read = tar->entry_padding;
-			(a->compression_read_consume)(a, bytes_read);
-			tar->entry_padding -= bytes_read;
-		}
+		tar->entry_padding = 0;
 		*buff = NULL;
 		*size = 0;
-		*offset = tar->entry_offset;
+		*offset = tar->realsize;
 		return (ARCHIVE_EOF);
 	}
+
+	bytes_read = (a->decompressor->read_ahead)(a, buff, 1);
+	if (bytes_read == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Truncated tar archive");
+		return (ARCHIVE_FATAL);
+	}
+	if (bytes_read < 0)
+		return (ARCHIVE_FATAL);
+	if (bytes_read > tar->entry_bytes_remaining)
+		bytes_read = tar->entry_bytes_remaining;
+	/* Don't read more than is available in the
+	 * current sparse block. */
+	if (tar->sparse_list->remaining < bytes_read)
+		bytes_read = tar->sparse_list->remaining;
+	*size = bytes_read;
+	*offset = tar->sparse_list->offset;
+	tar->sparse_list->remaining -= bytes_read;
+	tar->sparse_list->offset += bytes_read;
+	tar->entry_bytes_remaining -= bytes_read;
+	(a->decompressor->consume)(a, bytes_read);
+	return (ARCHIVE_OK);
 }
 
 static int
-archive_read_format_tar_skip(struct archive *a)
+archive_read_format_tar_skip(struct archive_read *a)
 {
 	off_t bytes_skipped;
 	struct tar* tar;
-	struct sparse_block *p;
-	int r = ARCHIVE_OK;
-	const void *b;	/* dummy variables */
-	size_t s;
-	off_t o;
 
-
-	tar = (struct tar *)*(a->pformat_data);
-	if (a->compression_skip == NULL) {
-		while (r == ARCHIVE_OK)
-			r = archive_read_format_tar_read_data(a, &b, &s, &o);
-		return (r);
-	}
+	tar = (struct tar *)(a->format->data);
 
 	/*
 	 * Compression layer skip functions are required to either skip the
 	 * length requested or fail, so we can rely upon the entire entry
 	 * plus padding being skipped.
 	 */
-	bytes_skipped = (a->compression_skip)(a, tar->entry_bytes_remaining +
+	bytes_skipped = (a->decompressor->skip)(a, tar->entry_bytes_remaining +
 	    tar->entry_padding);
 	if (bytes_skipped < 0)
 		return (ARCHIVE_FATAL);
@@ -570,11 +559,7 @@
 	tar->entry_padding = 0;
 
 	/* Free the sparse list. */
-	while (tar->sparse_list != NULL) {
-		p = tar->sparse_list;
-		tar->sparse_list = p->next;
-		free(p);
-	}
+	gnu_clear_sparse_list(tar);
 
 	return (ARCHIVE_OK);
 }
@@ -584,8 +569,8 @@
  * with a single entry.
  */
 static int
-tar_read_header(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st)
+tar_read_header(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry)
 {
 	ssize_t bytes;
 	int err;
@@ -593,7 +578,7 @@
 	const struct archive_entry_header_ustar *header;
 
 	/* Read 512-byte header record */
-	bytes = (a->compression_read_ahead)(a, &h, 512);
+	bytes = (a->decompressor->read_ahead)(a, &h, 512);
 	if (bytes < 512) {
 		/*
 		 * If we're here, it's becase the _bid function accepted
@@ -602,15 +587,15 @@
 		 */
 		return (ARCHIVE_EOF);
 	}
-	(a->compression_read_consume)(a, 512);
+	(a->decompressor->consume)(a, 512);
 
 	/* Check for end-of-archive mark. */
 	if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) {
 		/* Try to consume a second all-null record, as well. */
-		bytes = (a->compression_read_ahead)(a, &h, 512);
+		bytes = (a->decompressor->read_ahead)(a, &h, 512);
 		if (bytes > 0)
-			(a->compression_read_consume)(a, bytes);
-		archive_set_error(a, 0, NULL);
+			(a->decompressor->consume)(a, bytes);
+		archive_set_error(&a->archive, 0, NULL);
 		return (ARCHIVE_EOF);
 	}
 
@@ -622,12 +607,12 @@
 	 * TODO: Improve this by implementing a real header scan.
 	 */
 	if (!checksum(a, h)) {
-		archive_set_error(a, EINVAL, "Damaged tar archive");
+		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
 		return (ARCHIVE_RETRY); /* Retryable: Invalid header */
 	}
 
 	if (++tar->header_recursion_depth > 32) {
-		archive_set_error(a, EINVAL, "Too many special headers");
+		archive_set_error(&a->archive, EINVAL, "Too many special headers");
 		return (ARCHIVE_WARN);
 	}
 
@@ -635,50 +620,50 @@
 	header = (const struct archive_entry_header_ustar *)h;
 	switch(header->typeflag[0]) {
 	case 'A': /* Solaris tar ACL */
-		a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
-		a->archive_format_name = "Solaris tar";
-		err = header_Solaris_ACL(a, tar, entry, st, h);
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "Solaris tar";
+		err = header_Solaris_ACL(a, tar, entry, h);
 		break;
 	case 'g': /* POSIX-standard 'g' header. */
-		a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
-		a->archive_format_name = "POSIX pax interchange format";
-		err = header_pax_global(a, tar, entry, st, h);
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "POSIX pax interchange format";
+		err = header_pax_global(a, tar, entry, h);
 		break;
 	case 'K': /* Long link name (GNU tar, others) */
-		err = header_longlink(a, tar, entry, st, h);
+		err = header_longlink(a, tar, entry, h);
 		break;
 	case 'L': /* Long filename (GNU tar, others) */
-		err = header_longname(a, tar, entry, st, h);
+		err = header_longname(a, tar, entry, h);
 		break;
 	case 'V': /* GNU volume header */
-		err = header_volume(a, tar, entry, st, h);
+		err = header_volume(a, tar, entry, h);
 		break;
 	case 'X': /* Used by SUN tar; same as 'x'. */
-		a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
-		a->archive_format_name =
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name =
 		    "POSIX pax interchange format (Sun variant)";
-		err = header_pax_extensions(a, tar, entry, st, h);
+		err = header_pax_extensions(a, tar, entry, h);
 		break;
 	case 'x': /* POSIX-standard 'x' header. */
-		a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
-		a->archive_format_name = "POSIX pax interchange format";
-		err = header_pax_extensions(a, tar, entry, st, h);
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "POSIX pax interchange format";
+		err = header_pax_extensions(a, tar, entry, h);
 		break;
 	default:
 		if (memcmp(header->magic, "ustar  \0", 8) == 0) {
-			a->archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
-			a->archive_format_name = "GNU tar format";
-			err = header_gnutar(a, tar, entry, st, h);
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+			a->archive.archive_format_name = "GNU tar format";
+			err = header_gnutar(a, tar, entry, h);
 		} else if (memcmp(header->magic, "ustar", 5) == 0) {
-			if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
-				a->archive_format = ARCHIVE_FORMAT_TAR_USTAR;
-				a->archive_format_name = "POSIX ustar format";
+			if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+				a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+				a->archive.archive_format_name = "POSIX ustar format";
 			}
-			err = header_ustar(a, tar, entry, st, h);
+			err = header_ustar(a, tar, entry, h);
 		} else {
-			a->archive_format = ARCHIVE_FORMAT_TAR;
-			a->archive_format_name = "tar (non-POSIX)";
-			err = header_old_tar(a, tar, entry, st, h);
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+			a->archive.archive_format_name = "tar (non-POSIX)";
+			err = header_old_tar(a, tar, entry, h);
 		}
 	}
 	--tar->header_recursion_depth;
@@ -687,7 +672,7 @@
 		return (err);
 	if (err == ARCHIVE_EOF)
 		/* EOF when recursively reading a header is bad. */
-		archive_set_error(a, EINVAL, "Damaged tar archive");
+		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
 	return (ARCHIVE_FATAL);
 }
 
@@ -695,7 +680,7 @@
  * Return true if block checksum is correct.
  */
 static int
-checksum(struct archive *a, const void *h)
+checksum(struct archive_read *a, const void *h)
 {
 	const unsigned char *bytes;
 	const struct archive_entry_header_ustar	*header;
@@ -756,59 +741,58 @@
  * Interpret 'A' Solaris ACL header
  */
 static int
-header_Solaris_ACL(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_Solaris_ACL(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_ustar *header;
- 	size_t size;
- 	int err;
- 	char *acl, *p;
-  	wchar_t *wp;
-
- 	/*
- 	 * read_body_to_string adds a NUL terminator, but we need a little
- 	 * more to make sure that we don't overrun acl_text later.
- 	 */
- 	header = (const struct archive_entry_header_ustar *)h;
- 	size = tar_atol(header->size, sizeof(header->size));
+	size_t size;
+	int err;
+	char *acl, *p;
+	wchar_t *wp;
 
+	/*
+	 * read_body_to_string adds a NUL terminator, but we need a little
+	 * more to make sure that we don't overrun acl_text later.
+	 */
+	header = (const struct archive_entry_header_ustar *)h;
+	size = tar_atol(header->size, sizeof(header->size));
 	err = read_body_to_string(a, tar, &(tar->acl_text), h);
- 	if (err != ARCHIVE_OK)
- 		return (err);
- 	err = tar_read_header(a, tar, entry, st);
- 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
- 		return (err);
+	if (err != ARCHIVE_OK)
+		return (err);
+	err = tar_read_header(a, tar, entry);
+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+		return (err);
 
 	/* Skip leading octal number. */
 	/* XXX TODO: Parse the octal number and sanity-check it. */
- 	p = acl = tar->acl_text.s;
- 	while (*p != '\0' && p < acl + size)
+	p = acl = tar->acl_text.s;
+	while (*p != '\0' && p < acl + size)
 		p++;
 	p++;
 
- 	if (p >= acl + size) {
- 		archive_set_error(a, ARCHIVE_ERRNO_MISC,
- 		    "Malformed Solaris ACL attribute");
- 		return(ARCHIVE_WARN);
-  	}
-  
- 	/* Skip leading octal number. */
- 	size -= (p - acl);
- 	acl = p;
- 
- 	while (*p != '\0' && p < acl + size)
- 		p++;
- 
- 	wp = malloc((p - acl + 1) * sizeof(wchar_t));
- 	if (wp == NULL) {
- 		archive_set_error(a, ENOMEM,
- 		    "Can't allocate work buffer for ACL parsing");
- 		return (ARCHIVE_FATAL);
- 	}
- 	utf8_decode(wp, acl, p - acl);
- 	err = __archive_entry_acl_parse_w(entry, wp,
- 	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
- 	free(wp);
+	if (p >= acl + size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Malformed Solaris ACL attribute");
+		return(ARCHIVE_WARN);
+	}
+
+	/* Skip leading octal number. */
+	size -= (p - acl);
+	acl = p;
+
+	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);
+	err = __archive_entry_acl_parse_w(entry, wp,
+	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	free(wp);
 	return (err);
 }
 
@@ -816,15 +800,15 @@
  * Interpret 'K' long linkname header.
  */
 static int
-header_longlink(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_longlink(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	int err;
 
 	err = read_body_to_string(a, tar, &(tar->longlink), h);
 	if (err != ARCHIVE_OK)
 		return (err);
-	err = tar_read_header(a, tar, entry, st);
+	err = tar_read_header(a, tar, entry);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 	/* Set symlink if symlink already set, else hardlink. */
@@ -836,8 +820,8 @@
  * Interpret 'L' long filename header.
  */
 static int
-header_longname(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_longname(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	int err;
 
@@ -845,7 +829,7 @@
 	if (err != ARCHIVE_OK)
 		return (err);
 	/* Read and parse "real" header, then override name. */
-	err = tar_read_header(a, tar, entry, st);
+	err = tar_read_header(a, tar, entry);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 	archive_entry_set_pathname(entry, tar->longname.s);
@@ -857,20 +841,20 @@
  * Interpret 'V' GNU tar volume header.
  */
 static int
-header_volume(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_volume(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	(void)h;
 
 	/* Just skip this and read the next header. */
-	return (tar_read_header(a, tar, entry, st));
+	return (tar_read_header(a, tar, entry));
 }
 
 /*
  * Read body of an archive entry into an archive_string object.
  */
 static int
-read_body_to_string(struct archive *a, struct tar *tar,
+read_body_to_string(struct archive_read *a, struct tar *tar,
     struct archive_string *as, const void *h)
 {
 	off_t size, padded_size;
@@ -882,26 +866,31 @@
 	(void)tar; /* UNUSED */
 	header = (const struct archive_entry_header_ustar *)h;
 	size  = tar_atol(header->size, sizeof(header->size));
-
-	/* Sanity check. */
 	if ((size > 1048576) || (size < 0)) {
-		archive_set_error(a, EINVAL, "Special header too large");
+		archive_set_error(&a->archive, EINVAL,
+		    "Special header too large");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Fail if we can't make our buffer big enough. */
+	if (archive_string_ensure(as, size+1) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory");
 		return (ARCHIVE_FATAL);
 	}
 
 	/* Read the body into the string. */
-	archive_string_ensure(as, size+1);
 	padded_size = (size + 511) & ~ 511;
 	dest = as->s;
 	while (padded_size > 0) {
-		bytes_read = (a->compression_read_ahead)(a, &src, padded_size);
+		bytes_read = (a->decompressor->read_ahead)(a, &src, padded_size);
 		if (bytes_read == 0)
 			return (ARCHIVE_EOF);
 		if (bytes_read < 0)
 			return (ARCHIVE_FATAL);
 		if (bytes_read > padded_size)
 			bytes_read = padded_size;
-		(a->compression_read_consume)(a, bytes_read);
+		(a->decompressor->consume)(a, bytes_read);
 		bytes_to_copy = bytes_read;
 		if ((off_t)bytes_to_copy > size)
 			bytes_to_copy = (ssize_t)size;
@@ -925,8 +914,8 @@
  * common parsing into one place.
  */
 static int
-header_common(struct archive *a, struct tar *tar, struct archive_entry *entry,
-    struct stat *st, const void *h)
+header_common(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_ustar	*header;
 	char	tartype;
@@ -941,15 +930,15 @@
 		archive_string_empty(&(tar->entry_linkname));
 
 	/* Parse out the numeric fields (all are octal) */
-	st->st_mode  = tar_atol(header->mode, sizeof(header->mode));
-	st->st_uid   = tar_atol(header->uid, sizeof(header->uid));
-	st->st_gid   = tar_atol(header->gid, sizeof(header->gid));
-	st->st_size  = tar_atol(header->size, sizeof(header->size));
-	st->st_mtime = tar_atol(header->mtime, sizeof(header->mtime));
+	archive_entry_set_mode(entry, tar_atol(header->mode, sizeof(header->mode)));
+	archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
+	archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
+	tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+	archive_entry_set_size(entry, tar->entry_bytes_remaining);
+	archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
 
 	/* Handle the tar type flag appropriately. */
 	tartype = header->typeflag[0];
-	st->st_mode &= ~S_IFMT;
 
 	switch (tartype) {
 	case '1': /* Hard link */
@@ -963,8 +952,8 @@
 		 * implies that the underlying entry is a regular
 		 * file.
 		 */
-		if (st->st_size > 0)
-			st->st_mode |= S_IFREG;
+		if (archive_entry_size(entry) > 0)
+			archive_entry_set_filetype(entry, AE_IFREG);
 
 		/*
 		 * A tricky point: Traditionally, tar readers have
@@ -988,31 +977,38 @@
 		 * we encounter a hardlink entry for a file that is
 		 * itself an uncompressed tar archive.
 		 */
-		if (st->st_size > 0  &&
-		    a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE  &&
-		    archive_read_format_tar_bid(a) > 50)
-			st->st_size = 0;
+		if (archive_entry_size(entry) > 0  &&
+		    a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE  &&
+		    archive_read_format_tar_bid(a) > 50) {
+			archive_entry_set_size(entry, 0);
+			tar->entry_bytes_remaining = 0;
+		}
 		break;
 	case '2': /* Symlink */
-		st->st_mode |= S_IFLNK;
-		st->st_size = 0;
+		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);
 		break;
 	case '3': /* Character device */
-		st->st_mode |= S_IFCHR;
-		st->st_size = 0;
+		archive_entry_set_filetype(entry, AE_IFCHR);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
 		break;
 	case '4': /* Block device */
-		st->st_mode |= S_IFBLK;
-		st->st_size = 0;
+		archive_entry_set_filetype(entry, AE_IFBLK);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
 		break;
 	case '5': /* Dir */
-		st->st_mode |= S_IFDIR;
-		st->st_size = 0;
+		archive_entry_set_filetype(entry, AE_IFDIR);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
 		break;
 	case '6': /* FIFO device */
-		st->st_mode |= S_IFIFO;
-		st->st_size = 0;
+		archive_entry_set_filetype(entry, AE_IFIFO);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
 		break;
 	case 'D': /* GNU incremental directory type */
 		/*
@@ -1020,7 +1016,7 @@
 		 * It might be nice someday to preprocess the file list and
 		 * provide it to the client, though.
 		 */
-		st->st_mode |= S_IFDIR;
+		archive_entry_set_filetype(entry, AE_IFDIR);
 		break;
 	case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/
 		/*
@@ -1034,20 +1030,20 @@
 		/* The body of this entry is a script for renaming
 		 * previously-extracted entries.  Ugh.  It will never
 		 * be supported by libarchive. */
-		st->st_mode |= S_IFREG;
+		archive_entry_set_filetype(entry, AE_IFREG);
 		break;
 	case 'S': /* GNU sparse files */
 		/*
 		 * Sparse files are really just regular files with
 		 * sparse information in the extended area.
 		 */
-		/* FALL THROUGH */
+		/* FALLTHROUGH */
 	default: /* Regular file  and non-standard types */
 		/*
 		 * Per POSIX: non-recognized types should always be
 		 * treated as regular files.
 		 */
-		st->st_mode |= S_IFREG;
+		archive_entry_set_filetype(entry, AE_IFREG);
 		break;
 	}
 	return (0);
@@ -1057,8 +1053,8 @@
  * Parse out header elements for "old-style" tar archives.
  */
 static int
-header_old_tar(struct archive *a, struct tar *tar, struct archive_entry *entry,
-    struct stat *st, const void *h)
+header_old_tar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_ustar	*header;
 
@@ -1068,9 +1064,8 @@
 	archive_entry_set_pathname(entry, tar->entry_name.s);
 
 	/* Grab rest of common fields */
-	header_common(a, tar, entry, st, h);
+	header_common(a, tar, entry, h);
 
-	tar->entry_bytes_remaining = st->st_size;
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 	return (0);
 }
@@ -1079,21 +1074,21 @@
  * Parse a file header for a pax extended archive entry.
  */
 static int
-header_pax_global(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_pax_global(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	int err;
 
 	err = read_body_to_string(a, tar, &(tar->pax_global), h);
 	if (err != ARCHIVE_OK)
 		return (err);
-	err = tar_read_header(a, tar, entry, st);
+	err = tar_read_header(a, tar, entry);
 	return (err);
 }
 
 static int
-header_pax_extensions(struct archive *a, struct tar *tar,
-    struct archive_entry *entry, struct stat *st, const void *h)
+header_pax_extensions(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	int err, err2;
 
@@ -1102,7 +1097,7 @@
 		return (err);
 
 	/* Parse the next header. */
-	err = tar_read_header(a, tar, entry, st);
+	err = tar_read_header(a, tar, entry);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 
@@ -1116,9 +1111,8 @@
 	 * and then skip any fields in the standard header that were
 	 * defined in the pax header.
 	 */
-	err2 = pax_header(a, tar, entry, st, tar->pax_header.s);
+	err2 = pax_header(a, tar, entry, tar->pax_header.s);
 	err =  err_combine(err, err2);
-	tar->entry_bytes_remaining = st->st_size;
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 	return (err);
 }
@@ -1129,8 +1123,8 @@
  * handles "pax" or "extended ustar" entries.
  */
 static int
-header_ustar(struct archive *a, struct tar *tar, struct archive_entry *entry,
-    struct stat *st, const void *h)
+header_ustar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_ustar	*header;
 	struct archive_string *as;
@@ -1150,7 +1144,7 @@
 	archive_entry_set_pathname(entry, as->s);
 
 	/* Handle rest of common fields. */
-	header_common(a, tar, entry, st, h);
+	header_common(a, tar, entry, h);
 
 	/* Handle POSIX ustar fields. */
 	archive_strncpy(&(tar->entry_uname), header->uname,
@@ -1163,12 +1157,12 @@
 
 	/* Parse out device numbers only for char and block specials. */
 	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
-		st->st_rdev = makedev(
-		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)),
+		archive_entry_set_rdevmajor(entry,
+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+		archive_entry_set_rdevminor(entry,
 		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));
 	}
 
-	tar->entry_bytes_remaining = st->st_size;
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 
 	return (0);
@@ -1181,8 +1175,8 @@
  * Returns non-zero if there's an error in the data.
  */
 static int
-pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry,
-    struct stat *st, char *attr)
+pax_header(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, char *attr)
 {
 	size_t attr_length, l, line_length;
 	char *line, *p;
@@ -1203,14 +1197,14 @@
 				break;
 			}
 			if (*p < '0' || *p > '9') {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 				    "Ignoring malformed pax extended attributes");
 				return (ARCHIVE_WARN);
 			}
 			line_length *= 10;
 			line_length += *p - '0';
 			if (line_length > 999999) {
-				archive_set_error(a, ARCHIVE_ERRNO_MISC,
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 				    "Rejecting pax extended attribute > 1MB");
 				return (ARCHIVE_WARN);
 			}
@@ -1227,7 +1221,7 @@
 		    || line_length < 1
 		    || attr[line_length - 1] != '\n')
 		{
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Ignoring malformed pax extended attribute");
 			return (ARCHIVE_WARN);
 		}
@@ -1246,7 +1240,7 @@
 			    tar->pax_entry_length * sizeof(wchar_t));
 			if (tar->pax_entry == NULL) {
 				free(old_entry);
-				archive_set_error(a, ENOMEM,
+				archive_set_error(&a->archive, ENOMEM,
 					"No memory");
 				return (ARCHIVE_FATAL);
 			}
@@ -1255,7 +1249,7 @@
 		/* 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_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			   "Invalid UTF8 character in pax extended attribute");
 			err = err_combine(err, ARCHIVE_WARN);
 		}
@@ -1266,8 +1260,8 @@
 			return (-1);
 		while (*wp && *wp != L'=')
 			++wp;
-		if (*wp == L'\0' || wp == NULL) {
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		if (*wp == L'\0') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Invalid pax extended attributes");
 			return (ARCHIVE_WARN);
 		}
@@ -1277,7 +1271,7 @@
 		value = wp + 1;
 
 		/* Identify this attribute and set it in the entry. */
-		err2 = pax_attribute(entry, st, key, value);
+		err2 = pax_attribute(tar, entry, key, value);
 		err = err_combine(err, err2);
 
 		/* Skip to next line */
@@ -1332,19 +1326,72 @@
  * extensions should always have keywords of the form "VENDOR.attribute"
  * In particular, it's quite feasible to support many different
  * vendor extensions here.  I'm using "LIBARCHIVE" for extensions
- * unique to this library (currently, there are none).
+ * unique to this library.
  *
- * Investigate other vendor-specific extensions, as well and see if
+ * Investigate other vendor-specific extensions and see if
  * any of them look useful.
  */
 static int
-pax_attribute(struct archive_entry *entry, struct stat *st,
+pax_attribute(struct tar *tar, struct archive_entry *entry,
     wchar_t *key, wchar_t *value)
 {
 	int64_t s;
 	long n;
 
 	switch (key[0]) {
+	case 'G':
+		/* GNU "0.0" sparse pax format. */
+		if (wcscmp(key, L"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 (tar->sparse_numbytes != -1) {
+				gnu_add_sparse_entry(tar,
+				    tar->sparse_offset, tar->sparse_numbytes);
+				tar->sparse_offset = -1;
+				tar->sparse_numbytes = -1;
+			}
+		}
+		if (wcscmp(key, L"GNU.sparse.numbytes") == 0) {
+			tar->sparse_numbytes = tar_atol10(value, wcslen(value));
+			if (tar->sparse_numbytes != -1) {
+				gnu_add_sparse_entry(tar,
+				    tar->sparse_offset, tar->sparse_numbytes);
+				tar->sparse_offset = -1;
+				tar->sparse_numbytes = -1;
+			}
+		}
+		if (wcscmp(key, L"GNU.sparse.size") == 0)
+			archive_entry_set_size(entry,
+			    tar_atol10(value, wcslen(value)));
+
+		/* GNU "0.1" sparse pax format. */
+		if (wcscmp(key, L"GNU.sparse.map") == 0) {
+			tar->sparse_gnu_major = 0;
+			tar->sparse_gnu_minor = 1;
+			if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK)
+				return (ARCHIVE_WARN);
+		}
+
+		/* GNU "1.0" sparse pax format */
+		if (wcscmp(key, L"GNU.sparse.major") == 0) {
+			tar->sparse_gnu_major = tar_atol10(value, wcslen(value));
+			tar->sparse_gnu_pending = 1;
+		}
+		if (wcscmp(key, L"GNU.sparse.minor") == 0) {
+			tar->sparse_gnu_minor = tar_atol10(value, wcslen(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)
+			archive_entry_set_size(entry,
+			    tar_atol10(value, wcslen(value)));
+		break;
 	case 'L':
 		/* Our extensions */
 /* TODO: Handle arbitrary extended attributes... */
@@ -1364,28 +1411,28 @@
 			__archive_entry_acl_parse_w(entry, value,
 			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
 		else if (wcscmp(key, L"SCHILY.devmajor")==0)
-			st->st_rdev = makedev(tar_atol10(value, wcslen(value)),
-			    minor(st->st_rdev));
+			archive_entry_set_rdevmajor(entry, tar_atol10(value, wcslen(value)));
 		else if (wcscmp(key, L"SCHILY.devminor")==0)
-			st->st_rdev = makedev(major(st->st_rdev),
-			    tar_atol10(value, wcslen(value)));
+			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)
-			st->st_nlink = tar_atol10(value, wcslen(value));
+			archive_entry_set_nlink(entry, tar_atol10(value, wcslen(value)));
 		break;
 	case 'a':
 		if (wcscmp(key, L"atime")==0) {
 			pax_time(value, &s, &n);
-			st->st_atime = s;
-			ARCHIVE_STAT_SET_ATIME_NANOS(st, n);
+			archive_entry_set_atime(entry, s, n);
 		}
 		break;
 	case 'c':
 		if (wcscmp(key, L"ctime")==0) {
 			pax_time(value, &s, &n);
-			st->st_ctime = s;
-			ARCHIVE_STAT_SET_CTIME_NANOS(st, n);
+			archive_entry_set_ctime(entry, s, n);
 		} else if (wcscmp(key, L"charset")==0) {
 			/* TODO: Publish charset information in entry. */
 		} else if (wcscmp(key, L"comment")==0) {
@@ -1394,7 +1441,7 @@
 		break;
 	case 'g':
 		if (wcscmp(key, L"gid")==0)
-			st->st_gid = tar_atol10(value, wcslen(value));
+			archive_entry_set_gid(entry, tar_atol10(value, wcslen(value)));
 		else if (wcscmp(key, L"gname")==0)
 			archive_entry_copy_gname_w(entry, value);
 		break;
@@ -1410,8 +1457,7 @@
 	case 'm':
 		if (wcscmp(key, L"mtime")==0) {
 			pax_time(value, &s, &n);
-			st->st_mtime = s;
-			ARCHIVE_STAT_SET_MTIME_NANOS(st, n);
+			archive_entry_set_mtime(entry, s, n);
 		}
 		break;
 	case 'p':
@@ -1424,12 +1470,16 @@
 	case 's':
 		/* POSIX has reserved 'security.*' */
 		/* Someday: if (wcscmp(key, L"security.acl")==0) { ... } */
-		if (wcscmp(key, L"size")==0)
-			st->st_size = tar_atol10(value, wcslen(value));
+		if (wcscmp(key, L"size")==0) {
+			tar->entry_bytes_remaining = tar_atol10(value, wcslen(value));
+			archive_entry_set_size(entry, tar->entry_bytes_remaining);
+		}
+		tar->entry_bytes_remaining = 0;
+
 		break;
 	case 'u':
 		if (wcscmp(key, L"uid")==0)
-			st->st_uid = tar_atol10(value, wcslen(value));
+			archive_entry_set_uid(entry, tar_atol10(value, wcslen(value)));
 		else if (wcscmp(key, L"uname")==0)
 			archive_entry_copy_uname_w(entry, value);
 		break;
@@ -1451,8 +1501,8 @@
 	int sign;
 	int64_t limit, last_digit_limit;
 
-	limit = max_int64 / 10;
-	last_digit_limit = max_int64 % 10;
+	limit = INT64_MAX / 10;
+	last_digit_limit = INT64_MAX % 10;
 
 	s = 0;
 	sign = 1;
@@ -1464,7 +1514,7 @@
 		digit = *p - '0';
 		if (s > limit ||
 		    (s == limit && digit > last_digit_limit)) {
-			s = max_uint64;
+			s = UINT64_MAX;
 			break;
 		}
 		s = (s * 10) + digit;
@@ -1493,8 +1543,8 @@
  * Parse GNU tar header
  */
 static int
-header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry,
-    struct stat *st, const void *h)
+header_gnutar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_gnutar *header;
 
@@ -1507,7 +1557,7 @@
 	 */
 
 	/* Grab fields common to all tar variants. */
-	header_common(a, tar, entry, st, h);
+	header_common(a, tar, entry, h);
 
 	/* Copy filename over (to ensure null termination). */
 	header = (const struct archive_entry_header_gnutar *)h;
@@ -1528,26 +1578,28 @@
 	archive_entry_set_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')
-		st->st_rdev = makedev (
-		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)),
+	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+		archive_entry_set_rdevmajor(entry,
+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+		archive_entry_set_rdevminor(entry,
 		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));
-	else
-		st->st_rdev = 0;
+	} else
+		archive_entry_set_rdev(entry, 0);
 
-	tar->entry_bytes_remaining = st->st_size;
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 
 	/* Grab GNU-specific fields. */
-	st->st_atime = tar_atol(header->atime, sizeof(header->atime));
-	st->st_ctime = tar_atol(header->ctime, sizeof(header->ctime));
+	archive_entry_set_atime(entry,
+	    tar_atol(header->atime, sizeof(header->atime)), 0);
+	archive_entry_set_ctime(entry,
+	    tar_atol(header->ctime, sizeof(header->ctime)), 0);
 	if (header->realsize[0] != 0) {
-		st->st_size = tar_atol(header->realsize,
-		    sizeof(header->realsize));
+		archive_entry_set_size(entry,
+		    tar_atol(header->realsize, sizeof(header->realsize)));
 	}
 
 	if (header->sparse[0].offset[0] != 0) {
-		gnu_read_sparse_data(a, tar, header);
+		gnu_sparse_old_read(a, tar, header);
 	} else {
 		if (header->isextended[0] != 0) {
 			/* XXX WTF? XXX */
@@ -1557,8 +1609,51 @@
 	return (0);
 }
 
+static void
+gnu_add_sparse_entry(struct tar *tar, off_t offset, off_t remaining)
+{
+	struct sparse_block *p;
+
+	p = (struct sparse_block *)malloc(sizeof(*p));
+	if (p == NULL)
+		__archive_errx(1, "Out of memory");
+	memset(p, 0, sizeof(*p));
+	if (tar->sparse_last != NULL)
+		tar->sparse_last->next = p;
+	else
+		tar->sparse_list = p;
+	tar->sparse_last = p;
+	p->offset = offset;
+	p->remaining = remaining;
+}
+
+static void
+gnu_clear_sparse_list(struct tar *tar)
+{
+	struct sparse_block *p;
+
+	while (tar->sparse_list != NULL) {
+		p = tar->sparse_list;
+		tar->sparse_list = p->next;
+		free(p);
+	}
+	tar->sparse_last = NULL;
+}
+
+/*
+ * GNU tar old-format sparse data.
+ *
+ * GNU old-format sparse data is stored in a fixed-field
+ * format.  Offset/size values are 11-byte octal fields (same
+ * format as 'size' field in ustart header).  These are
+ * stored in the header, allocating subsequent header blocks
+ * as needed.  Extending the header in this way is a pretty
+ * severe POSIX violation; this design has earned GNU tar a
+ * lot of criticism.
+ */
+
 static int
-gnu_read_sparse_data(struct archive *a, struct tar *tar,
+gnu_sparse_old_read(struct archive_read *a, struct tar *tar,
     const struct archive_entry_header_gnutar *header)
 {
 	ssize_t bytes_read;
@@ -1570,23 +1665,23 @@
 	};
 	const struct extended *ext;
 
-	gnu_parse_sparse_data(a, tar, header->sparse, 4);
+	gnu_sparse_old_parse(tar, header->sparse, 4);
 	if (header->isextended[0] == 0)
 		return (ARCHIVE_OK);
 
 	do {
-		bytes_read = (a->compression_read_ahead)(a, &data, 512);
+		bytes_read = (a->decompressor->read_ahead)(a, &data, 512);
 		if (bytes_read < 0)
 			return (ARCHIVE_FATAL);
 		if (bytes_read < 512) {
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Truncated tar archive "
 			    "detected while reading sparse file data");
 			return (ARCHIVE_FATAL);
 		}
-		(a->compression_read_consume)(a, 512);
+		(a->decompressor->consume)(a, 512);
 		ext = (const struct extended *)data;
-		gnu_parse_sparse_data(a, tar, ext->sparse, 21);
+		gnu_sparse_old_parse(tar, ext->sparse, 21);
 	} while (ext->isextended[0] != 0);
 	if (tar->sparse_list != NULL)
 		tar->entry_offset = tar->sparse_list->offset;
@@ -1594,34 +1689,178 @@
 }
 
 static void
-gnu_parse_sparse_data(struct archive *a, struct tar *tar,
+gnu_sparse_old_parse(struct tar *tar,
     const struct gnu_sparse *sparse, int length)
 {
-	struct sparse_block *last;
-	struct sparse_block *p;
+	while (length > 0 && sparse->offset[0] != 0) {
+		gnu_add_sparse_entry(tar,
+		    tar_atol(sparse->offset, sizeof(sparse->offset)),
+		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes)));
+		sparse++;
+		length--;
+	}
+}
 
-	(void)a; /* UNUSED */
+/*
+ * GNU tar sparse format 0.0
+ *
+ * Beginning with GNU tar 1.15, sparse files are stored using
+ * information in the pax extended header.  The GNU tar maintainers
+ * have gone through a number of variations in the process of working
+ * out this scheme; furtunately, they're all numbered.
+ *
+ * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the
+ * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to
+ * store offset/size for each block.  The repeated instances of these
+ * latter fields violate the pax specification (which frowns on
+ * duplicate keys), so this format was quickly replaced.
+ */
 
-	last = tar->sparse_list;
-	while (last != NULL && last->next != NULL)
-		last = last->next;
+/*
+ * GNU tar sparse format 0.1
+ *
+ * This version replaced the offset/numbytes attributes with
+ * a single "map" attribute that stored a list of integers.  This
+ * format had two problems: First, the "map" attribute could be very
+ * long, which caused problems for some implementations.  More
+ * importantly, the sparse data was lost when extracted by archivers
+ * that didn't recognize this extension.
+ */
 
-	while (length > 0 && sparse->offset[0] != 0) {
-		p = (struct sparse_block *)malloc(sizeof(*p));
-		if (p == NULL)
-			__archive_errx(1, "Out of memory");
-		memset(p, 0, sizeof(*p));
-		if (last != NULL)
-			last->next = p;
+static int
+gnu_sparse_01_parse(struct tar *tar, const wchar_t *p)
+{
+	const wchar_t *e;
+	off_t offset = -1, size = -1;
+
+	for (;;) {
+		e = p;
+		while (*e != '\0' && *e != ',') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+		}
+		if (offset < 0) {
+			offset = tar_atol10(p, e - p);
+			if (offset < 0)
+				return (ARCHIVE_WARN);
+		} else {
+			size = tar_atol10(p, e - p);
+			if (size < 0)
+				return (ARCHIVE_WARN);
+			gnu_add_sparse_entry(tar, offset, size);
+			offset = -1;
+		}
+		if (*e == '\0')
+			return (ARCHIVE_OK);
+		p = e + 1;
+	}
+}
+
+/*
+ * GNU tar sparse format 1.0
+ *
+ * The idea: The offset/size data is stored as a series of base-10
+ * ASCII numbers prepended to the file data, so that dearchivers that
+ * 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.
+ *
+ * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
+ * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
+ */
+
+/*
+ * Read the next line from the input, and parse it as a decimal
+ * integer followed by '\n'.  Returns positive integer value or
+ * negative on error.
+ */
+static int64_t
+gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,
+    ssize_t *remaining)
+{
+	int64_t l, limit, last_digit_limit;
+	const char *p;
+	ssize_t bytes_read;
+	int base, digit;
+
+	base = 10;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	/*
+	 * Skip any lines starting with '#'; GNU tar specs
+	 * don't require this, but they should.
+	 */
+	do {
+		bytes_read = readline(a, tar, &p, tar_min(*remaining, 100));
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		*remaining -= bytes_read;
+	} while (p[0] == '#');
+
+	l = 0;
+	while (bytes_read > 0) {
+		if (*p == '\n')
+			return (l);
+		if (*p < '0' || *p >= '0' + base)
+			return (ARCHIVE_WARN);
+		digit = *p - '0';
+		if (l > limit || (l == limit && digit > last_digit_limit))
+			l = UINT64_MAX; /* Truncate on overflow. */
 		else
-			tar->sparse_list = p;
-		last = p;
-		p->offset = tar_atol(sparse->offset, sizeof(sparse->offset));
-		p->remaining =
-		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes));
-		sparse++;
-		length--;
+			l = (l * base) + digit;
+		p++;
+		bytes_read--;
 	}
+	/* TODO: Error message. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Returns length (in bytes) of the sparse data description
+ * that was read.
+ */
+static ssize_t
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar)
+{
+	ssize_t remaining, bytes_read;
+	int entries;
+	off_t offset, size, to_skip;
+
+	/* Clear out the existing sparse list. */
+	gnu_clear_sparse_list(tar);
+
+	remaining = tar->entry_bytes_remaining;
+
+	/* Parse entries. */
+	entries = gnu_sparse_10_atol(a, tar, &remaining);
+	if (entries < 0)
+		return (ARCHIVE_FATAL);
+	/* Parse the individual entries. */
+	while (entries-- > 0) {
+		/* Parse offset/size */
+		offset = gnu_sparse_10_atol(a, tar, &remaining);
+		if (offset < 0)
+			return (ARCHIVE_FATAL);
+		size = gnu_sparse_10_atol(a, tar, &remaining);
+		if (size < 0)
+			return (ARCHIVE_FATAL);
+		/* Add a new sparse entry. */
+		gnu_add_sparse_entry(tar, offset, size);
+	}
+	/* Skip rest of block... */
+	bytes_read = tar->entry_bytes_remaining - remaining;
+	to_skip = 0x1ff & -bytes_read;
+	if (to_skip != (a->decompressor->skip)(a, to_skip))
+		return (ARCHIVE_FATAL);
+	return (bytes_read + to_skip);
 }
 
 /*-
@@ -1665,8 +1904,8 @@
 	int digit, sign, base;
 
 	base = 8;
-	limit = max_int64 / base;
-	last_digit_limit = max_int64 % base;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
 
 	while (*p == ' ' || *p == '\t')
 		p++;
@@ -1680,7 +1919,7 @@
 	digit = *p - '0';
 	while (digit >= 0 && digit < base  && char_cnt-- > 0) {
 		if (l>limit || (l == limit && digit > last_digit_limit)) {
-			l = max_uint64; /* Truncate on overflow. */
+			l = UINT64_MAX; /* Truncate on overflow. */
 			break;
 		}
 		l = (l * base) + digit;
@@ -1689,7 +1928,6 @@
 	return (sign < 0) ? -l : l;
 }
 
-
 /*
  * Note that this implementation does not (and should not!) obey
  * locale settings; you cannot simply substitute strtol here, since
@@ -1702,8 +1940,8 @@
 	int base, digit, sign;
 
 	base = 10;
-	limit = max_int64 / base;
-	last_digit_limit = max_int64 % base;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
 
 	while (*p == ' ' || *p == '\t')
 		p++;
@@ -1717,7 +1955,7 @@
 	digit = *p - '0';
 	while (digit >= 0 && digit < base  && char_cnt-- > 0) {
 		if (l > limit || (l == limit && digit > last_digit_limit)) {
-			l = max_uint64; /* Truncate on overflow. */
+			l = UINT64_MAX; /* Truncate on overflow. */
 			break;
 		}
 		l = (l * base) + digit;
@@ -1740,8 +1978,8 @@
 	int64_t	l, upper_limit, lower_limit;
 	const unsigned char *p = (const unsigned char *)_p;
 
-	upper_limit = max_int64 / 256;
-	lower_limit = min_int64 / 256;
+	upper_limit = INT64_MAX / 256;
+	lower_limit = INT64_MIN / 256;
 
 	/* Pad with 1 or 0 bits, depending on sign. */
 	if ((0x40 & *p) == 0x40)
@@ -1751,10 +1989,10 @@
 	l = (l << 6) | (0x3f & *p++);
 	while (--char_cnt > 0) {
 		if (l > upper_limit) {
-			l = max_int64; /* Truncate on overflow */
+			l = INT64_MAX; /* Truncate on overflow */
 			break;
 		} else if (l < lower_limit) {
-			l = min_int64;
+			l = INT64_MIN;
 			break;
 		}
 		l = (l << 8) | (0xff & (int64_t)*p++);
@@ -1762,6 +2000,74 @@
 	return (l);
 }
 
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error.  'start' argument is updated to
+ * point to first character of line.  This avoids copying
+ * when possible.
+ */
+static ssize_t
+readline(struct archive_read *a, struct tar *tar, const char **start,
+    ssize_t limit)
+{
+	ssize_t bytes_read;
+	ssize_t total_size = 0;
+	const void *t;
+	const char *s;
+	void *p;
+
+	bytes_read = (a->decompressor->read_ahead)(a, &t, 1);
+	if (bytes_read <= 0)
+		return (ARCHIVE_FATAL);
+	s = t;  /* Start of line? */
+	p = memchr(t, '\n', bytes_read);
+	/* If we found '\n' in the read buffer, return pointer to that. */
+	if (p != NULL) {
+		bytes_read = 1 + ((const char *)p) - s;
+		if (bytes_read > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		(a->decompressor->consume)(a, bytes_read);
+		*start = s;
+		return (bytes_read);
+	}
+	/* Otherwise, we need to accumulate in a line buffer. */
+	for (;;) {
+		if (total_size + bytes_read > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate working buffer");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(tar->line.s + total_size, t, bytes_read);
+		(a->decompressor->consume)(a, bytes_read);
+		total_size += bytes_read;
+		/* If we found '\n', clean up and return. */
+		if (p != NULL) {
+			*start = tar->line.s;
+			return (total_size);
+		}
+		/* Read some more. */
+		bytes_read = (a->decompressor->read_ahead)(a, &t, 1);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		s = t;  /* Start of line? */
+		p = memchr(t, '\n', bytes_read);
+		/* If we found '\n', trim the read. */
+		if (p != NULL) {
+			bytes_read = 1 + ((const char *)p) - s;
+		}
+	}
+}
+
 static int
 utf8_decode(wchar_t *dest, const char *src, size_t length)
 {
Index: archive_read_support_compression_none.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_read_support_compression_none.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_read_support_compression_none.c -Llib/libarchive/archive_read_support_compression_none.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_read_support_compression_none.c
+++ lib/libarchive/archive_read_support_compression_none.c
@@ -24,9 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.6.2.5 2007/02/13 07:31:22 cperciva Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.17 2007/05/29 01:00:19 kientzle Exp $");
 
-#include <assert.h>
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -45,13 +44,14 @@
 
 #include "archive.h"
 #include "archive_private.h"
+#include "archive_read_private.h"
 
 struct archive_decompress_none {
 	char		*buffer;
 	size_t		 buffer_size;
 	char		*next;		/* Current read location. */
 	size_t		 avail;		/* Bytes in my buffer. */
-	const char	*client_buff;	/* Client buffer information. */
+	const void	*client_buff;	/* Client buffer information. */
 	size_t		 client_total;
 	const char	*client_next;
 	size_t		 client_avail;
@@ -75,21 +75,24 @@
 #define minimum(a, b) (a < b ? a : b)
 
 static int	archive_decompressor_none_bid(const void *, size_t);
-static int	archive_decompressor_none_finish(struct archive *);
-static int	archive_decompressor_none_init(struct archive *,
+static int	archive_decompressor_none_finish(struct archive_read *);
+static int	archive_decompressor_none_init(struct archive_read *,
 		    const void *, size_t);
-static ssize_t	archive_decompressor_none_read_ahead(struct archive *,
+static ssize_t	archive_decompressor_none_read_ahead(struct archive_read *,
 		    const void **, size_t);
-static ssize_t	archive_decompressor_none_read_consume(struct archive *,
+static ssize_t	archive_decompressor_none_read_consume(struct archive_read *,
 		    size_t);
-static off_t	archive_decompressor_none_skip(struct archive *, off_t);
+static off_t	archive_decompressor_none_skip(struct archive_read *, off_t);
 
 int
-archive_read_support_compression_none(struct archive *a)
+archive_read_support_compression_none(struct archive *_a)
 {
-	return (__archive_read_register_compression(a,
-		    archive_decompressor_none_bid,
-		    archive_decompressor_none_init));
+	struct archive_read *a = (struct archive_read *)_a;
+	if (__archive_read_register_compression(a,
+		archive_decompressor_none_bid,
+		archive_decompressor_none_init) != NULL)
+		return (ARCHIVE_OK);
+	return (ARCHIVE_FATAL);
 }
 
 /*
@@ -105,16 +108,16 @@
 }
 
 static int
-archive_decompressor_none_init(struct archive *a, const void *buff, size_t n)
+archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t n)
 {
 	struct archive_decompress_none	*state;
 
-	a->compression_code = ARCHIVE_COMPRESSION_NONE;
-	a->compression_name = "none";
+	a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
+	a->archive.compression_name = "none";
 
 	state = (struct archive_decompress_none *)malloc(sizeof(*state));
 	if (!state) {
-		archive_set_error(a, ENOMEM, "Can't allocate input data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate input data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(state, 0, sizeof(*state));
@@ -124,21 +127,21 @@
 	state->next = state->buffer;
 	if (state->buffer == NULL) {
 		free(state);
-		archive_set_error(a, ENOMEM, "Can't allocate input buffer");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate input buffer");
 		return (ARCHIVE_FATAL);
 	}
 
 	/* Save reference to first block of data. */
-	state->client_buff = (const char *)buff;
+	state->client_buff = buff;
 	state->client_total = n;
 	state->client_next = state->client_buff;
 	state->client_avail = state->client_total;
 
-	a->compression_data = state;
-	a->compression_read_ahead = archive_decompressor_none_read_ahead;
-	a->compression_read_consume = archive_decompressor_none_read_consume;
-	a->compression_skip = archive_decompressor_none_skip;
-	a->compression_finish = archive_decompressor_none_finish;
+	a->decompressor->data = state;
+	a->decompressor->read_ahead = archive_decompressor_none_read_ahead;
+	a->decompressor->consume = archive_decompressor_none_read_consume;
+	a->decompressor->skip = archive_decompressor_none_skip;
+	a->decompressor->finish = archive_decompressor_none_finish;
 
 	return (ARCHIVE_OK);
 }
@@ -149,13 +152,13 @@
  * buffer to combine reads.
  */
 static ssize_t
-archive_decompressor_none_read_ahead(struct archive *a, const void **buff,
+archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff,
     size_t min)
 {
 	struct archive_decompress_none *state;
 	ssize_t bytes_read;
 
-	state = (struct archive_decompress_none *)a->compression_data;
+	state = (struct archive_decompress_none *)a->decompressor->data;
 	if (state->fatal)
 		return (-1);
 
@@ -217,8 +220,8 @@
 			 * char ** should be compatible, but they
 			 * aren't, hence the cast.
 			 */
-			bytes_read = (a->client_reader)(a, a->client_data,
-			    (const void **)&state->client_buff);
+			bytes_read = (a->client_reader)(&a->archive,
+			    a->client_data, &state->client_buff);
 			if (bytes_read < 0) {		/* Read error. */
 				state->client_total = state->client_avail = 0;
 				state->client_next = state->client_buff = NULL;
@@ -231,7 +234,7 @@
 				state->end_of_file = 1;
 				break;
 			}
-			a->raw_position += bytes_read;
+			a->archive.raw_position += bytes_read;
 			state->client_total = bytes_read;
 			state->client_avail = state->client_total;
 			state->client_next = state->client_buff;
@@ -248,11 +251,11 @@
  * request.
  */
 static ssize_t
-archive_decompressor_none_read_consume(struct archive *a, size_t request)
+archive_decompressor_none_read_consume(struct archive_read *a, size_t request)
 {
 	struct archive_decompress_none *state;
 
-	state = (struct archive_decompress_none *)a->compression_data;
+	state = (struct archive_decompress_none *)a->decompressor->data;
 	if (state->avail > 0) {
 		/* Read came from copy buffer. */
 		state->next += request;
@@ -262,7 +265,7 @@
 		state->client_next += request;
 		state->client_avail -= request;
 	}
-	a->file_position += request;
+	a->archive.file_position += request;
 	return (request);
 }
 
@@ -272,13 +275,13 @@
  * read_ahead, which does not guarantee a minimum count.
  */
 static off_t
-archive_decompressor_none_skip(struct archive *a, off_t request)
+archive_decompressor_none_skip(struct archive_read *a, off_t request)
 {
 	struct archive_decompress_none *state;
 	off_t bytes_skipped, total_bytes_skipped = 0;
 	size_t min;
 
-	state = (struct archive_decompress_none *)a->compression_data;
+	state = (struct archive_decompress_none *)a->decompressor->data;
 	if (state->fatal)
 		return (-1);
 	/*
@@ -306,8 +309,8 @@
 #else
 	if (a->client_skipper != NULL) {
 #endif
-		bytes_skipped = (a->client_skipper)(a, a->client_data,
-		    request);
+		bytes_skipped = (a->client_skipper)(&a->archive,
+		    a->client_data, request);
 		if (bytes_skipped < 0) {	/* error */
 			state->client_total = state->client_avail = 0;
 			state->client_next = state->client_buff = NULL;
@@ -315,10 +318,10 @@
 			return (bytes_skipped);
 		}
 		total_bytes_skipped += bytes_skipped;
-		a->file_position += bytes_skipped;
+		a->archive.file_position += bytes_skipped;
 		request -= bytes_skipped;
 		state->client_next = state->client_buff;
-		a->raw_position += bytes_skipped;
+		a->archive.raw_position += bytes_skipped;
 		state->client_avail = state->client_total = 0;
 	}
 	/*
@@ -336,31 +339,27 @@
 			return (bytes_read);
 		if (bytes_read == 0) {
 			/* We hit EOF before we satisfied the skip request. */
-			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Truncated input file (need to skip %jd bytes)",
 			    (intmax_t)request);
 			return (ARCHIVE_FATAL);
 		}
-		assert(bytes_read >= 0); /* precondition for cast below */
 		min = (size_t)(minimum(bytes_read, request));
 		bytes_read = archive_decompressor_none_read_consume(a, min);
 		total_bytes_skipped += bytes_read;
 		request -= bytes_read;
 	}
-	assert(request == 0);
 	return (total_bytes_skipped);
 }
 
 static int
-archive_decompressor_none_finish(struct archive *a)
+archive_decompressor_none_finish(struct archive_read *a)
 {
 	struct archive_decompress_none	*state;
 
-	state = (struct archive_decompress_none *)a->compression_data;
+	state = (struct archive_decompress_none *)a->decompressor->data;
 	free(state->buffer);
 	free(state);
-	a->compression_data = NULL;
-	if (a->client_closer != NULL)
-		return ((a->client_closer)(a, a->client_data));
+	a->decompressor->data = NULL;
 	return (ARCHIVE_OK);
 }
Index: archive_write_set_format_pax.c
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write_set_format_pax.c,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write_set_format_pax.c -Llib/libarchive/archive_write_set_format_pax.c -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write_set_format_pax.c
+++ lib/libarchive/archive_write_set_format_pax.c
@@ -24,18 +24,8 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.27.2.2 2007/01/27 06:44:53 kientzle Exp $");
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_format_pax.c,v 1.41 2007/05/29 01:00:19 kientzle Exp $");
 
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#else
-#ifdef MAJOR_IN_SYSMACROS
-#include <sys/sysmacros.h>
-#endif
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
@@ -45,19 +35,16 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
+#include "archive_write_private.h"
 
 struct pax {
 	uint64_t	entry_bytes_remaining;
 	uint64_t	entry_padding;
 	struct archive_string	pax_header;
-	char		written;
 };
 
 static void		 add_pax_attr(struct archive_string *, const char *key,
@@ -69,11 +56,12 @@
 			     unsigned long nanos);
 static void		 add_pax_attr_w(struct archive_string *,
 			     const char *key, const wchar_t *wvalue);
-static ssize_t		 archive_write_pax_data(struct archive *,
+static ssize_t		 archive_write_pax_data(struct archive_write *,
 			     const void *, size_t);
-static int		 archive_write_pax_finish(struct archive *);
-static int		 archive_write_pax_finish_entry(struct archive *);
-static int		 archive_write_pax_header(struct archive *,
+static int		 archive_write_pax_finish(struct archive_write *);
+static int		 archive_write_pax_destroy(struct archive_write *);
+static int		 archive_write_pax_finish_entry(struct archive_write *);
+static int		 archive_write_pax_header(struct archive_write *,
 			     struct archive_entry *);
 static char		*base64_encode(const char *src, size_t len);
 static char		*build_pax_attribute_name(char *dest, const char *src);
@@ -82,7 +70,7 @@
 static char		*format_int(char *dest, int64_t);
 static int		 has_non_ASCII(const wchar_t *);
 static char		*url_encode(const char *in);
-static int		 write_nulls(struct archive *, size_t);
+static int		 write_nulls(struct archive_write *, size_t);
 
 /*
  * Set output format to 'restricted pax' format.
@@ -92,10 +80,11 @@
  * bsdtar, for instance.
  */
 int
-archive_write_set_format_pax_restricted(struct archive *a)
+archive_write_set_format_pax_restricted(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	int r;
-	r = archive_write_set_format_pax(a);
+	r = archive_write_set_format_pax(&a->archive);
 	a->archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
 	a->archive_format_name = "restricted POSIX pax interchange";
 	return (r);
@@ -105,16 +94,17 @@
  * Set output format to 'pax' format.
  */
 int
-archive_write_set_format_pax(struct archive *a)
+archive_write_set_format_pax(struct archive *_a)
 {
+	struct archive_write *a = (struct archive_write *)_a;
 	struct pax *pax;
 
-	if (a->format_finish != NULL)
-		(a->format_finish)(a);
+	if (a->format_destroy != NULL)
+		(a->format_destroy)(a);
 
 	pax = (struct pax *)malloc(sizeof(*pax));
 	if (pax == NULL) {
-		archive_set_error(a, ENOMEM, "Can't allocate pax data");
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(pax, 0, sizeof(*pax));
@@ -124,6 +114,7 @@
 	a->format_write_header = archive_write_pax_header;
 	a->format_write_data = archive_write_pax_data;
 	a->format_finish = archive_write_pax_finish;
+	a->format_destroy = archive_write_pax_destroy;
 	a->format_finish_entry = archive_write_pax_finish_entry;
 	a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 	a->archive_format_name = "POSIX pax interchange";
@@ -388,17 +379,17 @@
  * key/value data.
  */
 static int
-archive_write_pax_header(struct archive *a,
+archive_write_pax_header(struct archive_write *a,
     struct archive_entry *entry_original)
 {
 	struct archive_entry *entry_main;
 	const char *linkname, *p;
+	char *t;
 	const char *hardlink;
 	const wchar_t *wp;
 	const char *suffix_start;
 	int need_extension, r, ret;
 	struct pax *pax;
-	const struct stat *st_main, *st_original;
 
 	char paxbuff[512];
 	char ustarbuff[512];
@@ -407,30 +398,38 @@
 
 	need_extension = 0;
 	pax = (struct pax *)a->format_data;
-	pax->written = 1;
-
-	st_original = archive_entry_stat(entry_original);
 
 	hardlink = archive_entry_hardlink(entry_original);
 
 	/* Make sure this is a type of entry that we can handle here */
 	if (hardlink == NULL) {
-		switch (st_original->st_mode & S_IFMT) {
-		case S_IFREG:
-		case S_IFLNK:
-		case S_IFCHR:
-		case S_IFBLK:
-		case S_IFDIR:
-		case S_IFIFO:
+		switch (archive_entry_filetype(entry_original)) {
+		case AE_IFBLK:
+		case AE_IFCHR:
+		case AE_IFIFO:
+		case AE_IFLNK:
+		case AE_IFREG:
+			break;
+		case AE_IFDIR:
+			/*
+			 * Ensure a trailing '/'.  Modify the original
+			 * entry so the client sees the change.
+			 */
+			p = archive_entry_pathname(entry_original);
+			if (p[strlen(p) - 1] != '/') {
+				t = (char *)malloc(strlen(p) + 2);
+				if (t != NULL) {
+					strcpy(t, p);
+					strcat(t, "/");
+					archive_entry_copy_pathname(entry_original, t);
+					free(t);
+				}
+			}
 			break;
-		case S_IFSOCK:
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "tar format cannot archive socket");
-			return (ARCHIVE_WARN);
 		default:
-			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "tar format cannot archive this (mode=0%lo)",
-			    (unsigned long)st_original->st_mode);
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "tar format cannot archive this (type=0%lo)",
+			    (unsigned long)archive_entry_filetype(entry_original));
 			return (ARCHIVE_WARN);
 		}
 	}
@@ -438,7 +437,6 @@
 	/* Copy entry so we can modify it as needed. */
 	entry_main = archive_entry_clone(entry_original);
 	archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
-	st_main = archive_entry_stat(entry_main);
 
 	/*
 	 * Determining whether or not the name is too big is ugly
@@ -493,14 +491,16 @@
 	}
 
 	/* If file size is too large, add 'size' to pax extended attrs. */
-	if (st_main->st_size >= (((int64_t)1) << 33)) {
-		add_pax_attr_int(&(pax->pax_header), "size", st_main->st_size);
+	if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+		add_pax_attr_int(&(pax->pax_header), "size",
+		    archive_entry_size(entry_main));
 		need_extension = 1;
 	}
 
 	/* If numeric GID is too large, add 'gid' to pax extended attrs. */
-	if (st_main->st_gid >= (1 << 18)) {
-		add_pax_attr_int(&(pax->pax_header), "gid", st_main->st_gid);
+	if (archive_entry_gid(entry_main) >= (1 << 18)) {
+		add_pax_attr_int(&(pax->pax_header), "gid",
+		    archive_entry_gid(entry_main));
 		need_extension = 1;
 	}
 
@@ -515,8 +515,9 @@
 	}
 
 	/* If numeric UID is too large, add 'uid' to pax extended attrs. */
-	if (st_main->st_uid >= (1 << 18)) {
-		add_pax_attr_int(&(pax->pax_header), "uid", st_main->st_uid);
+	if (archive_entry_uid(entry_main) >= (1 << 18)) {
+		add_pax_attr_int(&(pax->pax_header), "uid",
+		    archive_entry_uid(entry_main));
 		need_extension = 1;
 	}
 
@@ -541,15 +542,15 @@
 	 *
 	 * Of course, this is only needed for block or char device entries.
 	 */
-	if (S_ISBLK(st_main->st_mode) ||
-	    S_ISCHR(st_main->st_mode)) {
+	if (archive_entry_filetype(entry_main) == AE_IFBLK
+	    || archive_entry_filetype(entry_main) == AE_IFCHR) {
 		/*
 		 * If rdevmajor is too large, add 'SCHILY.devmajor' to
 		 * extended attributes.
 		 */
 		dev_t rdevmajor, rdevminor;
-		rdevmajor = major(st_main->st_rdev);
-		rdevminor = minor(st_main->st_rdev);
+		rdevmajor = archive_entry_rdevmajor(entry_main);
+		rdevminor = archive_entry_rdevminor(entry_main);
 		if (rdevmajor >= (1 << 18)) {
 			add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",
 			    rdevmajor);
@@ -590,7 +591,8 @@
 	 * high-resolution timestamp in "restricted pax" mode.
 	 */
 	if (!need_extension &&
-	    ((st_main->st_mtime < 0) || (st_main->st_mtime >= 0x7fffffff)))
+	    ((archive_entry_mtime(entry_main) < 0)
+		|| (archive_entry_mtime(entry_main) >= 0x7fffffff)))
 		need_extension = 1;
 
 	/* I use a star-compatible file flag attribute. */
@@ -622,24 +624,24 @@
 	if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
 	    need_extension) {
 
-		if (st_main->st_mtime < 0  ||
-		    st_main->st_mtime >= 0x7fffffff  ||
-		    ARCHIVE_STAT_MTIME_NANOS(st_main) != 0)
+		if (archive_entry_mtime(entry_main) < 0  ||
+		    archive_entry_mtime(entry_main) >= 0x7fffffff  ||
+		    archive_entry_mtime_nsec(entry_main) != 0)
 			add_pax_attr_time(&(pax->pax_header), "mtime",
-			    st_main->st_mtime,
-			    ARCHIVE_STAT_MTIME_NANOS(st_main));
+			    archive_entry_mtime(entry_main),
+			    archive_entry_mtime_nsec(entry_main));
 
-		if (st_main->st_ctime != 0  ||
-		    ARCHIVE_STAT_CTIME_NANOS(st_main) != 0)
+		if (archive_entry_ctime(entry_main) != 0  ||
+		    archive_entry_ctime_nsec(entry_main) != 0)
 			add_pax_attr_time(&(pax->pax_header), "ctime",
-			    st_main->st_ctime,
-			    ARCHIVE_STAT_CTIME_NANOS(st_main));
+			    archive_entry_ctime(entry_main),
+			    archive_entry_ctime_nsec(entry_main));
 
-		if (st_main->st_atime != 0  ||
-		    ARCHIVE_STAT_ATIME_NANOS(st_main) != 0)
+		if (archive_entry_atime(entry_main) != 0 ||
+		    archive_entry_atime_nsec(entry_main) != 0)
 			add_pax_attr_time(&(pax->pax_header), "atime",
-			    st_main->st_atime,
-			    ARCHIVE_STAT_ATIME_NANOS(st_main));
+			    archive_entry_atime(entry_main),
+			    archive_entry_atime_nsec(entry_main));
 
 		/* I use a star-compatible file flag attribute. */
 		p = archive_entry_fflags_text(entry_main);
@@ -664,18 +666,18 @@
 		/* Note: "SCHILY.dev{major,minor}" are NOT the
 		 * major/minor portions of "SCHILY.dev". */
 		add_pax_attr_int(&(pax->pax_header), "SCHILY.dev",
-		    st_main->st_dev);
+		    archive_entry_dev(entry_main));
 		add_pax_attr_int(&(pax->pax_header), "SCHILY.ino",
-		    st_main->st_ino);
+		    archive_entry_ino(entry_main));
 		add_pax_attr_int(&(pax->pax_header), "SCHILY.nlink",
-		    st_main->st_nlink);
+		    archive_entry_nlink(entry_main));
 
 		/* Store extended attributes */
 		archive_write_pax_header_xattrs(pax, entry_original);
 	}
 
 	/* Only regular files have data. */
-	if (!S_ISREG(archive_entry_mode(entry_main)))
+	if (archive_entry_filetype(entry_main) != AE_IFREG)
 		archive_entry_set_size(entry_main, 0);
 
 	/*
@@ -730,36 +732,40 @@
 	/* If we built any extended attributes, write that entry first. */
 	ret = ARCHIVE_OK;
 	if (archive_strlen(&(pax->pax_header)) > 0) {
-		struct stat st;
 		struct archive_entry *pax_attr_entry;
 		time_t s;
+		uid_t uid;
+		gid_t gid;
+		mode_t mode;
 		long ns;
 
-		memset(&st, 0, sizeof(st));
 		pax_attr_entry = archive_entry_new();
 		p = archive_entry_pathname(entry_main);
 		archive_entry_set_pathname(pax_attr_entry,
 		    build_pax_attribute_name(pax_entry_name, p));
-		st.st_size = archive_strlen(&(pax->pax_header));
+		archive_entry_set_size(pax_attr_entry,
+		    archive_strlen(&(pax->pax_header)));
 		/* Copy uid/gid (but clip to ustar limits). */
-		st.st_uid = st_main->st_uid;
-		if (st.st_uid >= 1 << 18)
-			st.st_uid = (1 << 18) - 1;
-		st.st_gid = st_main->st_gid;
-		if (st.st_gid >= 1 << 18)
-			st.st_gid = (1 << 18) - 1;
+		uid = archive_entry_uid(entry_main);
+		if (uid >= 1 << 18)
+			uid = (1 << 18) - 1;
+		archive_entry_set_uid(pax_attr_entry, uid);
+		gid = archive_entry_gid(entry_main);
+		if (gid >= 1 << 18)
+			gid = (1 << 18) - 1;
+		archive_entry_set_gid(pax_attr_entry, gid);
 		/* Copy mode over (but not setuid/setgid bits) */
-		st.st_mode = st_main->st_mode;
+		mode = archive_entry_mode(entry_main);
 #ifdef S_ISUID
-		st.st_mode &= ~S_ISUID;
+		mode &= ~S_ISUID;
 #endif
 #ifdef S_ISGID
-		st.st_mode &= ~S_ISGID;
+		mode &= ~S_ISGID;
 #endif
 #ifdef S_ISVTX
-		st.st_mode &= ~S_ISVTX;
+		mode &= ~S_ISVTX;
 #endif
-		archive_entry_copy_stat(pax_attr_entry, &st);
+		archive_entry_set_mode(pax_attr_entry, mode);
 
 		/* Copy uname/gname. */
 		archive_entry_set_uname(pax_attr_entry,
@@ -796,7 +802,7 @@
 			write(2, msg, strlen(msg));
 			exit(1);
 		}
-		r = (a->compression_write)(a, paxbuff, 512);
+		r = (a->compressor.write)(a, paxbuff, 512);
 		if (r != ARCHIVE_OK) {
 			pax->entry_bytes_remaining = 0;
 			pax->entry_padding = 0;
@@ -806,7 +812,7 @@
 		pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
 		pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining);
 
-		r = (a->compression_write)(a, pax->pax_header.s,
+		r = (a->compressor.write)(a, pax->pax_header.s,
 		    archive_strlen(&(pax->pax_header)));
 		if (r != ARCHIVE_OK) {
 			/* If a write fails, we're pretty much toast. */
@@ -822,7 +828,7 @@
 	}
 
 	/* Write the header for main entry. */
-	r = (a->compression_write)(a, ustarbuff, 512);
+	r = (a->compressor.write)(a, ustarbuff, 512);
 	if (r != ARCHIVE_OK)
 		return (r);
 
@@ -1038,23 +1044,33 @@
 
 /* Write two null blocks for the end of archive */
 static int
-archive_write_pax_finish(struct archive *a)
+archive_write_pax_finish(struct archive_write *a)
 {
 	struct pax *pax;
 	int r;
 
-	r = ARCHIVE_OK;
+	if (a->compressor.write == NULL)
+		return (ARCHIVE_OK);
+
+	pax = (struct pax *)a->format_data;
+	r = write_nulls(a, 512 * 2);
+	return (r);
+}
+
+static int
+archive_write_pax_destroy(struct archive_write *a)
+{
+	struct pax *pax;
+
 	pax = (struct pax *)a->format_data;
-	if (pax->written && a->compression_write != NULL)
-		r = write_nulls(a, 512 * 2);
 	archive_string_free(&pax->pax_header);
 	free(pax);
 	a->format_data = NULL;
-	return (r);
+	return (ARCHIVE_OK);
 }
 
 static int
-archive_write_pax_finish_entry(struct archive *a)
+archive_write_pax_finish_entry(struct archive_write *a)
 {
 	struct pax *pax;
 	int ret;
@@ -1066,13 +1082,13 @@
 }
 
 static int
-write_nulls(struct archive *a, size_t padding)
+write_nulls(struct archive_write *a, size_t padding)
 {
 	int ret, to_write;
 
 	while (padding > 0) {
 		to_write = padding < a->null_length ? padding : a->null_length;
-		ret = (a->compression_write)(a, a->nulls, to_write);
+		ret = (a->compressor.write)(a, a->nulls, to_write);
 		if (ret != ARCHIVE_OK)
 			return (ret);
 		padding -= to_write;
@@ -1081,17 +1097,16 @@
 }
 
 static ssize_t
-archive_write_pax_data(struct archive *a, const void *buff, size_t s)
+archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
 {
 	struct pax *pax;
 	int ret;
 
 	pax = (struct pax *)a->format_data;
-	pax->written = 1;
 	if (s > pax->entry_bytes_remaining)
 		s = pax->entry_bytes_remaining;
 
-	ret = (a->compression_write)(a, buff, s);
+	ret = (a->compressor.write)(a, buff, s);
 	pax->entry_bytes_remaining -= s;
 	if (ret == ARCHIVE_OK)
 		return (s);
--- /dev/null
+++ lib/libarchive/archive_write_disk.3
@@ -0,0 +1,358 @@
+.\" Copyright (c) 2003-2007 Tim Kientzle
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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_write_disk.3,v 1.1 2007/03/03 07:37:36 kientzle Exp $
+.\"
+.Dd March 2, 2007
+.Dt archive_write_disk 3
+.Os
+.Sh NAME
+.Nm archive_write_disk_new ,
+.Nm archive_write_disk_set_options ,
+.Nm archive_write_disk_set_skip_file ,
+.Nm archive_write_disk_set_group_lookup ,
+.Nm archive_write_disk_set_standard_lookup ,
+.Nm archive_write_disk_set_user_lookup ,
+.Nm archive_write_header ,
+.Nm archive_write_data ,
+.Nm archive_write_finish_entry ,
+.Nm archive_write_close ,
+.Nm archive_write_finish
+.Nd functions for creating objects on disk
+.Sh SYNOPSIS
+.In archive.h
+.Ft struct archive *
+.Fn archive_write_disk_new "void"
+.Ft int
+.Fn archive_write_disk_set_options "struct archive *" "int flags"
+.Ft int
+.Fn archive_write_disk_set_skip_file "struct archive *" "dev_t" "ino_t"
+.Ft int
+.Fn archive_write_disk_set_group_lookup "struct archive *" "void *" "gid_t (*)(void *, const char *gname, gid_t gid)" "void (*cleanup)(void *)"
+.Ft int
+.Fn archive_write_disk_set_standard_lookup "struct archive *"
+.Ft int
+.Fn archive_write_disk_set_user_lookup "struct archive *" "void *" "uid_t (*)(void *, const char *uname, uid_t uid)" "void (*cleanup)(void *)"
+.Ft int
+.Fn archive_write_header "struct archive *" "struct archive_entry *"
+.Ft ssize_t
+.Fn archive_write_data "struct archive *" "const void *" "size_t"
+.Ft int
+.Fn archive_write_finish_entry "struct archive *"
+.Ft int
+.Fn archive_write_close "struct archive *"
+.Ft int
+.Fn archive_write_finish "struct archive *"
+.Sh DESCRIPTION
+These functions provide a complete API for creating objects on
+disk from
+.Tn struct archive_entry
+descriptions.
+They are most naturally used when extracting objects from an archive
+using the
+.Fn archive_read
+interface.
+The general process is to read
+.Tn struct archive_entry
+objects from an archive, then write those objects to a
+.Tn struct archive
+object created using the
+.Fn archive_write_disk
+family functions.
+This interface is deliberately very similar to the
+.Fn archive_write
+interface used to write objects to a streaming archive.
+.Bl -tag -width indent
+.It Fn archive_write_disk_new
+Allocates and initializes a
+.Tn struct archive
+object suitable for writing objects to disk.
+.It Fn archive_write_disk_set_skip_file
+Records the device and inode numbers of a file that should not be
+overwritten.
+This is typically used to ensure that an extraction process does not
+overwrite the archive from which objects are being read.
+This capability is technically unnecessary but can be a significant
+performance optimization in practice.
+.It Fn archive_write_disk_set_options
+The options field consists of a bitwise OR of one or more of the
+following values:
+.Bl -tag -compact -width "indent"
+.It Cm ARCHIVE_EXTRACT_OWNER
+The user and group IDs should be set on the restored file.
+By default, the user and group IDs are not restored.
+.It Cm ARCHIVE_EXTRACT_PERM
+Full permissions (including SGID, SUID, and sticky bits) should
+be restored exactly as specified, without obeying the
+current umask.
+Note that SUID and SGID bits can only be restored if the
+user and group ID of the object on disk are correct.
+If
+.Cm ARCHIVE_EXTRACT_OWNER
+is not specified, then SUID and SGID bits will only be restored
+if the default user and group IDs of newly-created objects on disk
+happen to match those specified in the archive entry.
+By default, only basic permissions are restored, and umask is obeyed.
+.It Cm ARCHIVE_EXTRACT_TIME
+The timestamps (mtime, ctime, and atime) should be restored.
+By default, they are ignored.
+Note that restoring of atime is not currently supported.
+.It Cm ARCHIVE_EXTRACT_NO_OVERWRITE
+Existing files on disk will not be overwritten.
+By default, existing regular files are truncated and overwritten;
+existing directories will have their permissions updated;
+other pre-existing objects are unlinked and recreated from scratch.
+.It Cm ARCHIVE_EXTRACT_UNLINK
+Existing files on disk will be unlinked before any attempt to
+create them.
+In some cases, this can prove to be a significant performance improvement.
+By default, existing files are truncated and rewritten, but
+the file is not recreated.
+In particular, the default behavior does not break existing hard links.
+.It Cm ARCHIVE_EXTRACT_ACL
+Attempt to restore ACLs.
+By default, extended ACLs are ignored.
+.It Cm ARCHIVE_EXTRACT_FFLAGS
+Attempt to restore extended file flags.
+By default, file flags are ignored.
+.It Cm ARCHIVE_EXTRACT_XATTR
+Attempt to restore POSIX.1e extended attributes.
+By default, they are ignored.
+.It Cm ARCHIVE_EXTRACT_SECURE_SYMLINKS
+Refuse to extract any object whose final location would be altered
+by a symlink on disk.
+This is intended to help guard against a variety of mischief
+caused by archives that (deliberately or otherwise) extract
+files outside of the current directory.
+The default is not to perform this check.
+If
+.Cm ARCHIVE_EXTRACT_UNLINK
+is specified together with this option, the library will
+remove any intermediate symlinks it finds and return an
+error only if such symlink could not be removed.
+.It Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT
+Refuse to extract a path that contains a
+.Pa ..
+element anywhere within it.
+The default is to not refuse such paths.
+Note that paths ending in
+.Pa ..
+always cause an error, regardless of this flag.
+.El
+.It Fn archive_write_disk_set_group_lookup , Fn archive_write_disk_set_user_lookup
+The
+.Tn struct archive_entry
+objects contain both names and ids that can be used to identify users
+and groups.
+These names and ids describe the ownership of the file itself and
+also appear in ACL lists.
+By default, the library uses the ids and ignores the names, but
+this can be overridden by registering user and group lookup functions.
+To register, you must provide a lookup function which
+accepts both a name and id and returns a suitable id.
+You may also provide a
+.Tn void *
+pointer to a private data structure and a cleanup function for
+that data.
+The cleanup function will be invoked when the
+.Tn struct archive
+object is destroyed.
+.It Fn archive_write_disk_set_standard_lookup
+This convenience function installs a standard set of user
+and group lookup functions.
+These functions use
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+to convert names to ids, defaulting to the ids if the names cannot
+be looked up.
+These functions also implement a simple memory cache to reduce
+the number of calls to
+.Xr getpwnam 3
+and
+.Xr getgrnam 3 .
+.It Fn archive_write_header
+Build and write a header using the data in the provided
+.Tn struct archive_entry
+structure.
+See
+.Xr archive_entry 3
+for information on creating and populating
+.Tn struct archive_entry
+objects.
+.It Fn archive_write_data
+Write data corresponding to the header just written.
+Returns number of bytes written or -1 on error.
+.It Fn archive_write_finish_entry
+Close out the entry just written.
+Ordinarily, clients never need to call this, as it
+is called automatically by
+.Fn archive_write_next_header
+and
+.Fn archive_write_close
+as needed.
+.It Fn archive_write_close
+Set any attributes that could not be set during the initial restore.
+For example, directory timestamps are not restored initially because
+restoring a subsequent file would alter that timestamp.
+Similarly, non-writable directories are initially created with
+write permissions (so that their contents can be restored).
+The
+.Nm
+library maintains a list of all such deferred attributes and
+sets them when this function is invoked.
+.It Fn archive_write_finish
+Invokes
+.Fn archive_write_close
+if it was not invoked manually, then releases all resources.
+.El
+More information about the
+.Va struct archive
+object and the overall design of the library can be found in the
+.Xr libarchive 3
+overview.
+Many of these functions are also documented under
+.Xr archive_write 3 .
+.Sh RETURN VALUES
+Most functions return
+.Cm ARCHIVE_OK
+(zero) on success, or one of several non-zero
+error codes for errors.
+Specific error codes include:
+.Cm ARCHIVE_RETRY
+for operations that might succeed if retried,
+.Cm ARCHIVE_WARN
+for unusual conditions that do not prevent further operations, and
+.Cm ARCHIVE_FATAL
+for serious errors that make remaining operations impossible.
+The
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions can be used to retrieve an appropriate error code and a
+textual error message.
+.Pp
+.Fn archive_write_disk_new
+returns a pointer to a newly-allocated
+.Tn struct archive
+object.
+.Pp
+.Fn archive_write_data
+returns a count of the number of bytes actually written.
+On error, -1 is returned and the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions will return appropriate values.
+.Sh SEE ALSO
+.Xr archive_read 3 ,
+.Xr archive_write 3 ,
+.Xr tar 1 ,
+.Xr libarchive 3
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+The
+.Nm archive_write_disk
+interface was added to
+.Nm libarchive 2.0
+and first appeared in
+.Fx 6.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle at acm.org .
+.Sh BUGS
+Directories are actually extracted in two distinct phases.
+Directories are created during
+.Fn archive_write_header ,
+but final permissions are not set until
+.Fn archive_write_close .
+This separation is necessary to correctly handle borderline
+cases such as a non-writable directory containing
+files, but can cause unexpected results.
+In particular, directory permissions are not fully
+restored until the archive is closed.
+If you use
+.Xr chdir 2
+to change the current directory between calls to
+.Fn archive_read_extract
+or before calling
+.Fn archive_read_close ,
+you may confuse the permission-setting logic with
+the result that directory permissions are restored
+incorrectly.
+.Pp
+The library attempts to create objects with filenames longer than
+.Cm PATH_MAX
+by creating prefixes of the full path and changing the current directory.
+Currently, this logic is limited in scope; the fixup pass does
+not work correctly for such objects and the symlink security check
+option disables the support for very long pathnames.
+.Pp
+Restoring the path
+.Pa aa/../bb
+does create each intermediate directory.
+In particular, the directory
+.Pa aa
+is created as well as the final object
+.Pa bb .
+In theory, this can be exploited to create an entire directory heirarchy
+with a single request.
+Of course, this does not work if the
+.Cm ARCHIVE_EXTRACT_NODOTDOT
+option is specified.
+.Pp
+Implicit directories are always created obeying the current umask.
+Explicit objects are created obeying the current umask unless
+.Cm ARCHIVE_EXTRACT_PERM
+is specified, in which case they current umask is ignored.
+.Pp
+SGID and SUID bits are restored only if the correct user and
+group could be set.
+If
+.Cm ARCHIVE_EXTRACT_OWNER
+is not specified, then no attempt is made to set the ownership.
+In this case, SGID and SUID bits are restored only if the
+user and group of the final object happen to match those specified
+in the entry.
+.Pp
+The
+.Dq standard
+user-id and group-id lookup functions are not the defaults because
+.Xr getgrnam 3
+and
+.Xr getpwnam 3
+are sometimes too large for particular applications.
+The current design allows the application author to use a more
+compact implementation when appropriate.
+.Pp
+There should be a corresponding
+.Nm archive_read_disk
+interface that walks a directory heirarchy and returns archive
+entry objects.
\ No newline at end of file
--- /dev/null
+++ lib/libarchive/archive_entry_copy_stat.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_copy_stat.c,v 1.1 2007/05/29 01:00:18 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "archive_entry.h"
+
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
+#else
+	archive_entry_set_atime(entry, st->st_atime, 0);
+	archive_entry_set_ctime(entry, st->st_ctime, 0);
+	archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+	archive_entry_set_dev(entry, st->st_dev);
+	archive_entry_set_gid(entry, st->st_gid);
+	archive_entry_set_uid(entry, st->st_uid);
+	archive_entry_set_ino(entry, st->st_ino);
+	archive_entry_set_nlink(entry, st->st_nlink);
+	archive_entry_set_rdev(entry, st->st_rdev);
+	archive_entry_set_size(entry, st->st_size);
+	archive_entry_set_mode(entry, st->st_mode);
+}
Index: archive_write.3
===================================================================
RCS file: /home/cvs/src/lib/libarchive/archive_write.3,v
retrieving revision 1.1.1.2
retrieving revision 1.2
diff -Llib/libarchive/archive_write.3 -Llib/libarchive/archive_write.3 -u -r1.1.1.2 -r1.2
--- lib/libarchive/archive_write.3
+++ lib/libarchive/archive_write.3
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.12.2.3 2007/01/27 06:44:53 kientzle Exp $
+.\" $FreeBSD: src/lib/libarchive/archive_write.3,v 1.22 2007/05/29 01:00:19 kientzle Exp $
 .\"
 .Dd August 19, 2006
 .Dt archive_write 3
@@ -38,8 +38,10 @@
 .Nm archive_write_get_bytes_per_block ,
 .Nm archive_write_set_bytes_per_block ,
 .Nm archive_write_set_bytes_in_last_block ,
-.Nm archive_write_set_compressor_gzip ,
-.Nm archive_write_set_compressor_bzip2 ,
+.Nm archive_write_set_compression_bzip2 ,
+.Nm archive_write_set_compression_gzip ,
+.Nm archive_write_set_compression_none ,
+.Nm archive_write_set_compression_program ,
 .Nm archive_write_open ,
 .Nm archive_write_open_fd ,
 .Nm archive_write_open_FILE ,
@@ -62,9 +64,13 @@
 .Ft int
 .Fn archive_write_set_bytes_in_last_block "struct archive *" "int"
 .Ft int
-.Fn archive_write_set_compressor_gzip "struct archive *"
+.Fn archive_write_set_compression_bzip2 "struct archive *"
 .Ft int
-.Fn archive_write_set_compressor_bzip2 "struct archive *"
+.Fn archive_write_set_compression_gzip "struct archive *"
+.Ft int
+.Fn archive_write_set_compression_none "struct archive *"
+.Ft int
+.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
 .Ft int
 .Fn archive_write_set_format_cpio "struct archive *"
 .Ft int
@@ -89,13 +95,13 @@
 .Fn archive_write_open_memory "struct archive *" "void *buffer" "size_t bufferSize" "size_t *outUsed"
 .Ft int
 .Fn archive_write_header "struct archive *" "struct archive_entry *"
-.Ft int
+.Ft ssize_t
 .Fn archive_write_data "struct archive *" "const void *" "size_t"
 .Ft int
 .Fn archive_write_finish_entry "struct archive *"
 .Ft int
 .Fn archive_write_close "struct archive *"
-.Ft void
+.Ft int
 .Fn archive_write_finish "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for creating streaming
@@ -168,9 +174,13 @@
 is the library default; this is the same as pax format, but suppresses
 the pax extended header for most normal files.
 In most cases, this will result in ordinary ustar archives.
-.It Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_bzip2
+.It Fn archive_write_set_compression_bzip2 , Fn archive_write_set_compression_gzip , Fn archive_write_set_compression_none
 The resulting archive will be compressed as specified.
 Note that the compressed output is always properly blocked.
+.It Fn archive_write_set_compression_program
+The archive will be fed into the specified compression program.
+The output of that program is blocked and written to the client
+write callbacks.
 .It Fn archive_write_open
 Freeze the settings, open the archive, and prepare for writing entries.
 This is the most generic form of this function, which accepts
@@ -260,6 +270,12 @@
 Invokes
 .Fn archive_write_close
 if it was not invoked manually, then releases all resources.
+Note that this function was declared to return
+.Ft void
+in libarchive 1.x, which made it impossible to detect errors when
+.Fn archive_write_close
+was invoked implicitly from this function.
+This is corrected beginning with libarchive 2.0.
 .El
 More information about the
 .Va struct archive
@@ -457,8 +473,9 @@
 may include
 .Fn archive_write_header ,
 .Fn archive_write_data ,
+.Fn archive_write_close ,
 or
-.Fn archive_write_close .
+.Fn archive_write_finish .
 The client callback can call
 .Fn archive_set_error
 to provide values that can then be retrieved by
--- /dev/null
+++ lib/libarchive/test/read_open_memory.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/read_open_memory.c,v 1.1 2007/07/13 15:12:52 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Read an archive from a block of memory.
+ *
+ * This is identical to archive_read_open_memory(), except
+ * that it goes out of its way to be a little bit unpleasant,
+ * in order to better test the libarchive internals.
+ */
+
+struct read_memory_data {
+	unsigned char	*buffer;
+	unsigned char	*end;
+	size_t	 read_size;
+	size_t copy_buff_size;
+	char *copy_buff;
+};
+
+static int	memory_read_close(struct archive *, void *);
+static int	memory_read_open(struct archive *, void *);
+#if ARCHIVE_API_VERSION < 2
+static ssize_t	memory_read_skip(struct archive *, void *, size_t request);
+#else
+static off_t	memory_read_skip(struct archive *, void *, off_t request);
+#endif
+static ssize_t	memory_read(struct archive *, void *, const void **buff);
+
+int
+read_open_memory(struct archive *a, void *buff, size_t size, size_t read_size)
+{
+	struct read_memory_data *mine;
+
+	mine = (struct read_memory_data *)malloc(sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	memset(mine, 0, sizeof(*mine));
+	mine->buffer = (unsigned char *)buff;
+	mine->end = mine->buffer + size;
+	mine->read_size = read_size;
+	mine->copy_buff_size = read_size + 64;
+	mine->copy_buff = malloc(mine->copy_buff_size);
+	return (archive_read_open2(a, mine, memory_read_open,
+		    memory_read, memory_read_skip, memory_read_close));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+	(void)a; /* UNUSED */
+	(void)client_data; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+/*
+ * In order to exercise libarchive's internal read-combining logic,
+ * we deliberately copy data for each read to a separate buffer.
+ * That way, code that runs off the end of the provided data
+ * will screw up.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+	ssize_t size;
+
+	(void)a; /* UNUSED */
+	size = mine->end - mine->buffer;
+	if (size > mine->read_size)
+		size = mine->read_size;
+	memset(mine->copy_buff, 0xA5, mine->copy_buff_size);
+	memcpy(mine->copy_buff, mine->buffer, size);
+	*buff = mine->copy_buff;
+
+        mine->buffer += size;
+	return (size);
+}
+
+/*
+ * How mean can a skip() routine be?  Let's try to find out.
+ */
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+memory_read_skip(struct archive *a, void *client_data, size_t skip)
+#else
+static off_t
+memory_read_skip(struct archive *a, void *client_data, off_t skip)
+#endif
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+	(void)a; /* UNUSED */
+	/* We can't skip by more than is available. */
+	if ((off_t)skip > (off_t)(mine->end - mine->buffer))
+		skip = mine->end - mine->buffer;
+	/* Always do small skips by prime amounts. */
+	if (skip > 71)
+		skip = 71;
+	mine->buffer += skip;
+	return (skip);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+	(void)a; /* UNUSED */
+	free(mine->copy_buff);
+	free(mine);
+	return (ARCHIVE_OK);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_pax_bz2.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_pax_bz2.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+'B','Z','h','9','1','A','Y','&','S','Y',152,180,30,185,0,0,140,127,176,212,
+144,0,' ','@',1,255,226,8,'d','H',' ',238,'/',159,'@',0,16,4,'@',0,8,'0',
+0,216,'A',164,167,147,'Q',147,'!',180,'#',0,'L',153,162,'i',181,'?','P',192,
+26,'h','h',209,136,200,6,128,13,12,18,132,202,'5','O',209,'5','=',26,'2',
+154,7,168,12,2,'d',252,13,254,29,'4',247,181,'l','T','i',130,5,195,1,'2',
+'@',146,18,251,245,'c','J',130,224,172,'$','l','4',235,170,186,'c','1',255,
+179,'K',188,136,18,208,152,192,149,153,10,'{','|','0','8',166,3,6,9,128,172,
+'(',164,220,244,149,6,' ',243,212,'B',25,17,'6',237,13,'I',152,'L',129,209,
+'G','J','<',137,'Y',16,'b',21,18,'a','Y','l','t','r',160,128,147,'l','f',
+'~',219,206,'=','?','S',233,'3',251,'L','~',17,176,169,'%',23,'_',225,'M',
+'C','u','k',218,8,'q',216,'(',22,235,'K',131,136,146,136,147,202,0,158,134,
+'F',23,160,184,'s','0','a',246,'*','P',7,2,238,'H',167,10,18,19,22,131,215,
+' '};
+
+DEFINE_TEST(test_read_format_pax_bz2)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_BZIP2);
+	assert(archive_format(a) == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_gz.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_gz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,0,'+','e',217,'D',0,3,211,211,'g',160,'9','0',0,2,'s','S','S',16,
+'m','h','n','j',128,'L',195,0,131,161,129,177,177,137,129,137,185,185,161,
+'!',131,129,161,129,153,161,'9',131,130,')',237,157,198,192,'P','Z','\\',
+146,'X',164,160,192,'P',146,153,139,'W',29,'!','y',152,'G','`',244,'(',24,
+5,163,'`',20,12,'r',0,0,226,234,'6',162,0,6,0,0};
+
+DEFINE_TEST(test_read_format_gtar_gz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_GZIP);
+	assert(archive_format(a) == ARCHIVE_FORMAT_TAR_GNUTAR);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_tz.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,157,144,'.',0,8,28,'H',176,160,193,131,8,19,'*','\\',200,176,'!','B',24,
+16,'o',212,168,1,2,0,196,24,18,'a','T',188,152,'q','#',196,143,' ','5',198,
+128,'1','c',6,13,24,'4','0',206,176,1,2,198,200,26,'6','b',0,0,'Q',195,161,
+205,155,'8','s',234,4,'P','g',14,157,'0','r',',',194,160,147,166,205,206,
+132,'D',141,30,'=',24,'R',163,'P',144,21,151,'J',157,'J',181,170,213,171,
+'X',179,'j',221,202,181,171,215,175,'`',195,138,29,'K',182,172,217,179,'h',
+211,170,']',203,182,173,219,183,'g',1};
+
+DEFINE_TEST(test_read_format_tz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_COMPRESS);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_TAR_USTAR);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_ar.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_ar.c,v 1.4 2007/07/06 15:43:11 kientzle Exp $");
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+/*
+ * This "archive" is created by "GNU ar". Here we try to verify
+ * our GNU format handling functionality.
+ */
+static unsigned char archive[] = {
+'!','<','a','r','c','h','>',10,'/','/',' ',' ',' ',' ',' ',' ',' ',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+' ',' ',' ',' ',' ','4','0',' ',' ',' ',' ',' ',' ',' ',' ','`',10,
+'y','y','y','t','t','t','s','s','s','a','a','a','f','f','f','.','o',
+'/',10,'h','h','h','h','j','j','j','j','k','k','k','k','l','l','l',
+'l','.','o','/',10,10,'/','0',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+' ',' ',' ',' ','1','1','7','5','4','6','5','6','5','2',' ',' ','1',
+'0','0','1',' ',' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4',
+' ',' ','8',' ',' ',' ',' ',' ',' ',' ',' ',' ','`',10,'5','5','6',
+'6','7','7','8','8','g','g','h','h','.','o','/',' ',' ',' ',' ',' ',
+' ',' ',' ',' ','1','1','7','5','4','6','5','6','6','8',' ',' ','1',
+'0','0','1',' ',' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4',
+' ',' ','4',' ',' ',' ',' ',' ',' ',' ',' ',' ','`',10,'3','3','3',
+'3','/','1','9',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+'1','1','7','5','4','6','5','7','1','3',' ',' ','1','0','0','1',' ',
+' ','0',' ',' ',' ',' ',' ','1','0','0','6','4','4',' ',' ','9',' ',
+' ',' ',' ',' ',' ',' ',' ',' ','`',10,'9','8','7','6','5','4','3',
+'2','1',10};
+
+char buff[64];
+#endif
+
+DEFINE_TEST(test_read_format_ar)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("test_read_support_format_ar()");
+#else
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+
+	/* Filename table.  */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("//", archive_entry_pathname(ae));
+	assertEqualInt(0, archive_entry_mtime(ae));
+	assertEqualInt(0, archive_entry_uid(ae));
+	assertEqualInt(0, archive_entry_gid(ae));
+	assertEqualInt(40, archive_entry_size(ae));
+	assertEqualIntA(a, 40, archive_read_data(a, buff, 50));
+	assert(0 == memcmp(buff, "yyytttsssaaafff.o/\nhhhhjjjjkkkkllll.o/\n\n", 40));
+
+	/* First Entry */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("yyytttsssaaafff.o", archive_entry_pathname(ae));
+	assertEqualInt(1175465652, archive_entry_mtime(ae));
+	assertEqualInt(1001, archive_entry_uid(ae));
+	assertEqualInt(0, archive_entry_gid(ae));
+	assert(8 == archive_entry_size(ae));
+	assertA(8 == archive_read_data(a, buff, 10));
+	assert(0 == memcmp(buff, "55667788", 8));
+
+	/* Second Entry */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("gghh.o", archive_entry_pathname(ae));
+	assertEqualInt(1175465668, archive_entry_mtime(ae));
+	assertEqualInt(1001, archive_entry_uid(ae));
+	assertEqualInt(0, archive_entry_gid(ae));
+	assert(4 == archive_entry_size(ae));
+	assertA(4 == archive_read_data(a, buff, 10));
+	assert(0 == memcmp(buff, "3333", 4));
+
+	/* Third Entry */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("hhhhjjjjkkkkllll.o", archive_entry_pathname(ae));
+	assertEqualInt(1175465713, archive_entry_mtime(ae));
+	assertEqualInt(1001, archive_entry_uid(ae));
+	assertEqualInt(0, archive_entry_gid(ae));
+	assert(9 == archive_entry_size(ae));
+	assertA(9 == archive_read_data(a, buff, 9));
+	assert(0 == memcmp(buff, "987654321", 9));
+
+	/* Test EOF */
+	assertA(1 == archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_read_compress_program.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_compress_program.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U',
+0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')',
+24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167,
+148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28,
+0,0,29,172,5,240,0,6,0,0};
+
+DEFINE_TEST(test_read_compress_program)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_read_support_compression_program()");
+#else
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, 0, archive_read_support_compression_none(a));
+	assertEqualIntA(a, 0, archive_read_support_compression_program(a, "gunzip"));
+	assert(0 == archive_read_support_format_all(a));
+	assertEqualIntA(a, 0, archive_read_open_memory(a, archive, sizeof(archive)));
+	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_PROGRAM);
+	assert(archive_format(a) == ARCHIVE_FORMAT_TAR_USTAR);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/main.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Various utility routines useful for test programs.
+ * Each test program is linked against this file.
+ */
+#include <errno.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/main.c,v 1.8 2007/07/31 05:03:27 kientzle Exp $");
+
+/* Interix doesn't define these in a standard header. */
+#if __INTERIX__
+extern char *optarg;
+extern int optind;
+#endif
+
+/* Default is to crash and try to force a core dump on failure. */
+static int dump_on_failure = 1;
+/* Default is to print some basic information about each test. */
+static int quiet_flag = 0;
+/* Cumulative count of component failures. */
+static int failures = 0;
+/* Cumulative count of skipped component tests. */
+static int skips = 0;
+
+/*
+ * My own implementation of the standard assert() macro emits the
+ * message in the same format as GCC (file:line: message).
+ * It also includes some additional useful information.
+ * This makes it a lot easier to skim through test failures in
+ * Emacs.  ;-)
+ *
+ * It also supports a few special features specifically to simplify
+ * libarchive test harnesses:
+ *    failure(fmt, args) -- Stores a text string that gets
+ *          printed if the following assertion fails, good for
+ *          explaining subtle tests.
+ *    assertA(a, cond) -- If the test fails, also prints out any error
+ *          message stored in archive object 'a'.
+ */
+static char msg[4096];
+
+/*
+ * For each test source file, we remember how many times each
+ * failure was reported.
+ */
+static const char *failed_filename;
+static struct line {
+	int line;
+	int count;
+}  failed_lines[1000];
+
+
+/* Count this failure; return the number of previous failures. */
+static int
+previous_failures(const char *filename, int line)
+{
+	int i;
+	int count;
+
+	if (failed_filename == NULL || strcmp(failed_filename, filename) != 0)
+		memset(failed_lines, 0, sizeof(failed_lines));
+	failed_filename = filename;
+
+	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+		if (failed_lines[i].line == line) {
+			count = failed_lines[i].count;
+			failed_lines[i].count++;
+			return (count);
+		}
+		if (failed_lines[i].line == 0) {
+			failed_lines[i].line = line;
+			failed_lines[i].count = 1;
+			return (0);
+		}
+	}
+}
+
+/* Inform user that we're skipping a test. */
+static const char *skipped_filename;
+static int skipped_line;
+void skipping_setup(const char *filename, int line)
+{
+	skipped_filename = filename;
+	skipped_line = line;
+}
+void
+test_skipping(const char *fmt, ...)
+{
+	int i;
+	int line = skipped_line;
+	va_list ap;
+
+	if (previous_failures(skipped_filename, skipped_line))
+		return;
+
+	va_start(ap, fmt);
+	fprintf(stderr, " *** SKIPPING: ");
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	++skips;
+}
+
+/* Common handling of failed tests. */
+static void
+test_failed(struct archive *a, int line)
+{
+	int i;
+
+	failures ++;
+
+	if (msg[0] != '\0') {
+		fprintf(stderr, "   Description: %s\n", msg);
+		msg[0] = '\0';
+	}
+	if (a != NULL) {
+		fprintf(stderr, "   archive error: %s\n", archive_error_string(a));
+	}
+
+	if (dump_on_failure) {
+		fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n");
+		*(char *)(NULL) = 0;
+		exit(1);
+	}
+}
+
+/* Summarize repeated failures in the just-completed test file. */
+int
+summarize_comparator(const void *a0, const void *b0)
+{
+	const struct line *a = a0, *b = b0;
+	if (a->line == 0 && b->line == 0)
+		return (0);
+	if (a->line == 0)
+		return (1);
+	if (b->line == 0)
+		return (-1);
+	return (a->line - b->line);
+}
+
+void
+summarize(const char *filename)
+{
+	int i;
+
+	qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]),
+	    sizeof(failed_lines[0]), summarize_comparator);
+	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
+		if (failed_lines[i].line == 0)
+			break;
+		if (failed_lines[i].count > 1)
+			fprintf(stderr, "%s:%d: Failed %d times\n",
+			    failed_filename, failed_lines[i].line,
+			    failed_lines[i].count);
+	}
+	/* Clear the failure history for the next file. */
+	memset(failed_lines, 0, sizeof(failed_lines));
+}
+
+/* Set up a message to display only after a test fails. */
+void
+failure(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vsprintf(msg, fmt, ap);
+	va_end(ap);
+}
+
+/* Generic assert() just displays the failed condition. */
+void
+test_assert(const char *file, int line, int value, const char *condition, struct archive *a)
+{
+	if (value) {
+		msg[0] = '\0';
+		return;
+	}
+	if (previous_failures(file, line))
+		return;
+	fprintf(stderr, "%s:%d: Assertion failed\n", file, line);
+	fprintf(stderr, "   Condition: %s\n", condition);
+	test_failed(a, line);
+}
+
+/* assertEqualInt() displays the values of the two integers. */
+void
+test_assert_equal_int(const char *file, int line,
+    int v1, const char *e1, int v2, const char *e2, struct archive *a)
+{
+	if (v1 == v2) {
+		msg[0] = '\0';
+		return;
+	}
+	if (previous_failures(file, line))
+		return;
+	fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n",
+	    file, line);
+	fprintf(stderr, "      %s=%d\n", e1, v1);
+	fprintf(stderr, "      %s=%d\n", e2, v2);
+	test_failed(a, line);
+}
+
+/* assertEqualString() displays the values of the two strings. */
+void
+test_assert_equal_string(const char *file, int line,
+    const char *v1, const char *e1,
+    const char *v2, const char *e2,
+    struct archive *a)
+{
+	if (v1 == NULL || v2 == NULL) {
+		if (v1 == v2) {
+			msg[0] = '\0';
+			return;
+		}
+	} else if (strcmp(v1, v2) == 0) {
+		msg[0] = '\0';
+		return;
+	}
+	if (previous_failures(file, line))
+		return;
+	fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n",
+	    file, line);
+	fprintf(stderr, "      %s = \"%s\"\n", e1, v1);
+	fprintf(stderr, "      %s = \"%s\"\n", e2, v2);
+	test_failed(a, line);
+}
+
+/* assertEqualWString() displays the values of the two strings. */
+void
+test_assert_equal_wstring(const char *file, int line,
+    const wchar_t *v1, const char *e1,
+    const wchar_t *v2, const char *e2,
+    struct archive *a)
+{
+	if (wcscmp(v1, v2) == 0) {
+		msg[0] = '\0';
+		return;
+	}
+	if (previous_failures(file, line))
+		return;
+	fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n",
+	    file, line);
+	fwprintf(stderr, L"      %s = \"%ls\"\n", e1, v1);
+	fwprintf(stderr, L"      %s = \"%ls\"\n", e2, v2);
+	test_failed(a, line);
+}
+
+/*
+ * "list.h" is automatically generated; it just has a lot of lines like:
+ * 	DEFINE_TEST(function_name)
+ * The common "test.h" includes it to declare all of the test functions.
+ * We reuse it here to define a list of all tests to run.
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(n) { n, #n },
+struct { void (*func)(void); const char *name; } tests[] = {
+	#include "list.h"
+};
+
+static int test_run(int i, const char *tmpdir)
+{
+	int failures_before = failures;
+
+	if (!quiet_flag)
+		printf("%d: %s\n", i, tests[i].name);
+	/*
+	 * Always explicitly chdir() in case the last test moved us to
+	 * a strange place.
+	 */
+	if (chdir(tmpdir)) {
+		fprintf(stderr,
+		    "ERROR: Couldn't chdir to temp dir %s\n",
+		    tmpdir);
+		exit(1);
+	}
+	/* Create a temp directory for this specific test. */
+	if (mkdir(tests[i].name, 0755)) {
+		fprintf(stderr,
+		    "ERROR: Couldn't create temp dir ``%s''\n",
+		    tests[i].name);
+		exit(1);
+	}
+	if (chdir(tests[i].name)) {
+		fprintf(stderr,
+		    "ERROR: Couldn't chdir to temp dir ``%s''\n",
+		    tests[i].name);
+		exit(1);
+	}
+	(*tests[i].func)();
+	summarize(tests[i].name);
+	return (failures == failures_before ? 0 : 1);
+}
+
+static void usage(void)
+{
+	static const int limit = sizeof(tests) / sizeof(tests[0]);
+	int i;
+
+	printf("Usage: libarchive_test [options] <test> <test> ...\n");
+	printf("Default is to run all tests.\n");
+	printf("Otherwise, specify the numbers of the tests you wish to run.\n");
+	printf("Options:\n");
+	printf("  -k  Keep running after failures.\n");
+	printf("      Default: Core dump after any failure.\n");
+	printf("  -q  Quiet.\n");
+	printf("Available tests:\n");
+	for (i = 0; i < limit; i++)
+		printf("  %d: %s\n", i, tests[i].name);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	static const int limit = sizeof(tests) / sizeof(tests[0]);
+	int i, tests_run = 0, tests_failed = 0, opt;
+	time_t now;
+	char tmpdir[256];
+
+	while ((opt = getopt(argc, argv, "kq")) != -1) {
+		switch (opt) {
+		case 'k':
+			dump_on_failure = 0;
+			break;
+		case 'q':
+			quiet_flag = 1;
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	/*
+	 * Create a temp directory for the following tests.
+	 * Include the time the tests started as part of the name,
+	 * to make it easier to track the results of multiple tests.
+	 */
+	now = time(NULL);
+	for (i = 0; i < 1000; i++) {
+		strftime(tmpdir, sizeof(tmpdir),
+		    "/tmp/libarchive_test.%Y-%m-%dT%H.%M.%S",
+		    localtime(&now));
+		sprintf(tmpdir + strlen(tmpdir), "-%03d", i);
+		if (mkdir(tmpdir,0755) == 0)
+			break;
+		if (errno == EEXIST)
+			continue;
+		fprintf(stderr, "ERROR: Unable to create temp directory %s\n",
+		    tmpdir);
+		exit(1);
+	}
+
+	if (!quiet_flag) {
+		printf("Running libarchive tests in: %s\n", tmpdir);
+		printf("Exercising %s\n", archive_version());
+	}
+
+	if (argc == 0) {
+		/* Default: Run all tests. */
+		for (i = 0; i < limit; i++) {
+			if (test_run(i, tmpdir))
+				tests_failed++;
+			tests_run++;
+		}
+	} else {
+		while (*(argv) != NULL) {
+			i = atoi(*argv);
+			if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) {
+				printf("*** INVALID Test %s\n", *argv);
+				usage();
+			} else {
+				if (test_run(i, tmpdir))
+					tests_failed++;
+				tests_run++;
+			}
+			argv++;
+		}
+	}
+	printf("\n");
+	printf("%d of %d test groups reported failures\n",
+	    tests_failed, tests_run);
+	printf(" Total of %d individual tests failed.\n", failures);
+	printf(" Total of %d individual tests were skipped.\n", skips);
+	return (tests_failed);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_disk_perms.c
@@ -0,0 +1,450 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_perms.c,v 1.7 2007/08/12 17:35:05 kientzle Exp $");
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+
+#define UMASK 022
+
+static long _default_gid = -1;
+static long _invalid_gid = -1;
+static long _alt_gid = -1;
+
+/*
+ * To fully test SGID restores, we need three distinct GIDs to work
+ * with:
+ *    * the GID that files are created with by default (for the
+ *      current user in the current directory)
+ *    * An "alt gid" that this user can create files with
+ *    * An "invalid gid" that this user is not permitted to create
+ *      files with.
+ * The second fails if this user doesn't belong to at least two groups;
+ * the third fails if the current user is root.
+ */
+static void
+searchgid(void)
+{
+	static int   _searched = 0;
+	uid_t uid = getuid();
+	gid_t gid = 0;
+	unsigned int n;
+	struct stat st;
+	int fd;
+
+	/* If we've already looked this up, we're done. */
+	if (_searched)
+		return;
+	_searched = 1;
+
+	/* Create a file on disk in the current default dir. */
+	fd = open("test_gid", O_CREAT, 0664);
+	failure("Couldn't create a file for gid testing.");
+	assert(fd > 0);
+
+	/* See what GID it ended up with.  This is our "valid" GID. */
+	assert(fstat(fd, &st) == 0);
+	_default_gid = st.st_gid;
+
+	/* Find a GID for which fchown() fails.  This is our "invalid" GID. */
+	_invalid_gid = -1;
+	/* This loop stops when we wrap the gid or examine 10,000 gids. */
+	for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) {
+		if (fchown(fd, uid, gid) != 0) {
+			_invalid_gid = gid;
+			break;
+		}
+	}
+
+	/*
+	 * Find a GID for which fchown() succeeds, but which isn't the
+	 * default.  This is the "alternate" gid.
+	 */
+	_alt_gid = -1;
+	for (gid = 0, n = 0; gid == n && n < 10000 ; n++, gid++) {
+		/* _alt_gid must be different than _default_gid */
+		if (gid == (gid_t)_default_gid)
+			continue;
+		if (fchown(fd, uid, gid) == 0) {
+			_alt_gid = gid;
+			break;
+		}
+	}
+	close(fd);
+}
+
+static int
+altgid(void)
+{
+	searchgid();
+	return (_alt_gid);
+}
+
+static int
+invalidgid(void)
+{
+	searchgid();
+	return (_invalid_gid);
+}
+
+static int
+defaultgid(void)
+{
+	searchgid();
+	return (_default_gid);
+}
+#endif
+
+/*
+ * Exercise permission and ownership restores.
+ * In particular, try to exercise a bunch of border cases related
+ * to files/dirs that already exist, SUID/SGID bits, etc.
+ */
+
+DEFINE_TEST(test_write_disk_perms)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_write_disk interface");
+#else
+	struct archive *a;
+	struct archive_entry *ae;
+	struct stat st;
+
+	/*
+	 * Set ownership of the current directory to the group of this
+	 * process.  Otherwise, the SGID tests below fail if the
+	 * /tmp directory is owned by a group to which we don't belong
+	 * and we're on a system where group ownership is inherited.
+	 * (Because we're not allowed to SGID files with defaultgid().)
+	 */
+	assertEqualInt(0, chown(".", getuid(), getgid()));
+
+	/* Create an archive_write_disk object. */
+	assert((a = archive_write_disk_new()) != NULL);
+
+	/* Write a regular file to it. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file_0755");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+	archive_entry_free(ae);
+
+	/* Write a regular file, then write over it. */
+	/* For files, the perms should get updated. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file_overwrite_0144");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	assert(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+	/* Check that file was created with different perms. */
+	assert(0 == stat("file_overwrite_0144", &st));
+	failure("file_overwrite_0144: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) != 0144);
+	/* Overwrite, this should change the perms. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file_overwrite_0144");
+	archive_entry_set_mode(ae, S_IFREG | 0144);
+	assert(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Write a regular dir. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir_0514");
+	archive_entry_set_mode(ae, S_IFDIR | 0514);
+	assert(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Overwrite an existing dir. */
+	/* For dir, the first perms should get left. */
+	assert(mkdir("dir_overwrite_0744", 0744) == 0);
+	/* Check original perms. */
+	assert(0 == stat("dir_overwrite_0744", &st));
+	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0744);
+	/* Overwrite shouldn't edit perms. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir_overwrite_0744");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assert(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+	/* Make sure they're unchanged. */
+	assert(0 == stat("dir_overwrite_0744", &st));
+	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0744);
+
+	/* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file_no_suid");
+	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0777);
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Write a regular file with ARCHIVE_EXTRACT_PERM. */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "file_0777");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "file_4742");
+	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
+	archive_entry_set_uid(ae, getuid());
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/*
+	 * Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit,
+	 * but wrong uid.  POSIX says you shouldn't restore SUID bit
+	 * unless the UID could be restored.
+	 */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "file_bad_suid");
+	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
+	archive_entry_set_uid(ae, getuid() + 1);
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+	assertA(0 == archive_write_header(a, ae));
+	/*
+	 * Because we didn't ask for owner, the failure to
+	 * restore SUID shouldn't return a failure.
+	 * We check below to make sure SUID really wasn't set.
+	 * See more detailed comments below.
+	 */
+	failure("Opportunistic SUID failure shouldn't return error.");
+	assertEqualInt(0, archive_write_finish_entry(a));
+
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "file_bad_suid2");
+	archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742);
+	archive_entry_set_uid(ae, getuid() + 1);
+	archive_write_disk_set_options(a,
+	    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
+	assertA(0 == archive_write_header(a, ae));
+	/* Owner change should fail here. */
+	failure("Non-opportunistic SUID failure should return error.");
+	assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a));
+
+	/* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "file_perm_sgid");
+	archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
+	archive_entry_set_gid(ae, defaultgid());
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+	assert(0 == archive_write_header(a, ae));
+	failure("Setting SGID bit should succeed here.");
+	assertEqualIntA(a, 0, archive_write_finish_entry(a));
+
+	if (altgid() == -1) {
+		/*
+		 * Current user must belong to at least two groups or
+		 * else we can't test setting the GID to another group.
+		 */
+		printf("Current user can't test gid restore: must belong to more than one group.\n");
+	} else {
+		/*
+		 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit
+		 * but without ARCHIVE_EXTRACT_OWNER.
+		 */
+		/*
+		 * This is a weird case: The user has asked for permissions to
+		 * be restored but not asked for ownership to be restored.  As
+		 * a result, the default file creation will create a file with
+		 * the wrong group.  There are several possible behaviors for
+		 * libarchive in this scenario:
+		 *  = Set the SGID bit.  It is wrong and a security hole to
+		 *    set SGID with the wrong group.  Even POSIX thinks so.
+		 *  = Implicitly set the group.  I don't like this.
+		 *  = drop the SGID bit and warn (the old libarchive behavior)
+		 *  = drop the SGID bit and don't warn (the current libarchive
+		 *    behavior).
+		 * The current behavior sees SGID/SUID restore when you
+		 * don't ask for owner restore as an "opportunistic"
+		 * action.  That is, libarchive should do it if it can,
+		 * but if it can't, it's not an error.
+		 */
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_alt_sgid");
+		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
+		archive_entry_set_uid(ae, getuid());
+		archive_entry_set_gid(ae, altgid());
+		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+		assert(0 == archive_write_header(a, ae));
+		failure("Setting SGID bit should fail because of group mismatch but the failure should be silent because we didn't ask for the group to be set.");
+		assertEqualIntA(a, 0, archive_write_finish_entry(a));
+
+		/*
+		 * As above, but add _EXTRACT_OWNER to verify that it
+		 * does succeed.
+		 */
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_alt_sgid_owner");
+		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
+		archive_entry_set_uid(ae, getuid());
+		archive_entry_set_gid(ae, altgid());
+		archive_write_disk_set_options(a,
+		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
+		assert(0 == archive_write_header(a, ae));
+		failure("Setting SGID bit should succeed here.");
+		assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
+	}
+
+	/*
+	 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit,
+	 * but wrong GID.  POSIX says you shouldn't restore SGID bit
+	 * unless the GID could be restored.
+	 */
+	if (invalidgid() == -1) {
+		/* This test always fails for root. */
+		printf("Running as root: Can't test SGID failures.\n");
+	} else {
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_bad_sgid");
+		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
+		archive_entry_set_gid(ae, invalidgid());
+		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM);
+		assertA(0 == archive_write_header(a, ae));
+		failure("This SGID restore should fail without an error.");
+		assertEqualIntA(a, 0, archive_write_finish_entry(a));
+
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_bad_sgid2");
+		archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742);
+		archive_entry_set_gid(ae, invalidgid());
+		archive_write_disk_set_options(a,
+		    ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER);
+		assertA(0 == archive_write_header(a, ae));
+		failure("This SGID restore should fail with an error.");
+		assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a));
+	}
+
+	/* Set ownership should fail if we're not root. */
+	if (getuid() == 0) {
+		printf("Running as root: Can't test setuid failures.\n");
+	} else {
+		assert(archive_entry_clear(ae) != NULL);
+		archive_entry_copy_pathname(ae, "file_bad_owner");
+		archive_entry_set_mode(ae, S_IFREG | 0744);
+		archive_entry_set_uid(ae, getuid() + 1);
+		archive_write_disk_set_options(a, ARCHIVE_EXTRACT_OWNER);
+		assertA(0 == archive_write_header(a, ae));
+		assertEqualIntA(a,ARCHIVE_WARN,archive_write_finish_entry(a));
+	}
+
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+	archive_entry_free(ae);
+
+	/* Test the entries on disk. */
+	assert(0 == stat("file_0755", &st));
+	failure("file_0755: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+
+	assert(0 == stat("file_overwrite_0144", &st));
+	failure("file_overwrite_0144: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0144);
+
+	assert(0 == stat("dir_0514", &st));
+	failure("dir_0514: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0514);
+
+	assert(0 == stat("dir_overwrite_0744", &st));
+	failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0744);
+
+	assert(0 == stat("file_no_suid", &st));
+	failure("file_0755: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+
+	assert(0 == stat("file_0777", &st));
+	failure("file_0777: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0777);
+
+	/* SUID bit should get set here. */
+	assert(0 == stat("file_4742", &st));
+	failure("file_4742: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == (S_ISUID | 0742));
+
+	/* SUID bit should NOT have been set here. */
+	assert(0 == stat("file_bad_suid", &st));
+	failure("file_bad_suid: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == (0742));
+
+	/* SUID bit should NOT have been set here. */
+	assert(0 == stat("file_bad_suid2", &st));
+	failure("file_bad_suid2: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == (0742));
+
+	/* SGID should be set here. */
+	assert(0 == stat("file_perm_sgid", &st));
+	failure("file_perm_sgid: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == (S_ISGID | 0742));
+
+	if (altgid() != -1) {
+		/* SGID should not be set here. */
+		assert(0 == stat("file_alt_sgid", &st));
+		failure("file_alt_sgid: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (0742));
+
+		/* SGID should be set here. */
+		assert(0 == stat("file_alt_sgid_owner", &st));
+		failure("file_alt_sgid: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (S_ISGID | 0742));
+	}
+
+	if (invalidgid() != -1) {
+		/* SGID should NOT be set here. */
+		assert(0 == stat("file_bad_sgid", &st));
+		failure("file_bad_sgid: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (0742));
+		/* SGID should NOT be set here. */
+		assert(0 == stat("file_bad_sgid2", &st));
+		failure("file_bad_sgid2: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (0742));
+	}
+
+	if (getuid() != 0) {
+		assert(0 == stat("file_bad_owner", &st));
+		failure("file_bad_owner: st.st_mode=%o", st.st_mode);
+		assert((st.st_mode & 07777) == (0744));
+		failure("file_bad_owner: st.st_uid=%d getuid()=%d",
+		    st.st_uid, getuid());
+		/* The entry had getuid()+1, but because we're
+		 * not root, we should not have been able to set that. */
+		assert(st.st_uid == getuid());
+	}
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_isorr_bz2.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_isorr_bz2.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+
+/*
+Execute the following to rebuild the data for this program:
+   tail -n +5 test-read_format-isorr_bz2.c | /bin/sh
+
+rm -rf /tmp/iso
+mkdir /tmp/iso
+mkdir /tmp/iso/dir
+echo "hello" >/tmp/iso/file
+ln /tmp/iso/file /tmp/iso/hardlink
+(cd /tmp/iso; ln -s file symlink)
+TZ=utc touch -afhm -t 197001010000.01 /tmp/iso /tmp/iso/file /tmp/iso/dir
+TZ=utc touch -afhm -t 196912312359.58 /tmp/iso/symlink
+mkhybrid -R -uid 1 -gid 2 /tmp/iso | bzip2 > data.iso.bz2
+cat data.iso.bz2 | ./maketest.pl > data.c
+exit 1
+ */
+
+static unsigned char archive[] = {
+'B','Z','h','9','1','A','Y','&','S','Y','G',11,4,'c',0,0,199,255,221,255,
+255,203,252,221,'c',251,248,'?',255,223,224,167,255,222,'&','!',234,'$',0,
+'0',1,' ',0,'D',2,129,8,192,3,14,'2','3','$',19,184,'J',' ','F',168,244,201,
+149,'6','Q',226,155,'S',212,209,160,'h','4','i',160,26,13,0,244,134,212,0,
+218,'O',212,153,1,144,244,128,148,' ',147,13,' ',213,'=','1','\'',169,166,
+128,'=','!',233,0,208,0,26,0,0,30,160,'h',0,'4','z',130,180,163,'@',0,0,4,
+211,0,0,0,2,'b','`',0,0,0,0,0,8,146,133,'F',154,'y','A',163,'A',161,163,'@',
+'z',134,'C','C','F',131,'F','@',0,0,0,0,6,154,26,'Q',24,234,180,'P',172,251,
+'=',2,'P','H','&','Y','o',130,28,'"',229,210,247,227,248,200,'?','6',161,
+'?',170,'H',172,'"','H','I',16,'2','"','&',148,'G',133,'T','z',224,1,215,
+' ',0,191,184,10,160,24,248,180,183,244,156,'K',202,133,208,'U',5,'6','C',
+26,144,'H',168,'H','H','(','"',151,'@','m',223,'(','P',169,'e',145,148,'6',
+237,235,7,227,204,']','k','{',241,187,227,244,251,':','a','L',138,'#','R',
+'"',221,'_',239,')',140,'*','*',172,'Q',16,1,16,207,166,251,233,'Z',169,'4',
+'_',195,'a',14,18,231,'}',14,139,137,'e',213,185,'T',194,'D','`',25,'$',187,
+208,'%','c',162,'~',181,'@',204,'2',238,'P',161,213,127,'I',169,3,' ','o',
+6,161,16,128,'F',214,'S','m',6,244,11,229,'Z','y','.',176,'q',' ',248,167,
+204,26,193,'q',211,241,214,133,221,212,'I','`',28,244,'N','N','f','H','9',
+'w',245,209,'*',20,26,208,'h','(',194,156,192,'l',';',192,'X','T',151,177,
+209,'0',156,16,'=',20,'k',184,144,'z',26,'j',133,194,'9',227,'<','[','^',
+17,'w','p',225,220,248,'>',205,'>','[',19,'5',155,17,175,28,28,168,175,'n',
+'\'','c','w',27,222,204,'k','n','x','I',23,237,'c',145,11,184,'A','(',1,169,
+'0',180,189,134,'\\','Y','x',187,'C',151,'d','k','y','-','L',218,138,'s',
+'*','(',12,'h',242,'*',17,'E','L',202,146,138,'l','0',217,160,'9','.','S',
+214,198,143,'3','&',237,'=','t','P',168,214,210,'`','p','J',181,'H',138,149,
+'1','B',206,22,164,'[','O','A',172,134,224,179,219,166,184,'X',185,'W',154,
+219,19,161,'Y',184,220,237,147,'9',191,237,'&','i','_',226,146,205,160,'@',
+'b',182,';',3,'!',183,'J','t',161,160,178,173,'S',235,':','2',159,':',245,
+'{','U',174,'P',142,'G','(',')',9,168,185,'A','U',231,193,'g',213,'e',12,
+'X',223,22,249,')',152,237,'G',150,156,3,201,245,212,'2',218,209,177,196,
+235,'_','~',137,24,31,196,232,'B',172,'w',159,24,'n',156,150,225,'1','y',
+22,'#',138,193,227,232,169,170,166,179,1,11,182,'i',')',160,180,198,175,128,
+249,167,5,194,142,183,'f',134,206,180,'&','E','!','[',31,195,':',192,'s',
+232,187,'N',131,'Y',137,243,15,'y',12,'J',163,'-',242,'5',197,151,130,163,
+240,220,'T',161,'L',159,141,159,152,'4',18,128,'.','^',250,168,200,163,'P',
+231,'Y','w','F','U',186,'x',190,16,'0',228,22,'9','F','t',168,157,'i',190,
+'+',246,141,142,18,' ','M',174,197,'O',165,'m',224,27,'b',150,'|','W','H',
+196,'.','*','Q','$',225,'I','-',148,169,'F',7,197,'m','-',130,153,0,158,21,
+'(',221,221,226,206,'g',13,159,163,'y',176,'~',158,'k','4','q','d','s',177,
+'7',14,217,'1',173,206,228,'t',250,200,170,162,'d','2','Z','$','e',168,224,
+223,129,174,229,165,187,252,203,'-',28,'`',207,183,'-','/',127,196,230,131,
+'B',30,237,' ',8,26,194,'O',132,'L','K','\\',144,'L','c',1,10,176,192,'c',
+0,244,2,168,3,0,'+',233,186,16,17,'P',17,129,252,'2',0,2,154,247,255,166,
+'.',228,138,'p',161,' ',142,22,8,198};
+
+DEFINE_TEST(test_read_format_isorr_bz2)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	const void *p;
+	size_t size;
+	off_t offset;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+
+	/* First entry is '.' root directory. */
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString(".", archive_entry_pathname(ae));
+	assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
+	assertEqualInt(2048, archive_entry_size(ae));
+	assertEqualInt(1, archive_entry_mtime(ae));
+	assertEqualInt(0, archive_entry_mtime_nsec(ae));
+	assertEqualInt(1, archive_entry_ctime(ae));
+	assertEqualInt(0, archive_entry_stat(ae)->st_nlink);
+	assertEqualInt(0, archive_entry_uid(ae));
+
+	/* A directory. */
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("dir", archive_entry_pathname(ae));
+	assert(S_ISDIR(archive_entry_stat(ae)->st_mode));
+	assert(2048 == archive_entry_size(ae));
+	assert(1 == archive_entry_mtime(ae));
+	assert(1 == archive_entry_atime(ae));
+	assert(2 == archive_entry_stat(ae)->st_nlink);
+	assert(1 == archive_entry_uid(ae));
+	assert(2 == archive_entry_gid(ae));
+
+	/* A regular file. */
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("file", archive_entry_pathname(ae));
+	assert(S_ISREG(archive_entry_stat(ae)->st_mode));
+	assert(6 == archive_entry_size(ae));
+	assert(0 == archive_read_data_block(a, &p, &size, &offset));
+	assert(6 == size);
+	assert(0 == offset);
+	assert(0 == memcmp(p, "hello\n", 6));
+	assert(1 == archive_entry_mtime(ae));
+	assert(1 == archive_entry_atime(ae));
+	assert(2 == archive_entry_stat(ae)->st_nlink);
+	assert(1 == archive_entry_uid(ae));
+	assert(2 == archive_entry_gid(ae));
+
+	/* A hardlink to the regular file. */
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("hardlink", archive_entry_pathname(ae));
+	assert(S_ISREG(archive_entry_stat(ae)->st_mode));
+	assertEqualString("file", archive_entry_hardlink(ae));
+	assert(6 == archive_entry_size(ae));
+	assert(1 == archive_entry_mtime(ae));
+	assert(1 == archive_entry_atime(ae));
+	assert(2 == archive_entry_stat(ae)->st_nlink);
+	assert(1 == archive_entry_uid(ae));
+	assert(2 == archive_entry_gid(ae));
+
+	/* A symlink to the regular file. */
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("symlink", archive_entry_pathname(ae));
+	assert(S_ISLNK(archive_entry_stat(ae)->st_mode));
+	assertEqualString("file", archive_entry_symlink(ae));
+	assert(0 == archive_entry_size(ae));
+	assert(-2 == archive_entry_mtime(ae));
+	assert(-2 == archive_entry_atime(ae));
+	assert(1 == archive_entry_stat(ae)->st_nlink);
+	assert(1 == archive_entry_uid(ae));
+	assert(2 == archive_entry_gid(ae));
+
+	/* End of archive. */
+	assert(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+
+	/* Verify archive format. */
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_BZIP2);
+	assert(archive_format(a) == ARCHIVE_FORMAT_ISO9660_ROCKRIDGE);
+
+	/* Close the archive. */
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_svr4c_Z.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_svr4c_Z.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,157,144,'0','n',4,132,'!',3,6,140,26,'8','n',228,16,19,195,160,'A',26,
+'1',202,144,'q','h','p','F',25,28,20,'a','X',196,152,145,' ',141,25,2,'k',
+192,160,'A',163,163,201,135,29,'c',136,'<',201,'2','c','A',147,'.',0,12,20,
+248,178,165,205,155,20,27,226,220,201,243,166,152,147,'T',164,4,'I',194,164,
+136,148,16,'H',1,'(',']',202,180,169,211,167,'P',163,'J',157,'J',181,170,
+213,171,'X',179,'j',221,202,181,171,215,175,'L',1};
+
+DEFINE_TEST(test_read_format_cpio_svr4c_Z)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+/*	printf("Archive address: start=%X, end=%X\n", archive, archive+sizeof(archive)); */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_COMPRESS);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_SVR4_CRC);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_bin_gz.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_bin_gz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,0,244,'M','p','C',0,3,';','^','(',202,178,177,242,173,227,11,230,
+23,204,'L',12,12,12,5,206,'_','|','A','4',3,131,30,195,241,'B',6,'8','`',
+132,210,220,'`','2','$',200,209,211,199,'5','H','Q','Q',145,'a',20,12,'i',
+0,0,170,199,228,195,0,2,0,0};
+
+DEFINE_TEST(test_read_format_cpio_bin_gz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_GZIP);
+	assert(archive_format(a) == ARCHIVE_FORMAT_CPIO_BIN_LE);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_truncated.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_truncated.c,v 1.3 2007/05/29 01:00:21 kientzle Exp $");
+
+char buff[1000000];
+char buff2[100000];
+
+DEFINE_TEST(test_read_truncated)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	unsigned int i;
+	size_t used;
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	for (i = 0; i < sizeof(buff2); i++)
+		buff2[i] = (unsigned char)rand();
+	archive_entry_set_size(ae, sizeof(buff2));
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(sizeof(buff2) == archive_write_data(a, buff2, sizeof(buff2)));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Now, read back a truncated version of the archive and
+	 * verify that we get an appropriate error. */
+	for (i = 1; i < used + 100; i += 100) {
+		assert((a = archive_read_new()) != NULL);
+		assertA(0 == archive_read_support_format_all(a));
+		assertA(0 == archive_read_support_compression_all(a));
+		assertA(0 == archive_read_open_memory(a, buff, i));
+
+		if (i < 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+			goto wrap_up;
+		} else {
+			assertA(0 == archive_read_next_header(a, &ae));
+		}
+
+		if (i < 512 + sizeof(buff2)) {
+			assertA(ARCHIVE_FATAL == archive_read_data(a, buff2, sizeof(buff2)));
+			goto wrap_up;
+		} else {
+			assertA(sizeof(buff2) == archive_read_data(a, buff2, sizeof(buff2)));
+		}
+
+		/* Verify the end of the archive. */
+		/* Archive must be long enough to capture a 512-byte
+		 * block of zeroes after the entry.  (POSIX requires a
+		 * second block of zeros to be written but libarchive
+		 * does not return an error if it can't consume
+		 * it.) */
+		if (i < 512 + 512*((sizeof(buff2) + 511)/512) + 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+		} else {
+			assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+		}
+	wrap_up:
+		assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_read_finish(a));
+#else
+		archive_read_finish(a);
+#endif
+	}
+
+
+
+	/* Same as above, except skip the body instead of reading it. */
+	for (i = 1; i < used + 100; i += 100) {
+		assert((a = archive_read_new()) != NULL);
+		assertA(0 == archive_read_support_format_all(a));
+		assertA(0 == archive_read_support_compression_all(a));
+		assertA(0 == archive_read_open_memory(a, buff, i));
+
+		if (i < 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+			goto wrap_up2;
+		} else {
+			assertA(0 == archive_read_next_header(a, &ae));
+		}
+
+		if (i < 512 + 512*((sizeof(buff2)+511)/512)) {
+			assertA(ARCHIVE_FATAL == archive_read_data_skip(a));
+			goto wrap_up2;
+		} else {
+			assertA(ARCHIVE_OK == archive_read_data_skip(a));
+		}
+
+		/* Verify the end of the archive. */
+		/* Archive must be long enough to capture a 512-byte
+		 * block of zeroes after the entry.  (POSIX requires a
+		 * second block of zeros to be written but libarchive
+		 * does not return an error if it can't consume
+		 * it.) */
+		if (i < 512 + 512*((sizeof(buff2) + 511)/512) + 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+		} else {
+			assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+		}
+	wrap_up2:
+		assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_read_finish(a));
+#else
+		archive_read_finish(a);
+#endif
+	}
+}
--- /dev/null
+++ lib/libarchive/test/Makefile
@@ -0,0 +1,83 @@
+# $FreeBSD: src/lib/libarchive/test/Makefile,v 1.11 2007/07/13 15:14:35 kientzle Exp $
+
+TESTS= \
+	test_acl_basic.c			\
+	test_acl_pax.c				\
+	test_archive_api_feature.c		\
+	test_bad_fd.c				\
+	test_entry.c				\
+	test_read_compress_program.c		\
+	test_read_data_large.c			\
+	test_read_extract.c			\
+	test_read_format_ar.c			\
+	test_read_format_cpio_bin.c		\
+	test_read_format_cpio_bin_Z.c		\
+	test_read_format_cpio_bin_bz2.c		\
+	test_read_format_cpio_bin_gz.c		\
+	test_read_format_cpio_odc.c		\
+	test_read_format_cpio_svr4_gzip.c	\
+	test_read_format_cpio_svr4c_Z.c		\
+	test_read_format_empty.c		\
+	test_read_format_gtar_gz.c		\
+	test_read_format_gtar_sparse.c		\
+	test_read_format_iso_gz.c		\
+	test_read_format_isorr_bz2.c		\
+	test_read_format_pax_bz2.c		\
+	test_read_format_tar.c			\
+	test_read_format_tbz.c			\
+	test_read_format_tgz.c			\
+	test_read_format_tz.c			\
+	test_read_format_zip.c			\
+	test_read_large.c			\
+	test_read_pax_truncated.c		\
+	test_read_position.c			\
+	test_read_truncated.c			\
+	test_tar_filenames.c			\
+	test_write_compress_program.c		\
+	test_write_disk.c			\
+	test_write_disk_perms.c			\
+	test_write_disk_secure.c		\
+	test_write_format_ar.c			\
+	test_write_format_cpio.c		\
+	test_write_format_cpio_empty.c		\
+	test_write_format_shar_empty.c		\
+	test_write_format_tar.c			\
+	test_write_format_tar_empty.c		\
+	test_write_open_memory.c
+
+SRCS= ${TESTS}					\
+	list.h					\
+	main.c					\
+	read_open_memory.c
+
+CLEANFILES+= list.h
+
+NO_MAN=yes
+
+PROG=libarchive_test
+DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
+LDADD= -larchive -lz -lbz2
+CFLAGS+= -static -g
+CFLAGS+= -I${.OBJDIR}
+
+# Uncomment to link against dmalloc
+#LDADD+= -L/usr/local/lib -ldmalloc
+#CFLAGS+= -I/usr/local/include -DUSE_DMALLOC
+#WARNS=6
+
+test:	libarchive_test
+	./libarchive_test -k
+
+list.h: ${TESTS} Makefile
+	(cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h
+
+clean:
+	rm -f *.out
+	rm -f *.o
+	rm -f *.core
+	rm -f *~
+	rm -f list.h
+	-chmod -R +w /tmp/libarchive_test.*
+	rm -rf /tmp/libarchive_test.*
+
+.include <bsd.prog.mk>
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_svr4_gzip.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_svr4_gzip.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,0,236,'c',217,'D',0,3,'3','0','7','0','7','0','4','0','0',181,'0',
+183,'L',2,210,6,6,'&',134,169,')',' ',218,192,'8',213,2,133,'6','0','0','2',
+'1','6','7','0','5','0','N','6','@',5,'&',16,202,208,212,0,';','0',130,'1',
+244,24,12,160,246,17,5,136,'U',135,14,146,'`',140,144,' ','G','O',31,215,
+' ','E','E','E',134,'Q',128,21,0,0,'%',215,202,221,0,2,0,0};
+
+DEFINE_TEST(test_read_format_cpio_svr4_gzip)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_GZIP);
+	assert(archive_format(a) == ARCHIVE_FORMAT_CPIO_SVR4_NOCRC);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_position.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_position.c,v 1.3 2007/05/29 01:00:21 kientzle Exp $");
+
+static unsigned char nulls[10000000];
+static unsigned char buff[10000000];
+
+/* Check that header_position tracks correctly on read. */
+DEFINE_TEST(test_read_position)
+{
+	struct archive *a;
+	struct archive_entry *ae;
+	size_t write_pos;
+	const size_t data_size = 1000000;
+
+	/* Create a simple archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_pathname(ae, "testfile");
+	archive_entry_set_mode(ae, S_IFREG);
+	archive_entry_set_size(ae, data_size);
+
+	assert(NULL != (a = archive_write_new()));
+	assertA(0 == archive_write_set_format_pax_restricted(a));
+	assertA(0 == archive_write_set_bytes_per_block(a, 512));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &write_pos));
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(data_size == (size_t)archive_write_data(a, nulls, sizeof(nulls)));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	assertA(0 == archive_write_close(a));
+	archive_write_finish(a);
+#endif
+	/* 512-byte header + data_size (rounded up) + 1024 end-of-archive */
+	assert(write_pos == ((512 + data_size + 1024 + 511)/512)*512);
+
+	/* Read the archive back. */
+	assert(NULL != (a = archive_read_new()));
+	assertA(0 == archive_read_support_format_tar(a));
+	assertA(0 == archive_read_open_memory2(a, buff, sizeof(buff), 512));
+	assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
+	assertA(0 == archive_read_data_skip(a));
+	assert((intmax_t)0 == (intmax_t)archive_read_header_position(a));
+	assertA(1 == archive_read_next_header(a, &ae));
+	assert((intmax_t)((data_size + 511 + 512)/512)*512 == (intmax_t)archive_read_header_position(a));
+	assertA(0 == archive_read_close(a));
+	assert((intmax_t)((data_size + 511 + 512)/512)*512 == (intmax_t)archive_read_header_position(a));
+	archive_read_finish(a);
+}
--- /dev/null
+++ lib/libarchive/test/test_tar_filenames.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_tar_filenames.c,v 1.7 2007/07/14 17:52:01 kientzle Exp $");
+
+/*
+ * Exercise various lengths of filenames in tar archives,
+ * especially around the magic sizes where ustar breaks
+ * filenames into prefix/suffix.
+ */
+
+static void
+test_filename(const char *prefix, int dlen, int flen)
+{
+	char buff[8192];
+	char filename[400];
+	char dirname[400];
+	struct archive_entry *ae;
+	struct archive *a;
+	size_t used;
+	size_t prefix_length = 0;
+	int i = 0;
+
+	if (prefix) {
+		strcpy(filename, prefix);
+		i = prefix_length = strlen(prefix);
+	}
+	for (; i < prefix_length + dlen; i++)
+		filename[i] = 'a';
+	filename[i++] = '/';
+	for (; i < prefix_length + dlen + flen + 1; i++)
+		filename[i] = 'b';
+	filename[i++] = '\0';
+
+	strcpy(dirname, filename);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_pax_restricted(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a,0));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, filename);
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	failure("Pathname %d/%d", dlen, flen);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/*
+	 * Write a dir to it (without trailing '/').
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, dirname);
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	failure("Dirname %d/%d", dlen, flen);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Tar adds a '/' to directory names. */
+	strcat(dirname, "/");
+
+	/*
+	 * Write a dir to it (with trailing '/').
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, dirname);
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	failure("Dirname %d/%d", dlen, flen);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Now, read the data back.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	/* Read the file and check the filename. */
+	assertA(0 == archive_read_next_header(a, &ae));
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("Leading '/' preserved on long filenames");
+#else
+	assertEqualString(filename, archive_entry_pathname(ae));
+#endif
+	assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae));
+
+	/*
+	 * Read the two dirs and check the names.
+	 *
+	 * Both dirs should read back with the same name, since
+	 * tar should add a trailing '/' to any dir that doesn't
+	 * already have one.  We only report the first such failure
+	 * here.
+	 */
+	assertA(0 == archive_read_next_header(a, &ae));
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("Trailing '/' preserved on dirnames");
+#else
+	assertEqualString(dirname, archive_entry_pathname(ae));
+#endif
+	assert((S_IFDIR | 0755) == archive_entry_mode(ae));
+
+	assertA(0 == archive_read_next_header(a, &ae));
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("Trailing '/' added to dir names");
+#else
+	assertEqualString(dirname, archive_entry_pathname(ae));
+#endif
+	assert((S_IFDIR | 0755) == archive_entry_mode(ae));
+
+	/* Verify the end of the archive. */
+	assert(1 == archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+DEFINE_TEST(test_tar_filenames)
+{
+	int dlen, flen;
+
+	/* Repeat the following for a variety of dir/file lengths. */
+	for (dlen = 40; dlen < 60; dlen++) {
+		for (flen = 40; flen < 60; flen++) {
+			test_filename(NULL, dlen, flen);
+			test_filename("/", dlen, flen);
+		}
+	}
+
+	for (dlen = 140; dlen < 160; dlen++) {
+		for (flen = 90; flen < 110; flen++) {
+			test_filename(NULL, dlen, flen);
+			test_filename("/", dlen, flen);
+		}
+	}
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_tgz.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tgz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,0,222,'C','p','C',0,3,211,'c',160,'=','0','0','0','0','7','5','U',
+0,210,134,230,166,6,200,'4',28,'(',24,26,24,27,155,24,152,24,154,27,155,')',
+24,24,26,152,154,25,'2','(',152,210,193,'m',12,165,197,'%',137,'E','@',167,
+148,'d',230,226,'U','G','H',30,234,15,'8','=',10,'F',193,'(',24,5,131,28,
+0,0,29,172,5,240,0,6,0,0};
+
+DEFINE_TEST(test_read_format_tgz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_GZIP);
+	assert(archive_format(a) == ARCHIVE_FORMAT_TAR_USTAR);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_large.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_large.c,v 1.3 2007/05/29 01:00:21 kientzle Exp $");
+
+static unsigned char testdata[10 * 1024 * 1024];
+static unsigned char testdatacopy[10 * 1024 * 1024];
+static unsigned char buff[11 * 1024 * 1024];
+
+/* Check correct behavior on large reads. */
+DEFINE_TEST(test_read_large)
+{
+	unsigned int i;
+	int tmpfilefd;
+	char tmpfilename[] = "/tmp/test-read_large.XXXXXX";
+	size_t used;
+	struct archive *a;
+	struct archive_entry *entry;
+
+	for (i = 0; i < sizeof(testdata); i++)
+		testdata[i] = (unsigned char)(rand());
+
+	assert(NULL != (a = archive_write_new()));
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+	assert(NULL != (entry = archive_entry_new()));
+	archive_entry_set_size(entry, sizeof(testdata));
+	archive_entry_set_mode(entry, S_IFREG | 0777);
+	archive_entry_set_pathname(entry, "test");
+	assertA(0 == archive_write_header(a, entry));
+	archive_entry_free(entry);
+	assertA(sizeof(testdata) == archive_write_data(a, testdata, sizeof(testdata)));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	assert(NULL != (a = archive_read_new()));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, sizeof(buff)));
+	assertA(0 == archive_read_next_header(a, &entry));
+	assertA(0 == archive_read_data_into_buffer(a, testdatacopy, sizeof(testdatacopy)));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+	assert(0 == memcmp(testdata, testdatacopy, sizeof(testdata)));
+
+
+	assert(NULL != (a = archive_read_new()));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, sizeof(buff)));
+	assertA(0 == archive_read_next_header(a, &entry));
+	assert(0 < (tmpfilefd = mkstemp(tmpfilename)));
+	assertA(0 == archive_read_data_into_fd(a, tmpfilefd));
+	close(tmpfilefd);
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+	tmpfilefd = open(tmpfilename, O_RDONLY);
+	read(tmpfilefd, testdatacopy, sizeof(testdatacopy));
+	close(tmpfilefd);
+	assert(0 == memcmp(testdata, testdatacopy, sizeof(testdata)));
+
+	unlink(tmpfilename);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_odc.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_odc.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+'0','7','0','7','0','7','0','0','2','0','2','5','0','7','4','6','6','1','0',
+'4','0','7','5','5','0','0','1','7','5','0','0','0','1','7','5','0','0','0',
+'0','0','0','2','0','0','0','0','0','0','1','0','3','3','4','0','5','0','0',
+'5','3','0','0','0','0','0','2','0','0','0','0','0','0','0','0','0','0','0',
+'.',0,'0','7','0','7','0','7','0','0','0','0','0','0','0','0','0','0','0',
+'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0',
+'0','0','0','0','0','1','0','0','0','0','0','0','0','0','0','0','0','0','0',
+'0','0','0','0','0','0','0','0','1','3','0','0','0','0','0','0','0','0','0',
+'0','0','T','R','A','I','L','E','R','!','!','!',0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0};
+
+DEFINE_TEST(test_read_format_cpio_odc)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_POSIX);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_data_large.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_data_large.c,v 1.3 2007/05/29 01:00:20 kientzle Exp $");
+
+/*
+ * Test read/write of a 10M block of data in a single operation.
+ * Uses an in-memory archive with a single 10M entry.  Exercises
+ * archive_read_data() to ensure it can handle large blocks like
+ * this and also exercises archive_read_data_into_fd() (which
+ * had a bug relating to this, fixed in Nov 2006).
+ */
+
+char buff1[11000000];
+char buff2[10000000];
+char buff3[10000000];
+
+DEFINE_TEST(test_read_data_large)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	char tmpfilename[] = "largefile";
+	int tmpfilefd;
+	unsigned int i;
+	size_t used;
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff1, sizeof(buff1), &used));
+
+	/*
+	 * Write a file (with random contents) to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	for (i = 0; i < sizeof(buff2); i++)
+		buff2[i] = (unsigned char)rand();
+	archive_entry_set_size(ae, sizeof(buff2));
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(sizeof(buff2) == archive_write_data(a, buff2, sizeof(buff2)));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Check that archive_read_data can handle 10*10^6 at a pop. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff1, sizeof(buff1)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	failure("Wrote 10MB, but didn't read the same amount");
+	assertEqualIntA(a, sizeof(buff2),archive_read_data(a, buff3, sizeof(buff3)));
+	failure("Read expected 10MB, but data read didn't match what was written");
+	assert(0 == memcmp(buff2, buff3, sizeof(buff3)));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Check archive_read_data_into_fd */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff1, sizeof(buff1)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	tmpfilefd = open(tmpfilename, O_WRONLY | O_CREAT, 0777);
+	assert(tmpfilefd != 0);
+	assertEqualIntA(a, 0, archive_read_data_into_fd(a, tmpfilefd));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+	close(tmpfilefd);
+
+	tmpfilefd = open(tmpfilename, O_RDONLY);
+	assert(tmpfilefd != 0);
+	assertEqualIntA(NULL, sizeof(buff3), read(tmpfilefd, buff3, sizeof(buff3)));
+	close(tmpfilefd);
+	assert(0 == memcmp(buff2, buff3, sizeof(buff3)));
+
+	unlink(tmpfilename);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_format_cpio_empty.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_cpio_empty.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+
+/*
+ * Check that an "empty" cpio archive is correctly created.
+ */
+
+/* Here's what an empty cpio archive should look like. */
+static char ref[] =
+"070707"  /* Magic number */
+"000000"  /* Dev = 0 */
+"000000"  /* ino = 0 */
+"000000"  /* mode = 0 */
+"000000"  /* uid = 0 */
+"000000"  /* gid = 0 */
+"000001"  /* nlink = 1 */
+"000000"  /* rdev = 0 */
+"00000000000" /* mtime = 0 */
+"000013"  /* Namesize = 11 */
+"00000000000" /* filesize = 0 */
+"TRAILER!!!\0"; /* Name */
+
+DEFINE_TEST(test_write_format_cpio_empty)
+{
+	struct archive *a;
+	char buff[2048];
+	size_t used;
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_cpio(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	/* 1-byte block size ensures we see only the required bytes. */
+	/* We're not testing the padding here. */
+	assertA(0 == archive_write_set_bytes_per_block(a, 1));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	failure("Empty cpio archive should be exactly 87 bytes, was %d.", used);
+	assert(used == 87);
+	failure("Empty cpio archive is incorrectly formatted.");
+	assert(memcmp(buff, ref, 87) == 0);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_bin_Z.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_bin_Z.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,157,144,199,226,'T',' ',16,'+','O',187,' ',232,6,'$',20,0,160,'!',156,
+'!',244,154,'0','l',216,208,5,128,128,20,'3','R',12,160,177,225,2,141,'T',
+164,4,'I',194,164,136,148,16,'(',';',170,'\\',201,178,165,203,151,'0','c',
+202,156,'I',179,166,205,155,'8','s',234,220,201,179,167,207,159,'@',127,2};
+
+DEFINE_TEST(test_read_format_cpio_bin_Z)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_COMPRESS);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_BIN_LE);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
Binary files /dev/null and - differ
--- /dev/null
+++ lib/libarchive/test/test_write_format_tar.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_tar.c,v 1.3 2007/05/29 01:00:21 kientzle Exp $");
+
+char buff[1000000];
+char buff2[64];
+
+DEFINE_TEST(test_write_format_tar)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	char *p;
+	size_t used;
+	size_t blocksize;
+
+	/* Repeat the following for a variety of odd blocksizes. */
+	for (blocksize = 1; blocksize < 100000; blocksize += blocksize + 3) {
+		/* Create a new archive in memory. */
+		assert((a = archive_write_new()) != NULL);
+		assertA(0 == archive_write_set_format_ustar(a));
+		assertA(0 == archive_write_set_compression_none(a));
+		assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
+		assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
+		assertA(blocksize == (size_t)archive_write_get_bytes_in_last_block(a));
+		assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+		assertA(blocksize == (size_t)archive_write_get_bytes_in_last_block(a));
+
+		/*
+		 * Write a file to it.
+		 */
+		assert((ae = archive_entry_new()) != NULL);
+		archive_entry_set_mtime(ae, 1, 10);
+		assert(1 == archive_entry_mtime(ae));
+#if !defined(__INTERIX)
+		assert(10 == archive_entry_mtime_nsec(ae));
+#endif
+		p = strdup("file");
+		archive_entry_copy_pathname(ae, p);
+		strcpy(p, "XXXX");
+		free(p);
+		assertEqualString("file", archive_entry_pathname(ae));
+		archive_entry_set_mode(ae, S_IFREG | 0755);
+		assert((S_IFREG | 0755) == archive_entry_mode(ae));
+		archive_entry_set_size(ae, 8);
+
+		assertA(0 == archive_write_header(a, ae));
+		archive_entry_free(ae);
+		assertA(8 == archive_write_data(a, "12345678", 9));
+
+		/* Close out the archive. */
+		assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assertA(0 == archive_write_finish(a));
+#else
+		archive_write_finish(a);
+#endif
+		/* This calculation gives "the smallest multiple of
+		 * the block size that is at least 2048 bytes". */
+		assert(((2048 - 1)/blocksize+1)*blocksize == used);
+
+		/*
+		 * Now, read the data back.
+		 */
+		assert((a = archive_read_new()) != NULL);
+		assertA(0 == archive_read_support_format_all(a));
+		assertA(0 == archive_read_support_compression_all(a));
+		assertA(0 == archive_read_open_memory(a, buff, used));
+
+		assertA(0 == archive_read_next_header(a, &ae));
+
+		assert(1 == archive_entry_mtime(ae));
+		/* Not the same as above: ustar doesn't store hi-res times. */
+		assert(0 == archive_entry_mtime_nsec(ae));
+		assert(0 == archive_entry_atime(ae));
+		assert(0 == archive_entry_ctime(ae));
+		assertEqualString("file", archive_entry_pathname(ae));
+		assert((S_IFREG | 0755) == archive_entry_mode(ae));
+		assert(8 == archive_entry_size(ae));
+		assertA(8 == archive_read_data(a, buff2, 10));
+		assert(0 == memcmp(buff2, "12345678", 8));
+
+		/* Verify the end of the archive. */
+		assert(1 == archive_read_next_header(a, &ae));
+		assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_read_finish(a));
+#else
+		archive_read_finish(a);
+#endif
+	}
+}
--- /dev/null
+++ lib/libarchive/test/test_write_compress_program.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_compress_program.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+
+char buff[1000000];
+char buff2[64];
+
+DEFINE_TEST(test_write_compress_program)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_write_set_compress_program()");
+#else
+	struct archive_entry *ae;
+	struct archive *a;
+	size_t used;
+	int blocksize = 1024;
+
+	/* Create a new archive in memory. */
+	/* Write it through an external "gzip" program. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_program(a, "gzip"));
+	assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, blocksize));
+	assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+	assertA(blocksize == archive_write_get_bytes_in_last_block(a));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(ae, 1, 10);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, 8);
+
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(8 == archive_write_data(a, "12345678", 9));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Now, read the data back through the built-in gzip support.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	assertA(0 == archive_read_next_header(a, &ae));
+
+	assert(1 == archive_entry_mtime(ae));
+	assert(0 == archive_entry_atime(ae));
+	assert(0 == archive_entry_ctime(ae));
+	assertEqualString("file", archive_entry_pathname(ae));
+	assert((S_IFREG | 0755) == archive_entry_mode(ae));
+	assert(8 == archive_entry_size(ae));
+	assertA(8 == archive_read_data(a, buff2, 10));
+	assert(0 == memcmp(buff2, "12345678", 8));
+
+	/* Verify the end of the archive. */
+	assert(1 == archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_bad_fd.c
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_bad_fd.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+/* Verify that attempting to open an invalid fd returns correct error. */
+DEFINE_TEST(test_bad_fd)
+{
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(ARCHIVE_FATAL == archive_read_open_fd(a, -1, 1024));
+	assertA(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_acl_basic.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_acl_basic.c,v 1.4 2007/07/06 15:43:11 kientzle Exp $");
+
+/*
+ * Exercise the system-independent portion of the ACL support.
+ * Check that archive_entry objects can save and restore ACL data
+ * and that pax archive can save and restore ACL data.
+ *
+ * This should work on all systems, regardless of whether local
+ * filesystems support ACLs or not.
+ */
+
+struct acl_t {
+	int type;  /* Type of ACL: "access" or "default" */
+	int permset; /* Permissions for this class of users. */
+	int tag; /* Owner, User, Owning group, group, other, etc. */
+	int qual; /* GID or UID of user/group, depending on tag. */
+	const char *name; /* Name of user/group, depending on tag. */
+};
+
+struct acl_t acls0[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
+};
+
+struct acl_t acls1[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+struct acl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static void
+set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
+{
+	int i;
+
+	archive_entry_acl_clear(ae);
+	for (i = 0; i < n; i++) {
+		archive_entry_acl_add_entry(ae,
+		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
+		    acls[i].name);
+	}
+}
+
+static int
+acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
+{
+	if (type != acl->type)
+		return (0);
+	if (permset != acl->permset)
+		return (0);
+	if (tag != acl->tag)
+		return (0);
+	if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+		return (1);
+	if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
+		return (1);
+	if (tag == ARCHIVE_ENTRY_ACL_OTHER)
+		return (1);
+	if (qual != acl->qual)
+		return (0);
+	if (name == NULL) {
+		if (acl->name == NULL || acl->name[0] == '\0')
+			return (1);
+	}
+	if (acl->name == NULL) {
+		if (name[0] == '\0')
+			return (1);
+	}
+	return (0 == strcmp(name, acl->name));
+}
+
+static void
+compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
+{
+	int *marker = malloc(sizeof(marker[0]) * n);
+	int i;
+	int r;
+	int type, permset, tag, qual;
+	int matched;
+	const char *name;
+
+	for (i = 0; i < n; i++)
+		marker[i] = i;
+
+	while (0 == (r = archive_entry_acl_next(ae,
+			 ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			 &type, &permset, &tag, &qual, &name))) {
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(&acls[marker[i]], type, permset,
+				tag, qual, name)) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+		if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
+			if (!matched) printf("No match for user_obj perm\n");
+			failure("USER_OBJ permset (%02o) != user mode (%02o)",
+			    permset, 07 & (mode >> 6));
+			assert((permset << 6) == (mode & 0700));
+		} else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
+			if (!matched) printf("No match for group_obj perm\n");
+			failure("GROUP_OBJ permset %02o != group mode %02o",
+			    permset, 07 & (mode >> 3));
+			assert((permset << 3) == (mode & 0070));
+		} else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
+			if (!matched) printf("No match for other perm\n");
+			failure("OTHER permset (%02o) != other mode (%02o)",
+			    permset, mode & 07);
+			assert((permset << 0) == (mode & 0007));
+		} else {
+			failure("Could not find match for ACL "
+			    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
+			    type, permset, tag, qual, name);
+			assert(matched == 1);
+		}
+	}
+#if ARCHIVE_VERSION_STAMP < 1009000
+	/* Known broken before 1.9.0. */
+	skipping("archive_entry_acl_next() exits with ARCHIVE_EOF");
+#else
+	assertEqualInt(ARCHIVE_EOF, r);
+#endif
+	assert((mode & 0777) == (archive_entry_mode(ae) & 0777));
+	failure("Could not find match for ACL "
+	    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
+	    acls[marker[0]].type, acls[marker[0]].permset,
+	    acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
+	assert(n == 0); /* Number of ACLs not matched should == 0 */
+	free(marker);
+}
+
+DEFINE_TEST(test_acl_basic)
+{
+	struct archive_entry *ae;
+
+	/* Create a simple archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_pathname(ae, "file");
+        archive_entry_set_mode(ae, S_IFREG | 0777);
+
+	/* Basic owner/owning group should just update mode bits. */
+	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	failure("Basic ACLs shouldn't be stored as extended ACLs");
+	assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+
+
+	/* With any extended ACL entry, we should read back a full set. */
+	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	failure("One extended ACL should flag all ACLs to be returned.");
+	assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+
+
+	/* A more extensive set of ACLs. */
+	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+	failure("Basic ACLs should set mode to 0543, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0543);
+
+	/*
+	 * Check that clearing ACLs gets rid of them all by repeating
+	 * the first test.
+	 */
+	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	failure("Basic ACLs shouldn't be stored as extended ACLs");
+	assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+	archive_entry_free(ae);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_disk.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk.c,v 1.6 2007/09/21 04:52:43 kientzle Exp $");
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+
+#define UMASK 022
+
+static void create(struct archive_entry *ae, const char *msg)
+{
+	struct archive *ad;
+	struct stat st;
+
+	/* Write the entry to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+	failure("%s", msg);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(0, archive_write_finish(ad));
+#else
+	archive_write_finish(ad);
+#endif
+	/* Test the entries on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+	    st.st_mode, archive_entry_mode(ae));
+	assert(st.st_mode == (archive_entry_mode(ae) & ~UMASK));
+}
+
+static void create_reg_file(struct archive_entry *ae, const char *msg)
+{
+	static const char data[]="abcdefghijklmnopqrstuvwxyz";
+	struct archive *ad;
+	struct stat st;
+
+	/* Write the entry to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+	failure("%s", msg);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(0, archive_write_finish(ad));
+#else
+	archive_write_finish(ad);
+#endif
+	/* Test the entries on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+	    st.st_mode, archive_entry_mode(ae));
+	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
+	assertEqualInt(st.st_size, sizeof(data));
+}
+
+static void create_reg_file2(struct archive_entry *ae, const char *msg)
+{
+	const int datasize = 100000;
+	char *data;
+	char *compare;
+	struct archive *ad;
+	struct stat st;
+	int i, fd;
+
+	data = malloc(datasize);
+	for (i = 0; i < datasize; i++)
+		data[i] = (char)(i % 256);
+
+	/* Write the entry to disk. */
+	assert((ad = archive_write_disk_new()) != NULL);
+	failure("%s", msg);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	for (i = 0; i < datasize - 999; i += 1000) {
+		assertEqualIntA(ad, ARCHIVE_OK,
+		    archive_write_data_block(ad, data + i, 1000, i));
+	}
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+#if ARCHIVE_API_VERSION > 1
+	assertEqualInt(0, archive_write_finish(ad));
+#else
+	archive_write_finish(ad);
+#endif
+	/* Test the entries on disk. */
+	assert(0 == stat(archive_entry_pathname(ae), &st));
+	failure("st.st_mode=%o archive_entry_mode(ae)=%o",
+	    st.st_mode, archive_entry_mode(ae));
+	assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
+	assertEqualInt(st.st_size, i);
+
+	compare = malloc(datasize);
+	fd = open(archive_entry_pathname(ae), O_RDONLY);
+	assertEqualInt(datasize, read(fd, compare, datasize));
+	close(fd);
+	assert(memcmp(compare, data, datasize) == 0);
+	free(compare);
+	free(data);
+}
+#endif
+
+DEFINE_TEST(test_write_disk)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_write_disk interface");
+#else
+	struct archive_entry *ae;
+
+	/* Force the umask to something predictable. */
+	umask(UMASK);
+
+	/* A regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	create_reg_file(ae, "Test creating a regular file");
+	archive_entry_free(ae);
+
+	/* Another regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file2");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	create_reg_file2(ae, "Test creating another regular file");
+	archive_entry_free(ae);
+
+	/* A regular file over an existing file */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0724);
+	create(ae, "Test creating a file over an existing file.");
+	archive_entry_free(ae);
+
+	/* A directory. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir");
+	archive_entry_set_mode(ae, S_IFDIR | 0555);
+	create(ae, "Test creating a regular dir.");
+	archive_entry_free(ae);
+
+	/* A directory over an existing file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFDIR | 0742);
+	create(ae, "Test creating a dir over an existing file.");
+	archive_entry_free(ae);
+
+	/* A file over an existing dir. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0744);
+	create(ae, "Test creating a file over an existing dir.");
+	archive_entry_free(ae);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_acl_pax.c
@@ -0,0 +1,521 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_acl_pax.c,v 1.4 2007/07/06 15:43:11 kientzle Exp $");
+
+/*
+ * Exercise the system-independent portion of the ACL support.
+ * Check that pax archive can save and restore ACL data.
+ *
+ * This should work on all systems, regardless of whether local
+ * filesystems support ACLs or not.
+ */
+
+static unsigned char buff[16384];
+
+static unsigned char reference[] = {
+'P','a','x','H','e','a','d','e','r','/','f','i','l','e',0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',0,'0','0',
+'0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','6','2',' ','0',
+'0','0','0','0','0','0','0','0','0','0',' ','0','1','1','7','6','7',0,' ',
+'x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',
+0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0',
+'0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=','0',10,
+'1','6',' ','S','C','H','I','L','Y','.','i','n','o','=','0',10,'1','8',' ',
+'S','C','H','I','L','Y','.','n','l','i','n','k','=','0',10,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,'f','i','l','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',0,'0','0',
+'0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','0','0',' ','0',
+'0','0','0','0','0','0','0','0','0','0',' ','0','1','0','0','0','6',0,' ',
+'0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',
+0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0',
+'0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,'P','a','x','H','e','a','d','e','r','/','f','i','l','e',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',
+0,'0','0','0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','1','7','2',
+' ','0','0','0','0','0','0','0','0','0','0','0',' ','0','1','1','7','7','1',
+0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t',
+'a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',
+'0','0','0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,'7','2',' ','S','C','H','I','L','Y','.','a','c','l','.',
+'a','c','c','e','s','s','=','u','s','e','r',':',':','-','-','x',',','g','r',
+'o','u','p',':',':','r','-','-',',','o','t','h','e','r',':',':','-','w','-',
+',','u','s','e','r',':','u','s','e','r','7','7',':','r','-','-',':','7','7',
+10,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=','0',10,'1','6',
+' ','S','C','H','I','L','Y','.','i','n','o','=','0',10,'1','8',' ','S','C',
+'H','I','L','Y','.','n','l','i','n','k','=','0',10,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'f','i','l','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',0,'0','0','0',
+'0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','0','0',' ','0','0',
+'0','0','0','0','0','0','0','0','0',' ','0','1','0','0','0','6',0,' ','0',
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0',
+'0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,'P','a','x','H','e','a','d','e','r','/','f','i','l','e',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'0','0','0','5','4','3',' ',0,'0','0','0','0','0','0',' ',
+0,'0','0','0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','2','4','3',
+' ','0','0','0','0','0','0','0','0','0','0','0',' ','0','1','1','7','7','5',
+0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t',
+'a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',
+'0','0','0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,'1','1','3',' ','S','C','H','I','L','Y','.','a','c','l',
+'.','a','c','c','e','s','s','=','u','s','e','r',':',':','r','-','x',',','g',
+'r','o','u','p',':',':','r','-','-',',','o','t','h','e','r',':',':','-','w',
+'x',',','g','r','o','u','p',':','g','r','o','u','p','7','8',':','r','w','x',
+':','7','8',',','u','s','e','r',':','u','s','e','r','7','8',':','-','-','-',
+':','7','8',',','u','s','e','r',':','u','s','e','r','7','7',':','r','-','-',
+':','7','7',10,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=','0',
+10,'1','6',' ','S','C','H','I','L','Y','.','i','n','o','=','0',10,'1','8',
+' ','S','C','H','I','L','Y','.','n','l','i','n','k','=','0',10,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,'f','i','l','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,'0','0','0','5','4','3',' ',0,'0','0','0','0','0','0',' ',0,'0','0',
+'0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','0','0',' ','0',
+'0','0','0','0','0','0','0','0','0','0',' ','0','1','0','0','1','3',0,' ',
+'0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',
+0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0',
+'0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,'P','a','x','H','e','a','d','e','r','/','f','i','l','e',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',
+0,'0','0','0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','6','2',
+' ','0','0','0','0','0','0','0','0','0','0','0',' ','0','1','1','7','6','7',
+0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t',
+'a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',
+'0','0','0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,'1','6',' ','S','C','H','I','L','Y','.','d','e','v','=',
+'0',10,'1','6',' ','S','C','H','I','L','Y','.','i','n','o','=','0',10,'1',
+'8',' ','S','C','H','I','L','Y','.','n','l','i','n','k','=','0',10,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'f','i','l','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,'0','0','0','1','4','2',' ',0,'0','0','0','0','0','0',' ',
+0,'0','0','0','0','0','0',' ',0,'0','0','0','0','0','0','0','0','0','0','0',
+' ','0','0','0','0','0','0','0','0','0','0','0',' ','0','1','0','0','0','6',
+0,' ','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t',
+'a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',
+'0','0','0','0','0',' ',0,'0','0','0','0','0','0',' ',0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+
+struct acl_t {
+	int type;  /* Type of ACL: "access" or "default" */
+	int permset; /* Permissions for this class of users. */
+	int tag; /* Owner, User, Owning group, group, other, etc. */
+	int qual; /* GID or UID of user/group, depending on tag. */
+	const char *name; /* Name of user/group, depending on tag. */
+};
+
+static struct acl_t acls0[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, 0, "" },
+};
+
+static struct acl_t acls1[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static struct acl_t acls2[] = {
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_USER, 77, "user77" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0,
+	  ARCHIVE_ENTRY_ACL_USER, 78, "user78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ,
+	  ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007,
+	  ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" },
+	{ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE,
+	  ARCHIVE_ENTRY_ACL_OTHER, -1, "" },
+};
+
+static void
+set_acls(struct archive_entry *ae, struct acl_t *acls, int n)
+{
+	int i;
+
+	archive_entry_acl_clear(ae);
+	for (i = 0; i < n; i++) {
+		archive_entry_acl_add_entry(ae,
+		    acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual,
+		    acls[i].name);
+	}
+}
+
+static int
+acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const char *name)
+{
+	if (type != acl->type)
+		return (0);
+	if (permset != acl->permset)
+		return (0);
+	if (tag != acl->tag)
+		return (0);
+	if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+		return (1);
+	if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
+		return (1);
+	if (tag == ARCHIVE_ENTRY_ACL_OTHER)
+		return (1);
+	if (qual != acl->qual)
+		return (0);
+	if (name == NULL) {
+		if (acl->name == NULL || acl->name[0] == '\0')
+			return (1);
+	}
+	if (acl->name == NULL) {
+		if (name[0] == '\0')
+			return (1);
+	}
+	return (0 == strcmp(name, acl->name));
+}
+
+static void
+compare_acls(struct archive_entry *ae, struct acl_t *acls, int n, int mode)
+{
+	int *marker = malloc(sizeof(marker[0]) * n);
+	int i;
+	int r;
+	int type, permset, tag, qual;
+	int matched;
+	const char *name;
+
+	for (i = 0; i < n; i++)
+		marker[i] = i;
+
+	while (0 == (r = archive_entry_acl_next(ae,
+			 ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			 &type, &permset, &tag, &qual, &name))) {
+		for (i = 0, matched = 0; i < n && !matched; i++) {
+			if (acl_match(&acls[marker[i]], type, permset,
+				tag, qual, name)) {
+				/* We found a match; remove it. */
+				marker[i] = marker[n - 1];
+				n--;
+				matched = 1;
+			}
+		}
+		if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ) {
+			if (!matched) printf("No match for user_obj perm\n");
+			failure("USER_OBJ permset (%02o) != user mode (%02o)",
+			    permset, 07 & (mode >> 6));
+			assert((permset << 6) == (mode & 0700));
+		} else if (tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) {
+			if (!matched) printf("No match for group_obj perm\n");
+			failure("GROUP_OBJ permset %02o != group mode %02o",
+			    permset, 07 & (mode >> 3));
+			assert((permset << 3) == (mode & 0070));
+		} else if (tag == ARCHIVE_ENTRY_ACL_OTHER) {
+			if (!matched) printf("No match for other perm\n");
+			failure("OTHER permset (%02o) != other mode (%02o)",
+			    permset, mode & 07);
+			assert((permset << 0) == (mode & 0007));
+		} else {
+			failure("Could not find match for ACL "
+			    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
+			    type, permset, tag, qual, name);
+			assert(matched == 1);
+		}
+	}
+#if ARCHIVE_VERSION_STAMP < 1009000
+	/* Known broken before 1.9.0. */
+	skipping("archive_entry_acl_next() exits with ARCHIVE_EOF");
+#else
+	assertEqualInt(ARCHIVE_EOF, r);
+#endif
+	assert((mode & 0777) == (archive_entry_mode(ae) & 0777));
+	failure("Could not find match for ACL "
+	    "(type=%d,permset=%d,tag=%d,qual=%d,name=``%s'')",
+	    acls[marker[0]].type, acls[marker[0]].permset,
+	    acls[marker[0]].tag, acls[marker[0]].qual, acls[marker[0]].name);
+	assert(n == 0); /* Number of ACLs not matched should == 0 */
+	free(marker);
+}
+
+DEFINE_TEST(test_acl_pax)
+{
+	struct archive *a;
+	struct archive_entry *ae;
+	size_t used;
+	int fd;
+
+	/* Write an archive to memory. */
+	assert(NULL != (a = archive_write_new()));
+	assertA(0 == archive_write_set_format_pax(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a, 1));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Write a series of files to the archive with different ACL info. */
+
+	/* Create a simple archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_pathname(ae, "file");
+        archive_entry_set_mode(ae, S_IFREG | 0777);
+
+	/* Basic owner/owning group should just update mode bits. */
+	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+	/* With any extended ACL entry, we should read back a full set. */
+	set_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+
+	/* A more extensive set of ACLs. */
+	set_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]));
+	assertA(0 == archive_write_header(a, ae));
+
+	/*
+	 * Check that clearing ACLs gets rid of them all by repeating
+	 * the first test.
+	 */
+	set_acls(ae, acls0, sizeof(acls0)/sizeof(acls0[0]));
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Write out the data we generated to a file for manual inspection. */
+	assert(-1 < (fd = open("testout", O_WRONLY | O_CREAT | O_TRUNC, 0775)));
+	assert(used == (size_t)write(fd, buff, used));
+	close(fd);
+
+	/* Write out the reference data to a file for manual inspection. */
+	assert(-1 < (fd = open("reference", O_WRONLY | O_CREAT | O_TRUNC, 0775)));
+	assert(sizeof(reference) == write(fd, reference, sizeof(reference)));
+	close(fd);
+
+	/* Assert that the generated data matches the built-in reference data.*/
+	failure("Generated pax archive does not match reference; check 'testout' and 'reference' files.");
+	assert(0 == memcmp(buff, reference, sizeof(reference)));
+	failure("Generated pax archive does not match reference; check 'testout' and 'reference' files.");
+	assertEqualInt(used, sizeof(reference));
+
+	/* Read back each entry and check that the ACL data is right. */
+	assert(NULL != (a = archive_read_new()));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	/* First item has no ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	failure("Basic ACLs shouldn't be stored as extended ACLs");
+	assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+
+	/* Second item has a few ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	failure("One extended ACL should flag all ACLs to be returned.");
+	assert(4 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	compare_acls(ae, acls1, sizeof(acls1)/sizeof(acls1[0]), 0142);
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+
+	/* Third item has pretty extensive ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(6, archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	compare_acls(ae, acls2, sizeof(acls2)/sizeof(acls2[0]), 0543);
+	failure("Basic ACLs should set mode to 0543, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0543);
+
+	/* Fourth item has no ACLs */
+	assertA(0 == archive_read_next_header(a, &ae));
+	failure("Basic ACLs shouldn't be stored as extended ACLs");
+	assert(0 == archive_entry_acl_reset(ae, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	failure("Basic ACLs should set mode to 0142, not %04o",
+	    archive_entry_mode(ae)&0777);
+	assert((archive_entry_mode(ae) & 0777) == 0142);
+
+	/* Close the archive. */
+	assertA(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_bin.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_bin.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+199,'q',21,4,177,'y',237,'A',232,3,232,3,2,0,0,0,'p','C',244,'M',2,0,0,0,
+0,0,'.',0,199,'q',0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,11,0,0,0,0,0,'T','R',
+'A','I','L','E','R','!','!','!',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+DEFINE_TEST(test_read_format_cpio_bin)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_CPIO_BIN_LE);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_write_format_cpio.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_cpio.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+
+/* The version stamp macro was introduced after cpio write support. */
+#if ARCHIVE_VERSION_STAMP >= 1009000
+static void
+test_format(int	(*set_format)(struct archive *))
+{
+	char filedata[64];
+	struct archive_entry *ae;
+	struct archive *a;
+	char *p;
+	size_t used;
+	size_t buffsize = 1000000;
+	char *buff;
+
+	buff = malloc(buffsize);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == (*set_format)(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff, buffsize, &used));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(ae, 1, 10);
+	assert(1 == archive_entry_mtime(ae));
+	assert(10 == archive_entry_mtime_nsec(ae));
+	p = strdup("file");
+	archive_entry_copy_pathname(ae, p);
+	strcpy(p, "XXXX");
+	free(p);
+	assertEqualString("file", archive_entry_pathname(ae));
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	assert((S_IFREG | 0755) == archive_entry_mode(ae));
+	archive_entry_set_size(ae, 8);
+
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(8 == archive_write_data(a, "12345678", 9));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Now, read the data back.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	assertA(0 == archive_read_next_header(a, &ae));
+
+	assert(1 == archive_entry_mtime(ae));
+	/* Not the same as above: cpio doesn't store hi-res times. */
+	assert(0 == archive_entry_mtime_nsec(ae));
+	assert(0 == archive_entry_atime(ae));
+	assert(0 == archive_entry_ctime(ae));
+	assertEqualString("file", archive_entry_pathname(ae));
+	assert((S_IFREG | 0755) == archive_entry_mode(ae));
+	assert(8 == archive_entry_size(ae));
+	assertA(8 == archive_read_data(a, filedata, 10));
+	assert(0 == memcmp(filedata, "12345678", 8));
+
+	/* Verify the end of the archive. */
+	assert(1 == archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	free(buff);
+}
+#endif
+
+DEFINE_TEST(test_write_format_cpio)
+{
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	test_format(archive_write_set_format_cpio);
+	test_format(archive_write_set_format_cpio_newc);
+#else
+	skipping("cpio write support");
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libarchive/test/test.h,v 1.6 2007/07/14 17:52:01 kientzle Exp $
+ */
+
+/* Every test program should #include "test.h" as the first thing. */
+
+/*
+ * The goal of this file (and the matching test.c) is to
+ * simplify the very repetitive test-*.c test programs.
+ */
+
+#define _FILE_OFFSET_BITS 64
+
+#include <archive.h>
+#include <archive_entry.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <wchar.h>
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+#if defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "../../config.h"
+#elif defined(__FreeBSD__)
+/* Building as part of FreeBSD system requires a pre-built config.h. */
+#include "../config_freebsd.h"
+#elif defined(_WIN32)
+/* Win32 can't run the 'configure' script. */
+#include "../config_windows.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in test.h.
+#endif
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>  /* For __FBSDID */
+#else
+#define	__FBSDID(a)     /* null */
+#endif
+
+/*
+ * ARCHIVE_VERSION_STAMP first appeared in 1.9 and libarchive 2.2.4.
+ * We can approximate it for earlier versions, though.
+ * This is used to disable tests of features not present in the current
+ * version.
+ */
+#ifndef ARCHIVE_VERSION_STAMP
+#define ARCHIVE_VERSION_STAMP	\
+		(ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
+#endif
+
+
+/*
+ * "list.h" is simply created by "grep DEFINE_TEST"; it has
+ * a line like
+ *      DEFINE_TEST(test_function)
+ * for each test.
+ * Include it here with a suitable DEFINE_TEST to declare all of the
+ * test functions.
+ */
+#define DEFINE_TEST(name) void name(void);
+#include "list.h"
+/*
+ * Redefine DEFINE_TEST for use in defining the test functions.
+ */
+#undef DEFINE_TEST
+#define DEFINE_TEST(name) void name(void)
+
+/* An implementation of the standard assert() macro */
+#define assert(e)   test_assert(__FILE__, __LINE__, (e), #e, NULL)
+/* As above, but reports any archive_error found in variable 'a' */
+#define assertA(e)   test_assert(__FILE__, __LINE__, (e), #e, (a))
+
+/* Asserts that two integers are the same.  Reports value of each one if not. */
+#define assertEqualIntA(a,v1,v2)   \
+  test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+#define assertEqualInt(v1,v2)   \
+  test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+
+/* Asserts that two strings are the same.  Reports value of each one if not. */
+#define assertEqualStringA(a,v1,v2)   \
+  test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (a))
+#define assertEqualString(v1,v2)   \
+  test_assert_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+/* As above, but v1 and v2 are wchar_t * */
+#define assertEqualWString(v1,v2)   \
+  test_assert_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+
+/*
+ * This would be simple with C99 variadic macros, but I don't want to
+ * require that.  Instead, I insert a function call before each
+ * skipping() call to pass the file and line information down.  Crude,
+ * but effective.
+ */
+#define skipping	\
+  skipping_setup(__FILE__, __LINE__);test_skipping
+
+/* Function declarations.  These are defined in test_utility.c. */
+void failure(const char *fmt, ...);
+void skipping_setup(const char *, int);
+void test_skipping(const char *fmt, ...);
+void test_assert(const char *, int, int, const char *, struct archive *);
+void test_assert_equal_int(const char *, int, int, const char *, int, const char *, struct archive *);
+void test_assert_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, struct archive *);
+void test_assert_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, struct archive *);
+
+/* Special customized read-from-memory interface. */
+int read_open_memory(struct archive *, void *, size_t, size_t);
+int	read_open_memory(struct archive *, void *, size_t, size_t);
--- /dev/null
+++ lib/libarchive/test/test_read_pax_truncated.c
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_pax_truncated.c,v 1.1 2007/07/13 15:14:35 kientzle Exp $");
+
+DEFINE_TEST(test_read_pax_truncated)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	size_t used, i;
+	size_t buff_size = 1000000;
+	size_t filedata_size = 100000;
+	char *buff = malloc(buff_size);
+	char *buff2 = malloc(buff_size);
+	char *filedata = malloc(filedata_size);
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_pax(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff, buff_size, &used));
+
+	/*
+	 * Write a file to it.
+	 */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	for (i = 0; i < filedata_size; i++)
+		filedata[i] = (unsigned char)rand();
+	archive_entry_set_atime(ae, 1, 2);
+	archive_entry_set_ctime(ae, 3, 4);
+	archive_entry_set_mtime(ae, 5, 6);
+	archive_entry_set_size(ae, filedata_size);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assertA(filedata_size == archive_write_data(a, filedata, filedata_size));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Now, read back a truncated version of the archive and
+	 * verify that we get an appropriate error. */
+	for (i = 1; i < used + 100; i += 100) {
+		assert((a = archive_read_new()) != NULL);
+		assertA(0 == archive_read_support_format_all(a));
+		assertA(0 == archive_read_support_compression_all(a));
+		assertA(0 == read_open_memory(a, buff, i, 13));
+
+		if (i < 1536) {
+			assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+			goto wrap_up;
+		} else {
+			failure("Archive truncated to %d bytes", i);
+			assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+		}
+
+		if (i < 1536 + filedata_size) {
+			assertA(ARCHIVE_FATAL == archive_read_data(a, filedata, filedata_size));
+			goto wrap_up;
+		} else {
+			failure("Archive truncated to %d bytes", i);
+			assertEqualIntA(a, filedata_size,
+			    archive_read_data(a, filedata, filedata_size));
+		}
+
+		/* Verify the end of the archive. */
+		/* Archive must be long enough to capture a 512-byte
+		 * block of zeroes after the entry.  (POSIX requires a
+		 * second block of zeros to be written but libarchive
+		 * does not return an error if it can't consume
+		 * it.) */
+		if (i < 1536 + 512*((filedata_size + 511)/512) + 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+		} else {
+			assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+		}
+	wrap_up:
+		assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_read_finish(a));
+#else
+		archive_read_finish(a);
+#endif
+	}
+
+
+
+	/* Same as above, except skip the body instead of reading it. */
+	for (i = 1; i < used + 100; i += 100) {
+		assert((a = archive_read_new()) != NULL);
+		assertA(0 == archive_read_support_format_all(a));
+		assertA(0 == archive_read_support_compression_all(a));
+		assertA(0 == read_open_memory(a, buff, i, 7));
+
+		if (i < 1536) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+			goto wrap_up2;
+		} else {
+			assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+		}
+
+		if (i < 1536 + 512*((filedata_size+511)/512)) {
+			assertA(ARCHIVE_FATAL == archive_read_data_skip(a));
+			goto wrap_up2;
+		} else {
+			assertA(ARCHIVE_OK == archive_read_data_skip(a));
+		}
+
+		/* Verify the end of the archive. */
+		/* Archive must be long enough to capture a 512-byte
+		 * block of zeroes after the entry.  (POSIX requires a
+		 * second block of zeros to be written but libarchive
+		 * does not return an error if it can't consume
+		 * it.) */
+		if (i < 1536 + 512*((filedata_size + 511)/512) + 512) {
+			assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+		} else {
+			assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+		}
+	wrap_up2:
+		assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_read_finish(a));
+#else
+		archive_read_finish(a);
+#endif
+	}
+
+	/* Now, damage the archive in various ways and test the responses. */
+
+	/* Damage the first size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	buff2[512] = '9';
+	buff2[513] = '9';
+	buff2[514] = 'A'; /* Non-digit in size. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	buff2[512] = 'A'; /* First character not a digit. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	for (i = 512; i < 520; i++) /* Size over 999999. */
+		buff2[i] = '9';
+	buff2[i] = ' ';
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	buff2[512] = '9'; /* Valid format, but larger than attribute area. */
+	buff2[513] = '9';
+	buff2[514] = '9';
+	buff2[515] = ' ';
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	buff2[512] = '1'; /* Too small. */
+	buff2[513] = ' ';
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the size field in the pax attributes. */
+	memcpy(buff2, buff, buff_size);
+	buff2[512] = ' '; /* No size given. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Damage the ustar header. */
+	memcpy(buff2, buff, buff_size);
+	buff2[1024]++; /* Break the checksum. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff2, used));
+	assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/*
+	 * TODO: Damage the ustar header in various ways and fixup the
+	 * checksum in order to test boundary cases in the innermost
+	 * ustar header parsing.
+	 */
+
+	free(buff);
+	free(buff2);
+	free(filedata);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_iso_gz.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_iso_gz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+31,139,8,8,201,'R','p','C',0,3,'t','e','s','t','-','r','e','a','d','_','f',
+'o','r','m','a','t','.','i','s','o',0,237,219,223,'k',211,'@',28,0,240,212,
+23,'K','}',20,169,143,135,15,162,224,218,180,']','W',186,183,173,'I',183,
+206,254,144,'d',19,246,'$',5,';',24,'2',11,235,240,239,221,127,162,233,'f',
+17,29,219,24,12,'\'',243,243,'!',185,187,220,']',146,';',8,9,223,131,'D',
+17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'P',234,
+'%','q',220,'(','E',253,211,217,'l',';','O',194,'u','z','I','6',25,']',219,
+26,194,234,'z',241,'o',217,13,247,'-',182,229,30,149,203,'Q',229,178,170,
+242,252,'W',243,139,'e',242,'*',170,'^',30,'U',163,242,'2','+','G',199,207,
+158,'V','_',190,';',127,178,':',255,134,1,241,23,140,222,15,242,'I','?',15,
+'E',26,186,27,27,'q','}',183,'8',232,15,134,'i','~',152,239,167,163,176,'}',
+'0',24,'&','i',22,'^','/',159,159,180,'7',201,146,162,176,150,213,147,143,
+'E','!','K',183,246,'\'','Y','x',211,'{',27,26,221,'n','+',164,181,195,201,
+193,'x','\'',217,26,166,171,202,'N',216,171,'}','H',183,178,'|','2',174,239,
+213,242,222,238,'`','8',28,140,'w',30,'j',186,205,'8','n','7',26,'q',167,
+217,'j',174,183,';','q','|','~',165,'"',254,'C','t',165,'G',')','z',168,209,
+243,'o',184,143,215,'6',220,139,239,'?',191,255,0,192,255,163,136,224,194,
+'h',254,'5',140,231,223,'B',232,132,'f','k',179,185,190,217,238,'\\',132,
+':',149,147,'/',199,139,249,209,'"','4','k','q',173,21,214,230,225,'l',182,
+'8','[',';',157,'M','?',127,':',154,159,158,'L',207,'j','E','{',152,'>',244,
+28,0,128,187,')',']',172,177,139,255,1,0,0,224,'1',187,136,252,171,22,0,0,
+0,0,224,'1',187,253,31,187,'[','{','X',';',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,206,
+'~',0,137,'#',195,182,0,128,1,0};
+
+DEFINE_TEST(test_read_format_iso_gz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_GZIP);
+	assert(archive_format(a) == ARCHIVE_FORMAT_ISO9660);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_write_disk_secure.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_disk_secure.c,v 1.3 2007/07/06 15:43:11 kientzle Exp $");
+
+#define UMASK 022
+
+/*
+ * Exercise security checks that should prevent certain
+ * writes.
+ */
+
+DEFINE_TEST(test_write_disk_secure)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("archive_write_disk interface");
+#else
+	struct archive *a;
+	struct archive_entry *ae;
+	struct stat st;
+
+	/* Start with a known umask. */
+	umask(UMASK);
+
+	/* Create an archive_write_disk object. */
+	assert((a = archive_write_disk_new()) != NULL);
+
+	/* Write a regular dir to it. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assert(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Write a symlink to the dir above. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir");
+	archive_entry_set_mode(ae, S_IFLNK | 0777);
+	archive_entry_set_symlink(ae, "dir");
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/*
+	 * Without security checks, we should be able to
+	 * extract a file through the link.
+	 */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir/filea");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/* But with security checks enabled, this should fail. */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir/fileb");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
+	failure("Extracting a file through a symlink should fail here.");
+	assertEqualInt(ARCHIVE_WARN, archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+
+	/* Create another link. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir2");
+	archive_entry_set_mode(ae, S_IFLNK | 0777);
+	archive_entry_set_symlink(ae, "dir");
+	archive_write_disk_set_options(a, 0);
+	assert(0 == archive_write_header(a, ae));
+	assert(0 == archive_write_finish_entry(a));
+
+	/*
+	 * With symlink check and unlink option, it should remove
+	 * the link and create the dir.
+	 */
+	assert(archive_entry_clear(ae) != NULL);
+	archive_entry_copy_pathname(ae, "link_to_dir2/filec");
+	archive_entry_set_mode(ae, S_IFREG | 0777);
+	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_UNLINK);
+	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+	archive_entry_free(ae);
+	assert(0 == archive_write_finish_entry(a));
+
+
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Test the entries on disk. */
+	assert(0 == lstat("dir", &st));
+	failure("dir: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+
+	assert(0 == lstat("link_to_dir", &st));
+	failure("link_to_dir: st.st_mode=%o", st.st_mode);
+	assert(S_ISLNK(st.st_mode));
+#if HAVE_LCHMOD
+	/* Systems that lack lchmod() can't set symlink perms, so skip this. */
+	failure("link_to_dir: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+#endif
+
+	assert(0 == lstat("dir/filea", &st));
+	failure("dir/filea: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+
+	failure("dir/fileb: This file should not have been created");
+	assert(0 != lstat("dir/fileb", &st));
+
+	assert(0 == lstat("link_to_dir2", &st));
+	failure("link_to_dir2 should have been re-created as a true dir");
+	assert(S_ISDIR(st.st_mode));
+	failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+
+	assert(0 == lstat("link_to_dir2/filec", &st));
+	assert(S_ISREG(st.st_mode));
+	failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode);
+	assert((st.st_mode & 07777) == 0755);
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_zip.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_zip.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+
+static unsigned char archive[] = {
+'P','K',3,4,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,21,0,
+'d','i','r','/','U','T',9,0,3,25,'U','O','F',25,'U','O','F','U','x',4,0,232,
+3,232,3,'P','K',3,4,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,0,0,0,
+18,0,0,0,5,0,21,0,'f','i','l','e','1','U','T',9,0,3,'A','U','O','F',172,'[',
+'O','F','U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P',
+'K',3,4,20,0,0,0,8,0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,
+0,21,0,'f','i','l','e','2','U','T',9,0,3,172,'[','O','F',172,'[','O','F',
+'U','x',4,0,232,3,232,3,203,'H',205,201,201,231,202,'@','"',1,'P','K',1,2,
+23,3,10,0,0,0,0,0,'Y','f',179,'6',0,0,0,0,0,0,0,0,0,0,0,0,4,0,13,0,0,0,0,
+0,0,0,16,0,237,'A',0,0,0,0,'d','i','r','/','U','T',5,0,3,25,'U','O','F','U',
+'x',0,0,'P','K',1,2,23,3,20,0,0,0,8,0,'o','f',179,'6',':','7','f','=',10,
+0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,0,0,164,129,'7',0,0,0,'f','i','l','e',
+'1','U','T',5,0,3,'A','U','O','F','U','x',0,0,'P','K',1,2,23,3,20,0,0,0,8,
+0,'Z','j',179,'6',':','7','f','=',10,0,0,0,18,0,0,0,5,0,13,0,0,0,0,0,1,0,
+0,0,164,129,'y',0,0,0,'f','i','l','e','2','U','T',5,0,3,172,'[','O','F','U',
+'x',0,0,'P','K',5,6,0,0,0,0,3,0,3,0,191,0,0,0,187,0,0,0,0,0};
+
+DEFINE_TEST(test_read_format_zip)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	char *buff[128];
+
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("dir/", archive_entry_pathname(ae));
+	assertEqualInt(1179604249, archive_entry_mtime(ae));
+	assertEqualInt(0, archive_entry_size(ae));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("file1", archive_entry_pathname(ae));
+	assertEqualInt(1179604289, archive_entry_mtime(ae));
+	assertEqualInt(18, archive_entry_size(ae));
+	assertEqualInt(18, archive_read_data(a, buff, 18));
+	assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualString("file2", archive_entry_pathname(ae));
+	assertEqualInt(1179605932, archive_entry_mtime(ae));
+	assertEqualInt(18, archive_entry_size(ae));
+	assertEqualInt(18, archive_read_data(a, buff, 18));
+	assert(0 == memcmp(buff, "hello\nhello\nhello\n", 18));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_ZIP);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_cpio_bin_bz2.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_cpio_bin_bz2.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+'B','Z','h','9','1','A','Y','&','S','Y',134,'J',208,'4',0,0,30,246,141,253,
+8,2,0,' ',1,'*','&',20,0,'`',' ',' ',2,0,128,0,'B',4,8,' ',0,'T','P',0,'4',
+0,13,6,137,168,245,27,'Q',160,'a',25,169,5,'I',187,'(',10,'d','E',177,177,
+142,218,232,'r',130,'4','D',247,'<','Z',190,'U',237,236,'d',227,31,' ','z',
+192,'E','_',23,'r','E','8','P',144,134,'J',208,'4'};
+
+DEFINE_TEST(test_read_format_cpio_bin_bz2)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_BZIP2);
+	assert(archive_format(a) == ARCHIVE_FORMAT_CPIO_BIN_LE);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_write_format_ar.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_ar.c,v 1.5 2007/07/06 15:43:11 kientzle Exp $");
+
+char buff[4096];
+char buff2[64];
+static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n";
+
+DEFINE_TEST(test_write_format_ar)
+{
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("ar write support");
+#else
+	struct archive_entry *ae;
+	struct archive* a;
+	size_t used;
+
+	/*
+	 * First we try to create a SVR4/GNU format archive.
+	 */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ar_svr4(a));
+	assertA(0 == archive_write_set_compression_gzip(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* write the filename table */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "//");
+	archive_entry_set_size(ae, strlen(strtab));
+	assertA(0 == archive_write_header(a, ae));
+	assertA(strlen(strtab) == (size_t)archive_write_data(a, strtab, strlen(strtab)));
+	archive_entry_free(ae);
+
+	/* write entries */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_mtime(ae, 1, 0);
+	assert(1 == archive_entry_mtime(ae));
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	assert((S_IFREG | 0755) == archive_entry_mode(ae));
+	archive_entry_copy_pathname(ae, "abcdefghijklmn.o");
+	archive_entry_set_size(ae, 8);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(8 == archive_write_data(a, "87654321", 15));
+	archive_entry_free(ae);
+
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "ggghhhjjjrrrttt.o");
+	archive_entry_set_filetype(ae, AE_IFREG);
+	archive_entry_set_size(ae, 7);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(7 == archive_write_data(a, "7777777", 7));
+	archive_entry_free(ae);
+
+	/* test full pathname */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "/usr/home/xx/iiijjjdddsssppp.o");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	archive_entry_set_size(ae, 8);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(8 == archive_write_data(a, "88877766", 8));
+	archive_entry_free(ae);
+
+	/* trailing "/" should be rejected */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "/usr/home/xx/iiijjj/");
+	archive_entry_set_size(ae, 8);
+	assertA(0 != archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	/* Non regular file should be rejected */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "gfgh.o");
+	archive_entry_set_mode(ae, S_IFDIR | 0755);
+	archive_entry_set_size(ae, 6);
+	assertA(0 != archive_write_header(a, ae));
+	archive_entry_free(ae);
+
+	archive_write_close(a);
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/*
+	 * Now, read the data back.
+	 */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(0, archive_entry_mtime(ae));
+	assertEqualString("//", archive_entry_pathname(ae));
+	assertEqualInt(strlen(strtab), archive_entry_size(ae));
+	assertEqualIntA(a, strlen(strtab), archive_read_data(a, buff2, 100));
+	assert(0 == memcmp(buff2, strtab, strlen(strtab)));
+
+	assertA(0 == archive_read_next_header(a, &ae));
+	assert(1 == archive_entry_mtime(ae));
+	assertEqualString("abcdefghijklmn.o", archive_entry_pathname(ae));
+	assert(8 == archive_entry_size(ae));
+	assertA(8 == archive_read_data(a, buff2, 10));
+	assert(0 == memcmp(buff2, "87654321", 8));
+
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("ggghhhjjjrrrttt.o", archive_entry_pathname(ae));
+	assert(7 == archive_entry_size(ae));
+	assertA(7 == archive_read_data(a, buff2, 11));
+	assert(0 == memcmp(buff2, "7777777", 7));
+
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("iiijjjdddsssppp.o", archive_entry_pathname(ae));
+	assert(8 == archive_entry_size(ae));
+	assertA(8 == archive_read_data(a, buff2, 17));
+	assert(0 == memcmp(buff2, "88877766", 8));
+
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/*
+	 * Then, we try to create a BSD format archive.
+	 */
+	memset(buff, 0, sizeof(buff));
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ar_bsd(a));
+	assertA(0 == archive_write_set_compression_bzip2(a));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* write a entry need long name extension */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "ttttyyyyuuuuiiii.o");
+	archive_entry_set_filetype(ae, AE_IFREG);
+	archive_entry_set_size(ae, 5);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(5 == archive_write_data(a, "12345", 7));
+	archive_entry_free(ae);
+
+	/* write a entry with a short name */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "ttyy.o");
+	archive_entry_set_filetype(ae, AE_IFREG);
+	archive_entry_set_size(ae, 6);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(6 == archive_write_data(a, "555555", 7));
+	archive_entry_free(ae);
+	archive_write_close(a);
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Now, Read the data back */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, used));
+
+	assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+	assertEqualString("ttttyyyyuuuuiiii.o", archive_entry_pathname(ae));
+	assertEqualInt(5, archive_entry_size(ae));
+	assertA(5 == archive_read_data(a, buff2, 10));
+	assert(0 == memcmp(buff2, "12345", 5));
+
+	assert(0 == archive_read_next_header(a, &ae));
+	assertEqualString("ttyy.o", archive_entry_pathname(ae));
+	assert(6 == archive_entry_size(ae));
+	assertA(6 == archive_read_data(a, buff2, 10));
+	assert(0 == memcmp(buff2, "555555", 6));
+
+	/* Test EOF */
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+#endif
+}
--- /dev/null
+++ lib/libarchive/test/test_read_extract.c
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_extract.c,v 1.3 2007/05/29 01:00:20 kientzle Exp $");
+
+#define BUFF_SIZE 1000000
+#define FILE_BUFF_SIZE 100000
+
+DEFINE_TEST(test_read_extract)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	struct stat st;
+	size_t used;
+	int i;
+	char *buff, *file_buff;
+	int fd;
+	ssize_t bytes_read;
+
+	buff = malloc(BUFF_SIZE);
+	file_buff = malloc(FILE_BUFF_SIZE);
+
+	/* Force the umask to something predictable. */
+	umask(022);
+
+	/* Create a new archive in memory containing various types of entries. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_open_memory(a, buff, BUFF_SIZE, &used));
+	/* A directory to be restored with EXTRACT_PERM. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir_0775");
+	archive_entry_set_mode(ae, S_IFDIR | 0775);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* A regular file. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "file");
+	archive_entry_set_mode(ae, S_IFREG | 0755);
+	for (i = 0; i < FILE_BUFF_SIZE; i++)
+		file_buff[i] = (unsigned char)rand();
+	archive_entry_set_size(ae, FILE_BUFF_SIZE);
+	assertA(0 == archive_write_header(a, ae));
+	assertA(FILE_BUFF_SIZE == archive_write_data(a, file_buff, FILE_BUFF_SIZE));
+	archive_entry_free(ae);
+	/* A directory that should obey umask when restored. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir");
+	archive_entry_set_mode(ae, S_IFDIR | 0777);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* A file in the directory. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir/file");
+	archive_entry_set_mode(ae, S_IFREG | 0700);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* A file in a dir that is not already in the archive. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir2/file");
+	archive_entry_set_mode(ae, S_IFREG | 0000);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* A dir with a trailing /. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir3/.");
+	archive_entry_set_mode(ae, S_IFDIR | 0710);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* Multiple dirs with a single entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir4/a/../b/../c/");
+	archive_entry_set_mode(ae, S_IFDIR | 0711);
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* A symlink. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "symlink");
+	archive_entry_set_mode(ae, S_IFLNK | 0755);
+	archive_entry_set_symlink(ae, "file");
+	assertA(0 == archive_write_header(a, ae));
+	archive_entry_free(ae);
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	/* Extract the entries to disk. */
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, BUFF_SIZE));
+	/* Restore first entry with _EXTRACT_PERM. */
+	failure("Error reading first entry", i);
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertA(0 == archive_read_extract(a, ae, ARCHIVE_EXTRACT_PERM));
+	/* Rest of entries get restored with no flags. */
+	for (i = 0; i < 7; i++) {
+		failure("Error reading entry %d", i+1);
+		assertA(0 == archive_read_next_header(a, &ae));
+		assertA(0 == archive_read_extract(a, ae, 0));
+	}
+	assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+
+	/* Test the entries on disk. */
+	assert(0 == stat("dir_0775", &st));
+	failure("This was 0775 in archive, and should be 0775 on disk");
+	assert(st.st_mode == (S_IFDIR | 0775));
+	assert(0 == stat("file", &st));
+	failure("st.st_mode=%o should be %o", st.st_mode, S_IFREG | 0755);
+	assert(st.st_mode == (S_IFREG | 0755));
+	failure("The file extracted to disk is the wrong size.");
+	assert(st.st_size == FILE_BUFF_SIZE);
+	fd = open("file", O_RDONLY);
+	failure("The file on disk could not be opened.");
+	assert(fd != 0);
+	bytes_read = read(fd, buff, FILE_BUFF_SIZE);
+	failure("The file contents read from disk are the wrong size");
+	assert(bytes_read == FILE_BUFF_SIZE);
+	failure("The file contents on disk do not match the file contents that were put into the archive.");
+	assert(memcmp(buff, file_buff, FILE_BUFF_SIZE) == 0);
+	assert(0 == stat("dir", &st));
+	failure("This was 0777 in archive, but umask should make it 0755");
+	assert(st.st_mode == (S_IFDIR | 0755));
+	assert(0 == stat("dir/file", &st));
+	assert(st.st_mode == (S_IFREG | 0700));
+	assert(0 == stat("dir2", &st));
+	assert(st.st_mode == (S_IFDIR | 0755));
+	assert(0 == stat("dir2/file", &st));
+	assert(st.st_mode == (S_IFREG | 0000));
+	assert(0 == stat("dir3", &st));
+	assert(st.st_mode == (S_IFDIR | 0710));
+	assert(0 == stat("dir4", &st));
+	assert(st.st_mode == (S_IFDIR | 0755));
+	assert(0 == stat("dir4/a", &st));
+	assert(st.st_mode == (S_IFDIR | 0755));
+	assert(0 == stat("dir4/b", &st));
+	assert(st.st_mode == (S_IFDIR | 0755));
+	assert(0 == stat("dir4/c", &st));
+	assert(st.st_mode == (S_IFDIR | 0711));
+	assert(0 == lstat("symlink", &st));
+	assert(S_ISLNK(st.st_mode));
+#if HAVE_LCHMOD
+	/* Systems that lack lchmod() can't set symlink perms, so skip this. */
+	assert((st.st_mode & 07777) == 0755);
+#endif
+	assert(0 == stat("symlink", &st));
+	assert(st.st_mode == (S_IFREG | 0755));
+
+	free(buff);
+	free(file_buff);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_tbz.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tbz.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = {
+'B','Z','h','9','1','A','Y','&','S','Y',237,7,140,'W',0,0,27,251,144,208,
+128,0,' ','@',1,'o',128,0,0,224,'"',30,0,0,'@',0,8,' ',0,'T','2',26,163,'&',
+129,160,211,212,18,'I',169,234,13,168,26,6,150,'1',155,134,'p',8,173,3,183,
+'J','S',26,20,'2',222,'b',240,160,'a','>',205,'f',29,170,227,'[',179,139,
+'\'','L','o',211,':',178,'0',162,134,'*','>','8',24,153,230,147,'R','?',23,
+'r','E','8','P',144,237,7,140,'W'};
+
+DEFINE_TEST(test_read_format_tbz)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_all(a));
+	assert(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assert(0 == archive_read_next_header(a, &ae));
+	assert(archive_compression(a) == ARCHIVE_COMPRESSION_BZIP2);
+	assert(archive_format(a) == ARCHIVE_FORMAT_TAR_USTAR);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_write_format_shar_empty.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_shar_empty.c,v 1.2 2007/05/29 01:00:21 kientzle Exp $");
+
+/*
+ * Check that an "empty" shar archive is correctly created as an empty file.
+ */
+
+DEFINE_TEST(test_write_format_shar_empty)
+{
+	struct archive *a;
+	char buff[2048];
+	size_t used;
+
+	/* Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_shar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	/* 1-byte block size ensures we see only the required bytes. */
+	/* We're not testing the padding here. */
+	assertA(0 == archive_write_set_bytes_per_block(a, 1));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+	failure("Empty shar archive should be exactly 0 bytes, was %d.", used);
+	assert(used == 0);
+}
--- /dev/null
+++ lib/libarchive/test/test_read_format_gtar_sparse.c
@@ -0,0 +1,927 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_gtar_sparse.c,v 1.6 2007/08/18 21:53:25 kientzle Exp $");
+
+
+struct contents {
+	off_t	o;
+	size_t	s;
+	char *d;
+};
+
+struct contents archive_contents_sparse[] = {
+	{ 1000000, 1, "a" },
+	{ 2000000, 1, "a" },
+	{ 3145728, 0, NULL }
+};
+
+struct contents archive_contents_sparse2[] = {
+	{ 1000000, 1, "a" },
+	{ 2000000, 1, "a" },
+	{ 3000000, 1, "a" },
+	{ 4000000, 1, "a" },
+	{ 5000000, 1, "a" },
+	{ 6000000, 1, "a" },
+	{ 7000000, 1, "a" },
+	{ 8000000, 1, "a" },
+	{ 9000000, 1, "a" },
+	{ 10000000, 1, "a" },
+	{ 11000000, 1, "a" },
+	{ 12000000, 1, "a" },
+	{ 13000000, 1, "a" },
+	{ 14000000, 1, "a" },
+	{ 15000000, 1, "a" },
+	{ 16000000, 1, "a" },
+	{ 17000000, 1, "a" },
+	{ 18000000, 1, "a" },
+	{ 19000000, 1, "a" },
+	{ 20000000, 1, "a" },
+	{ 21000000, 1, "a" },
+	{ 22000000, 1, "a" },
+	{ 23000000, 1, "a" },
+	{ 24000000, 1, "a" },
+	{ 25000000, 1, "a" },
+	{ 26000000, 1, "a" },
+	{ 27000000, 1, "a" },
+	{ 28000000, 1, "a" },
+	{ 29000000, 1, "a" },
+	{ 30000000, 1, "a" },
+	{ 31000000, 1, "a" },
+	{ 32000000, 1, "a" },
+	{ 33000000, 1, "a" },
+	{ 34000000, 1, "a" },
+	{ 35000000, 1, "a" },
+	{ 36000000, 1, "a" },
+	{ 37000000, 1, "a" },
+	{ 38000000, 1, "a" },
+	{ 39000000, 1, "a" },
+	{ 40000000, 1, "a" },
+	{ 41000000, 1, "a" },
+	{ 42000000, 1, "a" },
+	{ 43000000, 1, "a" },
+	{ 44000000, 1, "a" },
+	{ 45000000, 1, "a" },
+	{ 46000000, 1, "a" },
+	{ 47000000, 1, "a" },
+	{ 48000000, 1, "a" },
+	{ 49000000, 1, "a" },
+	{ 50000000, 1, "a" },
+	{ 51000000, 1, "a" },
+	{ 52000000, 1, "a" },
+	{ 53000000, 1, "a" },
+	{ 54000000, 1, "a" },
+	{ 55000000, 1, "a" },
+	{ 56000000, 1, "a" },
+	{ 57000000, 1, "a" },
+	{ 58000000, 1, "a" },
+	{ 59000000, 1, "a" },
+	{ 60000000, 1, "a" },
+	{ 61000000, 1, "a" },
+	{ 62000000, 1, "a" },
+	{ 63000000, 1, "a" },
+	{ 64000000, 1, "a" },
+	{ 65000000, 1, "a" },
+	{ 66000000, 1, "a" },
+	{ 67000000, 1, "a" },
+	{ 68000000, 1, "a" },
+	{ 69000000, 1, "a" },
+	{ 70000000, 1, "a" },
+	{ 71000000, 1, "a" },
+	{ 72000000, 1, "a" },
+	{ 73000000, 1, "a" },
+	{ 74000000, 1, "a" },
+	{ 75000000, 1, "a" },
+	{ 76000000, 1, "a" },
+	{ 77000000, 1, "a" },
+	{ 78000000, 1, "a" },
+	{ 79000000, 1, "a" },
+	{ 80000000, 1, "a" },
+	{ 81000000, 1, "a" },
+	{ 82000000, 1, "a" },
+	{ 83000000, 1, "a" },
+	{ 84000000, 1, "a" },
+	{ 85000000, 1, "a" },
+	{ 86000000, 1, "a" },
+	{ 87000000, 1, "a" },
+	{ 88000000, 1, "a" },
+	{ 89000000, 1, "a" },
+	{ 90000000, 1, "a" },
+	{ 91000000, 1, "a" },
+	{ 92000000, 1, "a" },
+	{ 93000000, 1, "a" },
+	{ 94000000, 1, "a" },
+	{ 95000000, 1, "a" },
+	{ 96000000, 1, "a" },
+	{ 97000000, 1, "a" },
+	{ 98000000, 1, "a" },
+	{ 99000000, 1, "a" },
+	{ 99000001, 0, NULL }
+};
+
+struct contents archive_contents_nonsparse[] = {
+	{ 0, 1, "a" },
+	{ 1, 0, NULL }
+};
+
+/*
+ * Describe an archive with three entries:
+ *
+ * File 1: named "sparse"
+ *   * a length of 3145728 bytes (3MiB)
+ *   * a single 'a' byte at offset 1000000
+ *   * a single 'a' byte at offset 2000000
+ * File 2: named "sparse2"
+ *   * a single 'a' byte at offset 1,000,000, 2,000,000, ..., 99,000,000
+ *   * length of 99,000,001
+ * File 3: named 'non-sparse'
+ *   * length of 1 byte
+ *   * contains a single byte 'a'
+ */
+
+struct archive_contents {
+	const char *filename;
+	struct contents *contents;
+} files[] = {
+	{ "sparse", archive_contents_sparse },
+	{ "sparse2", archive_contents_sparse2 },
+	{ "non-sparse", archive_contents_nonsparse },
+	{ NULL, NULL }
+};
+
+
+/* Old GNU tar sparse format, as created by gtar 1.13 */
+static unsigned char archive_old_gtar_1_13[] = {
+31,139,8,0,30,'%',193,'F',0,3,237,215,'K','n',219,'H',20,133,'a',246,'N',
+180,129,6,170,'n',189,22,210,'+',208,' ',131,12,146,14,',','g',255,'}',201,
+192,142,17,29,'(','A',159,24,'l',160,255,207,3,219,'e',193,186,'$',127,241,
+'q',251,'r','}',186,'}',216,222,'U',169,165,204,222,183,'R','J',']',163,188,
+253,190,139,252,'u',171,'e',206,18,17,189,205,'m','_',')',177,']',254,'z',
+223,177,190,249,'z','{',190,'>',']','.',219,243,199,'O',15,'_',247,179,191,
+255,'k',251,'.','h',179,231,'>','z',221,'#',175,'?',231,'^',10,177,'^',219,
+':',188,172,239,'K',15,223,160,246,'o',175,250,253,211,'_',127,255,191,196,
+255,8,253,0,231,185,29,215,255,'x',215,247,'x','x',253,175,'=',218,221,245,
+'?','j',31,'\\',255,31,'\\',255,'[','o','j','}','E',233,'?',174,255,'Q',202,
+'X','u',212,213,212,'M',194,'~',167,213,'J',31,226,191,197,'\\','e',138,245,
+22,163,'/',181,158,27,161,182,162,'G',12,181,21,'}',214,170,182,'"','G',29,
+'w','[',177,175,143,'Y',213,156,'3','c','Q','s',206,209,170,154,'s',213,':',
+139,'Z',207,157,'-',230,220,227,157,'b',206,154,'{','-',196,156,185,15,218,
+20,'s',214,',','=',196,156,'5',223,'s',138,'9','k',180,213,196,156,'5','V',
+30,'O',177,190,'G',161,230,'l','+',214,'}',21,175,199,191,246,'V',155,154,
+183,207,181,212,188,'#','f','S',243,142,'c',171,239,215,'g','4','U','w',157,
+'3','T',221,'G',196,'j',191,230,'f',23,'1','g',228,';','w','1','g',148,172,
+'H',204,25,181,198,16,'s','F','~','F','T',191,217,196,'R',253,230,185,'j',
+170,'~',143,143,147,154,'3',15,'O','U','s',246,220,0,'5','g',238,132,'P',
+'s',246,'5',167,154,'s',180,161,250,141,177,218,'}',191,223,143,127,30,205,
+'P',29,31,31,127,'5',239,218,191,212,250,'<','6',227,199,245,150,19,'7','1',
+'o','+','3',255,145,'X',175,'Q','U',199,'-',247,210,'}',199,251,233,168,'N',
+213,239,'q',154,18,'s',182,204,189,171,'9','s',247,21,'5','g',198,219,213,
+156,'=',207,130,'j',206,145,225,169,'9',247,'U','5','g','^',247,'T',191,'/',
+167,211,251,245,181,134,154,'3',15,'s','U','s',230,'^',27,15,142,127,223,
+247,136,152,'7','?','<','U','u',220,'3','z',213,'q',207,15,180,234,248,'8',
+253,139,'y','{',134,'7',197,188,'=','s',12,177,'_',243,206,' ',239,'"',196,
+'z',207,'3',134,154,'3','?',133,170,223,'>',242,'D',172,230,28,'#','T',191,
+199,'e','J',205,'9','3','/','5','g','~','l',154,154,'s','e','0','b',206,177,
+167,'\'',230,28,185,'G','U',191,251,177,'W',253,142,'<',209,171,'~',143,203,
+233,131,227,'?',242,196,'t',127,215,176,175,175,'P',247,5,'#','s','Q',247,
+5,'#',195,'T',247,5,'#',15,180,234,'8','O',218,']','u',156,135,161,169,142,
+143,203,191,154,'s',238,'W',0,181,190,127,137,245,227,'f',232,205,'z',145,
+'7','F',248,'%','<',191,195,'A','?','p',208,15,28,244,3,7,253,192,'A','?',
+'p',184,253,208,31,28,244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
+28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
+28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
+28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
+28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,3,7,253,192,'A','?','p',208,15,
+28,'<',255,227,'L',244,3,7,253,192,'A','?','p',208,15,28,244,3,7,253,192,
+193,243,'?',206,'D','?','p',208,15,28,244,227,249,252,247,231,'?','o','_',
+174,'O',183,15,239,247,30,165,150,'2','{',223,'J',')','u',141,242,246,251,
+139,173,150,'9','K','D',244,'6',243,245,'5',127,218,'.',229,253,'F',250,238,
+235,237,249,250,'t',185,'l',207,31,'?','=','|',221,207,254,14,0,0,0,0,0,0,
+0,255,'1',255,0,178,'s',140,'2',0,240,0,0};
+
+/* Old GNU tar sparse format, as created by gtar 1.17 */
+static unsigned char archive_old_gtar_1_17[] = {
+31,139,8,0,30,'%',193,'F',0,3,237,215,']','r',19,'G',20,134,'a','e','\'',
+218,'@',170,186,'O',255,'-','$','+',208,5,23,'\\','@','(',203,236,'?','g',
+134,216,'8',232,139,160,248,'P','M',170,242,'>',20,'%',211,'6',214,153,158,
+'W','#',205,245,211,229,233,250,238,244,'P','%',205,222,183,199,186,'F','y',
+251,184,137,252,'{',170,'e',206,18,17,189,205,'S','~','w',197,'<',157,255,
+'x',236,'X','_','|',190,'>','_',158,206,231,211,243,251,15,'w',127,238,'{',
+223,255,'i',219,22,180,217,235,182,11,127,239,200,235,215,185,'K','!',214,
+'k',255,242,239,151,245,253,235,'{','O',240,250,31,'~',185,203,175,255,149,
+248,31,161,159,'c',']',247,235,127,'<',244,'9',238,'^',255,'k',143,'V',234,
+'?',175,255,17,'5',127,156,235,255,191,'^',255,'[',235,'M',173,175,'(',253,
+219,245,223,'J',25,171,142,186,182,'m','V',207,158,251,223,135,248,'m','1',
+'W',153,'b',189,197,232,'K',173,231,'A',168,163,232,17,'C',29,'E',159,181,
+170,163,200,'Q',199,205,'Q','l',235,'c','V','5',231,172,'}',168,'9',231,'h',
+'U',205,185,'j',157,'E',173,231,'f',139,'9',243,'a','N','1','g',205,']',11,
+'1','g',238,'A',155,'b',206,154,165,135,152,179,230,'s','N','1','g',141,182,
+154,152,179,198,202,243,')',214,183,'(',212,156,'m',197,186,173,226,245,252,
+215,222,'j','S',243,246,185,150,154,'w',196,'l','j',222,177,31,245,237,250,
+140,166,234,174,'s',134,170,'{',143,'X',237,'k',30,'v',17,'s','F','>','s',
+23,'s','F',201,138,196,156,'Q','k',12,'1','g',228,'k','D',245,155,'M',',',
+213,'o','^',171,166,234,'w',127,'9',169,'9',243,244,'T','5','g',207,3,'P',
+'s',230,'&',132,154,179,175,'9',213,156,163,13,213,'o',140,213,'n',251,253,
+'z',254,243,'l',134,234,'x',127,249,171,'y',215,246,'G',173,207,253,'0',190,
+']','o','9','q',19,243,182,'2',243,23,137,245,26,'U','u',220,'r',151,'n',
+';',222,'.','G','u',170,'~',247,203,148,152,179,'e',238,']',205,153,219,'W',
+212,156,25,'o','W','s',246,188,10,170,'9','G',134,167,230,220,'V',213,156,
+249,190,167,250,'}',185,156,222,174,175,'5',212,156,'y',154,171,154,'3','w',
+'m',220,'9',255,'}',219,17,'1','o',190,'x',170,234,184,'g',244,170,227,158,
+'/','h',213,241,'~',249,23,243,246,12,'o',138,'y','{',230,24,'b','_',243,
+147,'A','~',138,16,235,'=',175,24,'j',206,'|',21,170,'~',251,200,11,177,154,
+'s',140,'P',253,238,'o','S','j',206,153,'y',169,'9',243,'e',211,212,156,'+',
+131,17,'s',142,'-','=','1',231,200,29,'U',253,'n',231,'^',245,';',242,'B',
+175,250,221,223,'N',239,156,255,145,23,166,219,'O',13,219,250,10,245,185,
+'`','d','.',234,'s',193,200,'0',213,231,130,145,'\'','Z','u',156,23,237,174,
+':',206,211,208,'T',199,251,219,191,154,'s','n',239,0,'j','}',251,'#',214,
+247,15,'C','o',214,139,252,'`',132,31,194,253,27,28,244,3,7,253,192,'A','?',
+'p',208,15,28,244,3,135,219,15,253,193,'A','?','p',208,15,28,244,3,7,253,
+192,'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,
+15,28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
+'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
+28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
+'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
+28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
+'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
+28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
+'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?','p',208,15,
+28,244,3,7,253,192,193,253,'?',142,'D','?','p',208,15,28,244,3,7,253,192,
+'A','?','p',208,15,28,220,255,227,'H',244,3,7,253,192,'A','?',158,143,127,
+'~',252,253,250,233,242,'t','}',247,184,231,'(','i',246,190,'=',214,'5',202,
+219,199,23,167,'Z',230,',',17,209,219,'<',149,'Z','#',234,233,'\\',30,'7',
+210,'W',159,175,207,151,167,243,249,244,252,254,195,221,159,251,222,247,1,
+0,0,0,0,0,0,0,248,15,249,11,162,'$',218,227,0,240,0,0};
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+/* libarchive < 1.9 does not support this. */
+/* GNU tar "posix" sparse format 0.0, as created by gtar 1.17 */
+static unsigned char archive_0_0_gtar_1_17[] = {
+31,139,8,0,31,'%',193,'F',0,3,237,217,207,'n',218,'X',20,199,'q',214,'<',
+5,'/','0',228,222,'s','}',255,'x',193,'z',186,26,'u',211,7,240,164,174,20,
+205,'$',169,'0',145,'2',243,244,'5','%',205,144,200,193,'p',14,141,203,232,
+251,217,'P','A',14,'8','9',191,'[',253,',',150,'W',31,155,199,15,'m',243,
+185,']','w',203,232,156,148,171,238,'k',179,238,218,217,249,184,'^',170,170,
+237,163,207,209,237,'?','~','W','9',153,'y',151,146,19,145,'*',228,153,243,
+161,'J','2','[','<',158,241,26,222,244,208,'m',154,'u',127,')',214,247,'y',
+250,']',158,31,'/',132,228,197,239,127,'|','Z',238,'v',190,236,'n',254,'m',
+'W',193,'W','1','K',153,'K',218,127,233,238,225,246,207,191,239,175,255,234,
+'V','a','.','e',255,149,251,'/','_',186,'v',179,170,'{','!',205,'_',190,225,
+'v',234,159,'M',219,173,162,151,185,212,3,'c',190,31,'+','Y','N',158,'{',
+190,202,'8','8',231,230,226,22,205,230,230,182,']','y','_',178,'K',193,'e',
+191,'}',238,250,229,'s','n','>',245,6,166,'u',246,195,'>','`',228,252,203,
+246,184,252,'w',254,'S',127,254,'}',14,'a',182,'x',151,'C',244,227,252,247,
+177,'8',248,'s','c',175,'_',232,249,183,'j',166,190,0,'\\','4',242,'3',173,
+229,'[',253,'O',206,247,25,135,255,255,247,193,247,157,239,'U',255,139,'1',
+210,255,222,195,203,'*',247,189,255,213,245,'n','/',3,149,'l','W',0,235,250,
+151,'h',128,178,157,'s',229,244,230,216,207,229,170,':','y',174,234,231,'R',
+'q','\'',207,197,237,156,'?',253,239,146,250,185,24,'O',255,187,148,']',2,
+'O',159,'S',238,175,30,223,'_','p','C','{','w',227,11,28,30,244,227,27,28,
+30,148,241,21,14,15,134,241,29,14,15,'V',227,'K',28,30,'L',227,'[','|','c',
+'p','|',141,195,131,'Y',187,199,162,221,'c',173,220,163,'8',229,30,197,'+',
+247,'(',162,220,163,'T',202,'=','J',165,220,163,'D',229,30,'%',')',247,'(',
+'Y',187,199,162,221,'c',173,220,'c','p',202,'=',6,'Q',238,'1',136,'r',143,
+'!','(',247,24,'*',229,30,'C','T',238,'1','$',229,30,'C',214,238,177,'(',
+247,248,'t',28,'O',191,212,202,')',247,'X',29,209,'o',134,7,143,'(','8',195,
+131,'G','4',156,225,193,'#','*',206,240,224,17,29,'g','x',240,136,146,'3',
+'<','x','D',203,'y','c','P',187,'G','m',207,137,218,158,19,181,'=','\'','j',
+'{','N',212,246,156,168,237,'9','Q',219,'s',162,182,231,'D','m',207,137,218,
+158,19,181,'=','\'','i','{','N',210,246,156,164,237,'9','I',219,'s',146,182,
+231,'$','m',207,'I',218,158,147,180,'=','\'','i','{','N',210,246,156,172,
+237,'9','Y',219,'s',178,182,231,'d','m',207,201,218,158,147,181,'=','\'',
+'k','{','N',214,246,156,172,237,'9','E',219,'s',138,182,231,20,'m',207,')',
+218,158,'S',180,'=',167,'h','{','N',209,246,156,162,237,'9','E',219,'s',138,
+182,231,20,'m',207,169,181,'=',167,214,246,156,'Z',219,'s','j','m',207,169,
+181,'=',167,214,246,156,'Z',219,'s','j','m',207,169,'G','z',142,175,3,'_',
+174,255,'_',236,150,'{',198,'/','{',6,28,252,254,199,'W',18,156,127,245,253,
+191,'8','I','|',255,127,9,248,254,22,22,228,7,22,228,7,22,228,7,22,228,7,
+22,228,7,22,214,252,144,'?','X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',
+144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,
+31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,
+'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',
+144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,
+31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',145,31,
+'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,143,')',
+145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X','p',255,
+143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',
+'p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,'X',144,
+31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',144,31,
+'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,'X',144,31,'X',
+144,31,'X',144,31,'X','p',255,143,')',145,31,'X',144,31,'X',144,31,155,229,
+213,199,230,241,'C',219,'|','n',215,221,'2',':','\'',229,234,238,254,238,
+183,238,'k',179,238,218,'3','}',134,235,165,170,218,'>',250,28,221,254,227,
+'N',255,'o',239,'R','r','"','R',133,'<','s',190,146,232,'g',139,199,'3','}',
+254,'A',15,221,166,'Y',247,151,'b','}',159,167,'_',229,249,241,'B',136,'[',
+'4',155,155,219,'v',229,'}',201,'.',5,151,221,188,127,238,250,245,'s','S',
+'_','\'','~',142,179,31,246,1,163,231,223,237,159,255,212,159,127,137,210,
+159,255,'w','9','D','?',206,127,31,248,131,'?','7',246,250,133,158,127,0,
+0,0,0,0,0,0,0,0,0,0,0,0,192,'e',250,6,'X',180,13,'8',0,24,1,0};
+#endif
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+/* libarchive < 1.9 does not support this. */
+/* GNU tar "posix" sparse format 0.1, as created by gtar 1.17 */
+static unsigned char archive_0_1_gtar_1_17[] = {
+31,139,8,0,31,'%',193,'F',0,3,237,215,205,'n',26,'W',24,135,'q',214,'\\',
+5,23,224,194,249,'>','3',11,182,'m','V','U',164,170,23,'0','u','f','a','%',
+'v',',',198,150,172,'^','}',135,15,'\'',127,187,9,'T','z',137,167,'D',207,
+'o',195,4,'l','^','0',207,'!',231,',','W',239,187,167,'w','}',247,161,223,
+12,203,236,'\\',244,171,225,190,219,12,253,236,'|',220,168,164,180,189,245,
+'5',';',189,221,9,'9',204,188,'+',197,133,16,'R',172,'3',231,'c',202,'u',
+182,'x',':',227,'k',248,174,199,225,161,219,140,'/',197,250,'<',135,247,242,
+229,246,'B',132,186,248,237,247,'?',151,251,207,'|','9',220,252,221,175,163,
+31,255,250,161,153,135,162,15,221,'=',222,254,245,233,243,245,199,'a',29,
+'_','?',210,221,246,235,253,245,'<','{','}',228,182,187,'_',183,163,'X',174,
+178,15,'W','~',188,'l','j',216,']',31,134,'\\',185,'y','p',139,238,225,'f',
+'|',10,239,155,234,'J','t',213,'o',239,187,'~','y',159,155,'O',253,151,250,
+'9','-','W',227,231,245,199,238,227,250,245,230,'S',255,'C',190,2,'N',172,
+255,176,']','.','_',215,127,25,215,127,246,169,204,22,'o',178,136,158,215,
+255,152,219,209,159,';',245,248,133,174,127,171,'n',234,23,128,139,'F','?',
+211,'Z','~','o',255,23,206,'7',227,212,247,127,241,178,255,'K','n',187,255,
+203,'1',179,255,'{',11,161,249,215,254,175,'m','w',239,'`',220,133,213,'o',
+'o',0,219,246,245,'C','_','w',128,'a',238,'C','[',254,227,'&','0','l',175,
+']',179,223,16,142,215,'5',165,221,'u',26,175,'K',227,'v',215,'y','{',237,
+247,191,'[',198,235,156,247,191,219,236,255,206,251,'k','y',254,'V',158,223,
+';',25,224,189,'L',240,'A','F',248,'(','3','|',146,'!',190,200,20,'_',244,
+'m','T',157,211,232,156,'V',230,4,'\'','s',130,151,'9','!',200,156,144,'d',
+'N','H','2','\'','d',253,'{',21,153,19,170,206,'i','t','N','+','s',162,147,
+'9','1',200,156,24,'d','N',140,186,'9','O',250,193,'d',153,19,139,204,137,
+'U',231,'4','2',231,240,'v',220,225,31,'2','\'',233,231,159,'4',128,244,162,
+0,'M',' ','i',3,'I','#','H','Z','A',210,12,146,'v',144,181,131,172,29,'d',
+237,' ','k',7,'Y',';',200,218,'A',214,14,178,'v',144,181,131,172,29,20,237,
+160,'h',7,'E',';','(',218,'A',209,14,138,'v','P',180,131,162,29,20,237,160,
+'h',7,'U',';',168,218,'A',213,14,170,'v','P',181,131,170,29,'T',237,160,'j',
+7,'U',';','h',180,131,'F',';','h',180,131,'F',';','h',180,131,'F',';','h',
+180,131,230,197,151,193,139,'o',3,237,160,209,14,'Z',237,160,213,14,'Z',237,
+160,213,14,'Z',237,160,213,14,'Z',237,160,213,14,218,231,14,'|',27,255,231,
+231,219,'c',231,191,'s','m',1,142,254,255,239,'S',136,206,191,'>',255,133,
+228,'9',255,']',2,246,239,176,160,31,'X',208,15,',',232,7,22,244,3,11,250,
+129,133,181,31,250,131,5,253,192,130,'~','`','A','?',176,160,31,'X',208,15,
+',',232,7,22,156,255,'1','%',250,129,5,253,192,130,'~','`','A','?',176,160,
+31,'X',208,15,',','8',255,'c','J',244,3,11,250,129,5,253,192,130,'~','`',
+'A','?',176,160,31,'X','p',254,199,148,232,7,22,244,3,11,250,129,5,253,192,
+130,'~','`','A','?',176,224,252,143,')',209,15,',',232,7,22,244,3,11,250,
+129,5,253,192,130,'~','`',193,249,31,'S',162,31,'X',208,15,',',232,7,22,244,
+3,11,250,129,5,253,192,130,243,'?',166,'D','?',176,160,31,'X',208,15,',',
+232,7,22,244,3,11,250,129,5,231,127,'L',137,'~','`','A','?',176,160,31,'X',
+208,15,',',232,7,22,244,3,11,206,255,152,18,253,192,130,'~','`','A','?',176,
+160,31,'X',208,15,',',232,7,22,156,255,'1','%',250,129,5,253,192,130,'~',
+'`','A','?',176,160,31,'X',208,15,',','8',255,'c','J',244,3,11,250,129,5,
+253,192,130,'~','`','A','?',176,160,31,'X','p',254,199,148,232,7,22,244,3,
+11,250,177,'Y',174,222,'w','O',239,250,238,'C',191,25,150,217,185,232,'W',
+'w',159,239,'~',25,238,187,205,208,159,'i',134,27,149,148,182,183,190,'f',
+167,183,'{',227,181,'w',165,184,16,'B','J',227,253,'>',133,152,'g',139,167,
+'3',205,'?',234,'q','x',232,'6',227,'K',177,'>',207,225,173,'|',185,189,16,
+193,'-',186,135,155,219,'~',237,'}','S',']',137,174,186,249,'x',223,245,235,
+251,166,'~',157,248,'1',206,190,216,191,225,228,250,'w',178,254,'c',25,215,
+127,200,193,207,22,'o',178,136,158,215,255,24,252,209,159,';',245,248,133,
+174,127,0,0,0,0,0,240,243,251,7,233,'Q','N','O',0,240,0,0};
+#endif
+
+#if ARCHIVE_VERSION_STAMP >= 1009000
+/* libarchive < 1.9 does not support this. */
+/* GNU tar "posix" sparse format 1.0, as created by gtar 1.17 */
+static unsigned char archive_1_0_gtar_1_17[] = {
+31,139,8,0,' ','%',193,'F',0,3,237,215,205,'n',26,'I',20,134,'a',214,'\\',
+5,'7',16,168,255,234,'^','x',155,'d',21,'E',138,230,2,'Z','I','/',24,197,
+'N',4,142,'d',205,213,'O',1,182,245,217,178,'A',163,'C',220,131,242,'>',155,
+'n',183,'m',14,'?','o',161,174,229,234,243,'p',247,'q',28,190,141,155,237,
+'2',';',23,211,'j',251,'s',216,'l',199,217,249,184,166,164,180,';',250,154,
+157,30,247,130,207,'3',239,'J','q','!',132,148,218,'u',31,'S',142,179,197,
+221,25,159,195,171,'~','m','o',135,'M','{','*',214,199,185,127,'-',143,199,
+11,17,194,226,195,167,191,150,135,207,'|','y','=',252,253,'c','s',229,231,
+207,174,174,'o',218,'U','7',15,'E',175,222,12,215,227,213,225,'|',30,189,
+254,'f','3',14,223,183,235,127,198,171,232,'S',174,161,155,7,183,24,'n',215,
+237,207,189,239,170,'+',209,'U',191,187,246,245,233,'5','7',159,250,205,248,
+3,'-','W',237,131,251,178,255,220,222,175,191,143,191,229,'+',224,196,250,
+143,187,229,242,184,254,'c','i',235,'?',251,28,'f',139,'7','Y','D',15,235,
+191,181,'x',244,239,'N',253,254,'B',215,127,156,247,'M',',',243,236,195,220,
+183,211,174,134,253,249,195,218,'e','U',226,136,'a',234,'\'',128,139,'F',
+'?',211,'Z',190,'v',255,31,206,'7',227,244,253,127,'}','~',255,159,'c',229,
+254,255,'-',252,167,251,255,250,202,253,127,187,'[',8,'/','n',0,250,'~',255,
+'f',248,23,'v',0,129,29,192,255,193,177,251,255,'s','}',5,28,']',255,237,
+'6','3',':',255,252,254,'?',164,202,253,255,'[',232,251,215,'6',0,'a','w',
+238,186,195,'f',160,157,215,148,246,231,169,157,151,206,237,207,243,238,220,
+31,254,183,180,243,156,15,255,219,29,222,132,195,185,'<','~','/',143,239,
+157,12,240,'^','&',248,' ','#','|',148,25,'>',201,16,'_','d',138,'/',250,
+'2',170,206,233,'t','N','/','s',130,147,'9',193,203,156,16,'d','N','H','2',
+'\'','$',153,19,178,190,'_','E',230,132,170,'s',':',157,211,203,156,232,'d',
+'N',12,'2','\'',6,153,19,163,'n',204,146,'~','0','Y',230,196,'"','s','b',
+213,'9',157,204,185,127,'9',238,254,7,153,147,244,243,'O',26,'@','z','R',
+128,'&',144,180,129,164,17,'$',173,' ','i',6,'I',';',200,218,'A',214,14,178,
+'v',144,181,131,172,29,'d',237,' ','k',7,'Y',';',200,218,'A',214,14,138,'v',
+'P',180,131,162,29,20,237,160,'h',7,'E',';','(',218,'A',209,14,138,'v','P',
+180,131,170,29,'T',237,160,'j',7,'U',';',168,218,'A',213,14,170,'v','P',181,
+131,170,29,'t',218,'A',167,29,'t',218,'A',167,29,'t',218,'A',167,29,'t',218,
+'A',247,228,203,224,201,183,129,'v',208,'i',7,189,'v',208,'k',7,189,'v',208,
+'k',7,189,'v',208,'k',7,189,'v',208,'k',7,253,'C',7,190,143,220,'o','X',177,
+127,131,5,253,192,130,'~','`','A','?',176,160,31,'X',208,15,',',172,253,208,
+31,',',232,7,22,244,3,11,250,129,5,253,192,130,'~','`','A','?',176,'`',255,
+143,')',209,15,',',232,7,22,244,3,11,250,129,5,253,192,130,'~','`',193,254,
+31,'S',162,31,'X',208,15,',',232,7,22,244,3,11,250,129,5,253,192,130,253,
+'?',166,'D','?',176,160,31,'X',208,15,',',232,7,22,244,3,11,250,129,5,251,
+127,'L',137,'~','`','A','?',176,160,31,'X',208,15,',',232,7,22,244,3,11,246,
+255,152,18,253,192,130,'~','`','A','?',176,160,31,'X',208,15,',',232,7,22,
+236,255,'1','%',250,129,5,253,192,130,'~','`','A','?',176,160,31,'X',208,
+15,',',216,255,'c','J',244,3,11,250,129,5,253,192,130,'~','`','A','?',176,
+160,31,'X',176,255,199,148,232,7,22,244,3,11,250,129,5,253,192,130,'~','`',
+'A','?',176,'`',255,143,')',209,15,',',232,7,22,244,3,11,250,129,5,253,192,
+130,'~','`',193,254,31,'S',162,31,'X',208,15,',',232,7,22,244,3,11,250,129,
+5,253,192,130,253,'?',166,'D','?',176,160,31,'X',208,143,205,'r',245,'y',
+184,251,'8',14,223,198,205,'v',153,157,139,'i','u',243,227,230,221,246,231,
+176,217,142,'g',154,225,154,146,210,238,232,'k','v','z','<','h',231,222,149,
+226,'B',8,')',181,235,'>',133,'v','X',220,157,'i',254,'Q',191,182,183,195,
+166,'=',21,235,227,220,191,148,199,227,133,8,'n','1',220,174,175,199,'+',
+239,187,234,'J','t',213,205,219,181,175,207,175,'M',253,'<',241,'{',156,'}',
+177,191,224,228,250,'w',178,254,'c','i',235,'?',228,224,'g',139,'7','Y','D',
+15,235,191,5,127,244,239,'N',253,254,'B',215,'?',0,0,0,128,'?',199,191,200,
+'e','(',171,0,240,0,0};
+#endif
+
+
+/*
+ * The following test archive is a little odd.  First, it's uncompressed,
+ * because that exercises some of the block reassembly code a little harder.
+ * Second, it includes some leading comments prior to the sparse block
+ * description.  GNU tar doesn't do this, but I think it should, so I
+ * want to ensure that libarchive correctly ignores such comments.
+ */
+#if ARCHIVE_VERSION_STAMP >= 1009000
+
+/* Because it's uncompressed, I've made this archive a bit simpler. */
+struct archive_contents files_1_0b[] = {
+	{ "sparse", archive_contents_sparse },
+	{ "non-sparse", archive_contents_nonsparse },
+	{ NULL, NULL }
+};
+
+static unsigned char archive_1_0b[] = {
+'.','/','P','a','x','H','e','a','d','e','r','s','.','7','5','4','7','/','s',
+'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7',
+'5','0',0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','0','2',
+'1','5',0,'1','0','6','5','7','4','5','4','6','1','3',0,'0','1','3','4','2',
+'5',0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s',
+'t','a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,'2','2',' ','G','N','U','.','s','p','a','r','s',
+'e','.','m','a','j','o','r','=','1',10,'2','2',' ','G','N','U','.','s','p',
+'a','r','s','e','.','m','i','n','o','r','=','0',10,'2','6',' ','G','N','U',
+'.','s','p','a','r','s','e','.','n','a','m','e','=','s','p','a','r','s','e',
+10,'3','1',' ','G','N','U','.','s','p','a','r','s','e','.','r','e','a','l',
+'s','i','z','e','=','3','1','4','5','7','2','8',10,'2','0',' ','a','t','i',
+'m','e','=','1','1','8','6','8','7','9','7','9','9',10,'2','0',' ','c','t',
+'i','m','e','=','1','1','8','6','8','7','9','5','2','8',10,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'.','/','G',
+'N','U','S','p','a','r','s','e','F','i','l','e','.','7','5','4','7','/','s',
+'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',
+0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','3','0','0','0',
+0,'1','0','6','5','7','4','5','4','0','5','0',0,'0','1','5','1','1','2',0,
+' ','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a',
+'r',0,'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+
+/* 32 added bytes containing extra comments at beginning of sparse block */
+'#','!','g','n','u','-','s','p','a','r','s','e','-','f','o','r','m','a',
+'t',10,'#','f','o','r','m','a','t',':','1','.','0',10,
+
+'3',10,'9','9','9','9','3','6',10,'5','1','2',
+10,'1','9','9','9','8','7','2',10,'5','1','2',10,'3','1','4','5','7','2',
+'8',10,'0',10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+
+/* 32 removed bytes to preserve alignment. */
+/* 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, */
+0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'a',0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'a',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'.','/','P','a',
+'x','H','e','a','d','e','r','s','.','7','5','4','7','/','n','o','n','-','s',
+'p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',
+0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','0','0','5','0',
+0,'1','0','6','5','7','4','5','4','6','1','3',0,'0','1','4','2','1','2',0,
+' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a',
+'r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,'2','0',' ','a','t','i','m','e','=','1','1','8','6','8','7',
+'9','7','9','9',10,'2','0',' ','c','t','i','m','e','=','1','1','8','6','8',
+'7','9','5','4','4',10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,'n','o','n','-','s','p','a','r','s','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'0','0','0','0','6','4','4',0,'0','0','0','1','7','5','0',0,'0','0','0',
+'1','7','5','0',0,'0','0','0','0','0','0','0','0','0','0','1',0,'1','0','6',
+'5','7','4','5','4','0','7','0',0,'0','1','2','5','3','1',0,' ','0',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,'0','0',
+'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'t',
+'i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,'a',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+#endif
+
+/*
+ * A tricky piece of code that verifies the contents of a sparse
+ * archive entry against a description as defined at the top of this
+ * source file.
+ */
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * A convenience wrapper that adds the size of the buffer and the
+ * name of the buffer to any call.
+ */
+#define verify_archive(buffer, contents)	\
+	_verify_archive(buffer, sizeof(buffer), #buffer, contents)
+
+static void
+_verify_archive(void *buffer, size_t length, const char *name,
+    struct archive_contents *ac)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	int err;
+	/* data, size, offset of next expected block. */
+	struct contents expect;
+	/* data, size, offset of block read from archive. */
+	struct contents actual;
+
+	assert((a = archive_read_new()) != NULL);
+	assert(0 == archive_read_support_compression_all(a));
+	assert(0 == archive_read_support_format_tar(a));
+	assert(0 == read_open_memory(a, buffer, length, 3));
+
+	while (ac->filename != NULL) {
+		struct contents *cts = ac->contents;
+
+		assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+		failure("Name mismatch in archive %s", name);
+		assertEqualString(ac->filename, archive_entry_pathname(ae));
+
+		expect = *cts++;
+		while (0 == (err = archive_read_data_block(a,
+				 (const void **)&actual.d,
+				 &actual.s, &actual.o))) {
+			while (actual.s > 0) {
+				char c = *(char *)actual.d;
+				if(actual.o < expect.o) {
+					/*
+					 * Any byte before the expected
+					 * data must be NULL.
+					 */
+					failure("%s: pad at offset %d "
+					    "should be zero", name, actual.o);
+					assertEqualInt(c, 0);
+				} else if (actual.o == expect.o) {
+					/*
+					 * Data at matching offsets must match.
+					 */
+					assertEqualInt(c, *expect.d);
+					expect.d++;
+					expect.o++;
+					expect.s--;
+					/* End of expected? step to next expected. */
+					if (expect.s <= 0)
+						expect = *cts++;
+				} else {
+					/*
+					 * We found data beyond that expected.
+					 */
+					failure("%s: Unexpected trailing data",
+					    name);
+					assert(actual.o <= expect.o);
+					archive_read_finish(a);
+					return;
+				}
+				actual.d++;
+				actual.o++;
+				actual.s--;
+			}
+		}
+		failure("%s: should be end of entry", name);
+		assertEqualIntA(a, err, ARCHIVE_EOF);
+		failure("%s: Size returned at EOF must be zero", name);
+		assertEqualInt(actual.s, 0);
+#if ARCHIVE_VERSION_STAMP < 1009000
+		/* libarchive < 1.9 doesn't get this right */
+		skipping("offset of final sparse chunk");
+#else
+		failure("%s: Offset of final empty chunk must be same as file size", name);
+		assertEqualInt(actual.o, expect.o);
+#endif
+		/* Step to next file description. */
+		++ac;
+	}
+
+	err = archive_read_next_header(a, &ae);
+	assertEqualIntA(a, ARCHIVE_EOF, err);
+
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+DEFINE_TEST(test_read_format_gtar_sparse)
+{
+	/*
+	   FILE *t = fopen("archive_1_0.tgz", "w");
+	   fwrite(archive_1_0, sizeof(archive_1_0), 1, t);
+	   fclose(t);
+	*/
+
+	verify_archive(archive_old_gtar_1_13, files);
+
+	/*
+	 * libarchive < 1.9 doesn't support the newer sparse formats
+	 * from GNU tar 1.15 and 1.16.
+	 */
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("read support for GNUtar sparse format 0.0");
+#else
+	verify_archive(archive_0_0_gtar_1_17, files);
+#endif
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("read support for GNUtar sparse format 0.1");
+#else
+	verify_archive(archive_0_1_gtar_1_17, files);
+#endif
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("read support for GNUtar sparse format 1.0");
+#else
+	verify_archive(archive_1_0_gtar_1_17, files);
+	verify_archive(archive_1_0b, files_1_0b);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_read_format_empty.c
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_empty.c,v 1.1 2007/03/03 07:37:37 kientzle Exp $");
+
+static unsigned char archive[] = { };
+
+DEFINE_TEST(test_read_format_empty)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+	assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+	assertA(archive_compression(a) == ARCHIVE_COMPRESSION_NONE);
+	assertA(archive_format(a) == ARCHIVE_FORMAT_EMPTY);
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+}
+
+
--- /dev/null
+++ lib/libarchive/test/test_write_open_memory.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_open_memory.c,v 1.3 2007/05/29 01:00:21 kientzle Exp $");
+
+/* Try to force archive_write_open_memory.c to write past the end of an array. */
+static unsigned char buff[16384];
+
+DEFINE_TEST(test_write_open_memory)
+{
+	unsigned int i;
+	struct archive *a;
+	struct archive_entry *ae;
+	const char *name="/tmp/test";
+
+	/* Create a simple archive_entry. */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_set_pathname(ae, name);
+	archive_entry_set_mode(ae, S_IFREG);
+	assertEqualString(archive_entry_pathname(ae), name);
+
+	/* Try writing with different buffer sizes. */
+	/* Make sure that we get failure on too-small buffers, success on
+	 * large enough ones. */
+	for (i = 100; i < 1600; i++) {
+		size_t s;
+		size_t blocksize = 94;
+		assert((a = archive_write_new()) != NULL);
+		assertA(0 == archive_write_set_format_ustar(a));
+		assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+		assertA(0 == archive_write_set_bytes_per_block(a, blocksize));
+		buff[i] = 0xAE;
+		assertA(0 == archive_write_open_memory(a, buff, i, &s));
+		/* If buffer is smaller than a tar header, this should fail. */
+		if (i < (511/blocksize)*blocksize)
+			assertA(ARCHIVE_FATAL == archive_write_header(a,ae));
+		else
+			assertA(0 == archive_write_header(a, ae));
+		/* If buffer is smaller than a tar header plus 1024 byte
+		 * end-of-archive marker, then this should fail. */
+		if (i < 1536)
+			assertA(ARCHIVE_FATAL == archive_write_close(a));
+		else
+			assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+		assert(0 == archive_write_finish(a));
+#else
+		archive_write_finish(a);
+#endif
+		assert(buff[i] == 0xAE);
+		assert(s <= i);
+	}
+	archive_entry_free(ae);
+}
--- /dev/null
+++ lib/libarchive/test/test_entry.c
@@ -0,0 +1,675 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_entry.c,v 1.2 2007/07/06 15:43:11 kientzle Exp $");
+
+/*
+ * Most of these tests are system-independent, though a few depend on
+ * features of the local system.  Such tests are conditionalized on
+ * the platform name.  On unsupported platforms, only the
+ * system-independent features will be tested.
+ *
+ * No, I don't want to use config.h in the test files because I want
+ * the tests to also serve as a check on the correctness of config.h.
+ * A mis-configured library build should cause tests to fail.
+ */
+
+DEFINE_TEST(test_entry)
+{
+	char buff[128];
+	wchar_t wbuff[128];
+	struct stat st;
+	struct archive_entry *e, *e2;
+	const struct stat *pst;
+	unsigned long set, clear; /* For fflag testing. */
+	int type, permset, tag, qual; /* For ACL testing. */
+	const char *name; /* For ACL testing. */
+	const char *xname; /* For xattr tests. */
+	const void *xval; /* For xattr tests. */
+	size_t xsize; /* For xattr tests. */
+	int c;
+
+	assert((e = archive_entry_new()) != NULL);
+
+	/*
+	 * Basic set/read tests for all fields.
+	 * We should be able to set any field and read
+	 * back the same value.
+	 *
+	 * For methods that "copy" a string, we should be able
+	 * to overwrite the original passed-in string without
+	 * changing the value in the entry.
+	 *
+	 * The following tests are ordered alphabetically by the
+	 * name of the field.
+	 */
+	/* atime */
+	archive_entry_set_atime(e, 13579, 24680);
+	assertEqualInt(archive_entry_atime(e), 13579);
+	assertEqualInt(archive_entry_atime_nsec(e), 24680);
+	/* ctime */
+	archive_entry_set_ctime(e, 13580, 24681);
+	assertEqualInt(archive_entry_ctime(e), 13580);
+	assertEqualInt(archive_entry_ctime_nsec(e), 24681);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	/* dev */
+	archive_entry_set_dev(e, 235);
+	assertEqualInt(archive_entry_dev(e), 235);
+#else
+	skipping("archive_entry_dev()");
+#endif
+	/* devmajor/devminor are tested specially below. */
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	/* filetype */
+	archive_entry_set_filetype(e, AE_IFREG);
+	assertEqualInt(archive_entry_filetype(e), AE_IFREG);
+#else
+	skipping("archive_entry_filetype()");
+#endif
+	/* fflags are tested specially below */
+	/* gid */
+	archive_entry_set_gid(e, 204);
+	assertEqualInt(archive_entry_gid(e), 204);
+	/* gname */
+	archive_entry_set_gname(e, "group");
+	assertEqualString(archive_entry_gname(e), "group");
+	wcscpy(wbuff, L"wgroup");
+	archive_entry_copy_gname_w(e, wbuff);
+	assertEqualWString(archive_entry_gname_w(e), L"wgroup");
+	memset(wbuff, 0, sizeof(wbuff));
+	assertEqualWString(archive_entry_gname_w(e), L"wgroup");
+	/* hardlink */
+	archive_entry_set_hardlink(e, "hardlinkname");
+	assertEqualString(archive_entry_hardlink(e), "hardlinkname");
+	strcpy(buff, "hardlinkname2");
+	archive_entry_copy_hardlink(e, buff);
+	assertEqualString(archive_entry_hardlink(e), "hardlinkname2");
+	memset(buff, 0, sizeof(buff));
+	assertEqualString(archive_entry_hardlink(e), "hardlinkname2");
+	wcscpy(wbuff, L"whardlink");
+	archive_entry_copy_hardlink_w(e, wbuff);
+	assertEqualWString(archive_entry_hardlink_w(e), L"whardlink");
+	memset(wbuff, 0, sizeof(wbuff));
+	assertEqualWString(archive_entry_hardlink_w(e), L"whardlink");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	/* ino */
+	archive_entry_set_ino(e, 8593);
+	assertEqualInt(archive_entry_ino(e), 8593);
+#else
+	skipping("archive_entry_ino()");
+#endif
+	/* link */
+	/* TODO: implement these tests. */
+	/* mode */
+	archive_entry_set_mode(e, 0123456);
+	assertEqualInt(archive_entry_mode(e), 0123456);
+	/* mtime */
+	archive_entry_set_mtime(e, 13581, 24682);
+	assertEqualInt(archive_entry_mtime(e), 13581);
+	assertEqualInt(archive_entry_mtime_nsec(e), 24682);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	/* nlink */
+	archive_entry_set_nlink(e, 736);
+	assertEqualInt(archive_entry_nlink(e), 736);
+#else
+	skipping("archive_entry_nlink()");
+#endif
+	/* pathname */
+	archive_entry_set_pathname(e, "path");
+	assertEqualString(archive_entry_pathname(e), "path");
+	archive_entry_set_pathname(e, "path");
+	assertEqualString(archive_entry_pathname(e), "path");
+	strcpy(buff, "path2");
+	archive_entry_copy_pathname(e, buff);
+	assertEqualString(archive_entry_pathname(e), "path2");
+	memset(buff, 0, sizeof(buff));
+	assertEqualString(archive_entry_pathname(e), "path2");
+	wcscpy(wbuff, L"wpath");
+	archive_entry_copy_pathname_w(e, wbuff);
+	assertEqualWString(archive_entry_pathname_w(e), L"wpath");
+	memset(wbuff, 0, sizeof(wbuff));
+	assertEqualWString(archive_entry_pathname_w(e), L"wpath");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	/* rdev */
+	archive_entry_set_rdev(e, 532);
+	assertEqualInt(archive_entry_rdev(e), 532);
+#else
+	skipping("archive_entry_rdev()");
+#endif
+	/* rdevmajor/rdevminor are tested specially below. */
+	/* size */
+	archive_entry_set_size(e, 987654321);
+	assertEqualInt(archive_entry_size(e), 987654321);
+	/* symlink */
+	archive_entry_set_symlink(e, "symlinkname");
+	assertEqualString(archive_entry_symlink(e), "symlinkname");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	strcpy(buff, "symlinkname2");
+	archive_entry_copy_symlink(e, buff);
+	assertEqualString(archive_entry_symlink(e), "symlinkname2");
+	memset(buff, 0, sizeof(buff));
+	assertEqualString(archive_entry_symlink(e), "symlinkname2");
+#endif
+	archive_entry_copy_symlink_w(e, L"wsymlink");
+	assertEqualWString(archive_entry_symlink_w(e), L"wsymlink");
+	/* uid */
+	archive_entry_set_uid(e, 83);
+	assertEqualInt(archive_entry_uid(e), 83);
+	/* uname */
+	archive_entry_set_uname(e, "user");
+	assertEqualString(archive_entry_uname(e), "user");
+	wcscpy(wbuff, L"wuser");
+	archive_entry_copy_gname_w(e, wbuff);
+	assertEqualWString(archive_entry_gname_w(e), L"wuser");
+	memset(wbuff, 0, sizeof(wbuff));
+	assertEqualWString(archive_entry_gname_w(e), L"wuser");
+
+	/* Test fflags interface. */
+	archive_entry_set_fflags(e, 0x55, 0xAA);
+	archive_entry_fflags(e, &set, &clear);
+	failure("Testing set/get of fflags data.");
+	assertEqualInt(set, 0x55);
+	failure("Testing set/get of fflags data.");
+	assertEqualInt(clear, 0xAA);
+#ifdef __FreeBSD__
+	/* Converting fflags bitmap to string is currently system-dependent. */
+	/* TODO: Make this system-independent. */
+	assertEqualString(archive_entry_fflags_text(e),
+	    "uappnd,nouchg,nodump,noopaque,uunlnk");
+	/* TODO: Test archive_entry_copy_fflags_text_w() */
+#endif
+
+	/* See test_acl_basic.c for tests of ACL set/get consistency. */
+
+	/* Test xattrs set/get consistency. */
+	archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12);
+	assertEqualInt(1, archive_entry_xattr_reset(e));
+	assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualString(xname, "xattr1");
+	assertEqualString(xval, "xattrvalue1");
+	assertEqualInt(xsize, 12);
+	assertEqualInt(1, archive_entry_xattr_count(e));
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	archive_entry_xattr_clear(e);
+	assertEqualInt(0, archive_entry_xattr_reset(e));
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue1", 12);
+	assertEqualInt(1, archive_entry_xattr_reset(e));
+	archive_entry_xattr_add_entry(e, "xattr2", "xattrvalue2", 12);
+	assertEqualInt(2, archive_entry_xattr_reset(e));
+	assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualInt(0, archive_entry_xattr_next(e, &xname, &xval, &xsize));
+	assertEqualInt(ARCHIVE_WARN,
+	    archive_entry_xattr_next(e, &xname, &xval, &xsize));
+
+
+	/*
+	 * Test clone() implementation.
+	 */
+
+	/* Set values in 'e' */
+	archive_entry_clear(e);
+	archive_entry_set_atime(e, 13579, 24680);
+	archive_entry_set_ctime(e, 13580, 24681);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_dev(e, 235);
+#endif
+	archive_entry_set_fflags(e, 0x55, 0xAA);
+	archive_entry_set_gid(e, 204);
+	archive_entry_set_gname(e, "group");
+	archive_entry_set_hardlink(e, "hardlinkname");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_ino(e, 8593);
+#endif
+	archive_entry_set_mode(e, 0123456);
+	archive_entry_set_mtime(e, 13581, 24682);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_nlink(e, 736);
+#endif
+	archive_entry_set_pathname(e, "path");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_rdev(e, 532);
+#endif
+	archive_entry_set_size(e, 987654321);
+	archive_entry_set_symlink(e, "symlinkname");
+	archive_entry_set_uid(e, 83);
+	archive_entry_set_uname(e, "user");
+	/* Add an ACL entry. */
+	archive_entry_acl_add_entry(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+	    ARCHIVE_ENTRY_ACL_READ, ARCHIVE_ENTRY_ACL_USER, 77, "user77");
+	/* Add an extended attribute. */
+	archive_entry_xattr_add_entry(e, "xattr1", "xattrvalue", 11);
+
+	/* Make a clone. */
+	e2 = archive_entry_clone(e);
+
+	/* Clone should have same contents. */
+	assertEqualInt(archive_entry_atime(e2), 13579);
+	assertEqualInt(archive_entry_atime_nsec(e2), 24680);
+	assertEqualInt(archive_entry_ctime(e2), 13580);
+	assertEqualInt(archive_entry_ctime_nsec(e2), 24681);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_dev(e2), 235);
+#endif
+	archive_entry_fflags(e, &set, &clear);
+	assertEqualInt(clear, 0xAA);
+	assertEqualInt(set, 0x55);
+	assertEqualInt(archive_entry_gid(e2), 204);
+	assertEqualString(archive_entry_gname(e2), "group");
+	assertEqualString(archive_entry_hardlink(e2), "hardlinkname");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_ino(e2), 8593);
+#endif
+	assertEqualInt(archive_entry_mode(e2), 0123456);
+	assertEqualInt(archive_entry_mtime(e2), 13581);
+	assertEqualInt(archive_entry_mtime_nsec(e2), 24682);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_nlink(e2), 736);
+#endif
+	assertEqualString(archive_entry_pathname(e2), "path");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_rdev(e2), 532);
+#endif
+	assertEqualInt(archive_entry_size(e2), 987654321);
+	assertEqualString(archive_entry_symlink(e2), "symlinkname");
+	assertEqualInt(archive_entry_uid(e2), 83);
+	assertEqualString(archive_entry_uname(e2), "user");
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("ACL preserved by archive_entry_clone()");
+#else
+	/* Verify ACL was copied. */
+	assertEqualInt(4, c = archive_entry_acl_reset(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	/* First three are standard permission bits. */
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 4);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 5);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 6);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	/* Fourth is custom one. */
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER);
+	assertEqualInt(qual, 77);
+	assertEqualString(name, "user77");
+#endif
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("xattr data preserved by archive_entry_clone");
+#else
+	/* Verify xattr was copied. */
+	assertEqualInt(1, c = archive_entry_xattr_reset(e2));
+	assertEqualInt(0, archive_entry_xattr_next(e2, &xname, &xval, &xsize));
+	assertEqualString(xname, "xattr1");
+	assertEqualString(xval, "xattrvalue");
+	assertEqualInt(xsize, 11);
+#endif
+
+	/* Change the original */
+	archive_entry_set_atime(e, 13580, 24690);
+	archive_entry_set_ctime(e, 13590, 24691);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_dev(e, 245);
+#endif
+	archive_entry_set_fflags(e, 0x85, 0xDA);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_filetype(e, AE_IFLNK);
+#endif
+	archive_entry_set_gid(e, 214);
+	archive_entry_set_gname(e, "grouper");
+	archive_entry_set_hardlink(e, "hardlinkpath");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_ino(e, 8763);
+#endif
+	archive_entry_set_mode(e, 0123654);
+	archive_entry_set_mtime(e, 18351, 28642);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_nlink(e, 73);
+#endif
+	archive_entry_set_pathname(e, "pathest");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_rdev(e, 132);
+#endif
+	archive_entry_set_size(e, 987456321);
+	archive_entry_set_symlink(e, "symlinkpath");
+	archive_entry_set_uid(e, 93);
+	archive_entry_set_uname(e, "username");
+	archive_entry_acl_clear(e);
+	archive_entry_xattr_clear(e);
+
+	/* Clone should still have same contents. */
+	assertEqualInt(archive_entry_atime(e2), 13579);
+	assertEqualInt(archive_entry_atime_nsec(e2), 24680);
+	assertEqualInt(archive_entry_ctime(e2), 13580);
+	assertEqualInt(archive_entry_ctime_nsec(e2), 24681);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_dev(e2), 235);
+#endif
+	archive_entry_fflags(e2, &set, &clear);
+	assertEqualInt(clear, 0xAA);
+	assertEqualInt(set, 0x55);
+	assertEqualInt(archive_entry_gid(e2), 204);
+	assertEqualString(archive_entry_gname(e2), "group");
+	assertEqualString(archive_entry_hardlink(e2), "hardlinkname");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_ino(e2), 8593);
+#endif
+	assertEqualInt(archive_entry_mode(e2), 0123456);
+	assertEqualInt(archive_entry_mtime(e2), 13581);
+	assertEqualInt(archive_entry_mtime_nsec(e2), 24682);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_nlink(e2), 736);
+#endif
+	assertEqualString(archive_entry_pathname(e2), "path");
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_rdev(e2), 532);
+#endif
+	assertEqualInt(archive_entry_size(e2), 987654321);
+	assertEqualString(archive_entry_symlink(e2), "symlinkname");
+	assertEqualInt(archive_entry_uid(e2), 83);
+	assertEqualString(archive_entry_uname(e2), "user");
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("ACL held by clone of archive_entry");
+#else
+	/* Verify ACL was unchanged. */
+	assertEqualInt(4, c = archive_entry_acl_reset(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+	/* First three are standard permission bits. */
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 4);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER_OBJ);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 5);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_GROUP_OBJ);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, 6);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_OTHER);
+	assertEqualInt(qual, -1);
+	assertEqualString(name, NULL);
+	/* Fourth is custom one. */
+	assertEqualInt(0, archive_entry_acl_next(e2,
+			   ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			   &type, &permset, &tag, &qual, &name));
+	assertEqualInt(type, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+	assertEqualInt(permset, ARCHIVE_ENTRY_ACL_READ);
+	assertEqualInt(tag, ARCHIVE_ENTRY_ACL_USER);
+	assertEqualInt(qual, 77);
+	assertEqualString(name, "user77");
+#endif
+#if ARCHIVE_VERSION_STAMP < 1009000
+	skipping("xattr preserved in archive_entry copy");
+#else
+	/* Verify xattr was unchanged. */
+	assertEqualInt(1, archive_entry_xattr_reset(e2));
+#endif
+
+	/* Release clone. */
+	archive_entry_free(e2);
+
+	/*
+	 * Test clear() implementation.
+	 */
+	archive_entry_clear(e);
+	assertEqualInt(archive_entry_atime(e), 0);
+	assertEqualInt(archive_entry_atime_nsec(e), 0);
+	assertEqualInt(archive_entry_ctime(e), 0);
+	assertEqualInt(archive_entry_ctime_nsec(e), 0);
+	assertEqualInt(archive_entry_dev(e), 0);
+	archive_entry_fflags(e, &set, &clear);
+	assertEqualInt(clear, 0);
+	assertEqualInt(set, 0);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_filetype(e), 0);
+#endif
+	assertEqualInt(archive_entry_gid(e), 0);
+	assertEqualString(archive_entry_gname(e), NULL);
+	assertEqualString(archive_entry_hardlink(e), NULL);
+	assertEqualInt(archive_entry_ino(e), 0);
+	assertEqualInt(archive_entry_mode(e), 0);
+	assertEqualInt(archive_entry_mtime(e), 0);
+	assertEqualInt(archive_entry_mtime_nsec(e), 0);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_nlink(e), 0);
+#endif
+	assertEqualString(archive_entry_pathname(e), NULL);
+	assertEqualInt(archive_entry_rdev(e), 0);
+	assertEqualInt(archive_entry_size(e), 0);
+	assertEqualString(archive_entry_symlink(e), NULL);
+	assertEqualInt(archive_entry_uid(e), 0);
+	assertEqualString(archive_entry_uname(e), NULL);
+	/* ACLs should be cleared. */
+	assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_ACCESS), 0);
+	assertEqualInt(archive_entry_acl_count(e, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT), 0);
+	/* Extended attributes should be cleared. */
+	assertEqualInt(archive_entry_xattr_count(e), 0);
+
+	/*
+	 * Test archive_entry_copy_stat().
+	 */
+	memset(&st, 0, sizeof(st));
+	/* Set all of the standard 'struct stat' fields. */
+	st.st_atime = 456789;
+	st.st_ctime = 345678;
+	st.st_dev = 123;
+	st.st_gid = 34;
+	st.st_ino = 234;
+	st.st_mode = 077777;
+	st.st_mtime = 234567;
+	st.st_nlink = 345;
+	st.st_size = 123456789;
+	st.st_uid = 23;
+#ifdef __FreeBSD__
+	/* On FreeBSD, high-res timestamp data should come through. */
+	st.st_atimespec.tv_nsec = 6543210;
+	st.st_ctimespec.tv_nsec = 5432109;
+	st.st_mtimespec.tv_nsec = 3210987;
+#endif
+	/* Copy them into the entry. */
+	archive_entry_copy_stat(e, &st);
+	/* Read each one back separately and compare. */
+	assertEqualInt(archive_entry_atime(e), 456789);
+	assertEqualInt(archive_entry_ctime(e), 345678);
+	assertEqualInt(archive_entry_dev(e), 123);
+	assertEqualInt(archive_entry_gid(e), 34);
+	assertEqualInt(archive_entry_ino(e), 234);
+	assertEqualInt(archive_entry_mode(e), 077777);
+	assertEqualInt(archive_entry_mtime(e), 234567);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(archive_entry_nlink(e), 345);
+#endif
+	assertEqualInt(archive_entry_size(e), 123456789);
+	assertEqualInt(archive_entry_uid(e), 23);
+#if __FreeBSD__
+	/* On FreeBSD, high-res timestamp data should come through. */
+	assertEqualInt(archive_entry_atime_nsec(e), 6543210);
+	assertEqualInt(archive_entry_ctime_nsec(e), 5432109);
+	assertEqualInt(archive_entry_mtime_nsec(e), 3210987);
+#endif
+
+	/*
+	 * Test archive_entry_stat().
+	 */
+	/* First, clear out any existing stat data. */
+	memset(&st, 0, sizeof(st));
+	archive_entry_copy_stat(e, &st);
+	/* Set a bunch of fields individually. */
+	archive_entry_set_atime(e, 456789, 321);
+	archive_entry_set_ctime(e, 345678, 432);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_dev(e, 123);
+#endif
+	archive_entry_set_gid(e, 34);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_ino(e, 234);
+#endif
+	archive_entry_set_mode(e, 012345);
+	archive_entry_set_mode(e, 012345);
+	archive_entry_set_mtime(e, 234567, 543);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_nlink(e, 345);
+#endif
+	archive_entry_set_size(e, 123456789);
+	archive_entry_set_uid(e, 23);
+	/* Retrieve a stat structure. */
+	assert((pst = archive_entry_stat(e)) != NULL);
+	/* Check that the values match. */
+	assertEqualInt(pst->st_atime, 456789);
+	assertEqualInt(pst->st_ctime, 345678);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(pst->st_dev, 123);
+#endif
+	assertEqualInt(pst->st_gid, 34);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(pst->st_ino, 234);
+#endif
+	assertEqualInt(pst->st_mode, 012345);
+	assertEqualInt(pst->st_mtime, 234567);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	assertEqualInt(pst->st_nlink, 345);
+#endif
+	assertEqualInt(pst->st_size, 123456789);
+	assertEqualInt(pst->st_uid, 23);
+#ifdef __FreeBSD__
+	/* On FreeBSD, high-res timestamp data should come through. */
+	assertEqualInt(pst->st_atimespec.tv_nsec, 321);
+	assertEqualInt(pst->st_ctimespec.tv_nsec, 432);
+	assertEqualInt(pst->st_mtimespec.tv_nsec, 543);
+#endif
+
+	/* Changing any one value should update struct stat. */
+	archive_entry_set_atime(e, 456788, 0);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_atime, 456788);
+	archive_entry_set_ctime(e, 345677, 431);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_ctime, 345677);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_dev(e, 122);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_dev, 122);
+#endif
+	archive_entry_set_gid(e, 33);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_gid, 33);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_ino(e, 233);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_ino, 233);
+#endif
+	archive_entry_set_mode(e, 012344);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_mode, 012344);
+	archive_entry_set_mtime(e, 234566, 542);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_mtime, 234566);
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_nlink(e, 344);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_nlink, 344);
+#endif
+	archive_entry_set_size(e, 123456788);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_size, 123456788);
+	archive_entry_set_uid(e, 22);
+	assert((pst = archive_entry_stat(e)) != NULL);
+	assertEqualInt(pst->st_uid, 22);
+	/* We don't need to check high-res fields here. */
+
+	/*
+	 * Test dev/major/minor interfaces.  Setting 'dev' or 'rdev'
+	 * should change the corresponding major/minor values, and
+	 * vice versa.
+	 *
+	 * The test here is system-specific because it assumes that
+	 * makedev(), major(), and minor() are defined in sys/stat.h.
+	 * I'm not too worried about it, though, because the code is
+	 * simple.  If it works on FreeBSD, it's unlikely to be broken
+	 * anywhere else.  Note: The functionality is present on every
+	 * platform even if these tests only run some places;
+	 * libarchive's more extensive configuration logic should find
+	 * the necessary definitions on every platform.
+	 */
+#if __FreeBSD__
+#if ARCHIVE_VERSION_STAMP >= 1009000
+	archive_entry_set_dev(e, 0x12345678);
+	assertEqualInt(archive_entry_devmajor(e), major(0x12345678));
+	assertEqualInt(archive_entry_devminor(e), minor(0x12345678));
+	assertEqualInt(archive_entry_dev(e), 0x12345678);
+	archive_entry_set_devmajor(e, 0xfe);
+	archive_entry_set_devminor(e, 0xdcba98);
+	assertEqualInt(archive_entry_devmajor(e), 0xfe);
+	assertEqualInt(archive_entry_devminor(e), 0xdcba98);
+	assertEqualInt(archive_entry_dev(e), makedev(0xfe, 0xdcba98));
+	archive_entry_set_rdev(e, 0x12345678);
+	assertEqualInt(archive_entry_rdevmajor(e), major(0x12345678));
+	assertEqualInt(archive_entry_rdevminor(e), minor(0x12345678));
+	assertEqualInt(archive_entry_rdev(e), 0x12345678);
+	archive_entry_set_rdevmajor(e, 0xfe);
+	archive_entry_set_rdevminor(e, 0xdcba98);
+	assertEqualInt(archive_entry_rdevmajor(e), 0xfe);
+	assertEqualInt(archive_entry_rdevminor(e), 0xdcba98);
+	assertEqualInt(archive_entry_rdev(e), makedev(0xfe, 0xdcba98));
+#endif
+#endif
+
+	/* Release the experimental entry. */
+	archive_entry_free(e);
+}
--- /dev/null
+++ lib/libarchive/test/test_write_format_tar_empty.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_write_format_tar_empty.c,v 1.3 2007/07/06 15:43:11 kientzle Exp $");
+
+/*
+ * Check that an "empty" tar archive is correctly created.
+ */
+
+DEFINE_TEST(test_write_format_tar_empty)
+{
+	struct archive *a;
+	char buff[2048];
+	size_t used;
+	unsigned int i;
+
+	/* USTAR format: Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_ustar(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a, 512));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 512));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+#if ARCHIVE_VERSION_STAMP < 1009000
+	/* Earlier versions wrote 0-length files for empty tar archives. */
+	skipping("empty tar archive size");
+#else
+	assert(used == 1024);
+#endif
+	for (i = 0; i < used; i++) {
+		failure("Empty tar archive should be all nulls.");
+		assert(buff[i] == 0);
+	}
+
+	/* PAX format: Create a new archive in memory. */
+	assert((a = archive_write_new()) != NULL);
+	assertA(0 == archive_write_set_format_pax(a));
+	assertA(0 == archive_write_set_compression_none(a));
+	assertA(0 == archive_write_set_bytes_per_block(a, 512));
+	assertA(0 == archive_write_set_bytes_in_last_block(a, 512));
+	assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+	/* Close out the archive. */
+	assertA(0 == archive_write_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assertA(0 == archive_write_finish(a));
+#else
+	archive_write_finish(a);
+#endif
+
+#if ARCHIVE_VERSION_STAMP < 1009000
+	/* Earlier versions wrote 0-length files for empty tar archives. */
+	skipping("empty tar archive size");
+#else
+	assertEqualInt(used, 1024);
+#endif
+	for (i = 0; i < used; i++) {
+		failure("Empty tar archive should be all nulls.");
+		assert(buff[i] == 0);
+	}
+}
--- /dev/null
+++ lib/libarchive/test/test_archive_api_feature.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_archive_api_feature.c,v 1.3 2007/07/06 15:43:11 kientzle Exp $");
+
+DEFINE_TEST(test_archive_api_feature)
+{
+	assertEqualInt(ARCHIVE_API_FEATURE, archive_api_feature());
+	assertEqualInt(ARCHIVE_API_VERSION, archive_api_version());
+	/*
+	 * Even though ARCHIVE_VERSION_STAMP only appears in
+	 * archive.h after 1.9.0 and 2.2.3, the macro is synthesized
+	 * in test.h, so this test is always valid.
+	 */
+	assertEqualInt(ARCHIVE_VERSION_STAMP / 1000, ARCHIVE_API_VERSION * 1000 + ARCHIVE_API_FEATURE);
+	/*
+	 * The function, however, isn't always available.  It appeared
+	 * sometime in the middle of 2.2.3, but the synthesized value
+	 * never has a release version, so the following conditional
+	 * exactly determines whether the current library has the
+	 * function.
+	 */
+#if ARCHIVE_VERSION_STAMP / 1000 == 1009 || ARCHIVE_VERSION_STAMP > 2002000
+	assertEqualInt(ARCHIVE_VERSION_STAMP, archive_version_stamp());
+#else
+	skipping("archive_version_stamp()");
+#endif
+	assertEqualString(ARCHIVE_LIBRARY_VERSION, archive_version());
+}
--- /dev/null
+++ lib/libarchive/test/README
@@ -0,0 +1,55 @@
+$FreeBSD: src/lib/libarchive/test/README,v 1.2 2007/05/29 01:00:20 kientzle Exp $
+
+This is the test harness for libarchive.
+
+It compiles into a single program "libarchive_test" that is intended
+to exercise as much of the library as possible.  It is, of course,
+very much a work in progress.
+
+Each test is a function named test_foo in a file named test_foo.c.
+Note that the file name is the same as the function name.
+Each file must start with this line:
+
+  #include "test.h"
+
+The test function must be declared with a line of this form
+
+  DEFINE_TEST(test_foo)
+
+Nothing else should appear on that line.
+
+When you add a test, please update the Makefile to add your
+file to the list of tests.  The Makefile and main.c use various
+macro trickery to automatically collect a list of test functions
+to be invoked.
+
+Each test function can rely on the following:
+
+  * The current directory will be a freshly-created empty directory
+    suitable for that test.  (The top-level main() creates a
+    directory for each separate test and chdir()s to that directory
+    before running the test.)
+
+  * The test function should use assert(), assertA() and similar macros
+    defined in test.h.  If you need to add new macros of this form, feel
+    free to do so.
+
+  * You are encouraged to document each assertion with a failure() call
+    just before the assert.  The failure() function is a printf-like
+    function whose text is displayed only if the assertion fails.  It
+    can be used to display additional information relevant to the failure:
+
+       failure("The data read from file %s did not match the data written to that file.", filename);
+       assert(strcmp(buff1, buff2) == 0);
+
+  * Tests are encouraged to be economical with their memory and disk usage,
+    though this is not essential.
+
+  * Disable tests on specific platforms as necessary.  Please don't
+    use config.h to adjust feature requirements, as I want the tests
+    to also serve as a check on the configure process.  The following
+    form is appropriate:
+
+#if !defined(__PLATFORM) && !defined(__Platform2__)
+    assert(xxxx)
+#endif
--- /dev/null
+++ lib/libarchive/test/test_read_format_tar.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/test/test_read_format_tar.c,v 1.2 2007/07/14 05:35:17 kientzle Exp $");
+
+/*
+ * Each of these archives is a short archive with a single entry.  The
+ * corresponding verify function verifies the entry structure returned
+ * from libarchive is what it should be.  The support functions pad with
+ * lots of zeros, so we can trim trailing zero bytes from each hardcoded
+ * archive to save space.
+ *
+ * The naming here follows the tar file type flags.  E.g. '1' is a hardlink,
+ * '2' is a symlink, '5' is a dir, etc.
+ */
+
+/* Single entry with a hardlink. */
+static unsigned char archive1[] = {
+'h','a','r','d','l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','6','4','4',' ',0,'0','0','1','7','5','0',' ',0,'0','0','1','7','5','0',
+' ',0,'0','0','0','0','0','0','0','0','0','0','0',' ','1','0','6','4','6',
+'0','5','2','6','6','2',' ','0','1','3','0','5','7',0,' ','1','f','i','l',
+'e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,'0',
+'0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',
+'0','0','0','0','0',' ',0,'0','0','0','0','0','0',' '};
+
+static void verify1(struct archive_entry *ae)
+{
+	/* A hardlink is not a symlink. */
+	assert(!S_ISLNK(archive_entry_mode(ae)));
+	/* Nor is it a directory. */
+	assert(!S_ISDIR(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0644);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "hardlink");
+	assertEqualString(archive_entry_hardlink(ae), "file");
+	assert(archive_entry_symlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184388530);
+}
+
+/* Verify that symlinks are read correctly. */
+static unsigned char archive2[] = {
+'s','y','m','l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','7','5','5',' ','0','0','0','1','7','5','0',' ','0','0','0','1','7',
+'5','0',' ','0','0','0','0','0','0','0','0','0','0','0',' ','1','0','6','4',
+'6','0','5','4','1','0','1',' ','0','0','1','3','3','2','3',' ','2','f','i',
+'l','e',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'0','0','0','0','0','0','0',' ','0','0','0','0','0','0','0',' '};
+
+static void verify2(struct archive_entry *ae)
+{
+	assert(S_ISLNK(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "symlink");
+	assertEqualString(archive_entry_symlink(ae), "file");
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184389185);
+}
+
+/* Character device node. */
+static unsigned char archive3[] = {
+'d','e','v','c','h','a','r',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','7','5','5',' ','0','0','0','1','7','5','0',' ','0','0','0','1','7',
+'5','0',' ','0','0','0','0','0','0','0','0','0','0','0',' ','1','0','6','4',
+'6','0','5','4','1','0','1',' ','0','0','1','2','4','1','2',' ','3',0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'0','0','0','0','0','0','0',' ','0','0','0','0','0','0','0',' '};
+
+static void verify3(struct archive_entry *ae)
+{
+	assert(S_ISCHR(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "devchar");
+	assert(archive_entry_symlink(ae) == NULL);
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184389185);
+}
+
+/* Block device node. */
+static unsigned char archive4[] = {
+'d','e','v','b','l','o','c','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','7','5','5',' ','0','0','0','1','7','5','0',' ','0','0','0','1','7',
+'5','0',' ','0','0','0','0','0','0','0','0','0','0','0',' ','1','0','6','4',
+'6','0','5','4','1','0','1',' ','0','0','1','2','5','7','0',' ','4',0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'0','0','0','0','0','0','0',' ','0','0','0','0','0','0','0',' '};
+
+static void verify4(struct archive_entry *ae)
+{
+	assert(S_ISBLK(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "devblock");
+	assert(archive_entry_symlink(ae) == NULL);
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184389185);
+}
+
+/* Directory. */
+static unsigned char archive5[] = {
+'.',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0',
+'7','5','5',' ',0,'0','0','1','7','5','0',' ',0,'0','0','1','7','5','0',
+' ',0,'0','0','0','0','0','0','0','0','0','0','0',' ','1','0','3','3',
+'4','0','4','1','7','3','6',' ','0','1','0','5','6','1',0,' ','5',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'0','0','0','0','0','0',' ',0,'0','0','0','0','0','0',' '};
+
+static void verify5(struct archive_entry *ae)
+{
+	assert(S_ISDIR(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mtime(ae), 1131430878);
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+}
+
+/* fifo */
+static unsigned char archive6[] = {
+'f','i','f','o',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0',
+'0','0','7','5','5',' ','0','0','0','1','7','5','0',' ','0','0','0','1','7',
+'5','0',' ','0','0','0','0','0','0','0','0','0','0','0',' ','1','0','6','4',
+'6','0','5','4','1','0','1',' ','0','0','1','1','7','2','4',' ','6',0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',0,
+'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'0','0','0','0','0','0','0',' ','0','0','0','0','0','0','0',' '};
+
+static void verify6(struct archive_entry *ae)
+{
+	assert(S_ISFIFO(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "fifo");
+	assert(archive_entry_symlink(ae) == NULL);
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184389185);
+}
+
+/* GNU long link name */
+static unsigned char archiveK[] = {
+'.','/','.','/','@','L','o','n','g','L','i','n','k',0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,'0','0','0',
+'0','0','0','0',0,'0','0','0','0','0','0','0','0','6','6','6',0,'0','0','0',
+'0','0','0','0','0','0','0','0',0,'0','1','1','7','1','5',0,' ','K',0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u','s','t','a','r',' ',' ',
+0,'r','o','o','t',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'w','h','e','e','l',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'t',
+'h','i','s','_','i','s','_','a','_','v','e','r','y','_','l','o','n','g','_',
+'s','y','m','l','i','n','k','_','b','o','d','y','_','a','b','c','d','e','f',
+'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y',
+'z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
+'r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i',
+'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a',
+'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t',
+'u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l',
+'m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d',
+'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w',
+'x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+'p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g',
+'h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
+'_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
+'s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j',
+'k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b',
+'c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u',
+'v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m',
+'n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e',
+'f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x',
+'y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+'q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g','h',
+'i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+'s','y','m','l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','1',
+'2','0','7','5','5',0,'0','0','0','1','7','5','0',0,'0','0','0','1','7','5',
+'0',0,'0','0','0','0','0','0','0','0','0','0','0',0,'1','0','6','4','6','0',
+'5','6','7','7','0',0,'0','3','5','4','4','7',0,' ','2','t','h','i','s','_',
+'i','s','_','a','_','v','e','r','y','_','l','o','n','g','_','s','y','m','l',
+'i','n','k','_','b','o','d','y','_','a','b','c','d','e','f','g','h','i','j',
+'k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b',
+'c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u',
+'v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l',0,
+'u','s','t','a','r',' ',' ',0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,'t','i','m'};
+
+static void verifyK(struct archive_entry *ae)
+{
+	assert(S_ISLNK(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "symlink");
+	assertEqualString(archive_entry_symlink(ae),
+	    "this_is_a_very_long_symlink_body_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz");
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184390648);
+}
+
+/* TODO: GNU long name */
+
+/* TODO: Solaris ACL */
+
+/* Pax extended long link name */
+static unsigned char archivexL[] = {
+'.','/','P','a','x','H','e','a','d','e','r','s','.','8','6','9','7','5','/',
+'s','y','m','l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0','0','6','4','4',0,'0','0','0','1',
+'7','5','0',0,'0','0','0','1','7','5','0',0,'0','0','0','0','0','0','0','0',
+'7','5','3',0,'1','0','6','4','6','0','5','7','6','1','1',0,'0','1','3','7',
+'1','4',0,' ','x',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'u',
+'s','t','a','r',0,'0','0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0',0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'4','5','1',' ','l','i','n','k','p','a','t',
+'h','=','t','h','i','s','_','i','s','_','a','_','v','e','r','y','_','l','o',
+'n','g','_','s','y','m','l','i','n','k','_','b','o','d','y','_','a','b','c',
+'d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+'w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n',
+'o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f',
+'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y',
+'z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
+'r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i',
+'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a',
+'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t',
+'u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l',
+'m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d',
+'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w',
+'x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+'p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g',
+'h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
+'_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
+'s','t','u','v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j',
+'k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b',
+'c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u',
+'v','w','x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m',
+'n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d','e',
+'f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x',
+'y','z',10,'2','0',' ','a','t','i','m','e','=','1','1','8','4','3','9','1',
+'0','2','5',10,'2','0',' ','c','t','i','m','e','=','1','1','8','4','3','9',
+'0','6','4','8',10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'s','y','m',
+'l','i','n','k',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0','0','0','0','7',
+'5','5',0,'0','0','0','1','7','5','0',0,'0','0','0','1','7','5','0',0,'0',
+'0','0','0','0','0','0','0','0','0','0',0,'1','0','6','4','6','0','5','6',
+'7','7','0',0,'0','3','7','1','2','1',0,' ','2','t','h','i','s','_','i','s',
+'_','a','_','v','e','r','y','_','l','o','n','g','_','s','y','m','l','i','n',
+'k','_','b','o','d','y','_','a','b','c','d','e','f','g','h','i','j','k','l',
+'m','n','o','p','q','r','s','t','u','v','w','x','y','z','_','a','b','c','d',
+'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w',
+'x','y','z','_','a','b','c','d','e','f','g','h','i','j','k','l','m','u','s',
+'t','a','r',0,'0','0','t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,'t','i','m',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,'0','0','0','0','0','0','0',0,'0','0','0','0','0','0','0'};
+
+static void verifyxL(struct archive_entry *ae)
+{
+	assert(S_ISLNK(archive_entry_mode(ae)));
+	assertEqualInt(archive_entry_mode(ae) & 0777, 0755);
+	assertEqualInt(archive_entry_uid(ae), 1000);
+	assertEqualInt(archive_entry_gid(ae), 1000);
+	assertEqualString(archive_entry_uname(ae), "tim");
+	assertEqualString(archive_entry_gname(ae), "tim");
+	assertEqualString(archive_entry_pathname(ae), "symlink");
+	assertEqualString(archive_entry_symlink(ae),
+	    "this_is_a_very_long_symlink_body_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz_"
+	    "abcdefghijklmnopqrstuvwxyz_abcdefghijklmnopqrstuvwxyz");
+	assert(archive_entry_hardlink(ae) == NULL);
+	assertEqualInt(archive_entry_mtime(ae), 1184390648);
+}
+
+
+/* TODO: Any other types of headers? */
+
+static void verify(unsigned char *d, size_t s,
+    void (*f)(struct archive_entry *),
+    int compression, int format)
+{
+	struct archive_entry *ae;
+	struct archive *a;
+	unsigned char *buff = malloc(100000);
+
+	memcpy(buff, d, s);
+	memset(buff + s, 0, 2048);
+
+	assert((a = archive_read_new()) != NULL);
+	assertA(0 == archive_read_support_compression_all(a));
+	assertA(0 == archive_read_support_format_all(a));
+	assertA(0 == archive_read_open_memory(a, buff, s + 1024));
+	assertA(0 == archive_read_next_header(a, &ae));
+	assertEqualInt(archive_compression(a), compression);
+	assertEqualInt(archive_format(a), format);
+
+	/* Verify the only entry. */
+	f(ae);
+
+	assert(0 == archive_read_close(a));
+#if ARCHIVE_API_VERSION > 1
+	assert(0 == archive_read_finish(a));
+#else
+	archive_read_finish(a);
+#endif
+	free(buff);
+}
+
+DEFINE_TEST(test_read_format_tar)
+{
+	verify(archive1, sizeof(archive1), verify1,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archive2, sizeof(archive2), verify2,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archive3, sizeof(archive3), verify3,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archive4, sizeof(archive4), verify4,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archive5, sizeof(archive5), verify5,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archive6, sizeof(archive6), verify6,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_USTAR);
+	verify(archiveK, sizeof(archiveK), verifyK,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_GNUTAR);
+	verify(archivexL, sizeof(archivexL), verifyxL,
+	    ARCHIVE_COMPRESSION_NONE, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE);
+}
+
+


More information about the Midnightbsd-cvs mailing list