From 1a60578adb6be1f7da9ed95f38cf52c20be13591 Mon Sep 17 00:00:00 2001 From: kientzle Date: Sat, 3 Mar 2007 07:37:37 +0000 Subject: libarchive 2.0 * libarchive_test program exercises many of the core features * Refactored old "read_extract" into new "archive_write_disk", which uses archive_write methods to put entries onto disk. In particular, you can now use archive_write_disk to create objects on disk without having an archive available. * Pushed some security checks from bsdtar down into libarchive, where they can be better optimized. * Rearchitected the logic for creating objects on disk to reduce the number of system calls. Several common cases now use a minimum number of system calls. * Virtualized some internal interfaces to provide a clearer separation of read and write handling and make it simpler to override key methods. * New "empty" format reader. * Corrected return types (this ABI breakage required the "2.0" version bump) * Many bug fixes. --- lib/libarchive/Makefile | 18 +- lib/libarchive/README | 14 +- lib/libarchive/archive.h.in | 124 +- lib/libarchive/archive_entry.3 | 3 + lib/libarchive/archive_private.h | 204 +-- lib/libarchive/archive_read.3 | 97 +- lib/libarchive/archive_read.c | 168 +- lib/libarchive/archive_read_data_into_fd.c | 2 - lib/libarchive/archive_read_extract.c | 1617 +--------------- lib/libarchive/archive_read_private.h | 188 ++ .../archive_read_support_compression_bzip2.c | 70 +- .../archive_read_support_compression_compress.c | 54 +- .../archive_read_support_compression_gzip.c | 74 +- .../archive_read_support_compression_none.c | 54 +- lib/libarchive/archive_read_support_format_cpio.c | 62 +- lib/libarchive/archive_read_support_format_empty.c | 20 +- .../archive_read_support_format_iso9660.c | 46 +- lib/libarchive/archive_read_support_format_tar.c | 151 +- lib/libarchive/archive_read_support_format_zip.c | 108 +- lib/libarchive/archive_util.3 | 6 + lib/libarchive/archive_util.c | 6 + lib/libarchive/archive_virtual.c | 81 + lib/libarchive/archive_write.3 | 13 +- lib/libarchive/archive_write.c | 154 +- lib/libarchive/archive_write_disk.3 | 358 ++++ lib/libarchive/archive_write_disk.c | 1929 ++++++++++++++++++++ lib/libarchive/archive_write_disk_private.h | 34 + .../archive_write_disk_set_standard_lookup.c | 212 +++ lib/libarchive/archive_write_private.h | 177 ++ .../archive_write_set_compression_bzip2.c | 69 +- .../archive_write_set_compression_gzip.c | 74 +- .../archive_write_set_compression_none.c | 59 +- lib/libarchive/archive_write_set_format_cpio.c | 44 +- lib/libarchive/archive_write_set_format_pax.c | 62 +- lib/libarchive/archive_write_set_format_shar.c | 54 +- lib/libarchive/archive_write_set_format_ustar.c | 89 +- lib/libarchive/test/Makefile | 67 + lib/libarchive/test/README | 46 + lib/libarchive/test/main.c | 168 ++ lib/libarchive/test/test.h | 89 + lib/libarchive/test/test_archive_api_feature.c | 33 + lib/libarchive/test/test_bad_fd.c | 41 + lib/libarchive/test/test_read_data_large.c | 116 ++ lib/libarchive/test/test_read_extract.c | 177 ++ lib/libarchive/test/test_read_format_cpio_bin.c | 64 + lib/libarchive/test/test_read_format_cpio_bin_Z.c | 53 + .../test/test_read_format_cpio_bin_bz2.c | 54 + lib/libarchive/test/test_read_format_cpio_bin_gz.c | 53 + lib/libarchive/test/test_read_format_cpio_odc.c | 68 + .../test/test_read_format_cpio_svr4_gzip.c | 54 + .../test/test_read_format_cpio_svr4c_Z.c | 56 + lib/libarchive/test/test_read_format_empty.c | 49 + lib/libarchive/test/test_read_format_gtar_gz.c | 54 + lib/libarchive/test/test_read_format_iso_gz.c | 73 + lib/libarchive/test/test_read_format_isorr_bz2.c | 180 ++ lib/libarchive/test/test_read_format_pax_bz2.c | 62 + lib/libarchive/test/test_read_format_tar.c | 93 + lib/libarchive/test/test_read_format_tbz.c | 55 + lib/libarchive/test/test_read_format_tgz.c | 54 + lib/libarchive/test/test_read_format_tz.c | 56 + lib/libarchive/test/test_read_format_zip.c | 54 + lib/libarchive/test/test_read_large.c | 93 + lib/libarchive/test/test_read_position.c | 74 + lib/libarchive/test/test_read_truncated.c | 148 ++ lib/libarchive/test/test_write_disk.c | 92 + lib/libarchive/test/test_write_disk_perms.c | 324 ++++ lib/libarchive/test/test_write_disk_secure.c | 140 ++ lib/libarchive/test/test_write_format_cpio_empty.c | 76 + lib/libarchive/test/test_write_format_shar_empty.c | 59 + lib/libarchive/test/test_write_format_tar.c | 111 ++ lib/libarchive/test/test_write_format_tar_empty.c | 84 + lib/libarchive/test/test_write_open_memory.c | 75 + 72 files changed, 7206 insertions(+), 2434 deletions(-) create mode 100644 lib/libarchive/archive_read_private.h create mode 100644 lib/libarchive/archive_virtual.c create mode 100644 lib/libarchive/archive_write_disk.3 create mode 100644 lib/libarchive/archive_write_disk.c create mode 100644 lib/libarchive/archive_write_disk_private.h create mode 100644 lib/libarchive/archive_write_disk_set_standard_lookup.c create mode 100644 lib/libarchive/archive_write_private.h create mode 100644 lib/libarchive/test/Makefile create mode 100644 lib/libarchive/test/README create mode 100644 lib/libarchive/test/main.c create mode 100644 lib/libarchive/test/test.h create mode 100644 lib/libarchive/test/test_archive_api_feature.c create mode 100644 lib/libarchive/test/test_bad_fd.c create mode 100644 lib/libarchive/test/test_read_data_large.c create mode 100644 lib/libarchive/test/test_read_extract.c create mode 100644 lib/libarchive/test/test_read_format_cpio_bin.c create mode 100644 lib/libarchive/test/test_read_format_cpio_bin_Z.c create mode 100644 lib/libarchive/test/test_read_format_cpio_bin_bz2.c create mode 100644 lib/libarchive/test/test_read_format_cpio_bin_gz.c create mode 100644 lib/libarchive/test/test_read_format_cpio_odc.c create mode 100644 lib/libarchive/test/test_read_format_cpio_svr4_gzip.c create mode 100644 lib/libarchive/test/test_read_format_cpio_svr4c_Z.c create mode 100644 lib/libarchive/test/test_read_format_empty.c create mode 100644 lib/libarchive/test/test_read_format_gtar_gz.c create mode 100644 lib/libarchive/test/test_read_format_iso_gz.c create mode 100644 lib/libarchive/test/test_read_format_isorr_bz2.c create mode 100644 lib/libarchive/test/test_read_format_pax_bz2.c create mode 100644 lib/libarchive/test/test_read_format_tar.c create mode 100644 lib/libarchive/test/test_read_format_tbz.c create mode 100644 lib/libarchive/test/test_read_format_tgz.c create mode 100644 lib/libarchive/test/test_read_format_tz.c create mode 100644 lib/libarchive/test/test_read_format_zip.c create mode 100644 lib/libarchive/test/test_read_large.c create mode 100644 lib/libarchive/test/test_read_position.c create mode 100644 lib/libarchive/test/test_read_truncated.c create mode 100644 lib/libarchive/test/test_write_disk.c create mode 100644 lib/libarchive/test/test_write_disk_perms.c create mode 100644 lib/libarchive/test/test_write_disk_secure.c create mode 100644 lib/libarchive/test/test_write_format_cpio_empty.c create mode 100644 lib/libarchive/test/test_write_format_shar_empty.c create mode 100644 lib/libarchive/test/test_write_format_tar.c create mode 100644 lib/libarchive/test/test_write_format_tar_empty.c create mode 100644 lib/libarchive/test/test_write_open_memory.c (limited to 'lib') diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 3c4170d..f5a37e9 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -9,14 +9,14 @@ LDADD= -lbz2 -lz # 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.0.20 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/\..*//' # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system. # It has no real relation to the version number above. -SHLIB_MAJOR= 3 +SHLIB_MAJOR= 4 CFLAGS+= -DPACKAGE_NAME=\"lib${LIB}\" CFLAGS+= -DPACKAGE_VERSION=\"${VERSION}\" @@ -67,7 +67,10 @@ SRCS= archive.h \ 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 \ @@ -87,6 +90,7 @@ MAN= archive_entry.3 \ archive_read.3 \ archive_util.3 \ archive_write.3 \ + archive_write_disk.3 \ libarchive.3 \ libarchive-formats.5 \ tar.5 @@ -173,6 +177,7 @@ 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 @@ -201,6 +206,15 @@ MLINKS+= archive_write.3 archive_write_set_compression_gzip.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 +test: + cd ${.CURDIR}/test && make test + .include diff --git a/lib/libarchive/README b/lib/libarchive/README index 1380b4a..3157108 100644 --- a/lib/libarchive/README +++ b/lib/libarchive/README @@ -6,16 +6,16 @@ This is all under a BSD license. Use, enjoy, but don't blame me if it breaks! 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 @@ Notes: 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. diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index cdcc21f..6ba6e9e 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -37,7 +37,14 @@ #include /* Linux requires this for off_t */ @ARCHIVE_H_INCLUDE_INTTYPES_H@ #include /* For FILE * */ +#ifndef _WIN32 #include /* 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" { @@ -59,6 +66,7 @@ extern "C" { * 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 */ #define ARCHIVE_API_VERSION @ARCHIVE_API_MAJOR@ int archive_api_version(void); @@ -138,8 +146,17 @@ typedef int archive_close_callback(struct archive *, void *_client_data); * 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) @@ -274,15 +291,28 @@ int archive_read_data_into_fd(struct archive *, int fd); */ /* 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) int archive_read_extract(struct archive *, struct archive_entry *, int flags); @@ -298,7 +328,13 @@ void archive_read_extract_set_skip_file(struct archive *, 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: @@ -362,11 +398,76 @@ int archive_write_open_memory(struct archive *, */ 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,6 +484,7 @@ int archive_errno(struct archive *); const char *archive_error_string(struct archive *); const char *archive_format_name(struct archive *); int archive_format(struct archive *); +void archive_clear_error(struct archive *); void archive_set_error(struct archive *, int _err, const char *fmt, ...); #ifdef __cplusplus diff --git a/lib/libarchive/archive_entry.3 b/lib/libarchive/archive_entry.3 index 796bf2e..8205c11 100644 --- a/lib/libarchive/archive_entry.3 +++ b/lib/libarchive/archive_entry.3 @@ -46,6 +46,7 @@ .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_w , .Nm archive_entry_dev , @@ -124,6 +125,8 @@ .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_w "struct archive_entry *" "const wchar_t *" diff --git a/lib/libarchive/archive_private.h b/lib/libarchive/archive_private.h index 6e6867a..a6b7fd6 100644 --- a/lib/libarchive/archive_private.h +++ b/lib/libarchive/archive_private.h @@ -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 { /* @@ -44,202 +66,34 @@ struct archive { unsigned magic; unsigned state; - struct archive_entry *entry; - uid_t user_uid; /* UID of current user. */ - - /* Dev/ino of the archive being read/written. */ - dev_t skip_file_dev; - ino_t skip_file_ino; - - /* Utility: Pointer to a block of nulls. */ - const unsigned char *nulls; - size_t null_length; - /* - * Used by archive_read_data() to track blocks and copy - * data to client buffers, filling gaps with zero bytes. + * 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; + struct archive_vtable *vtable; - /* 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; + 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_errx(int retvalue, const char *msg); #define err_combine(a,b) ((a) < (b) ? (a) : (b)) - -/* - * Utility function to format a USTAR header into a buffer. If - * "strict" is set, this tries to create the absolutely most portable - * version of a ustar header. If "strict" is set to 0, then it will - * relax certain requirements. - * - * Generally, format-specific declarations don't belong in this - * header; this is a rare example of a function that is shared by - * two very similar formats (ustar and pax). - */ -int -__archive_write_format_header_ustar(struct archive *, char buff[512], - struct archive_entry *, int tartype, int strict); - #endif diff --git a/lib/libarchive/archive_read.3 b/lib/libarchive/archive_read.3 index f968238..46be4d4 100644 --- a/lib/libarchive/archive_read.3 +++ b/lib/libarchive/archive_read.3 @@ -113,7 +113,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. @@ -148,7 +148,7 @@ ustar, pax interchange format, and many common variants. For convenience, .Fn archive_read_support_format_all enables support for all available formats. -Note that there is no default. +Only empty archives are supported by default. .It Fn archive_read_open The same as .Fn archive_read_open2 , @@ -233,58 +233,27 @@ Note that the client is responsible for sizing the buffer appropriately. 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 archiv_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 +269,12 @@ Complete the archive and invoke the close callback. 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 +498,6 @@ The library was written by .An Tim Kientzle Aq kientzle@acm.org . .Sh BUGS -Directories are actually extracted in two distinct phases. -Directories are created during -.Fn archive_read_extract , -but final permissions are not set until -.Fn archive_read_close . -This separation is necessary to correctly handle borderline -cases such as a non-writable directory containing -files, but can cause unexpected results. -In particular, directory permissions are not fully -restored until the archive is closed. -If you use -.Xr chdir 2 -to change the current directory between calls to -.Fn archive_read_extract -or before calling -.Fn archive_read_close , -you may confuse the permission-setting logic with -the result that directory permissions are restored -incorrectly. -.Pp Many traditional archiver programs treat empty files as valid empty archives. For example, many implementations of diff --git a/lib/libarchive/archive_read.c b/lib/libarchive/archive_read.c index 8574483..c92b20c 100644 --- a/lib/libarchive/archive_read.c +++ b/lib/libarchive/archive_read.c @@ -51,9 +51,10 @@ __FBSDID("$FreeBSD$"); #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 int choose_decompressor(struct archive_read *, const void*, size_t); +static int choose_format(struct archive_read *); /* * Allocate, initialize and return a struct archive object. @@ -61,44 +62,45 @@ static int choose_format(struct archive *); 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 +121,19 @@ archive_read_open(struct archive *a, void *client_data, } 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 @@ archive_read_open2(struct archive *a, void *client_data, /* 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); + 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); } @@ -186,7 +189,7 @@ archive_read_open2(struct archive *a, void *client_data, e = (a->decompressors[high_bidder].init)(a, buffer, bytes_read); if (e == ARCHIVE_OK) - a->state = ARCHIVE_STATE_HEADER; + a->archive.state = ARCHIVE_STATE_HEADER; return (e); } @@ -196,7 +199,8 @@ archive_read_open2(struct archive *a, void *client_data, * wants to handle this stream. Return index of winning bidder. */ static int -choose_decompressor(struct archive *a, const void *buffer, size_t bytes_read) +choose_decompressor(struct archive_read *a, + const void *buffer, size_t bytes_read) { int decompression_slots, i, bid, best_bid, best_bid_slot; @@ -231,7 +235,7 @@ choose_decompressor(struct archive *a, const void *buffer, size_t bytes_read) * 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); } @@ -243,28 +247,30 @@ choose_decompressor(struct archive *a, const void *buffer, size_t bytes_read) * 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,11 +278,11 @@ archive_read_next_header(struct archive *a, struct archive_entry **entryp) } /* 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]); @@ -290,18 +296,18 @@ archive_read_next_header(struct archive *a, struct archive_entry **entryp) */ 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 +322,7 @@ archive_read_next_header(struct archive *a, struct archive_entry **entryp) * 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; @@ -356,7 +362,7 @@ choose_format(struct archive *a) * 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 +375,11 @@ choose_format(struct archive *a) * 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,8 +395,9 @@ archive_read_header_position(struct archive *a) * 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; size_t bytes_read; size_t len; @@ -398,7 +408,7 @@ archive_read_data(struct archive *a, void *buff, size_t s) while (s > 0) { if (a->read_data_remaining <= 0) { - r = archive_read_data_block(a, + r = archive_read_data_block(&a->archive, (const void **)&a->read_data_block, &a->read_data_remaining, &a->read_data_offset); @@ -414,7 +424,7 @@ archive_read_data(struct archive *a, void *buff, size_t s) } 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); } @@ -459,19 +469,22 @@ archive_read_data(struct archive *a, void *buff, size_t s) * 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 +492,7 @@ archive_read_data_skip(struct archive *a) if (r == ARCHIVE_EOF) r = ARCHIVE_OK; - a->state = ARCHIVE_STATE_HEADER; + a->archive.state = ARCHIVE_STATE_HEADER; return (r); } @@ -492,13 +505,15 @@ archive_read_data_skip(struct archive *a) * 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,12 +530,14 @@ archive_read_data_block(struct archive *a, * 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; - __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) @@ -541,15 +558,23 @@ archive_read_close(struct archive *a) /* * 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]); @@ -561,11 +586,14 @@ archive_read_finish(struct archive *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 +601,19 @@ archive_read_finish(struct archive *a) * 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]); @@ -610,13 +640,15 @@ __archive_read_register_format(struct archive *a, * initialization functions. */ int -__archive_read_register_compression(struct archive *a, +__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]); diff --git a/lib/libarchive/archive_read_data_into_fd.c b/lib/libarchive/archive_read_data_into_fd.c index 4b4669e..028baf6 100644 --- a/lib/libarchive/archive_read_data_into_fd.c +++ b/lib/libarchive/archive_read_data_into_fd.c @@ -80,8 +80,6 @@ archive_read_data_into_fd(struct archive *a, int fd) total_written += bytes_written; p += bytes_written; size -= bytes_written; - if (a->extract_progress != NULL) - (*a->extract_progress)(a->extract_progress_user_data); } } diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index ef2e2b5..29586da 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -78,1590 +78,125 @@ __FBSDID("$FreeBSD$"); #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. */ -#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); + a->extract = (struct extract *)malloc(sizeof(*a->extract)); + if (a->extract == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't extract"); + return (NULL); } - - /* - * 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->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; - - extract = a->extract; - name = archive_entry_pathname(entry); - mode = archive_entry_mode(entry) & 0777; - r = ARCHIVE_OK; - - /* - * If we're not supposed to overwrite pre-existing files, - * use O_EXCL. Otherwise, use O_TRUNC. - */ - if (flags & (ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_NO_OVERWRITE)) - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); - else - fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); - - /* Try removing a pre-existing file. */ - if (fd < 0 && !(flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { - unlink(name); - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); - } - - /* Might be a non-existent parent dir; try fixing that. */ - if (fd < 0) { - create_parent_dir(a, name, flags); - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, mode); - } - if (fd < 0) { - archive_set_error(a, errno, "Can't open '%s'", name); - return (ARCHIVE_WARN); - } - r = archive_read_data_into_fd(a, fd); - extract->pst = NULL; /* Cached stat data no longer valid. */ - r2 = restore_metadata(a, fd, entry, flags); - close(fd); - return (err_combine(r, r2)); -} - -static int -extract_dir(struct archive *a, struct archive_entry *entry, int flags) -{ - struct extract *extract; - struct fixup_entry *fe; - char *path, *p; - 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); + /* Set up for this particular entry. */ + extract = a->extract; + 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_OK) + /* If there's an FD, pour data into it. */ + r = copy_data(_a, a->extract->ad); + if (r != ARCHIVE_OK) + archive_set_error(&a->archive, + archive_errno(extract->ad), + "%s", archive_error_string(extract->ad)); + r2 = archive_write_finish_entry(a->extract->ad); + /* Use the first message. */ + if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) + archive_set_error(&a->archive, + archive_errno(extract->ad), + "%s", archive_error_string(extract->ad)); + /* Use the worst error return. */ + if (r2 < r) + r = r2; 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); - 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)); - } - - if (r != 0) { - archive_set_error(a, errno, "Can't restore device node"); - return (ARCHIVE_WARN); + 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; } - - r = restore_metadata(a, -1, entry, flags); - return (r); -} - -static int -extract_char_device(struct archive *a, struct archive_entry *entry, int flags) -{ - mode_t mode; - - mode = (archive_entry_mode(entry) & ~S_IFMT) | S_IFCHR; - return (extract_device(a, entry, flags, mode)); } static int -extract_block_device(struct archive *a, struct archive_entry *entry, int flags) +copy_data(struct archive *ar, struct archive *aw) { - 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); + r = archive_write_data_block(aw, buff, size, offset); if (r != ARCHIVE_OK) return (r); } - - /* - * Make 'critical_flags' hold all file flags that can't be - * immediately restored. For example, on BSD systems, - * SF_IMMUTABLE prevents hardlinks from being created, so - * should not be set until after any hardlinks are created. To - * preserve some semblance of portability, this uses #ifdef - * extensively. Ugly, but it works. - * - * Yes, Virginia, this does create a security race. It's mitigated - * somewhat by the practice of creating dirs 0700 until the extract - * is done, but it would be nice if we could do more than that. - * People restoring critical file systems should be wary of - * other programs that might try to muck with files as they're - * being restored. - */ - /* Hopefully, the compiler will optimize this mess into a constant. */ - critical_flags = 0; -#ifdef SF_IMMUTABLE - critical_flags |= SF_IMMUTABLE; -#endif -#ifdef UF_IMMUTABLE - critical_flags |= UF_IMMUTABLE; -#endif -#ifdef SF_APPEND - critical_flags |= SF_APPEND; -#endif -#ifdef UF_APPEND - critical_flags |= UF_APPEND; -#endif -#ifdef EXT2_APPEND_FL - critical_flags |= EXT2_APPEND_FL; -#endif -#ifdef EXT2_IMMUTABLE_FL - critical_flags |= EXT2_IMMUTABLE_FL; -#endif - - if (flags & ARCHIVE_EXTRACT_FFLAGS) { - archive_entry_fflags(entry, &set, &clear); - - /* - * The first test encourages the compiler to eliminate - * all of this if it's not necessary. - */ - if ((critical_flags != 0) && (set & critical_flags)) { - le = current_fixup(a, archive_entry_pathname(entry)); - le->fixup |= FIXUP_FFLAGS; - le->fflags_set = set; - /* Store the mode if it's not already there. */ - if ((le->fixup & FIXUP_MODE) == 0) - le->mode = mode; - } else { - r = set_fflags(a, fd, archive_entry_pathname(entry), - mode, set, clear); - if (r != ARCHIVE_OK) - return (r); - } - } - return (ARCHIVE_OK); -} - - -#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && !defined(__linux) -static int -set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) -{ - struct extract *extract; - - extract = a->extract; - if (set == 0 && clear == 0) - return (ARCHIVE_OK); - - (void)mode; /* UNUSED */ - /* - * XXX Is the stat here really necessary? Or can I just use - * the 'set' flags directly? In particular, I'm not sure - * about the correct approach if we're overwriting an existing - * file that already has flags on it. XXX - */ - if (extract->pst != NULL) { - /* Already have stat() data available. */ - } else if (fd >= 0 && fstat(fd, &extract->st) == 0) - extract->pst = &extract->st; - else if (stat(name, &extract->st) == 0) - extract->pst = &extract->st; - else { - archive_set_error(a, errno, - "Couldn't stat file"); - return (ARCHIVE_WARN); - } - - extract->st.st_flags &= ~clear; - extract->st.st_flags |= set; -#ifdef HAVE_FCHFLAGS - /* If platform has fchflags() and we were given an fd, use it. */ - if (fd >= 0 && fchflags(fd, extract->st.st_flags) == 0) - return (ARCHIVE_OK); -#endif - /* - * If we can't use the fd to set the flags, we'll use the - * pathname to set flags. We prefer lchflags() but will use - * chflags() if we must. - */ -#ifdef HAVE_LCHFLAGS - if (lchflags(name, extract->st.st_flags) == 0) - return (ARCHIVE_OK); -#elif defined(HAVE_CHFLAGS) - if (chflags(name, extract->st.st_flags) == 0) - return (ARCHIVE_OK); -#endif - archive_set_error(a, errno, - "Failed to set file flags"); - return (ARCHIVE_WARN); -} - -#elif defined(__linux) && defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) - -/* - * Linux has flags too, but uses ioctl() to access them instead of - * having a separate chflags() system call. - */ -static int -set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) -{ - struct extract *extract; - int ret; - int myfd = fd; - unsigned long newflags, oldflags; - unsigned long sf_mask = 0; - - extract = a->extract; - if (set == 0 && clear == 0) - return (ARCHIVE_OK); - /* Only regular files and dirs can have flags. */ - if (!S_ISREG(mode) && !S_ISDIR(mode)) - return (ARCHIVE_OK); - - /* If we weren't given an fd, open it ourselves. */ - if (myfd < 0) - myfd = open(name, O_RDONLY|O_NONBLOCK); - if (myfd < 0) - return (ARCHIVE_OK); - - /* - * Linux has no define for the flags that are only settable by - * the root user. This code may seem a little complex, but - * there seem to be some Linux systems that lack these - * defines. (?) The code below degrades reasonably gracefully - * if sf_mask is incomplete. - */ -#ifdef EXT2_IMMUTABLE_FL - sf_mask |= EXT2_IMMUTABLE_FL; -#endif -#ifdef EXT2_APPEND_FL - sf_mask |= EXT2_APPEND_FL; -#endif - /* - * XXX As above, this would be way simpler if we didn't have - * to read the current flags from disk. XXX - */ - ret = ARCHIVE_OK; - /* Try setting the flags as given. */ - if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { - newflags = (oldflags & ~clear) | set; - if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) - goto cleanup; - if (errno != EPERM) - goto fail; - } - /* If we couldn't set all the flags, try again with a subset. */ - if (ioctl(myfd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { - newflags &= ~sf_mask; - oldflags &= sf_mask; - newflags |= oldflags; - if (ioctl(myfd, EXT2_IOC_SETFLAGS, &newflags) >= 0) - goto cleanup; - } - /* We couldn't set the flags, so report the failure. */ -fail: - archive_set_error(a, errno, - "Failed to set file flags"); - ret = ARCHIVE_WARN; -cleanup: - if (fd < 0) - close(myfd); - return (ret); -} - -#else /* Not HAVE_CHFLAGS && Not __linux */ - -/* - * Of course, some systems have neither BSD chflags() nor Linux' flags - * support through ioctl(). - */ -static int -set_fflags(struct archive *a, int fd, const char *name, mode_t mode, - unsigned long set, unsigned long clear) -{ - (void)a; - (void)fd; - (void)name; - (void)mode; - (void)set; - (void)clear; - return (ARCHIVE_OK); -} - -#endif /* __linux */ - -#ifndef HAVE_POSIX_ACL -/* Default empty function body to satisfy mainline code. */ -static int -set_acls(struct archive *a, int fd, struct archive_entry *entry) -{ - (void)a; - (void)fd; - (void)entry; - - return (ARCHIVE_OK); -} - -#else - -/* - * XXX TODO: What about ACL types other than ACCESS and DEFAULT? - */ -static int -set_acls(struct archive *a, int fd, struct archive_entry *entry) -{ - int ret; - - ret = set_acl(a, fd, entry, ACL_TYPE_ACCESS, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); - if (ret != ARCHIVE_OK) - return (ret); - ret = set_acl(a, fd, entry, ACL_TYPE_DEFAULT, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); - return (ret); -} - - -static int -set_acl(struct archive *a, int fd, struct archive_entry *entry, - acl_type_t acl_type, int ae_requested_type, const char *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; -} diff --git a/lib/libarchive/archive_read_private.h b/lib/libarchive/archive_read_private.h new file mode 100644 index 0000000..7789919 --- /dev/null +++ b/lib/libarchive/archive_read_private.h @@ -0,0 +1,188 @@ +/*- + * 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$ + */ + +#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; + + /* + * 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. + */ + struct { + int (*bid)(const void *buff, size_t); + int (*init)(struct archive_read *, 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_read *); /* Initialize. */ + int (*compression_finish)(struct archive_read *); + int (*compression_write)(struct archive_read *, 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_read *, + const void **, size_t request); + ssize_t (*compression_read_consume)(struct archive_read *, size_t); + off_t (*compression_skip)(struct archive_read *, 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. + */ + + struct archive_format_descriptor { + 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 *); + 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; + 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 *)); + +int __archive_read_register_compression(struct archive_read *a, + int (*bid)(const void *, size_t), + int (*init)(struct archive_read *, const void *, size_t)); + +#endif diff --git a/lib/libarchive/archive_read_support_compression_bzip2.c b/lib/libarchive/archive_read_support_compression_bzip2.c index 307da68..969387e 100644 --- a/lib/libarchive/archive_read_support_compression_bzip2.c +++ b/lib/libarchive/archive_read_support_compression_bzip2.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_read_private.h" #if HAVE_BZLIB_H struct private_data { @@ -56,19 +57,20 @@ struct private_data { int64_t total_out; }; -static int finish(struct archive *); -static ssize_t read_ahead(struct archive *, const void **, size_t); -static ssize_t read_consume(struct archive *, size_t); -static int drive_decompressor(struct archive *a, struct private_data *); +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. */ 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) { + struct archive_read *a = (struct archive_read *)_a; return (__archive_read_register_compression(a, bid, init)); } @@ -152,19 +154,19 @@ init(struct archive *a, const void *buff, size_t n) * 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 +178,9 @@ init(struct archive *a, const void *buff, size_t n) 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); } @@ -215,25 +217,26 @@ init(struct archive *a, const void *buff, size_t n) } /* 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, ARCHIVE_ERRNO_MISC, "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,7 +250,7 @@ init(struct archive *a, const void *buff, size_t n) * 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; @@ -255,7 +258,7 @@ read_ahead(struct archive *a, const void **p, size_t min) state = (struct private_data *)a->compression_data; was_avail = -1; 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); @@ -289,12 +292,12 @@ read_ahead(struct archive *a, const void **p, size_t min) * 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; + 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,7 +309,7 @@ read_consume(struct archive *a, size_t n) * Clean up the decompressor. */ static int -finish(struct archive *a) +finish(struct archive_read *a) { struct private_data *state; int ret; @@ -317,8 +320,9 @@ finish(struct archive *a) 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; } @@ -327,7 +331,7 @@ finish(struct archive *a) a->compression_data = NULL; if (a->client_closer != NULL) - (a->client_closer)(a, a->client_data); + (a->client_closer)(&a->archive, a->client_data); return (ret); } @@ -337,7 +341,7 @@ finish(struct archive *a) * 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; @@ -346,7 +350,7 @@ drive_decompressor(struct archive *a, struct private_data *state) total_decompressed = 0; for (;;) { if (state->stream.avail_in == 0) { - ret = (a->client_reader)(a, a->client_data, + ret = (a->client_reader)(&a->archive, a->client_data, (const void **)&state->stream.next_in); if (ret < 0) { /* @@ -356,12 +360,12 @@ drive_decompressor(struct archive *a, struct private_data *state) 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; } @@ -393,8 +397,8 @@ drive_decompressor(struct archive *a, struct private_data *state) /* 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); } diff --git a/lib/libarchive/archive_read_support_compression_compress.c b/lib/libarchive/archive_read_support_compression_compress.c index 3bd4a42..5159099 100644 --- a/lib/libarchive/archive_read_support_compression_compress.c +++ b/lib/libarchive/archive_read_support_compression_compress.c @@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_read_private.h" /* * Because LZW decompression is pretty simple, I've just implemented @@ -133,16 +134,17 @@ struct private_data { }; 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) { + struct archive_read *a = (struct archive_read *)_a; return (__archive_read_register_compression(a, bid, init)); } @@ -187,13 +189,13 @@ bid(const void *buff, size_t len) * 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; @@ -202,9 +204,9 @@ init(struct archive *a, const void *buff, size_t n) 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)); @@ -214,9 +216,9 @@ init(struct archive *a, const void *buff, size_t n) 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 +237,7 @@ init(struct archive *a, const void *buff, size_t n) * 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,7 +272,7 @@ fatal: * 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; @@ -278,7 +280,7 @@ read_ahead(struct archive *a, const void **p, size_t min) state = (struct private_data *)a->compression_data; was_avail = -1; 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); @@ -325,12 +327,12 @@ read_ahead(struct archive *a, const void **p, size_t min) * 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; + 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,7 +344,7 @@ read_consume(struct archive *a, size_t n) * Clean up the decompressor. */ static int -finish(struct archive *a) +finish(struct archive_read *a) { struct private_data *state; int ret = ARCHIVE_OK; @@ -357,7 +359,7 @@ finish(struct archive *a) a->compression_data = NULL; if (a->client_closer != NULL) - ret = (a->client_closer)(a, a->client_data); + ret = (a->client_closer)(&a->archive, a->client_data); return (ret); } @@ -368,7 +370,7 @@ finish(struct archive *a) * 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 +414,7 @@ next_code(struct archive *a, struct private_data *state) 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,7 +458,7 @@ next_code(struct archive *a, struct private_data *state) * -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[] = { @@ -467,13 +469,13 @@ getbits(struct archive *a, struct private_data *state, int n) while (state->bits_avail < n) { if (state->avail_in <= 0) { - ret = (a->client_reader)(a, a->client_data, + ret = (a->client_reader)(&a->archive, a->client_data, (const void **)&state->next_in); 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; diff --git a/lib/libarchive/archive_read_support_compression_gzip.c b/lib/libarchive/archive_read_support_compression_gzip.c index b64e1d2..49e209d 100644 --- a/lib/libarchive/archive_read_support_compression_gzip.c +++ b/lib/libarchive/archive_read_support_compression_gzip.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_read_private.h" #ifdef HAVE_ZLIB_H struct private_data { @@ -58,19 +59,20 @@ struct private_data { char header_done; }; -static int finish(struct archive *); -static ssize_t read_ahead(struct archive *, const void **, size_t); -static ssize_t read_consume(struct archive *, size_t); -static int drive_decompressor(struct archive *a, struct private_data *); +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. */ 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) { + struct archive_read *a = (struct archive_read *)_a; return (__archive_read_register_compression(a, bid, init)); } @@ -135,7 +137,7 @@ bid(const void *buff, size_t len) * 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 +155,19 @@ init(struct archive *a, const void *buff, size_t n) * 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 +182,9 @@ init(struct archive *a, const void *buff, size_t n) 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); } @@ -220,25 +222,26 @@ init(struct archive *a, const void *buff, size_t n) } /* 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,7 +255,7 @@ init(struct archive *a, const void *buff, size_t n) * 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; @@ -260,7 +263,7 @@ read_ahead(struct archive *a, const void **p, size_t min) state = (struct private_data *)a->compression_data; was_avail = -1; 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); @@ -294,12 +297,12 @@ read_ahead(struct archive *a, const void **p, size_t min) * 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; + 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,7 +314,7 @@ read_consume(struct archive *a, size_t n) * Clean up the decompressor. */ static int -finish(struct archive *a) +finish(struct archive_read *a) { struct private_data *state; int ret; @@ -322,8 +325,9 @@ finish(struct archive *a) 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; } @@ -332,7 +336,7 @@ finish(struct archive *a) a->compression_data = NULL; if (a->client_closer != NULL) - (a->client_closer)(a, a->client_data); + (a->client_closer)(&a->archive, a->client_data); return (ret); } @@ -342,7 +346,7 @@ finish(struct archive *a) * 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; @@ -356,7 +360,7 @@ drive_decompressor(struct archive *a, struct private_data *state) total_decompressed = 0; for (;;) { if (state->stream.avail_in == 0) { - ret = (a->client_reader)(a, a->client_data, + ret = (a->client_reader)(&a->archive, a->client_data, (const void **)&state->stream.next_in); if (ret < 0) { /* @@ -366,12 +370,12 @@ drive_decompressor(struct archive *a, struct private_data *state) 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; } @@ -520,7 +524,7 @@ drive_decompressor(struct archive *a, struct private_data *state) return (ARCHIVE_OK); default: /* Any other return value is an error. */ - archive_set_error(a, ARCHIVE_ERRNO_MISC, + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "gzip decompression failed (%s)", state->stream.msg); goto fatal; @@ -531,8 +535,8 @@ drive_decompressor(struct archive *a, struct private_data *state) /* 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); } diff --git a/lib/libarchive/archive_read_support_compression_none.c b/lib/libarchive/archive_read_support_compression_none.c index 1109488..72328a3 100644 --- a/lib/libarchive/archive_read_support_compression_none.c +++ b/lib/libarchive/archive_read_support_compression_none.c @@ -26,7 +26,6 @@ #include "archive_platform.h" __FBSDID("$FreeBSD$"); -#include #ifdef HAVE_ERRNO_H #include #endif @@ -45,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_read_private.h" struct archive_decompress_none { char *buffer; @@ -75,18 +75,19 @@ struct archive_decompress_none { #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) { + struct archive_read *a = (struct archive_read *)_a; return (__archive_read_register_compression(a, archive_decompressor_none_bid, archive_decompressor_none_init)); @@ -105,16 +106,16 @@ archive_decompressor_none_bid(const void *buff, size_t len) } 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,7 +125,7 @@ archive_decompressor_none_init(struct archive *a, const void *buff, size_t n) 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); } @@ -149,7 +150,7 @@ archive_decompressor_none_init(struct archive *a, const void *buff, size_t n) * 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; @@ -217,7 +218,8 @@ archive_decompressor_none_read_ahead(struct archive *a, const void **buff, * char ** should be compatible, but they * aren't, hence the cast. */ - bytes_read = (a->client_reader)(a, a->client_data, + bytes_read = (a->client_reader)(&a->archive, + a->client_data, (const void **)&state->client_buff); if (bytes_read < 0) { /* Read error. */ state->client_total = state->client_avail = 0; @@ -231,7 +233,7 @@ archive_decompressor_none_read_ahead(struct archive *a, const void **buff, 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,7 +250,7 @@ archive_decompressor_none_read_ahead(struct archive *a, const void **buff, * 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; @@ -262,7 +264,7 @@ archive_decompressor_none_read_consume(struct archive *a, size_t request) state->client_next += request; state->client_avail -= request; } - a->file_position += request; + a->archive.file_position += request; return (request); } @@ -272,7 +274,7 @@ archive_decompressor_none_read_consume(struct archive *a, size_t request) * 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; @@ -306,8 +308,8 @@ archive_decompressor_none_skip(struct archive *a, off_t request) #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 +317,10 @@ archive_decompressor_none_skip(struct archive *a, off_t request) 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,23 +338,21 @@ archive_decompressor_none_skip(struct archive *a, off_t request) 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; @@ -361,6 +361,6 @@ archive_decompressor_none_finish(struct archive *a) free(state); a->compression_data = NULL; if (a->client_closer != NULL) - return ((a->client_closer)(a, a->client_data)); + return ((a->client_closer)(&a->archive, a->client_data)); return (ARCHIVE_OK); } diff --git a/lib/libarchive/archive_read_support_format_cpio.c b/lib/libarchive/archive_read_support_format_cpio.c index 12e24a1..3a0f334 100644 --- a/lib/libarchive/archive_read_support_format_cpio.c +++ b/lib/libarchive/archive_read_support_format_cpio.c @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #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,7 +109,7 @@ struct links_entry { #define CPIO_MAGIC 0x13141516 struct cpio { int magic; - int (*read_header)(struct archive *, struct cpio *, + int (*read_header)(struct archive_read *, struct cpio *, struct stat *, size_t *, size_t *); struct links_entry *links_head; struct archive_string entry_name; @@ -120,34 +121,35 @@ struct cpio { 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 *, +static int header_bin_be(struct archive_read *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_bin_le(struct archive *, struct cpio *, struct stat *, +static int header_bin_le(struct archive_read *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_newc(struct archive *, struct cpio *, struct stat *, +static int header_newc(struct archive_read *, struct cpio *, struct stat *, size_t *, size_t *); -static int header_odc(struct archive *, struct cpio *, struct stat *, +static int header_odc(struct archive_read *, struct cpio *, struct stat *, 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); 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,7 +170,7 @@ archive_read_support_format_cpio(struct archive *a) 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; @@ -227,7 +229,7 @@ archive_read_format_cpio_bid(struct archive *a) } 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; @@ -274,7 +276,7 @@ archive_read_format_cpio_read_header(struct archive *a, /* 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); } @@ -285,7 +287,7 @@ archive_read_format_cpio_read_header(struct archive *a, } 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; @@ -322,7 +324,7 @@ archive_read_format_cpio_read_data(struct archive *a, } static int -header_newc(struct archive *a, struct cpio *cpio, struct stat *st, +header_newc(struct archive_read *a, struct cpio *cpio, struct stat *st, size_t *namelength, size_t *name_pad) { const void *h; @@ -339,11 +341,11 @@ header_newc(struct archive *a, struct cpio *cpio, struct stat *st, 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? */ } @@ -379,15 +381,15 @@ header_newc(struct archive *a, struct cpio *cpio, struct stat *st, } static int -header_odc(struct archive *a, struct cpio *cpio, struct stat *st, +header_odc(struct archive_read *a, struct cpio *cpio, struct stat *st, 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)); @@ -423,15 +425,15 @@ header_odc(struct archive *a, struct cpio *cpio, struct stat *st, } static int -header_bin_le(struct archive *a, struct cpio *cpio, struct stat *st, +header_bin_le(struct archive_read *a, struct cpio *cpio, struct stat *st, 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)); @@ -460,15 +462,15 @@ header_bin_le(struct archive *a, struct cpio *cpio, struct stat *st, } static int -header_bin_be(struct archive *a, struct cpio *cpio, struct stat *st, +header_bin_be(struct archive_read *a, struct cpio *cpio, struct stat *st, 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, @@ -497,7 +499,7 @@ header_bin_be(struct archive *a, struct cpio *cpio, struct stat *st, } static int -archive_read_format_cpio_cleanup(struct archive *a) +archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; diff --git a/lib/libarchive/archive_read_support_format_empty.c b/lib/libarchive/archive_read_support_format_empty.c index 2df2194..f729ae6 100644 --- a/lib/libarchive/archive_read_support_format_empty.c +++ b/lib/libarchive/archive_read_support_format_empty.c @@ -29,15 +29,17 @@ __FBSDID("$FreeBSD$"); #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,7 +55,7 @@ archive_read_support_format_empty(struct archive *a) 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; @@ -65,20 +67,20 @@ archive_read_format_empty_bid(struct archive *a) } 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 */ diff --git a/lib/libarchive/archive_read_support_format_iso9660.c b/lib/libarchive/archive_read_support_format_iso9660.c index a2dc6fd..f115b0e 100644 --- a/lib/libarchive/archive_read_support_format_iso9660.c +++ b/lib/libarchive/archive_read_support_format_iso9660.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_read_private.h" #include "archive_string.h" /* @@ -222,12 +223,12 @@ struct iso9660 { }; 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 +237,7 @@ static time_t isodate17(const unsigned char *); 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 +249,15 @@ static void release_file(struct iso9660 *, struct file_info *); 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,7 +281,7 @@ archive_read_support_format_iso9660(struct archive *a) 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; @@ -338,7 +340,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) } 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; @@ -349,9 +351,9 @@ archive_read_format_iso9660_read_header(struct archive *a, iso9660 = (struct iso9660 *)*(a->pformat_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. */ @@ -397,7 +399,7 @@ archive_read_format_iso9660_read_header(struct archive *a, /* 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; @@ -419,7 +421,7 @@ archive_read_format_iso9660_read_header(struct archive *a, step = iso9660->entry_bytes_remaining; bytes_read = (a->compression_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); @@ -445,9 +447,9 @@ archive_read_format_iso9660_read_header(struct archive *a, 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 +461,7 @@ archive_read_format_iso9660_read_header(struct archive *a, } 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,7 +470,7 @@ archive_read_format_iso9660_read_data_skip(struct archive *a) } 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; @@ -484,7 +486,7 @@ archive_read_format_iso9660_read_data(struct archive *a, bytes_read = (a->compression_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); @@ -500,7 +502,7 @@ archive_read_format_iso9660_read_data(struct archive *a, } 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; @@ -889,7 +891,7 @@ release_file(struct iso9660 *iso9660, struct file_info *file) } 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; diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index 1934d55..8aa5a38 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -83,6 +83,7 @@ static size_t wcslen(const wchar_t *s) #include "archive.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_read_private.h" /* * Layout of POSIX 'ustar' tar header. @@ -174,50 +175,50 @@ struct tar { 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 int gnu_read_sparse_data(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_parse_sparse_data(struct archive_read *, struct tar *, const struct gnu_sparse *sparse, int length); -static int header_Solaris_ACL(struct archive *, struct tar *, +static int header_Solaris_ACL(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *); -static int header_common(struct archive *, struct tar *, +static int header_common(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *); -static int header_old_tar(struct archive *, struct tar *, +static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *); -static int header_pax_extensions(struct archive *, struct tar *, +static int header_pax_extensions(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *); -static int header_pax_global(struct archive *, struct tar *, +static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *h); -static int header_longlink(struct archive *, struct tar *, +static int header_longlink(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *h); -static int header_longname(struct archive *, struct tar *, +static int header_longname(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *h); -static int header_volume(struct archive *, struct tar *, +static int header_volume(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *h); -static int header_ustar(struct archive *, struct tar *, +static int header_ustar(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, const void *h); -static int header_gnutar(struct archive *, struct tar *, +static int header_gnutar(struct archive_read *, 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 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 checksum(struct archive_read *, const void *); static int pax_attribute(struct archive_entry *, struct stat *, wchar_t *key, wchar_t *value); -static int pax_header(struct archive *, struct tar *, +static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, struct stat *, 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 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 *, +static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *, struct stat *); static int tohex(int c); static char *url_decode(const char *); @@ -254,14 +255,16 @@ archive_read_support_format_gnutar(struct archive *a) 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,7 +282,7 @@ archive_read_support_format_tar(struct archive *a) } static int -archive_read_format_tar_cleanup(struct archive *a) +archive_read_format_tar_cleanup(struct archive_read *a) { struct tar *tar; @@ -300,7 +303,7 @@ archive_read_format_tar_cleanup(struct archive *a) 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 +314,8 @@ archive_read_format_tar_bid(struct archive *a) * 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,7 +324,7 @@ archive_read_format_tar_bid(struct archive *a) * 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++; @@ -345,7 +348,7 @@ archive_read_format_tar_bid(struct archive *a) * 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 +356,7 @@ archive_read_format_tar_bid(struct archive *a) /* 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 +415,7 @@ archive_read_format_tar_bid(struct archive *a) * 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) { /* @@ -472,7 +475,7 @@ archive_read_format_tar_read_header(struct archive *a, } 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; @@ -497,7 +500,7 @@ archive_read_format_tar_read_data(struct archive *a, 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, + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated tar archive"); return (ARCHIVE_FATAL); } @@ -538,7 +541,7 @@ archive_read_format_tar_read_data(struct archive *a, } 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; @@ -584,7 +587,7 @@ archive_read_format_tar_skip(struct archive *a) * with a single entry. */ static int -tar_read_header(struct archive *a, struct tar *tar, +tar_read_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st) { ssize_t bytes; @@ -610,7 +613,7 @@ tar_read_header(struct archive *a, struct tar *tar, bytes = (a->compression_read_ahead)(a, &h, 512); if (bytes > 0) (a->compression_read_consume)(a, bytes); - archive_set_error(a, 0, NULL); + archive_set_error(&a->archive, 0, NULL); return (ARCHIVE_EOF); } @@ -622,12 +625,12 @@ tar_read_header(struct archive *a, struct tar *tar, * 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,13 +638,13 @@ tar_read_header(struct archive *a, struct tar *tar, 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"; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; + a->archive.archive_format_name = "Solaris tar"; err = header_Solaris_ACL(a, tar, entry, st, h); break; case 'g': /* POSIX-standard 'g' header. */ - a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive_format_name = "POSIX pax interchange format"; + 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, st, h); break; case 'K': /* Long link name (GNU tar, others) */ @@ -654,30 +657,30 @@ tar_read_header(struct archive *a, struct tar *tar, err = header_volume(a, tar, entry, st, 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); break; case 'x': /* POSIX-standard 'x' header. */ - a->archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; - a->archive_format_name = "POSIX pax interchange format"; + 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, st, 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"; + a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; + a->archive.archive_format_name = "GNU tar format"; err = header_gnutar(a, tar, entry, st, 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); } else { - a->archive_format = ARCHIVE_FORMAT_TAR; - a->archive_format_name = "tar (non-POSIX)"; + a->archive.archive_format = ARCHIVE_FORMAT_TAR; + a->archive.archive_format_name = "tar (non-POSIX)"; err = header_old_tar(a, tar, entry, st, h); } } @@ -689,7 +692,7 @@ tar_read_header(struct archive *a, struct tar *tar, * 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; @@ -750,7 +753,7 @@ archive_block_is_null(const unsigned char *p) * Interpret 'A' Solaris ACL header */ static int -header_Solaris_ACL(struct archive *a, struct tar *tar, +header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { int err, err2; @@ -786,7 +789,7 @@ header_Solaris_ACL(struct archive *a, struct tar *tar, * Interpret 'K' long linkname header. */ static int -header_longlink(struct archive *a, struct tar *tar, +header_longlink(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { int err, err2; @@ -804,7 +807,7 @@ header_longlink(struct archive *a, struct tar *tar, * Interpret 'L' long filename header. */ static int -header_longname(struct archive *a, struct tar *tar, +header_longname(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { int err, err2; @@ -822,7 +825,7 @@ header_longname(struct archive *a, struct tar *tar, * Interpret 'V' GNU tar volume header. */ static int -header_volume(struct archive *a, struct tar *tar, +header_volume(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { (void)h; @@ -835,7 +838,7 @@ header_volume(struct archive *a, struct tar *tar, * 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,7 +885,7 @@ read_body_to_string(struct archive *a, struct tar *tar, * common parsing into one place. */ static int -header_common(struct archive *a, struct tar *tar, struct archive_entry *entry, +header_common(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { const struct archive_entry_header_ustar *header; @@ -946,7 +949,7 @@ header_common(struct archive *a, struct tar *tar, struct archive_entry *entry, * itself an uncompressed tar archive. */ if (st->st_size > 0 && - a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && + a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && archive_read_format_tar_bid(a) > 50) st->st_size = 0; break; @@ -1014,7 +1017,7 @@ header_common(struct archive *a, struct tar *tar, struct archive_entry *entry, * Parse out header elements for "old-style" tar archives. */ static int -header_old_tar(struct archive *a, struct tar *tar, struct archive_entry *entry, +header_old_tar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { const struct archive_entry_header_ustar *header; @@ -1036,7 +1039,7 @@ header_old_tar(struct archive *a, struct tar *tar, struct archive_entry *entry, * Parse a file header for a pax extended archive entry. */ static int -header_pax_global(struct archive *a, struct tar *tar, +header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { int err, err2; @@ -1047,7 +1050,7 @@ header_pax_global(struct archive *a, struct tar *tar, } static int -header_pax_extensions(struct archive *a, struct tar *tar, +header_pax_extensions(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { int err, err2; @@ -1080,7 +1083,7 @@ header_pax_extensions(struct archive *a, struct tar *tar, * handles "pax" or "extended ustar" entries. */ static int -header_ustar(struct archive *a, struct tar *tar, struct archive_entry *entry, +header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { const struct archive_entry_header_ustar *header; @@ -1132,7 +1135,7 @@ header_ustar(struct archive *a, struct tar *tar, struct archive_entry *entry, * 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, +pax_header(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, char *attr) { size_t attr_length, l, line_length; @@ -1158,7 +1161,7 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry, 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); } @@ -1183,7 +1186,7 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry, 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); } @@ -1192,7 +1195,7 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry, /* 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); } @@ -1204,7 +1207,7 @@ pax_header(struct archive *a, struct tar *tar, struct archive_entry *entry, while (*wp && *wp != L'=') ++wp; if (*wp == L'\0' || wp == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_MISC, + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid pax extended attributes"); return (ARCHIVE_WARN); } @@ -1430,7 +1433,7 @@ pax_time(const wchar_t *p, int64_t *ps, long *pn) * Parse GNU tar header */ static int -header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry, +header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, struct stat *st, const void *h) { const struct archive_entry_header_gnutar *header; @@ -1495,7 +1498,7 @@ header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry, } static int -gnu_read_sparse_data(struct archive *a, struct tar *tar, +gnu_read_sparse_data(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header) { ssize_t bytes_read; @@ -1516,7 +1519,7 @@ gnu_read_sparse_data(struct archive *a, struct tar *tar, 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); @@ -1531,7 +1534,7 @@ gnu_read_sparse_data(struct archive *a, struct tar *tar, } static void -gnu_parse_sparse_data(struct archive *a, struct tar *tar, +gnu_parse_sparse_data(struct archive_read *a, struct tar *tar, const struct gnu_sparse *sparse, int length) { struct sparse_block *last; diff --git a/lib/libarchive/archive_read_support_format_zip.c b/lib/libarchive/archive_read_support_format_zip.c index 5807e56..f15eba0 100644 --- a/lib/libarchive/archive_read_support_format_zip.c +++ b/lib/libarchive/archive_read_support_format_zip.c @@ -44,15 +44,16 @@ __FBSDID("$FreeBSD$"); #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,8 +75,8 @@ struct zip { 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; @@ -115,36 +116,40 @@ static const char *compression_names[] = { "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); +/* Largest 32-bit unsigned value, stored in a 64-bit constant. */ +static const uint64_t max_uint32 = (((uint64_t)1) << 32) - 1; + 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,14 +169,14 @@ archive_read_support_format_zip(struct archive *a) 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); @@ -194,7 +199,7 @@ archive_read_format_zip_bid(struct archive *a) } 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,9 +207,9 @@ archive_read_format_zip_read_header(struct archive *a, 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->decompress_init = 0; @@ -218,7 +223,7 @@ archive_read_format_zip_read_header(struct archive *a, 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,19 +248,19 @@ archive_read_format_zip_read_header(struct archive *a, * 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; @@ -266,7 +271,7 @@ zip_read_file_header(struct archive *a, struct archive_entry *entry, bytes_read = (a->compression_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); } @@ -299,7 +304,7 @@ zip_read_file_header(struct archive *a, struct archive_entry *entry, /* Read the filename. */ bytes_read = (a->compression_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); } @@ -316,7 +321,7 @@ zip_read_file_header(struct archive *a, struct archive_entry *entry, /* Read the extra data. */ bytes_read = (a->compression_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); } @@ -341,7 +346,7 @@ zip_read_file_header(struct archive *a, struct archive_entry *entry, 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,7 +373,7 @@ zip_time(const char *p) } 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; @@ -388,7 +393,7 @@ archive_read_format_zip_read_data(struct archive *a, int bytes_read = (a->compression_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); @@ -402,19 +407,21 @@ archive_read_format_zip_read_data(struct archive *a, /* 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 & max_uint32) + != (zip->entry_uncompressed_bytes_read & max_uint32)) { + 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 +444,7 @@ archive_read_format_zip_read_data(struct archive *a, *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,7 +479,7 @@ archive_read_format_zip_read_data(struct archive *a, * 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; @@ -495,7 +502,7 @@ zip_read_data_none(struct archive *a, const void **buff, */ bytes_avail = (a->compression_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); } @@ -513,7 +520,7 @@ zip_read_data_none(struct archive *a, const void **buff, #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; @@ -529,7 +536,7 @@ zip_read_data_deflate(struct archive *a, const void **buff, 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); } @@ -540,7 +547,7 @@ zip_read_data_deflate(struct archive *a, const void **buff, 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); } @@ -555,7 +562,7 @@ zip_read_data_deflate(struct archive *a, const void **buff, */ bytes_avail = (a->compression_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,11 +588,11 @@ zip_read_data_deflate(struct archive *a, const void **buff, 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); } @@ -605,20 +612,20 @@ zip_read_data_deflate(struct archive *a, const void **buff, } #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; @@ -648,7 +655,8 @@ archive_read_format_zip_read_data_skip(struct archive *a) while (zip->entry_bytes_remaining > 0) { bytes_avail = (a->compression_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); } @@ -663,7 +671,7 @@ archive_read_format_zip_read_data_skip(struct archive *a) } static int -archive_read_format_zip_cleanup(struct archive *a) +archive_read_format_zip_cleanup(struct archive_read *a) { struct zip *zip; diff --git a/lib/libarchive/archive_util.3 b/lib/libarchive/archive_util.3 index 676abef..a67e529 100644 --- a/lib/libarchive/archive_util.3 +++ b/lib/libarchive/archive_util.3 @@ -28,6 +28,7 @@ .Dt archive_util 3 .Os .Sh NAME +.Nm archive_clear_error , .Nm archive_compression , .Nm archive_compression_name , .Nm archive_errno , @@ -38,6 +39,8 @@ .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 * @@ -59,6 +62,9 @@ object used in the .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 diff --git a/lib/libarchive/archive_util.c b/lib/libarchive/archive_util.c index a31f6c8..82e5e02 100644 --- a/lib/libarchive/archive_util.c +++ b/lib/libarchive/archive_util.c @@ -118,6 +118,12 @@ archive_position_uncompressed(struct archive *a) 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, ...) diff --git a/lib/libarchive/archive_virtual.c b/lib/libarchive/archive_virtual.c new file mode 100644 index 0000000..6b7b12e --- /dev/null +++ b/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$"); + +#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)); +} diff --git a/lib/libarchive/archive_write.3 b/lib/libarchive/archive_write.3 index d90af7d..6725a71 100644 --- a/lib/libarchive/archive_write.3 +++ b/lib/libarchive/archive_write.3 @@ -89,13 +89,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 @@ -260,6 +260,12 @@ Complete the archive and invoke the close callback. 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 +463,9 @@ through whatever API function resulted in that call, which 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 diff --git a/lib/libarchive/archive_write.c b/lib/libarchive/archive_write.c index 0aff2f1..e68b0b8 100644 --- a/lib/libarchive/archive_write.c +++ b/lib/libarchive/archive_write.c @@ -55,6 +55,31 @@ __FBSDID("$FreeBSD$"); #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,18 +87,18 @@ __FBSDID("$FreeBSD$"); 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. */ @@ -91,17 +116,19 @@ archive_write_new(void) * 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 +137,11 @@ archive_write_set_bytes_per_block(struct archive *a, int bytes_per_block) * 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 +150,11 @@ archive_write_get_bytes_per_block(struct archive *a) * 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 +163,11 @@ archive_write_set_bytes_in_last_block(struct archive *a, int bytes) * 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 +177,11 @@ archive_write_get_bytes_in_last_block(struct archive *a) * 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,16 +192,18 @@ archive_write_set_skip_file(struct archive *a, dev_t d, ino_t i) * 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; @@ -185,15 +222,17 @@ archive_write_open(struct archive *a, void *client_data, * 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,6 +242,13 @@ archive_write_close(struct archive *a) r = r1; } + /* Release 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); @@ -210,41 +256,47 @@ archive_write_close(struct archive *a) r = r1; } - a->state = ARCHIVE_STATE_CLOSED; + 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,7 +304,8 @@ archive_write_header(struct archive *a, struct archive_entry *entry) 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"); + archive_set_error(&a->archive, 0, + "Can't add archive to itself"); return (ARCHIVE_WARN); } @@ -261,33 +314,34 @@ archive_write_header(struct archive *a, struct archive_entry *entry) 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. */ -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)); } diff --git a/lib/libarchive/archive_write_disk.3 b/lib/libarchive/archive_write_disk.3 new file mode 100644 index 0000000..6b58d9c --- /dev/null +++ b/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$ +.\" +.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@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 diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c new file mode 100644 index 0000000..f6187b8 --- /dev/null +++ b/lib/libarchive/archive_write_disk.c @@ -0,0 +1,1929 @@ +/*- + * 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$"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_EXT2FS_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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_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 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; + a->entry = 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) { + /* + * 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) + return (ARCHIVE_OK); + 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; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_DATA, "archive_write_data"); + if (a->fd < 0) + return (ARCHIVE_OK); + + return (_archive_write_data_block(_a, buff, size, a->offset)); +} + +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; + } + 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(); + archive_string_ensure(&a->path_safe, 64); + 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) + if (unlink(a->name) != 0 && errno != ENOENT) { + /* If the file doesn't exist, that's okay. */ + /* Anything else is a problem. */ + 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) { + /* 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 == EEXIST) { + /* If we're not overwriting, we're done. */ + if (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE) { + archive_set_error(&a->archive, en, "Already exists"); + return (ARCHIVE_WARN); + } + + /* Find out what's in the way 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 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_WARN); + } + + 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. + */ + if (a->mode != a->st.st_mode) + a->deferred |= 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: + /* Fall through, as required by POSIX. */ + 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); + /* 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) { + 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 + } + 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); + 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, '/'); + 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(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); +} + +static int +set_time(struct archive_write_disk *a) +{ + const struct stat *st; + struct timeval times[2]; + + st = archive_entry_stat(a->entry); + + 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 (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); +} + +static int +set_mode(struct archive_write_disk *a, int mode) +{ + int r = ARCHIVE_OK; + + 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; + 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; + 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; + 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"); + 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"); + 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"); + r = ARCHIVE_WARN; + } + } + return (r); +} + +static int +set_fflags(struct archive_write_disk *a) +{ + struct fixup_entry *le; + 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)ad; /* 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); +} diff --git a/lib/libarchive/archive_write_disk_private.h b/lib/libarchive/archive_write_disk_private.h new file mode 100644 index 0000000..562229b --- /dev/null +++ b/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$ + */ + +#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED +#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED + +struct archive_write_disk; + +#endif diff --git a/lib/libarchive/archive_write_disk_set_standard_lookup.c b/lib/libarchive/archive_write_disk_set_standard_lookup.c new file mode 100644 index 0000000..273b9af --- /dev/null +++ b/lib/libarchive/archive_write_disk_set_standard_lookup.c @@ -0,0 +1,212 @@ +/*- + * 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$"); + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#include +#endif +#ifdef HAVE_ATTR_XATTR_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#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 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(sizeof(struct bucket[127])); + struct bucket *gcache = malloc(sizeof(struct bucket[127])); + memset(ucache, 0, sizeof(struct bucket[127])); + memset(gcache, 0, sizeof(struct bucket[127])); + 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; + int cache_size; + struct bucket *gcache = (struct bucket *)private_data; + + cache_size = 127; + + /* 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; + int cache_size; + struct bucket *ucache = (struct bucket *)private_data; + + cache_size = 127; + + /* 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) +{ + free(private); +} + + +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; +} diff --git a/lib/libarchive/archive_write_private.h b/lib/libarchive/archive_write_private.h new file mode 100644 index 0000000..c551b46 --- /dev/null +++ b/lib/libarchive/archive_write_private.h @@ -0,0 +1,177 @@ +/*- + * 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$ + */ + +#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; + + 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. */ + + /* + * On write, the client just invokes an archive_write_set function + * which sets up the data here directly. + */ + void *compression_data; /* Data for (de)compressor. */ + int (*compression_init)(struct archive_write *); /* Initialize. */ + int (*compression_finish)(struct archive_write *); + int (*compression_write)(struct archive_write *, 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_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); + + /* + * Various information needed by archive_extract. + */ + struct extract *extract; + int (*cleanup_archive_extract)(struct archive *); +}; + +/* + * 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 diff --git a/lib/libarchive/archive_write_set_compression_bzip2.c b/lib/libarchive/archive_write_set_compression_bzip2.c index 85974f8..eab8c70 100644 --- a/lib/libarchive/archive_write_set_compression_bzip2.c +++ b/lib/libarchive/archive_write_set_compression_bzip2.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_write_private.h" struct private_data { bz_stream stream; @@ -62,23 +63,23 @@ struct private_data { #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"); + 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->compression_init = &archive_compressor_bzip2_init; - a->compression_code = ARCHIVE_COMPRESSION_BZIP2; - a->compression_name = "bzip2"; return (ARCHIVE_OK); } @@ -86,23 +87,23 @@ archive_write_set_compression_bzip2(struct archive *a) * 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 @@ archive_compressor_bzip2_init(struct archive *a) 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); @@ -131,7 +132,7 @@ archive_compressor_bzip2_init(struct archive *a) } /* 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 @@ archive_compressor_bzip2_init(struct archive *a) /* 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 @@ archive_compressor_bzip2_init(struct archive *a) * 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; 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 @@ archive_compressor_bzip2_write(struct archive *a, const void *buff, 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 @@ archive_compressor_bzip2_write(struct archive *a, const void *buff, * 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; @@ -207,7 +208,7 @@ archive_compressor_bzip2_finish(struct archive *a) state = (struct private_data *)a->compression_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 @@ archive_compressor_bzip2_finish(struct archive *a) } /* 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,7 +275,7 @@ cleanup: 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; } @@ -284,7 +285,7 @@ cleanup: /* Close the output */ if (a->client_closer != NULL) - (a->client_closer)(a, a->client_data); + (a->client_closer)(&a->archive, a->client_data); return (ret); } @@ -297,15 +298,16 @@ cleanup: * 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 +319,7 @@ drive_compressor(struct archive *a, struct private_data *state, int finishing) 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 +342,8 @@ drive_compressor(struct archive *a, struct private_data *state, int finishing) 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); } diff --git a/lib/libarchive/archive_write_set_compression_gzip.c b/lib/libarchive/archive_write_set_compression_gzip.c index 09f43f6..38742f3 100644 --- a/lib/libarchive/archive_write_set_compression_gzip.c +++ b/lib/libarchive/archive_write_set_compression_gzip.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_write_private.h" struct private_data { z_stream stream; @@ -63,11 +64,11 @@ struct private_data { #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 @@ static int drive_compressor(struct archive *, struct private_data *, * 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"); + 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->compression_init = &archive_compressor_gzip_init; - a->compression_code = ARCHIVE_COMPRESSION_GZIP; - a->compression_name = "gzip"; + a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; + a->archive.compression_name = "gzip"; return (ARCHIVE_OK); } @@ -88,24 +91,24 @@ archive_write_set_compression_gzip(struct archive *a) * 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 @@ archive_compressor_gzip_init(struct archive *a) 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); @@ -157,7 +160,7 @@ archive_compressor_gzip_init(struct archive *a) } /* 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 @@ archive_compressor_gzip_init(struct archive *a) /* 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,7 +190,7 @@ archive_compressor_gzip_init(struct archive *a) * 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; @@ -195,7 +198,7 @@ archive_compressor_gzip_write(struct archive *a, const void *buff, state = (struct private_data *)a->compression_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 @@ archive_compressor_gzip_write(struct archive *a, const void *buff, 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 @@ archive_compressor_gzip_write(struct archive *a, const void *buff, * 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; @@ -231,7 +234,7 @@ archive_compressor_gzip_finish(struct archive *a) state = (struct private_data *)a->compression_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 @@ archive_compressor_gzip_finish(struct archive *a) /* 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 @@ archive_compressor_gzip_finish(struct archive *a) } /* 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,7 +334,7 @@ cleanup: 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; } @@ -340,7 +343,7 @@ cleanup: /* Close the output */ if (a->client_closer != NULL) - (a->client_closer)(a, a->client_data); + (a->client_closer)(&a->archive, a->client_data); return (ret); } @@ -353,15 +356,16 @@ cleanup: * 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 +376,7 @@ drive_compressor(struct archive *a, struct private_data *state, int finishing) 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 +400,7 @@ drive_compressor(struct archive *a, struct private_data *state, int finishing) 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); } diff --git a/lib/libarchive/archive_write_set_compression_none.c b/lib/libarchive/archive_write_set_compression_none.c index 329bd76..44ac83e 100644 --- a/lib/libarchive/archive_write_set_compression_none.c +++ b/lib/libarchive/archive_write_set_compression_none.c @@ -38,11 +38,12 @@ __FBSDID("$FreeBSD$"); #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 @@ struct archive_none { }; 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"); + 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->compression_init = &archive_compressor_none_init; - a->compression_code = ARCHIVE_COMPRESSION_NONE; - a->compression_name = "none"; return (0); } @@ -65,23 +66,23 @@ archive_write_set_compression_none(struct archive *a) * 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 @@ archive_compressor_none_init(struct archive *a) 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); @@ -111,7 +112,7 @@ archive_compressor_none_init(struct archive *a) * 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; @@ -122,7 +123,7 @@ archive_compressor_none_write(struct archive *a, const void *vbuff, state = (struct archive_none *)a->compression_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,14 @@ archive_compressor_none_write(struct archive *a, const void *vbuff, */ 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); remaining -= bytes_written; buff += bytes_written; } - a->file_position += length; + a->archive.file_position += length; return (ARCHIVE_OK); } @@ -155,12 +156,12 @@ archive_compressor_none_write(struct archive *a, const void *vbuff, * 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 +175,7 @@ archive_compressor_none_write(struct archive *a, const void *vbuff, buff += to_copy; remaining -= to_copy; } - a->file_position += length; + a->archive.file_position += length; return (ARCHIVE_OK); } @@ -183,7 +184,7 @@ archive_compressor_none_write(struct archive *a, const void *vbuff, * 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; @@ -195,7 +196,7 @@ archive_compressor_none_finish(struct archive *a) state = (struct archive_none *)a->compression_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,19 +223,19 @@ archive_compressor_none_finish(struct archive *a) 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); + ret2 = (a->client_closer)(&a->archive, a->client_data); free(state->buffer); free(state); diff --git a/lib/libarchive/archive_write_set_format_cpio.c b/lib/libarchive/archive_write_set_format_cpio.c index 8aeb02b..e16fe31 100644 --- a/lib/libarchive/archive_write_set_format_cpio.c +++ b/lib/libarchive/archive_write_set_format_cpio.c @@ -43,12 +43,14 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" #include "archive_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 *, +#include "archive_write_private.h" + +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 +77,18 @@ struct cpio_header { * 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,13 +99,14 @@ archive_write_set_format_cpio(struct archive *a) 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; @@ -126,7 +130,7 @@ archive_write_cpio_header(struct archive *a, struct archive_entry *entry) * field only limits the number of files in the archive. */ if (st->st_ino > 0777777) { - archive_set_error(a, ERANGE, "large inode number truncated"); + archive_set_error(&a->archive, ERANGE, "large inode number truncated"); ret = ARCHIVE_WARN; } @@ -167,7 +171,7 @@ archive_write_cpio_header(struct archive *a, struct archive_entry *entry) } 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; @@ -215,7 +219,7 @@ format_octal_recursive(int64_t v, char *p, int s) } static int -archive_write_cpio_finish(struct archive *a) +archive_write_cpio_finish(struct archive_write *a) { struct cpio *cpio; struct stat st; @@ -230,14 +234,22 @@ archive_write_cpio_finish(struct archive *a) 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; diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index 8bc4f9e..35a43ff 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -52,12 +52,12 @@ __FBSDID("$FreeBSD$"); #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 +69,12 @@ static void add_pax_attr_time(struct archive_string *, 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 +83,7 @@ static char *build_ustar_entry_name(char *dest, const char *src, 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 +93,11 @@ static int write_nulls(struct archive *, size_t); * 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 +107,17 @@ archive_write_set_format_pax_restricted(struct archive *a) * 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 +127,7 @@ archive_write_set_format_pax(struct archive *a) 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,7 +392,7 @@ archive_write_pax_header_xattrs(struct pax *pax, struct archive_entry *entry) * 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; @@ -407,7 +411,6 @@ archive_write_pax_header(struct archive *a, need_extension = 0; pax = (struct pax *)a->format_data; - pax->written = 1; st_original = archive_entry_stat(entry_original); @@ -424,11 +427,11 @@ archive_write_pax_header(struct archive *a, case S_IFIFO: break; case S_IFSOCK: - archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); return (ARCHIVE_WARN); 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_original->st_mode); return (ARCHIVE_WARN); @@ -1038,23 +1041,33 @@ build_pax_attribute_name(char *dest, const char *src) /* 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->compression_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,7 +1079,7 @@ archive_write_pax_finish_entry(struct archive *a) } static int -write_nulls(struct archive *a, size_t padding) +write_nulls(struct archive_write *a, size_t padding) { int ret, to_write; @@ -1081,13 +1094,12 @@ write_nulls(struct archive *a, size_t padding) } 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; diff --git a/lib/libarchive/archive_write_set_format_shar.c b/lib/libarchive/archive_write_set_format_shar.c index cf4c38e..5751b3e 100644 --- a/lib/libarchive/archive_write_set_format_shar.c +++ b/lib/libarchive/archive_write_set_format_shar.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_write_private.h" struct shar { int dump; @@ -60,19 +61,20 @@ struct shar { 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; @@ -91,17 +93,18 @@ shar_printf(struct archive *a, const char *fmt, ...) * 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 +113,7 @@ archive_write_set_format_shar(struct archive *a) 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 +128,12 @@ archive_write_set_format_shar(struct archive *a) * 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,7 +143,7 @@ archive_write_set_format_shar_dump(struct archive *a) } 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; @@ -186,7 +191,7 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) 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); } @@ -323,7 +328,7 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) /* 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; @@ -387,7 +392,7 @@ uuencode_group(struct shar *shar) } 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 +424,7 @@ archive_write_shar_data_uuencode(struct archive *a, const void *buff, } 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 +509,7 @@ archive_write_shar_finish_entry(struct archive *a) } static int -archive_write_shar_finish(struct archive *a) +archive_write_shar_finish(struct archive_write *a) { struct shar *shar; int ret; @@ -527,12 +532,21 @@ archive_write_shar_finish(struct archive *a) 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) diff --git a/lib/libarchive/archive_write_set_format_ustar.c b/lib/libarchive/archive_write_set_format_ustar.c index 2829ea6..87fc16a 100644 --- a/lib/libarchive/archive_write_set_format_ustar.c +++ b/lib/libarchive/archive_write_set_format_ustar.c @@ -50,11 +50,11 @@ __FBSDID("$FreeBSD$"); #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 +150,40 @@ static const char template_header[] = { 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 +193,7 @@ archive_write_set_format_ustar(struct archive *a) 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,14 +201,13 @@ archive_write_set_format_ustar(struct archive *a) } 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 || @@ -236,7 +238,7 @@ archive_write_ustar_header(struct archive *a, struct archive_entry *entry) * 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; @@ -272,11 +274,11 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], * 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 +296,7 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], 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 +308,7 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], 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 +320,7 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], 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; @@ -329,27 +331,27 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], 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"); + 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"); + 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"); + 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"); + 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, + archive_set_error(&a->archive, ERANGE, "File modification time too large"); ret = ARCHIVE_WARN; } @@ -357,14 +359,14 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { if (format_number(major(st->st_rdev), 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, 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; } @@ -383,12 +385,12 @@ __archive_write_format_header_ustar(struct archive *a, char h[512], 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, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); ret = ARCHIVE_WARN; 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); ret = ARCHIVE_WARN; @@ -486,27 +488,32 @@ format_octal(int64_t v, char *p, int s) } 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->compression_write == NULL) + return (ARCHIVE_OK); + + ustar = (struct ustar *)a->format_data; + 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,7 +526,7 @@ archive_write_ustar_finish_entry(struct archive *a) } static int -write_nulls(struct archive *a, size_t padding) +write_nulls(struct archive_write *a, size_t padding) { int ret, to_write; @@ -534,7 +541,7 @@ write_nulls(struct archive *a, size_t padding) } 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; diff --git a/lib/libarchive/test/Makefile b/lib/libarchive/test/Makefile new file mode 100644 index 0000000..4bd434e --- /dev/null +++ b/lib/libarchive/test/Makefile @@ -0,0 +1,67 @@ +# $FreeBSD$ + +TESTS= \ + test_archive_api_feature.c \ + test_bad_fd.c \ + test_read_data_large.c \ + test_read_extract.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_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_position.c \ + test_read_truncated.c \ + test_write_disk.c \ + test_write_disk_perms.c \ + test_write_disk_secure.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 + +CLEANFILES+= list.h + +MK_MAN=no +NO_MAN=yes + +PROG=libarchive_test +DPADD=${LIBARCHIVE} ${LIBBZ2} ${LIBZ} +LDADD= -larchive -lz -lbz2 +CFLAGS+= -static -g +CFLAGS+= -I${.OBJDIR} + +test: libarchive_test + ./libarchive_test + +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 \ No newline at end of file diff --git a/lib/libarchive/test/README b/lib/libarchive/test/README new file mode 100644 index 0000000..c6c242e --- /dev/null +++ b/lib/libarchive/test/README @@ -0,0 +1,46 @@ +$FreeBSD$ + +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. diff --git a/lib/libarchive/test/main.c b/lib/libarchive/test/main.c new file mode 100644 index 0000000..f9abce0 --- /dev/null +++ b/lib/libarchive/test/main.c @@ -0,0 +1,168 @@ +/* + * 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. + */ + +/* + * Various utility routines useful for test programs. + * Each test program is linked against this file. + */ +#include +#include + +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * 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]; + +void +failure(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsprintf(msg, fmt, ap); + va_end(ap); +} + +void +test_assert(const char *file, int line, int value, const char *condition, struct archive *a) +{ + if (value) { + msg[0] = '\0'; + return; + } + fprintf(stderr, "%s:%d: Assertion failed\n", file, line); + fprintf(stderr, " Condition: %s\n", condition); + 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)); + } + *(char *)(NULL) = 0; + exit(1); +} + +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; + } + fprintf(stderr, "%s:%d: Assertion failed\n", file, line); + fprintf(stderr, " Condition: %s==%s\n", e1, e2); + fprintf(stderr, " %s=%d\n", e1, v1); + fprintf(stderr, " %s=%d\n", e2, v2); + 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)); + } + *(char *)(NULL) = 0; + exit(1); +} + +/* + * "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); char *name; } tests[] = { + #include "list.h" +}; + +int main(int argc, char **argv) +{ + void (*f)(void); + int limit = sizeof(tests) / sizeof(tests[0]); + int i; + time_t now; + char tmpdir[256]; + int tmpdirHandle; + + /* + * 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); + strftime(tmpdir, sizeof(tmpdir), + "/tmp/libarchive_test.%Y-%m-%dT%H.%M.%S.XXXXXX", + localtime(&now)); + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, "ERROR: Unable to create temp directory %s\n", + tmpdir); + exit(1); + } + + printf("Running libarchive tests in: %s\n", tmpdir); + + for (i = 0; i < limit; i++) { + printf("%d: %s\n", i, tests[i].name); + 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)(); + } + printf("%d tests succeeded.\n", limit); + return (0); +} diff --git a/lib/libarchive/test/test.h b/lib/libarchive/test/test.h new file mode 100644 index 0000000..7849215 --- /dev/null +++ b/lib/libarchive/test/test.h @@ -0,0 +1,89 @@ +/* + * 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$ + */ + +/* 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 + + +/* + * "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 values 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)) +/* Asserts that two values are the same. Reports value of each one if not. */ +#define assertEqualInt(v1,v2) \ + test_assert_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL) + +/* Function declarations. These are defined in test_utility.c. */ +void failure(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 *); + diff --git a/lib/libarchive/test/test_archive_api_feature.c b/lib/libarchive/test/test_archive_api_feature.c new file mode 100644 index 0000000..876cd9e --- /dev/null +++ b/lib/libarchive/test/test_archive_api_feature.c @@ -0,0 +1,33 @@ +/*- + * 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$"); + +DEFINE_TEST(test_archive_api_feature) +{ + assert(ARCHIVE_API_FEATURE == archive_api_feature()); + assert(ARCHIVE_API_VERSION == archive_api_version()); + assert(0 == (strcmp(ARCHIVE_LIBRARY_VERSION, archive_version()))); +} diff --git a/lib/libarchive/test/test_bad_fd.c b/lib/libarchive/test/test_bad_fd.c new file mode 100644 index 0000000..dc83d18 --- /dev/null +++ b/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$"); + +/* 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 +} diff --git a/lib/libarchive/test/test_read_data_large.c b/lib/libarchive/test/test_read_data_large.c new file mode 100644 index 0000000..7a97bb9 --- /dev/null +++ b/lib/libarchive/test/test_read_data_large.c @@ -0,0 +1,116 @@ +/*- + * 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$"); + +/* + * 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 tmpfile; + 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)); + 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)); + tmpfile = open(tmpfilename, O_WRONLY | O_CREAT, 0777); + assert(tmpfile != 0); + assertEqualIntA(a, 0, archive_read_data_into_fd(a, tmpfile)); + assert(0 == archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif + close(tmpfile); + + tmpfile = open(tmpfilename, O_RDONLY); + assert(tmpfile != 0); + assertEqualIntA(NULL, sizeof(buff3), read(tmpfile, buff3, sizeof(buff3))); + close(tmpfile); + assert(0 == memcmp(buff2, buff3, sizeof(buff3))); + + unlink(tmpfilename); +} diff --git a/lib/libarchive/test/test_read_extract.c b/lib/libarchive/test/test_read_extract.c new file mode 100644 index 0000000..37cabbc --- /dev/null +++ b/lib/libarchive/test/test_read_extract.c @@ -0,0 +1,177 @@ +/*- + * 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$"); + +#define BUFF_SIZE 1000000 +#define FILE_BUFF_SIZE 100000 + +DEFINE_TEST(test_read_extract) +{ + struct archive_entry *ae; + struct archive *a; + struct stat st; + ssize_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)); + /* 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)); + /* 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)); + /* 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)); + /* 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)); + /* 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)); + /* 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)); + /* 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); +} diff --git a/lib/libarchive/test/test_read_format_cpio_bin.c b/lib/libarchive/test/test_read_format_cpio_bin.c new file mode 100644 index 0000000..dc840c4 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_bin_Z.c b/lib/libarchive/test/test_read_format_cpio_bin_Z.c new file mode 100644 index 0000000..a22c259 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_bin_bz2.c b/lib/libarchive/test/test_read_format_cpio_bin_bz2.c new file mode 100644 index 0000000..4aeb70a --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_bin_gz.c b/lib/libarchive/test/test_read_format_cpio_bin_gz.c new file mode 100644 index 0000000..cefa0b3 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_odc.c b/lib/libarchive/test/test_read_format_cpio_odc.c new file mode 100644 index 0000000..de4d8d5 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_svr4_gzip.c b/lib/libarchive/test/test_read_format_cpio_svr4_gzip.c new file mode 100644 index 0000000..e46f0f8 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_cpio_svr4c_Z.c b/lib/libarchive/test/test_read_format_cpio_svr4c_Z.c new file mode 100644 index 0000000..bc89c8f --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_empty.c b/lib/libarchive/test/test_read_format_empty.c new file mode 100644 index 0000000..761b2a9 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_gtar_gz.c b/lib/libarchive/test/test_read_format_gtar_gz.c new file mode 100644 index 0000000..5ea549a --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_iso_gz.c b/lib/libarchive/test/test_read_format_iso_gz.c new file mode 100644 index 0000000..fd8f60e --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_isorr_bz2.c b/lib/libarchive/test/test_read_format_isorr_bz2.c new file mode 100644 index 0000000..3a7c56d --- /dev/null +++ b/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$"); + +/* +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)); + assert(0 == strcmp(".", 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(0 == archive_entry_mtime_nsec(ae)); + assert(1 == archive_entry_ctime(ae)); + assert(0 == archive_entry_stat(ae)->st_nlink); + assert(0 == archive_entry_uid(ae)); + + /* A directory. */ + assert(0 == archive_read_next_header(a, &ae)); + assert(0 == strcmp("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)); + assert(0 == strcmp("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)); + assert(0 == strcmp("hardlink", archive_entry_pathname(ae))); + assert(S_ISREG(archive_entry_stat(ae)->st_mode)); + assert(0 == strcmp("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)); + assert(0 == strcmp("symlink", archive_entry_pathname(ae))); + assert(S_ISLNK(archive_entry_stat(ae)->st_mode)); + assert(0 == strcmp("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 +} + + diff --git a/lib/libarchive/test/test_read_format_pax_bz2.c b/lib/libarchive/test/test_read_format_pax_bz2.c new file mode 100644 index 0000000..039bd53 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_tar.c b/lib/libarchive/test/test_read_format_tar.c new file mode 100644 index 0000000..1515527 --- /dev/null +++ b/lib/libarchive/test/test_read_format_tar.c @@ -0,0 +1,93 @@ +/*- + * 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$"); + +static unsigned char archive[] = { +'.',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,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',' ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,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_tar) +{ + 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_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 +} + + diff --git a/lib/libarchive/test/test_read_format_tbz.c b/lib/libarchive/test/test_read_format_tbz.c new file mode 100644 index 0000000..17f24ea --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_tgz.c b/lib/libarchive/test/test_read_format_tgz.c new file mode 100644 index 0000000..4235358 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_tz.c b/lib/libarchive/test/test_read_format_tz.c new file mode 100644 index 0000000..d7a91e2 --- /dev/null +++ b/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$"); + +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 +} + + diff --git a/lib/libarchive/test/test_read_format_zip.c b/lib/libarchive/test/test_read_format_zip.c new file mode 100644 index 0000000..98b4ff1 --- /dev/null +++ b/lib/libarchive/test/test_read_format_zip.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$"); + +static unsigned char archive[] = { +'P','K',3,4,10,0,0,0,0,0,162,186,'g','3',0,0,0,0,0,0,0,0,0,0,0,0,1,0,21,0, +'a','U','T',9,0,3,224,'Q','p','C',224,'Q','p','C','U','x',4,0,232,3,232,3, +'P','K',1,2,23,3,10,0,0,0,0,0,162,186,'g','3',0,0,0,0,0,0,0,0,0,0,0,0,1,0, +13,0,0,0,0,0,0,0,0,0,164,129,0,0,0,0,'a','U','T',5,0,3,224,'Q','p','C','U', +'x',0,0,'P','K',5,6,0,0,0,0,1,0,1,0,'<',0,0,0,'4',0,0,0,0,0}; + +DEFINE_TEST(test_read_format_zip) +{ + 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_ZIP); + assert(0 == archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assert(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif +} + + diff --git a/lib/libarchive/test/test_read_large.c b/lib/libarchive/test/test_read_large.c new file mode 100644 index 0000000..8c32c3d --- /dev/null +++ b/lib/libarchive/test/test_read_large.c @@ -0,0 +1,93 @@ +/*- + * 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$"); + +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) +{ + int i; + int tmpfile; + 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)); + 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 < (tmpfile = mkstemp(tmpfilename))); + assertA(0 == archive_read_data_into_fd(a, tmpfile)); + close(tmpfile); +#if ARCHIVE_API_VERSION > 1 + assertA(0 == archive_read_finish(a)); +#else + archive_read_finish(a); +#endif + tmpfile = open(tmpfilename, O_RDONLY); + read(tmpfile, testdatacopy, sizeof(testdatacopy)); + close(tmpfile); + assert(0 == memcmp(testdata, testdatacopy, sizeof(testdata))); + + unlink(tmpfilename); +} diff --git a/lib/libarchive/test/test_read_position.c b/lib/libarchive/test/test_read_position.c new file mode 100644 index 0000000..3b1f110 --- /dev/null +++ b/lib/libarchive/test/test_read_position.c @@ -0,0 +1,74 @@ +/*- + * 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$"); + +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)); + assertA(data_size == 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); +} diff --git a/lib/libarchive/test/test_read_truncated.c b/lib/libarchive/test/test_read_truncated.c new file mode 100644 index 0000000..eba2e81 --- /dev/null +++ b/lib/libarchive/test/test_read_truncated.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$"); + +char buff[1000000]; +char buff2[100000]; + +DEFINE_TEST(test_read_truncated) +{ + struct archive_entry *ae; + struct archive *a; + 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)); + 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 + } +} diff --git a/lib/libarchive/test/test_write_disk.c b/lib/libarchive/test/test_write_disk.c new file mode 100644 index 0000000..4ff70c3 --- /dev/null +++ b/lib/libarchive/test/test_write_disk.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$"); + +#define UMASK 022 + +static void create(struct archive_entry *ae) +{ + struct archive *ad; + struct stat st; + + /* Write the entry to disk. */ + assert((ad = archive_write_disk_new()) != NULL); + assert(0 == archive_write_header(ad, ae)); + assert(0 == archive_write_finish_entry(ad)); +#if ARCHIVE_API_VERSION > 1 + assert(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)); +} + +DEFINE_TEST(test_write_disk) +{ + 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(ae); + 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); + 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); + 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); + 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); + archive_entry_free(ae); +} diff --git a/lib/libarchive/test/test_write_disk_perms.c b/lib/libarchive/test/test_write_disk_perms.c new file mode 100644 index 0000000..285b516 --- /dev/null +++ b/lib/libarchive/test/test_write_disk_perms.c @@ -0,0 +1,324 @@ +/*- + * 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$"); + +#define UMASK 022 + +static gid_t _default_gid = 0; +static gid_t _invalid_gid = 0; +static gid_t _alt_gid = 0; + +/* + * 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 int +searchgid(void) +{ + static int _searched = 0; + uid_t uid = getuid(); + gid_t gid = 0; + 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. */ + 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 = 0; + /* 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 = 0; + for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) { + /* _alt_gid must be different than _default_gid */ + if (gid == _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); +} + +/* + * 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) +{ + struct archive *a; + struct archive_entry *ae; + struct stat st; + + /* 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)); + + /* 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)); + assertA(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() == 0) { + /* + * 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 */ + /* + * 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 two reasonable behaviors: warn + * and drop the SGID bit (the current libarchive behavior) or + * try to set the group. It is completely wrong to set the + * SGID bit with the wrong group (which is, incidentally, + * exactly what gtar 1.15 does). + */ + 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 not succeed here."); + assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a)); + + /* As above, but add _EXTRACT_OWNER. */ + 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() == 0) { + /* 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."); + 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 + + /* 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_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)); + + /* 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() != 0) { + /* 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() != 0) { + /* 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)); + } + + 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()); + } + +} diff --git a/lib/libarchive/test/test_write_disk_secure.c b/lib/libarchive/test/test_write_disk_secure.c new file mode 100644 index 0000000..09560e0 --- /dev/null +++ b/lib/libarchive/test/test_write_disk_secure.c @@ -0,0 +1,140 @@ +/*- + * 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$"); + +#define UMASK 022 + +/* + * Exercise security checks that should prevent certain + * writes. + */ + +DEFINE_TEST(test_write_disk_secure) +{ + 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)); + 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)); + 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)); + 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); +} diff --git a/lib/libarchive/test/test_write_format_cpio_empty.c b/lib/libarchive/test/test_write_format_cpio_empty.c new file mode 100644 index 0000000..c2e46d1 --- /dev/null +++ b/lib/libarchive/test/test_write_format_cpio_empty.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$"); + +/* + * 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; + int i; + + /* 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); +} diff --git a/lib/libarchive/test/test_write_format_shar_empty.c b/lib/libarchive/test/test_write_format_shar_empty.c new file mode 100644 index 0000000..81a024f --- /dev/null +++ b/lib/libarchive/test/test_write_format_shar_empty.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$"); + +/* + * 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; + int i; + + /* 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); +} diff --git a/lib/libarchive/test/test_write_format_tar.c b/lib/libarchive/test/test_write_format_tar.c new file mode 100644 index 0000000..4b63bb2 --- /dev/null +++ b/lib/libarchive/test/test_write_format_tar.c @@ -0,0 +1,111 @@ +/*- + * 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$"); + +char buff[1000000]; +char buff2[64]; + +DEFINE_TEST(test_write_format_tar) +{ + struct archive_entry *ae; + struct archive *a; + char *p; + size_t used; + int 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 == 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); + 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); + assert(0 == strcmp("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)); + 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 timestamps. */ + assert(0 == archive_entry_mtime_nsec(ae)); + assert(0 == archive_entry_atime(ae)); + assert(0 == archive_entry_ctime(ae)); + assert(0 == strcmp("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 + } +} diff --git a/lib/libarchive/test/test_write_format_tar_empty.c b/lib/libarchive/test/test_write_format_tar_empty.c new file mode 100644 index 0000000..3273c0d --- /dev/null +++ b/lib/libarchive/test/test_write_format_tar_empty.c @@ -0,0 +1,84 @@ +/*- + * 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$"); + +/* + * 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; + 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 + + failure("Empty tar archive should be exactly 1024 bytes, was %d.", used); + assert(used == 1024); + 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 + + failure("Empty tar archive should be exactly 1024 bytes, was %d.", used); + assert(used == 1024); + for (i = 0; i < used; i++) { + failure("Empty tar archive should be all nulls."); + assert(buff[i] == 0); + } +} diff --git a/lib/libarchive/test/test_write_open_memory.c b/lib/libarchive/test/test_write_open_memory.c new file mode 100644 index 0000000..dd4171e --- /dev/null +++ b/lib/libarchive/test/test_write_open_memory.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$"); + +/* 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) +{ + 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); + assert(0 == strcmp(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); + } +} -- cgit v1.1