[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, ×);
+#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, ×) != 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