diff options
author | kientzle <kientzle@FreeBSD.org> | 2008-05-26 17:00:24 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2008-05-26 17:00:24 +0000 |
commit | a24d28f8e3d3892010c91baad77e2178c6a16ba2 (patch) | |
tree | d200bbdabe22d32b68a61172999be232d0c31d75 /lib/libarchive | |
parent | 268a4c430f25bc479b5e1e097f51394aa0f1fffb (diff) | |
download | FreeBSD-src-a24d28f8e3d3892010c91baad77e2178c6a16ba2.zip FreeBSD-src-a24d28f8e3d3892010c91baad77e2178c6a16ba2.tar.gz |
MFp4: libarchive 2.5.4b. (Still 'b' until I get a bit more
feedback, but the 2.5 branch is shaping up nicely.)
In addition to many small bug fixes and code improvements:
* Another iteration of versioning; I think I've got it right now.
* Portability: A lot of progress on Windows support (though I'm
not committing all of the Windows support files to FreeBSD CVS)
* Explicit tracking of MBS, WCS, and UTF-8 versions of strings
in archive_entry; the archive_entry routines now correctly return
NULL only when something is unset, setting NULL properly clears
string values. Most charset conversions have been pushed down to
archive_string.
* Better handling of charset conversion failure when writing or
reading UTF-8 headers in pax archives
* archive_entry_linkify() provides multiple strategies for
hardlink matching to suit different format expectations
* More accurate bzip2 format detection
* Joerg Sonnenberger's extensive improvements to mtree support
* Rough support for self-extracting ZIP archives. Not an ideal
approach, but it works for the archives I've tried.
* New "sparsify" option in archive_write_disk converts blocks of nulls
into seeks.
* Better default behavior for the test harness; it now reports
all failures by default instead of coredumping at the first one.
Diffstat (limited to 'lib/libarchive')
49 files changed, 3609 insertions, 938 deletions
diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 70f818c..db591df 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -4,19 +4,8 @@ LIB= archive DPADD= ${LIBBZ2} ${LIBZ} LDADD= -lbz2 -lz -# The libarchive version stamp. -# Version is three numbers: -# Major: Bumped ONLY when API/ABI breakage happens (see SHLIB_MAJOR) -# Minor: Bumped when significant new features are added -# Revision: Bumped on any notable change - -# The useful version number (one integer, easy to compare) -LIBARCHIVE_VERSION= 2004012 -# The pretty version string -LIBARCHIVE_VERSION_STRING!= echo $$((${LIBARCHIVE_VERSION} / 1000000)).$$((${LIBARCHIVE_VERSION} / 1000 % 1000)).$$((${LIBARCHIVE_VERSION} % 1000)) - # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system. -# It has no real relation to the version number above. +# It has no real relation to the libarchive version number. SHLIB_MAJOR= 4 CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" @@ -27,15 +16,9 @@ WARNS?= 6 # Headers to be installed in /usr/include INCS= archive.h archive_entry.h -# Build archive.h from archive.h.in by substituting version information. -# Note: FreeBSD has inttypes.h, so enable that include in archive.h.in -archive.h: archive.h.in Makefile - cat ${.CURDIR}/archive.h.in | sed \ - -e 's/@LIBARCHIVE_VERSION@/${LIBARCHIVE_VERSION}/g' \ - -e 's/@LIBARCHIVE_VERSION_STRING@/${LIBARCHIVE_VERSION_STRING}/g' \ - -e 's/@SHLIB_MAJOR@/${SHLIB_MAJOR}/g' \ - -e 's|@ARCHIVE_H_INCLUDE_INTTYPES_H@|#include <inttypes.h> /* For int64_t */|g' \ - > archive.h +# For now, archive.h is the same as archive.h.in. +archive.h: archive.h.in + cat ${.CURDIR}/archive.h.in > archive.h # archive.h needs to be cleaned CLEANFILES+= archive.h @@ -117,6 +100,7 @@ MLINKS+= archive_entry.3 archive_entry_acl_reset.3 MLINKS+= archive_entry.3 archive_entry_acl_text_w.3 MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 +MLINKS+= archive_entry.3 archive_entry_copy_fflags_text.3 MLINKS+= archive_entry.3 archive_entry_copy_fflags_text_w.3 MLINKS+= archive_entry.3 archive_entry_copy_gname.3 MLINKS+= archive_entry.3 archive_entry_copy_gname_w.3 @@ -232,6 +216,7 @@ MLINKS+= archive_write.3 archive_write_set_bytes_in_last_block.3 MLINKS+= archive_write.3 archive_write_set_bytes_per_block.3 MLINKS+= archive_write.3 archive_write_set_callbacks.3 MLINKS+= archive_write.3 archive_write_set_compression_bzip2.3 +MLINKS+= archive_write.3 archive_write_set_compression_compress.3 MLINKS+= archive_write.3 archive_write_set_compression_gzip.3 MLINKS+= archive_write.3 archive_write_set_compression_none.3 MLINKS+= archive_write.3 archive_write_set_compression_program.3 @@ -246,7 +231,8 @@ 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 -check: +.PHONY: check test +check test: cd ${.CURDIR}/test && make test .include <bsd.lib.mk> diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in index 842b432..edc352c 100644 --- a/lib/libarchive/archive.h.in +++ b/lib/libarchive/archive.h.in @@ -28,18 +28,59 @@ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED +/* + * Note: archive.h is for use outside of libarchive; the configuration + * headers (config.h, archive_platform.h, etc.) are purely internal. + * Do NOT use HAVE_XXX configuration macros to control the behavior of + * this header! If you must conditionalize, use predefined compiler and/or + * platform macros. + */ + #include <sys/types.h> /* Linux requires this for off_t */ -@ARCHIVE_H_INCLUDE_INTTYPES_H@ +#if !defined(__WATCOMC__) && !defined(_MSC_VER) +/* Header unavailable on Watcom C or MS Visual C++. */ +#include <inttypes.h> /* int64_t, etc. */ +#endif #include <stdio.h> /* For FILE * */ -#ifndef _WIN32 -#include <unistd.h> /* For ssize_t and size_t */ + +/* Get appropriate definitions of standard POSIX-style types. */ +/* These should match the types used in 'struct stat' */ +#ifdef _WIN32 +#define __LA_SSIZE_T long +#define __LA_UID_T unsigned int +#define __LA_GID_T unsigned int +#else +#include <unistd.h> /* ssize_t, uid_t, and gid_t */ +#define __LA_SSIZE_T ssize_t +#define __LA_UID_T uid_t +#define __LA_GID_T gid_t +#endif + +/* + * On Windows, define LIBARCHIVE_STATIC if you're building or using a + * .lib. The default here assumes you're building a DLL. Only + * libarchive source should ever define __LIBARCHIVE_BUILD. + */ +#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +# ifdef __LIBARCHIVE_BUILD +# ifdef __GNUC__ +# define __LA_DECL __attribute__((dllexport)) extern +# else +# define __LA_DECL __declspec(dllexport) +# endif +# else +# ifdef __GNUC__ +# define __LA_DECL __attribute__((dllimport)) extern +# else +# define __LA_DECL __declspec(dllimport) +# endif +# endif #else -typedef long ssize_t; -typedef unsigned int uid_t; -typedef unsigned int gid_t; -typedef unsigned short mode_t; +/* Static libraries or non-Windows needs no special declaration. */ +# define __LA_DECL #endif + #ifdef __cplusplus extern "C" { #endif @@ -48,7 +89,9 @@ extern "C" { * The version number is provided as both a macro and a function. * The macro identifies the installed header; the function identifies * the library version (which may not be the same if you're using a - * dynamically-linked version of the library). + * dynamically-linked version of the library). Of course, if the + * header and library are very different, you should expect some + * strangeness. Don't do that. */ /* @@ -69,13 +112,14 @@ extern "C" { * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * #endif */ -#define ARCHIVE_VERSION_NUMBER @LIBARCHIVE_VERSION@ -int archive_version_number(void); +#define ARCHIVE_VERSION_NUMBER 2005004 +__LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -const char * archive_version_string(void); +#define ARCHIVE_VERSION_STRING "libarchive 2.5.4b" +__LA_DECL const char * archive_version_string(void); #if ARCHIVE_VERSION_NUMBER < 3000000 /* @@ -83,13 +127,13 @@ const char * archive_version_string(void); * the simpler definitions above. */ #define ARCHIVE_VERSION_STAMP ARCHIVE_VERSION_NUMBER -int archive_version_stamp(void); -#define ARCHIVE_LIBRARY_VERSION "libarchive @LIBARCHIVE_VERSION_STRING@" -const char * archive_version(void); +__LA_DECL int archive_version_stamp(void); +#define ARCHIVE_LIBRARY_VERSION ARCHIVE_VERSION_STRING +__LA_DECL const char * archive_version(void); #define ARCHIVE_API_VERSION (ARCHIVE_VERSION_NUMBER / 1000000) -int archive_api_version(void); +__LA_DECL int archive_api_version(void); #define ARCHIVE_API_FEATURE ((ARCHIVE_VERSION_NUMBER / 1000) % 1000) -int archive_api_feature(void); +__LA_DECL int archive_api_feature(void); #endif #if ARCHIVE_VERSION_NUMBER < 3000000 @@ -139,18 +183,18 @@ struct archive_entry; */ /* Returns pointer and size of next block of data from archive. */ -typedef ssize_t archive_read_callback(struct archive *, void *_client_data, +typedef __LA_SSIZE_T archive_read_callback(struct archive *, void *_client_data, const void **_buffer); /* Skips at most request bytes from archive and returns the skipped amount */ #if ARCHIVE_VERSION_NUMBER < 2000000 -typedef ssize_t archive_skip_callback(struct archive *, void *_client_data, +typedef __LA_SSIZE_T archive_skip_callback(struct archive *, void *_client_data, size_t request); #else typedef off_t archive_skip_callback(struct archive *, void *_client_data, off_t request); #endif /* Returns size actually written, zero on EOF, -1 on error. */ -typedef ssize_t archive_write_callback(struct archive *, void *_client_data, +typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); typedef int archive_open_callback(struct archive *, void *_client_data); typedef int archive_close_callback(struct archive *, void *_client_data); @@ -218,7 +262,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data); * data for entries of interest. * 5) Call archive_read_finish to end processing. */ -struct archive *archive_read_new(void); +__LA_DECL struct archive *archive_read_new(void); /* * The archive_read_support_XXX calls enable auto-detect for this @@ -227,30 +271,30 @@ struct archive *archive_read_new(void); * support_compression_bzip2(). The "all" functions provide the * obvious shorthand. */ -int archive_read_support_compression_all(struct archive *); -int archive_read_support_compression_bzip2(struct archive *); -int archive_read_support_compression_compress(struct archive *); -int archive_read_support_compression_gzip(struct archive *); -int archive_read_support_compression_none(struct archive *); -int archive_read_support_compression_program(struct archive *, +__LA_DECL int archive_read_support_compression_all(struct archive *); +__LA_DECL int archive_read_support_compression_bzip2(struct archive *); +__LA_DECL int archive_read_support_compression_compress(struct archive *); +__LA_DECL int archive_read_support_compression_gzip(struct archive *); +__LA_DECL int archive_read_support_compression_none(struct archive *); +__LA_DECL int archive_read_support_compression_program(struct archive *, const char *command); -int archive_read_support_format_all(struct archive *); -int archive_read_support_format_ar(struct archive *); -int archive_read_support_format_cpio(struct archive *); -int archive_read_support_format_empty(struct archive *); -int archive_read_support_format_gnutar(struct archive *); -int archive_read_support_format_iso9660(struct archive *); -int archive_read_support_format_mtree(struct archive *); -int archive_read_support_format_tar(struct archive *); -int archive_read_support_format_zip(struct archive *); +__LA_DECL int archive_read_support_format_all(struct archive *); +__LA_DECL int archive_read_support_format_ar(struct archive *); +__LA_DECL int archive_read_support_format_cpio(struct archive *); +__LA_DECL int archive_read_support_format_empty(struct archive *); +__LA_DECL int archive_read_support_format_gnutar(struct archive *); +__LA_DECL int archive_read_support_format_iso9660(struct archive *); +__LA_DECL int archive_read_support_format_mtree(struct archive *); +__LA_DECL int archive_read_support_format_tar(struct archive *); +__LA_DECL int archive_read_support_format_zip(struct archive *); /* Open the archive using callbacks for archive I/O. */ -int archive_read_open(struct archive *, void *_client_data, +__LA_DECL int archive_read_open(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_close_callback *); -int archive_read_open2(struct archive *, void *_client_data, +__LA_DECL int archive_read_open2(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_skip_callback *, archive_close_callback *); @@ -260,43 +304,43 @@ int archive_read_open2(struct archive *, void *_client_data, * accept a block size handle tape blocking correctly. */ /* Use this if you know the filename. Note: NULL indicates stdin. */ -int archive_read_open_filename(struct archive *, +__LA_DECL int archive_read_open_filename(struct archive *, const char *_filename, size_t _block_size); /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ -int archive_read_open_file(struct archive *, +__LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size); /* Read an archive that's stored in memory. */ -int archive_read_open_memory(struct archive *, +__LA_DECL int archive_read_open_memory(struct archive *, void * buff, size_t size); /* A more involved version that is only used for internal testing. */ -int archive_read_open_memory2(struct archive *a, void *buff, +__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ -int archive_read_open_fd(struct archive *, int _fd, +__LA_DECL int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); /* Read an archive that's already open, using a FILE *. */ /* Note: DO NOT use this with tape drives. */ -int archive_read_open_FILE(struct archive *, FILE *_file); +__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ -int archive_read_next_header(struct archive *, +__LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); /* * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ -int64_t archive_read_header_position(struct archive *); +__LA_DECL int64_t archive_read_header_position(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ -ssize_t archive_read_data(struct archive *, void *, size_t); +__LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t); /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will * be strictly increasing and that returned blocks will not overlap. */ -int archive_read_data_block(struct archive *a, +__LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, off_t *offset); /*- @@ -305,10 +349,10 @@ int archive_read_data_block(struct archive *a, * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ -int archive_read_data_skip(struct archive *); -int archive_read_data_into_buffer(struct archive *, void *buffer, - ssize_t len); -int archive_read_data_into_fd(struct archive *, int fd); +__LA_DECL int archive_read_data_skip(struct archive *); +__LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer, + __LA_SSIZE_T len); +__LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /*- * Convenience function to recreate the current entry (whose header @@ -327,51 +371,55 @@ int archive_read_data_into_fd(struct archive *, int fd); /* The "flags" argument selects optional behavior, 'OR' the flags you want. */ /* Default: Do not try to set owner/group. */ -#define ARCHIVE_EXTRACT_OWNER (1) +#define ARCHIVE_EXTRACT_OWNER (0x0001) /* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */ -#define ARCHIVE_EXTRACT_PERM (2) +#define ARCHIVE_EXTRACT_PERM (0x0002) /* Default: Do not restore mtime/atime. */ -#define ARCHIVE_EXTRACT_TIME (4) +#define ARCHIVE_EXTRACT_TIME (0x0004) /* Default: Replace existing files. */ -#define ARCHIVE_EXTRACT_NO_OVERWRITE (8) +#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008) /* Default: Try create first, unlink only if create fails with EEXIST. */ -#define ARCHIVE_EXTRACT_UNLINK (16) +#define ARCHIVE_EXTRACT_UNLINK (0x0010) /* Default: Do not restore ACLs. */ -#define ARCHIVE_EXTRACT_ACL (32) +#define ARCHIVE_EXTRACT_ACL (0x0020) /* Default: Do not restore fflags. */ -#define ARCHIVE_EXTRACT_FFLAGS (64) +#define ARCHIVE_EXTRACT_FFLAGS (0x0040) /* Default: Do not restore xattrs. */ -#define ARCHIVE_EXTRACT_XATTR (128) +#define ARCHIVE_EXTRACT_XATTR (0x0080) /* 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) +#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100) /* Default: Do not reject entries with '..' as path elements. */ -#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (512) +#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200) /* Default: Create parent directories as needed. */ -#define ARCHIVE_EXTRACT_NO_AUTODIR (1024) +#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400) /* Default: Overwrite files, even if one on disk is newer. */ -#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (2048) +#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800) +/* Detect blocks of 0 and write holes instead. */ +#define ARCHIVE_EXTRACT_SPARSE (0x1000) -int archive_read_extract(struct archive *, struct archive_entry *, +__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); -void archive_read_extract_set_progress_callback(struct archive *, +__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, + struct archive * /* dest */); +__LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); /* Record the dev/ino of a file that will not be written. This is * generally set to the dev/ino of the archive being read. */ -void archive_read_extract_set_skip_file(struct archive *, +__LA_DECL void archive_read_extract_set_skip_file(struct archive *, dev_t, ino_t); /* Close the file and release most resources. */ -int archive_read_close(struct archive *); +__LA_DECL 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_VERSION_NUMBER >= 2000000 -int archive_read_finish(struct archive *); +__LA_DECL 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 *); +__LA_DECL void archive_read_finish(struct archive *); #endif /*- @@ -389,75 +437,75 @@ void archive_read_finish(struct archive *); * 5) archive_write_close to close the output * 6) archive_write_finish to cleanup the writer and release resources */ -struct archive *archive_write_new(void); -int archive_write_set_bytes_per_block(struct archive *, +__LA_DECL struct archive *archive_write_new(void); +__LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); -int archive_write_get_bytes_per_block(struct archive *); +__LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ -int archive_write_set_bytes_in_last_block(struct archive *, +__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); -int archive_write_get_bytes_in_last_block(struct archive *); +__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ -int archive_write_set_skip_file(struct archive *, dev_t, ino_t); +__LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t); -int archive_write_set_compression_bzip2(struct archive *); -int archive_write_set_compression_compress(struct archive *); -int archive_write_set_compression_gzip(struct archive *); -int archive_write_set_compression_none(struct archive *); -int archive_write_set_compression_program(struct archive *, +__LA_DECL int archive_write_set_compression_bzip2(struct archive *); +__LA_DECL int archive_write_set_compression_compress(struct archive *); +__LA_DECL int archive_write_set_compression_gzip(struct archive *); +__LA_DECL int archive_write_set_compression_none(struct archive *); +__LA_DECL int archive_write_set_compression_program(struct archive *, const char *cmd); /* A convenience function to set the format based on the code or name. */ -int archive_write_set_format(struct archive *, int format_code); -int archive_write_set_format_by_name(struct archive *, +__LA_DECL int archive_write_set_format(struct archive *, int format_code); +__LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ -int archive_write_set_format_ar_bsd(struct archive *); -int archive_write_set_format_ar_svr4(struct archive *); -int archive_write_set_format_cpio(struct archive *); -int archive_write_set_format_cpio_newc(struct archive *); +__LA_DECL int archive_write_set_format_ar_bsd(struct archive *); +__LA_DECL int archive_write_set_format_ar_svr4(struct archive *); +__LA_DECL int archive_write_set_format_cpio(struct archive *); +__LA_DECL int archive_write_set_format_cpio_newc(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ -int archive_write_set_format_pax(struct archive *); -int archive_write_set_format_pax_restricted(struct archive *); -int archive_write_set_format_shar(struct archive *); -int archive_write_set_format_shar_dump(struct archive *); -int archive_write_set_format_ustar(struct archive *); -int archive_write_open(struct archive *, void *, +__LA_DECL int archive_write_set_format_pax(struct archive *); +__LA_DECL int archive_write_set_format_pax_restricted(struct archive *); +__LA_DECL int archive_write_set_format_shar(struct archive *); +__LA_DECL int archive_write_set_format_shar_dump(struct archive *); +__LA_DECL int archive_write_set_format_ustar(struct archive *); +__LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); -int archive_write_open_fd(struct archive *, int _fd); -int archive_write_open_filename(struct archive *, const char *_file); +__LA_DECL int archive_write_open_fd(struct archive *, int _fd); +__LA_DECL int archive_write_open_filename(struct archive *, const char *_file); /* A deprecated synonym for archive_write_open_filename() */ -int archive_write_open_file(struct archive *, const char *_file); -int archive_write_open_FILE(struct archive *, FILE *); +__LA_DECL int archive_write_open_file(struct archive *, const char *_file); +__LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ -int archive_write_open_memory(struct archive *, +__LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ -int archive_write_header(struct archive *, +__LA_DECL int archive_write_header(struct archive *, struct archive_entry *); #if ARCHIVE_VERSION_NUMBER >= 2000000 -ssize_t archive_write_data(struct archive *, const void *, size_t); +__LA_DECL __LA_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); +__LA_DECL 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 *); +__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, const void *, size_t, off_t); +__LA_DECL int archive_write_finish_entry(struct archive *); +__LA_DECL int archive_write_close(struct archive *); #if ARCHIVE_VERSION_NUMBER >= 2000000 -int archive_write_finish(struct archive *); +__LA_DECL 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 *); +__LA_DECL void archive_write_finish(struct archive *); #endif /*- @@ -474,12 +522,12 @@ void archive_write_finish(struct archive *); * 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); +__LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ -int archive_write_disk_set_skip_file(struct archive *, +__LA_DECL 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 *, +__LA_DECL int archive_write_disk_set_options(struct archive *, int flags); /* * The lookup functions are given uname/uid (or gname/gid) pairs and @@ -498,42 +546,47 @@ int archive_write_disk_set_options(struct archive *, * particular, these match the specifications of POSIX "pax" and old * POSIX "tar". */ -int archive_write_disk_set_standard_lookup(struct archive *); +__LA_DECL 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 *)); +__LA_DECL int archive_write_disk_set_group_lookup(struct archive *, + void * /* private_data */, + __LA_GID_T (*)(void *, const char *, __LA_GID_T), + void (* /* cleanup */)(void *)); +__LA_DECL int archive_write_disk_set_user_lookup(struct archive *, + void * /* private_data */, + __LA_UID_T (*)(void *, const char *, __LA_UID_T), + void (* /* cleanup */)(void *)); /* * Accessor functions to read/set various information in * the struct archive object: */ /* Bytes written after compression or read before decompression. */ -int64_t archive_position_compressed(struct archive *); +__LA_DECL int64_t archive_position_compressed(struct archive *); /* Bytes written to compressor or read from decompressor. */ -int64_t archive_position_uncompressed(struct archive *); - -const char *archive_compression_name(struct archive *); -int archive_compression(struct archive *); -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, ...); -void archive_copy_error(struct archive *dest, struct archive *src); +__LA_DECL int64_t archive_position_uncompressed(struct archive *); + +__LA_DECL const char *archive_compression_name(struct archive *); +__LA_DECL int archive_compression(struct archive *); +__LA_DECL int archive_errno(struct archive *); +__LA_DECL const char *archive_error_string(struct archive *); +__LA_DECL const char *archive_format_name(struct archive *); +__LA_DECL int archive_format(struct archive *); +__LA_DECL void archive_clear_error(struct archive *); +__LA_DECL void archive_set_error(struct archive *, int _err, + const char *fmt, ...); +__LA_DECL void archive_copy_error(struct archive *dest, + struct archive *src); #ifdef __cplusplus } #endif +/* This is meaningless outside of this header. */ +#undef __LA_DECL + #endif /* !ARCHIVE_H_INCLUDED */ diff --git a/lib/libarchive/archive_endian.h b/lib/libarchive/archive_endian.h index 7c7fced..7613178 100644 --- a/lib/libarchive/archive_endian.h +++ b/lib/libarchive/archive_endian.h @@ -28,9 +28,23 @@ * Borrowed from FreeBSD's <sys/endian.h> */ +/* Note: This is a purely internal header! */ +/* Do not use this outside of libarchive internal code! */ + #ifndef ARCHIVE_ENDIAN_H_INCLUDED #define ARCHIVE_ENDIAN_H_INCLUDED + +/* Watcom C++ doesn't support 'inline' in C code. (For any version?) */ +#if defined( __WATCOMC__ ) + #define inline +#endif + +/* Visual C++ 6.0 doesn't support 'inline' in C code. (Does VC7? VC8?) */ +#if defined(_MSC_VER) + #define inline +#endif + /* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ static inline uint16_t diff --git a/lib/libarchive/archive_entry.3 b/lib/libarchive/archive_entry.3 index ba9fd6c..21a6d2a 100644 --- a/lib/libarchive/archive_entry.3 +++ b/lib/libarchive/archive_entry.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd December 15, 2003 +.Dd May 12, 2008 .Dt archive_entry 3 .Os .Sh NAME @@ -40,6 +40,7 @@ .Nm archive_entry_atime_nsec , .Nm archive_entry_clear , .Nm archive_entry_clone , +.Nm archive_entry_copy_fflags_text , .Nm archive_entry_copy_fflags_text_w , .Nm archive_entry_copy_gname , .Nm archive_entry_copy_gname_w , @@ -48,6 +49,7 @@ .Nm archive_entry_copy_link , .Nm archive_entry_copy_link_w , .Nm archive_entry_copy_pathname_w , +.Nm archive_entry_copy_sourcepath , .Nm archive_entry_copy_stat , .Nm archive_entry_copy_symlink , .Nm archive_entry_copy_symlink_w , @@ -95,6 +97,7 @@ .Nm archive_entry_set_uid , .Nm archive_entry_set_uname , .Nm archive_entry_size , +.Nm archive_entry_sourcepath , .Nm archive_entry_stat , .Nm archive_entry_symlink , .Nm archive_entry_uid , @@ -156,6 +159,8 @@ .Fn archive_entry_clear "struct archive_entry *" .Ft struct archive_entry * .Fn archive_entry_clone "struct archive_entry *" +.Ft const char * * +.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *" .Ft const wchar_t * .Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *" .Ft void @@ -167,6 +172,8 @@ .Ft void .Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *" .Ft void +.Fn archive_entry_copy_sourcepath "struct archive_entry *" "const char *" +.Ft void .Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *" .Ft void .Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *" @@ -270,6 +277,8 @@ .Fn archive_entry_set_uname "struct archive_entry *" "const char *" .Ft int64_t .Fn archive_entry_size "struct archive_entry *" +.Ft const char * +.Fn archive_entry_sourcepath "struct archive_entry *" .Ft const struct stat * .Fn archive_entry_stat "struct archive_entry *" .Ft const char * @@ -376,8 +385,10 @@ will be ignored. .Pp The canonical text format is a comma-separated list of flag names. The +.Fn archive_entry_copy_fflags_text +and .Fn archive_entry_copy_fflags_text_w -function parses the provided text and sets the internal bitmap values. +functions parse the provided text and sets the internal bitmap values. This is a platform-specific operation; names that are not meaningful on the current platform will be ignored. The function returns a pointer to the start of the first name that was not diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index 8da0215..fef2293 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -91,15 +91,17 @@ static void aes_clean(struct aes *); static void aes_copy(struct aes *dest, struct aes *src); static const char * aes_get_mbs(struct aes *); static const wchar_t * aes_get_wcs(struct aes *); -static void aes_set_mbs(struct aes *, const char *mbs); -static void aes_copy_mbs(struct aes *, const char *mbs); +static int aes_set_mbs(struct aes *, const char *mbs); +static int aes_copy_mbs(struct aes *, const char *mbs); /* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ -static void aes_copy_wcs(struct aes *, const wchar_t *wcs); -static void aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); +static int aes_copy_wcs(struct aes *, const wchar_t *wcs); +static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); +static const char *ae_strtofflags(const char *stringp, + unsigned long *setp, unsigned long *clrp); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); @@ -144,173 +146,216 @@ static size_t wcslen(const wchar_t *s) #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif - static void aes_clean(struct aes *aes) { - if (aes->aes_mbs_alloc) { - free(aes->aes_mbs_alloc); - aes->aes_mbs_alloc = NULL; - } - if (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; + if (aes->aes_wcs) { + free((wchar_t *)(uintptr_t)aes->aes_wcs); + aes->aes_wcs = NULL; } - memset(aes, 0, sizeof(*aes)); + archive_string_free(&(aes->aes_mbs)); + archive_string_free(&(aes->aes_utf8)); + aes->aes_set = 0; } static void aes_copy(struct aes *dest, struct aes *src) { - *dest = *src; - if (src->aes_mbs != NULL) { - dest->aes_mbs_alloc = strdup(src->aes_mbs); - dest->aes_mbs = dest->aes_mbs_alloc; - if (dest->aes_mbs == NULL) - __archive_errx(1, "No memory for aes_copy()"); - } + wchar_t *wp; + + dest->aes_set = src->aes_set; + archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); + archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); if (src->aes_wcs != NULL) { - dest->aes_wcs_alloc = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) + wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) * sizeof(wchar_t)); - dest->aes_wcs = dest->aes_wcs_alloc; - if (dest->aes_wcs == NULL) + if (wp == NULL) __archive_errx(1, "No memory for aes_copy()"); - wcscpy(dest->aes_wcs_alloc, src->aes_wcs); + wcscpy(wp, src->aes_wcs); + dest->aes_wcs = wp; } } static const char * +aes_get_utf8(struct aes *aes) +{ + if (aes->aes_set & AES_SET_UTF8) + return (aes->aes_utf8.s); + if ((aes->aes_set & AES_SET_WCS) + && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) { + aes->aes_set |= AES_SET_UTF8; + return (aes->aes_utf8.s); + } + return (NULL); +} + +static const char * aes_get_mbs(struct aes *aes) { - if (aes->aes_mbs == NULL && aes->aes_wcs == NULL) - return NULL; - if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) { - /* - * XXX Need to estimate the number of byte in the - * multi-byte form. Assume that, on average, wcs - * chars encode to no more than 3 bytes. There must - * be a better way... XXX - */ - size_t mbs_length = wcslen(aes->aes_wcs) * 3 + 64; - - aes->aes_mbs_alloc = (char *)malloc(mbs_length); - aes->aes_mbs = aes->aes_mbs_alloc; - if (aes->aes_mbs == NULL) - __archive_errx(1, "No memory for aes_get_mbs()"); - wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1); - aes->aes_mbs_alloc[mbs_length - 1] = 0; + /* If we already have an MBS form, return that immediately. */ + if (aes->aes_set & AES_SET_MBS) + return (aes->aes_mbs.s); + /* If there's a WCS form, try converting with the native locale. */ + if ((aes->aes_set & AES_SET_WCS) + && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) { + aes->aes_set |= AES_SET_MBS; + return (aes->aes_mbs.s); } - return (aes->aes_mbs); + /* We'll use UTF-8 for MBS if all else fails. */ + return (aes_get_utf8(aes)); } static const wchar_t * aes_get_wcs(struct aes *aes) { + wchar_t *w; int r; - if (aes->aes_wcs == NULL && aes->aes_mbs == NULL) - return NULL; - if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) { + /* Return WCS form if we already have it. */ + if (aes->aes_set & AES_SET_WCS) + return (aes->aes_wcs); + + if (aes->aes_set & AES_SET_MBS) { + /* Try converting MBS to WCS using native locale. */ /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ - size_t wcs_length = strlen(aes->aes_mbs); + size_t wcs_length = aes->aes_mbs.length; - aes->aes_wcs_alloc - = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); - aes->aes_wcs = aes->aes_wcs_alloc; - if (aes->aes_wcs == NULL) + w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); + if (w == NULL) __archive_errx(1, "No memory for aes_get_wcs()"); - r = mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length); - aes->aes_wcs_alloc[wcs_length] = 0; - if (r == -1) { - /* Conversion failed, don't lie to our clients. */ - free(aes->aes_wcs_alloc); - aes->aes_wcs = aes->aes_wcs_alloc = NULL; + r = mbstowcs(w, aes->aes_mbs.s, wcs_length); + w[wcs_length] = 0; + if (r > 0) { + aes->aes_set |= AES_SET_WCS; + return (aes->aes_wcs = w); } + free(w); + } + + if (aes->aes_set & AES_SET_UTF8) { + /* Try converting UTF8 to WCS. */ + aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); + aes->aes_set |= AES_SET_WCS; + return (aes->aes_wcs); } - return (aes->aes_wcs); + return (NULL); } -static void +static int aes_set_mbs(struct aes *aes, const char *mbs) { - if (aes->aes_mbs_alloc) { - free(aes->aes_mbs_alloc); - aes->aes_mbs_alloc = NULL; - } - if (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; - } - aes->aes_mbs = mbs; - aes->aes_wcs = NULL; + return (aes_copy_mbs(aes, mbs)); } -static void +static int aes_copy_mbs(struct aes *aes, const char *mbs) { - if (aes->aes_mbs_alloc) { - free(aes->aes_mbs_alloc); - aes->aes_mbs_alloc = NULL; + if (mbs == NULL) { + aes->aes_set = 0; + return (0); } - if (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; + aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ + archive_strcpy(&(aes->aes_mbs), mbs); + archive_string_empty(&(aes->aes_utf8)); + if (aes->aes_wcs) { + free((wchar_t *)(uintptr_t)aes->aes_wcs); + aes->aes_wcs = NULL; } - aes->aes_mbs_alloc = (char *)malloc((strlen(mbs) + 1) * sizeof(char)); - if (aes->aes_mbs_alloc == NULL) - __archive_errx(1, "No memory for aes_copy_mbs()"); - strcpy(aes->aes_mbs_alloc, mbs); - aes->aes_mbs = aes->aes_mbs_alloc; - aes->aes_wcs = NULL; + return (0); } -#if 0 -static void -aes_set_wcs(struct aes *aes, const wchar_t *wcs) +/* + * The 'update' form tries to proactively update all forms of + * this string (WCS and MBS) and returns an error if any of + * them fail. This is used by the 'pax' handler, for instance, + * to detect and report character-conversion failures early while + * still allowing clients to get potentially useful values from + * the more tolerant lazy conversions. (get_mbs and get_wcs will + * strive to give the user something useful, so you can get hopefully + * usable values even if some of the character conversions are failing.) + */ +static int +aes_update_utf8(struct aes *aes, const char *utf8) { - if (aes->aes_mbs_alloc) { - free(aes->aes_mbs_alloc); - aes->aes_mbs_alloc = NULL; + if (utf8 == NULL) { + aes->aes_set = 0; + return (1); /* Succeeded in clearing everything. */ } - if (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; + + /* Save the UTF8 string. */ + archive_strcpy(&(aes->aes_utf8), utf8); + + /* Empty the mbs and wcs strings. */ + archive_string_empty(&(aes->aes_mbs)); + if (aes->aes_wcs) { + free((wchar_t *)(uintptr_t)aes->aes_wcs); + aes->aes_wcs = NULL; } - aes->aes_mbs = NULL; - aes->aes_wcs = wcs; + + aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ + + /* TODO: We should just do a direct UTF-8 to MBS conversion + * here. That would be faster, use less space, and give the + * same information. (If a UTF-8 to MBS conversion succeeds, + * then UTF-8->WCS and Unicode->MBS conversions will both + * succeed.) */ + + /* Try converting UTF8 to WCS, return false on failure. */ + aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); + if (aes->aes_wcs == NULL) + return (0); + aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ + + /* Try converting WCS to MBS, return false on failure. */ + if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL) + return (0); + aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; + + /* All conversions succeeded. */ + return (1); } -#endif -static void +static int aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { - aes_copy_wcs_len(aes, wcs, wcslen(wcs)); + return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } -static void +static int aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) { - if (aes->aes_mbs_alloc) { - free(aes->aes_mbs_alloc); - aes->aes_mbs_alloc = NULL; + wchar_t *w; + + if (wcs == NULL) { + aes->aes_set = 0; + return (0); } - if (aes->aes_wcs_alloc) { - free(aes->aes_wcs_alloc); - aes->aes_wcs_alloc = NULL; + aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ + archive_string_empty(&(aes->aes_mbs)); + archive_string_empty(&(aes->aes_utf8)); + if (aes->aes_wcs) { + free((wchar_t *)(uintptr_t)aes->aes_wcs); + aes->aes_wcs = NULL; } - aes->aes_mbs = NULL; - aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); - if (aes->aes_wcs_alloc == NULL) + w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); + if (w == NULL) __archive_errx(1, "No memory for aes_copy_wcs()"); - wmemcpy(aes->aes_wcs_alloc, wcs, len); - aes->aes_wcs_alloc[len] = L'\0'; - aes->aes_wcs = aes->aes_wcs_alloc; + wmemcpy(w, wcs, len); + w[len] = L'\0'; + aes->aes_wcs = w; + return (0); } +/**************************************************************************** + * + * Public Interface + * + ****************************************************************************/ + struct archive_entry * archive_entry_clear(struct archive_entry *entry) { @@ -350,6 +395,8 @@ archive_entry_clone(struct archive_entry *entry) aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); aes_copy(&entry2->ae_pathname, &entry->ae_pathname); aes_copy(&entry2->ae_symlink, &entry->ae_symlink); + entry2->ae_hardlinkset = entry->ae_hardlinkset; + entry2->ae_symlinkset = entry->ae_symlinkset; aes_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ @@ -515,12 +562,16 @@ archive_entry_gname_w(struct archive_entry *entry) const char * archive_entry_hardlink(struct archive_entry *entry) { + if (!entry->ae_hardlinkset) + return (NULL); return (aes_get_mbs(&entry->ae_hardlink)); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { + if (!entry->ae_hardlinkset) + return (NULL); return (aes_get_wcs(&entry->ae_hardlink)); } @@ -601,14 +652,24 @@ archive_entry_size(struct archive_entry *entry) } const char * +archive_entry_sourcepath(struct archive_entry *entry) +{ + return (aes_get_mbs(&entry->ae_sourcepath)); +} + +const char * archive_entry_symlink(struct archive_entry *entry) { + if (!entry->ae_symlinkset) + return (NULL); return (aes_get_mbs(&entry->ae_symlink)); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { + if (!entry->ae_symlinkset) + return (NULL); return (aes_get_wcs(&entry->ae_symlink)); } @@ -651,6 +712,15 @@ archive_entry_set_fflags(struct archive_entry *entry, entry->ae_fflags_clear = clear; } +const char * +archive_entry_copy_fflags_text(struct archive_entry *entry, + const char *flags) +{ + aes_copy_mbs(&entry->ae_fflags_text, flags); + return (ae_strtofflags(flags, + &entry->ae_fflags_set, &entry->ae_fflags_clear)); +} + const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) @@ -685,6 +755,12 @@ archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) aes_copy_wcs(&entry->ae_gname, name); } +int +archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) +{ + return (aes_update_utf8(&entry->ae_gname, name)); +} + void archive_entry_set_ino(struct archive_entry *entry, unsigned long ino) { @@ -696,18 +772,24 @@ void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { aes_set_mbs(&entry->ae_hardlink, target); + if (target != NULL) + entry->ae_hardlinkset = 1; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { aes_copy_mbs(&entry->ae_hardlink, target); + if (target != NULL) + entry->ae_hardlinkset = 1; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { aes_copy_wcs(&entry->ae_hardlink, target); + if (target != NULL) + entry->ae_hardlinkset = 1; } void @@ -754,8 +836,7 @@ archive_entry_set_devminor(struct archive_entry *entry, dev_t m) void archive_entry_set_link(struct archive_entry *entry, const char *target) { - if (entry->ae_symlink.aes_mbs != NULL || - entry->ae_symlink.aes_wcs != NULL) + if (entry->ae_symlinkset) aes_set_mbs(&entry->ae_symlink, target); else aes_set_mbs(&entry->ae_hardlink, target); @@ -765,8 +846,7 @@ archive_entry_set_link(struct archive_entry *entry, const char *target) void archive_entry_copy_link(struct archive_entry *entry, const char *target) { - if (entry->ae_symlink.aes_mbs != NULL || - entry->ae_symlink.aes_wcs != NULL) + if (entry->ae_symlinkset) aes_copy_mbs(&entry->ae_symlink, target); else aes_copy_mbs(&entry->ae_hardlink, target); @@ -776,13 +856,21 @@ archive_entry_copy_link(struct archive_entry *entry, const char *target) void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { - if (entry->ae_symlink.aes_mbs != NULL || - entry->ae_symlink.aes_wcs != NULL) + if (entry->ae_symlinkset) aes_copy_wcs(&entry->ae_symlink, target); else aes_copy_wcs(&entry->ae_hardlink, target); } +int +archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) +{ + if (entry->ae_symlinkset) + return (aes_update_utf8(&entry->ae_symlink, target)); + else + return (aes_update_utf8(&entry->ae_hardlink, target)); +} + void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { @@ -823,6 +911,12 @@ archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) aes_copy_wcs(&entry->ae_pathname, name); } +int +archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) +{ + return (aes_update_utf8(&entry->ae_pathname, name)); +} + void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { @@ -863,21 +957,33 @@ archive_entry_set_size(struct archive_entry *entry, int64_t s) } void +archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) +{ + aes_set_mbs(&entry->ae_sourcepath, path); +} + +void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { aes_set_mbs(&entry->ae_symlink, linkname); + if (linkname != NULL) + entry->ae_symlinkset = 1; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { aes_copy_mbs(&entry->ae_symlink, linkname); + if (linkname != NULL) + entry->ae_symlinkset = 1; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { aes_copy_wcs(&entry->ae_symlink, linkname); + if (linkname != NULL) + entry->ae_symlinkset = 1; } void @@ -905,6 +1011,12 @@ archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) aes_copy_wcs(&entry->ae_uname, name); } +int +archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) +{ + return (aes_update_utf8(&entry->ae_uname, name)); +} + /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and @@ -1744,7 +1856,7 @@ static struct flag { * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ -char * +static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; @@ -1789,6 +1901,70 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) } /* + * strtofflags -- + * Take string of arguments and return file flags. This + * version works a little differently than strtofflags(3). + * In particular, it always tests every token, skipping any + * unrecognized tokens. It returns a pointer to the first + * unrecognized token, or NULL if every token was recognized. + * This version is also const-correct and does not modify the + * provided string. + */ +static const char * +ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) +{ + const char *start, *end; + struct flag *flag; + unsigned long set, clear; + const char *failed; + + set = clear = 0; + start = s; + failed = NULL; + /* Find start of first token. */ + while (*start == '\t' || *start == ' ' || *start == ',') + start++; + while (*start != '\0') { + /* Locate end of token. */ + end = start; + while (*end != '\0' && *end != '\t' && + *end != ' ' && *end != ',') + end++; + for (flag = flags; flag->name != NULL; flag++) { + if (memcmp(start, flag->name, end - start) == 0) { + /* Matched "noXXXX", so reverse the sense. */ + clear |= flag->set; + set |= flag->clear; + break; + } else if (memcmp(start, flag->name + 2, end - start) + == 0) { + /* Matched "XXXX", so don't reverse. */ + set |= flag->set; + clear |= flag->clear; + break; + } + } + /* Ignore unknown flag names. */ + if (flag->name == NULL && failed == NULL) + failed = start; + + /* Find start of next token. */ + start = end; + while (*start == '\t' || *start == ' ' || *start == ',') + start++; + + } + + if (setp) + *setp = set; + if (clrp) + *clrp = clear; + + /* Return location of first failure. */ + return (failed); +} + +/* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). @@ -1798,7 +1974,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear) * This version is also const-correct and does not modify the * provided string. */ -const wchar_t * +static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index 8c61b95..c453483 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -28,20 +28,74 @@ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED +/* + * Note: archive_entry.h is for use outside of libarchive; the + * configuration headers (config.h, archive_platform.h, etc.) are + * purely internal. Do NOT use HAVE_XXX configuration macros to + * control the behavior of this header! If you must conditionalize, + * use predefined compiler and/or platform macros. + */ + #include <sys/types.h> #include <stddef.h> /* for wchar_t */ #include <time.h> + +/* Get appropriate definitions of standard POSIX-style types. */ +/* These should match the types used in 'struct stat' */ +#ifdef _WIN32 +#define __LA_UID_T unsigned int +#define __LA_GID_T unsigned int +#define __LA_DEV_T unsigned int +#define __LA_MODE_T unsigned short +#else #include <unistd.h> +#define __LA_UID_T uid_t +#define __LA_GID_T gid_t +#define __LA_DEV_T dev_t +#define __LA_MODE_T mode_t +#endif + +/* + * XXX Is this defined for all Windows compilers? If so, in what + * header? It would be nice to remove the __LA_INO_T indirection and + * just use plain ino_t everywhere. Likewise for the other types just + * above. + */ +#define __LA_INO_T ino_t + + +/* + * On Windows, define LIBARCHIVE_STATIC if you're building or using a + * .lib. The default here assumes you're building a DLL. Only + * libarchive source should ever define __LIBARCHIVE_BUILD. + */ +#if ((defined __WIN32__) || (defined _WIN32)) && (!defined LIBARCHIVE_STATIC) +# ifdef __LIBARCHIVE_BUILD +# ifdef __GNUC__ +# define __LA_DECL __attribute__((dllexport)) extern +# else +# define __LA_DECL __declspec(dllexport) +# endif +# else +# ifdef __GNUC__ +# define __LA_DECL __attribute__((dllimport)) extern +# else +# define __LA_DECL __declspec(dllimport) +# endif +# endif +#else +/* Static libraries on all platforms and shared libraries on non-Windows. */ +# define __LA_DECL +#endif #ifdef __cplusplus extern "C" { #endif - /* * Description of an archive entry. * - * Basically, a "struct stat" with a few text fields added in. + * You can think of this as "struct stat" with some text fields added in. * * TODO: Add "comment", "charset", and possibly other entries that are * supported by "pax interchange" format. However, GNU, ustar, cpio, @@ -90,50 +144,51 @@ struct archive_entry; * Basic object manipulation */ -struct archive_entry *archive_entry_clear(struct archive_entry *); +__LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *); /* The 'clone' function does a deep copy; all of the strings are copied too. */ -struct archive_entry *archive_entry_clone(struct archive_entry *); -void archive_entry_free(struct archive_entry *); -struct archive_entry *archive_entry_new(void); +__LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *); +__LA_DECL void archive_entry_free(struct archive_entry *); +__LA_DECL struct archive_entry *archive_entry_new(void); /* * Retrieve fields from an archive_entry. */ -time_t archive_entry_atime(struct archive_entry *); -long archive_entry_atime_nsec(struct archive_entry *); -time_t archive_entry_ctime(struct archive_entry *); -long archive_entry_ctime_nsec(struct archive_entry *); -dev_t archive_entry_dev(struct archive_entry *); -dev_t archive_entry_devmajor(struct archive_entry *); -dev_t archive_entry_devminor(struct archive_entry *); -mode_t archive_entry_filetype(struct archive_entry *); -void archive_entry_fflags(struct archive_entry *, +__LA_DECL time_t archive_entry_atime(struct archive_entry *); +__LA_DECL long archive_entry_atime_nsec(struct archive_entry *); +__LA_DECL time_t archive_entry_ctime(struct archive_entry *); +__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *); +__LA_DECL dev_t archive_entry_dev(struct archive_entry *); +__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *); +__LA_DECL dev_t archive_entry_devminor(struct archive_entry *); +__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *); +__LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); -const char *archive_entry_fflags_text(struct archive_entry *); -gid_t archive_entry_gid(struct archive_entry *); -const char *archive_entry_gname(struct archive_entry *); -const wchar_t *archive_entry_gname_w(struct archive_entry *); -const char *archive_entry_hardlink(struct archive_entry *); -const wchar_t *archive_entry_hardlink_w(struct archive_entry *); -ino_t archive_entry_ino(struct archive_entry *); -mode_t archive_entry_mode(struct archive_entry *); -time_t archive_entry_mtime(struct archive_entry *); -long archive_entry_mtime_nsec(struct archive_entry *); -unsigned int archive_entry_nlink(struct archive_entry *); -const char *archive_entry_pathname(struct archive_entry *); -const wchar_t *archive_entry_pathname_w(struct archive_entry *); -dev_t archive_entry_rdev(struct archive_entry *); -dev_t archive_entry_rdevmajor(struct archive_entry *); -dev_t archive_entry_rdevminor(struct archive_entry *); -int64_t archive_entry_size(struct archive_entry *); -const char *archive_entry_strmode(struct archive_entry *); -const char *archive_entry_symlink(struct archive_entry *); -const wchar_t *archive_entry_symlink_w(struct archive_entry *); -uid_t archive_entry_uid(struct archive_entry *); -const char *archive_entry_uname(struct archive_entry *); -const wchar_t *archive_entry_uname_w(struct archive_entry *); +__LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); +__LA_DECL __LA_GID_T archive_entry_gid(struct archive_entry *); +__LA_DECL const char *archive_entry_gname(struct archive_entry *); +__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *); +__LA_DECL const char *archive_entry_hardlink(struct archive_entry *); +__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); +__LA_DECL __LA_INO_T archive_entry_ino(struct archive_entry *); +__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); +__LA_DECL time_t archive_entry_mtime(struct archive_entry *); +__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); +__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *); +__LA_DECL const char *archive_entry_pathname(struct archive_entry *); +__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); +__LA_DECL dev_t archive_entry_rdev(struct archive_entry *); +__LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *); +__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *); +__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *); +__LA_DECL int64_t archive_entry_size(struct archive_entry *); +__LA_DECL const char *archive_entry_strmode(struct archive_entry *); +__LA_DECL const char *archive_entry_symlink(struct archive_entry *); +__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); +__LA_DECL __LA_UID_T archive_entry_uid(struct archive_entry *); +__LA_DECL const char *archive_entry_uname(struct archive_entry *); +__LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); /* * Set fields in an archive_entry. @@ -142,48 +197,54 @@ const wchar_t *archive_entry_uname_w(struct archive_entry *); * In contrast, 'copy' functions do copy the object pointed to. */ -void archive_entry_set_atime(struct archive_entry *, time_t, long); -void archive_entry_set_ctime(struct archive_entry *, time_t, long); -void archive_entry_set_dev(struct archive_entry *, dev_t); -void archive_entry_set_devmajor(struct archive_entry *, dev_t); -void archive_entry_set_devminor(struct archive_entry *, dev_t); -void archive_entry_set_filetype(struct archive_entry *, unsigned int); -void archive_entry_set_fflags(struct archive_entry *, +__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int); +__LA_DECL void archive_entry_set_fflags(struct archive_entry *, unsigned long /* set */, unsigned long /* clear */); /* Returns pointer to start of first invalid token, or NULL if none. */ /* Note that all recognized tokens are processed, regardless. */ -const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, +__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *, + const char *); +__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_gid(struct archive_entry *, gid_t); -void archive_entry_set_gname(struct archive_entry *, const char *); -void archive_entry_copy_gname(struct archive_entry *, const char *); -void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_hardlink(struct archive_entry *, const char *); -void archive_entry_copy_hardlink(struct archive_entry *, const char *); -void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_ino(struct archive_entry *, unsigned long); -void archive_entry_set_link(struct archive_entry *, const char *); -void archive_entry_copy_link(struct archive_entry *, const char *); -void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_mode(struct archive_entry *, mode_t); -void archive_entry_set_mtime(struct archive_entry *, time_t, long); -void archive_entry_set_nlink(struct archive_entry *, unsigned int); -void archive_entry_set_pathname(struct archive_entry *, const char *); -void archive_entry_copy_pathname(struct archive_entry *, const char *); -void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_perm(struct archive_entry *, mode_t); -void archive_entry_set_rdev(struct archive_entry *, dev_t); -void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); -void archive_entry_set_rdevminor(struct archive_entry *, dev_t); -void archive_entry_set_size(struct archive_entry *, int64_t); -void archive_entry_set_symlink(struct archive_entry *, const char *); -void archive_entry_copy_symlink(struct archive_entry *, const char *); -void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); -void archive_entry_set_uid(struct archive_entry *, uid_t); -void archive_entry_set_uname(struct archive_entry *, const char *); -void archive_entry_copy_uname(struct archive_entry *, const char *); -void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); - +__LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_GID_T); +__LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); +__LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); +__LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long); +__LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *); +__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T); +__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); +__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int); +__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *); +__LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T); +__LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t); +__LA_DECL void archive_entry_set_size(struct archive_entry *, int64_t); +__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); +__LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_UID_T); +__LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); +__LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each @@ -193,8 +254,8 @@ void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *); * * TODO: On Linux, provide both stat32 and stat64 versions of these functions. */ -const struct stat *archive_entry_stat(struct archive_entry *); -void archive_entry_copy_stat(struct archive_entry *, const struct stat *); +__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *); +__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *); /* * ACL routines. This used to simply store and return text-format ACL @@ -242,11 +303,11 @@ void archive_entry_copy_stat(struct archive_entry *, const struct stat *); * POSIX.1e) is useful for handling archive formats that combine * default and access information in a single ACL list. */ -void archive_entry_acl_clear(struct archive_entry *); -void archive_entry_acl_add_entry(struct archive_entry *, +__LA_DECL void archive_entry_acl_clear(struct archive_entry *); +__LA_DECL void archive_entry_acl_add_entry(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const char * /* name */); -void archive_entry_acl_add_entry_w(struct archive_entry *, +__LA_DECL void archive_entry_acl_add_entry_w(struct archive_entry *, int /* type */, int /* permset */, int /* tag */, int /* qual */, const wchar_t * /* name */); @@ -255,11 +316,11 @@ void archive_entry_acl_add_entry_w(struct archive_entry *, * "next" entry. The want_type parameter allows you to request only * access entries or only default entries. */ -int archive_entry_acl_reset(struct archive_entry *, int /* want_type */); -int archive_entry_acl_next(struct archive_entry *, int /* want_type */, +__LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */); +__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); -int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, +__LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const wchar_t ** /* name */); @@ -276,11 +337,11 @@ int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, */ #define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024 #define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048 -const wchar_t *archive_entry_acl_text_w(struct archive_entry *, +__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *, int /* flags */); /* Return a count of entries matching 'want_type' */ -int archive_entry_acl_count(struct archive_entry *, int /* want_type */); +__LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */); /* * Private ACL parser. This is private because it handles some @@ -295,15 +356,15 @@ int archive_entry_acl_count(struct archive_entry *, int /* want_type */); * TODO: Move this declaration out of the public header and into * a private header. Warnings above are silly. */ -int __archive_entry_acl_parse_w(struct archive_entry *, +__LA_DECL int __archive_entry_acl_parse_w(struct archive_entry *, const wchar_t *, int /* type */); /* * extended attributes */ -void archive_entry_xattr_clear(struct archive_entry *); -void archive_entry_xattr_add_entry(struct archive_entry *, +__LA_DECL void archive_entry_xattr_clear(struct archive_entry *); +__LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *, const char * /* name */, const void * /* value */, size_t /* size */); @@ -312,37 +373,93 @@ void archive_entry_xattr_add_entry(struct archive_entry *, * "next" entry. */ -int archive_entry_xattr_count(struct archive_entry *); -int archive_entry_xattr_reset(struct archive_entry *); -int archive_entry_xattr_next(struct archive_entry *, +__LA_DECL int archive_entry_xattr_count(struct archive_entry *); +__LA_DECL int archive_entry_xattr_reset(struct archive_entry *); +__LA_DECL int archive_entry_xattr_next(struct archive_entry *, const char ** /* name */, const void ** /* value */, size_t *); /* - * Utility to detect hardlinks. + * Utility to match up hardlinks. + * + * The 'struct archive_entry_linkresolver' is a cache of archive entries + * for files with multiple links. Here's how to use it: + * 1. Create a lookup object with archive_entry_linkresolver_new() + * 2. Tell it the archive format you're using. + * 3. Hand each archive_entry to archive_entry_linkify(). + * That function will return 0, 1, or 2 entries that should + * be written. + * 4. Call archive_entry_linkify(resolver, NULL) until + * no more entries are returned. + * 5. Call archive_entry_link_resolver_free(resolver) to free resources. * - * The 'struct archive_hardlink_lookup' is a cache of entry - * names and dev/ino numbers. Here's how to use it: - * 1. Create a lookup object with archive_hardlink_lookup_new() - * 2. Hand each archive_entry to archive_hardlink_lookup(). - * That function will return NULL (this is not a hardlink to - * a previous entry) or the pathname of the first entry - * that matched this. - * 3. Use archive_hardlink_lookup_free() to release the cache. + * The entries returned have their hardlink and size fields updated + * appropriately. If an entry is passed in that does not refer to + * a file with multiple links, it is returned unchanged. The intention + * is that you should be able to simply filter all entries through + * this machine. * * To make things more efficient, be sure that each entry has a valid * nlinks value. The hardlink cache uses this to track when all links * have been found. If the nlinks value is zero, it will keep every * name in the cache indefinitely, which can use a lot of memory. + * + * Note that archive_entry_size() is reset to zero if the file + * body should not be written to the archive. Pay attention! */ -struct archive_entry_linkresolver; +__LA_DECL struct archive_entry_linkresolver; -struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); -void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); -const char *archive_entry_linkresolve(struct archive_entry_linkresolver *, - struct archive_entry *); +/* + * There are three different strategies for marking hardlinks. + * The descriptions below name them after the best-known + * formats that rely on each strategy: + * + * "Old cpio" is the simplest, it always returns any entry unmodified. + * As far as I know, only cpio formats use this. Old cpio archives + * store every link with the full body; the onus is on the dearchiver + * to detect and properly link the files as they are restored. + * "tar" is also pretty simple; it caches a copy the first time it sees + * any link. Subsequent appearances are modified to be hardlink + * references to the first one without any body. Used by all tar + * formats, although the newest tar formats permit the "old cpio" strategy + * as well. This strategy is very simple for the dearchiver, + * and reasonably straightforward for the archiver. + * "new cpio" is trickier. It stores the body only with the last + * occurrence. The complication is that we might not + * see every link to a particular file in a single session, so + * there's no easy way to know when we've seen the last occurrence. + * The solution here is to queue one link until we see the next. + * At the end of the session, you can enumerate any remaining + * entries by calling archive_entry_linkify(NULL) and store those + * bodies. If you have a file with three links l1, l2, and l3, + * you'll get the following behavior if you see all three links: + * linkify(l1) => NULL (the resolver stores l1 internally) + * linkify(l2) => l1 (resolver stores l2, you write l1) + * linkify(l3) => l2, l3 (all links seen, you can write both). + * If you only see l1 and l2, you'll get this behavior: + * linkify(l1) => NULL + * linkify(l2) => l1 + * linkify(NULL) => l2 (at end, you retrieve remaining links) + * As the name suggests, this strategy is used by newer cpio variants. + * It's noticably more complex for the archiver, slightly more complex + * for the dearchiver than the tar strategy, but makes it straightforward + * to restore a file using any link by simply continuing to scan until + * you see a link that is stored with a body. In contrast, the tar + * strategy requires you to rescan the archive from the beginning to + * correctly extract an arbitrary link. + */ + +__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void); +__LA_DECL void archive_entry_linkresolver_set_strategy( + struct archive_entry_linkresolver *, int /* format_code */); +__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *); +__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, + struct archive_entry **, struct archive_entry **); #ifdef __cplusplus } #endif +/* This is meaningless outside of this header. */ +#undef __LA_DECL + #endif /* !ARCHIVE_ENTRY_H_INCLUDED */ diff --git a/lib/libarchive/archive_entry_link_resolver.c b/lib/libarchive/archive_entry_link_resolver.c index 0f07e06..c6e17d4 100644 --- a/lib/libarchive/archive_entry_link_resolver.c +++ b/lib/libarchive/archive_entry_link_resolver.c @@ -40,135 +40,236 @@ __FBSDID("$FreeBSD$"); #include <string.h> #endif +#include "archive.h" #include "archive_entry.h" +/* + * This is mostly a pretty straightforward hash table implementation. + * The only interesting bit is the different strategies used to + * match up links. These strategies match those used by various + * archiving formats: + * tar - content stored with first link, remainder refer back to it. + * This requires us to match each subsequent link up with the + * first appearance. + * cpio - Old cpio just stored body with each link, match-ups were + * implicit. This is trivial. + * new cpio - New cpio only stores body with last link, match-ups + * are implicit. This is actually quite tricky; see the notes + * below. + */ + +/* Users pass us a format code, we translate that into a strategy here. */ +#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0 +#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1 +#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2 +#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3 + /* Initial size of link cache. */ #define links_cache_initial_size 1024 +struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; /* # links not yet seen */ + int hash; + struct archive_entry *canonical; + struct archive_entry *entry; +}; + struct archive_entry_linkresolver { - char *last_name; + struct links_entry **buckets; + struct links_entry *spare; unsigned long number_entries; size_t number_buckets; - struct links_entry **buckets; + int strategy; }; -struct links_entry { - struct links_entry *next; - struct links_entry *previous; - int links; - dev_t dev; - ino_t ino; - char *name; -}; +static struct links_entry *find_entry(struct archive_entry_linkresolver *, + struct archive_entry *); +static void grow_hash(struct archive_entry_linkresolver *); +static struct links_entry *insert_entry(struct archive_entry_linkresolver *, + struct archive_entry *); +static struct links_entry *next_entry(struct archive_entry_linkresolver *); struct archive_entry_linkresolver * archive_entry_linkresolver_new(void) { - struct archive_entry_linkresolver *links_cache; + struct archive_entry_linkresolver *res; size_t i; - links_cache = malloc(sizeof(struct archive_entry_linkresolver)); - if (links_cache == NULL) + res = malloc(sizeof(struct archive_entry_linkresolver)); + if (res == NULL) return (NULL); - memset(links_cache, 0, sizeof(struct archive_entry_linkresolver)); - links_cache->number_buckets = links_cache_initial_size; - links_cache->buckets = malloc(links_cache->number_buckets * - sizeof(links_cache->buckets[0])); - if (links_cache->buckets == NULL) { - free(links_cache); + memset(res, 0, sizeof(struct archive_entry_linkresolver)); + res->number_buckets = links_cache_initial_size; + res->buckets = malloc(res->number_buckets * + sizeof(res->buckets[0])); + if (res->buckets == NULL) { + free(res); return (NULL); } - for (i = 0; i < links_cache->number_buckets; i++) - links_cache->buckets[i] = NULL; - return (links_cache); + for (i = 0; i < res->number_buckets; i++) + res->buckets[i] = NULL; + return (res); } void -archive_entry_linkresolver_free(struct archive_entry_linkresolver *links_cache) +archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res, + int fmt) { - size_t i; + int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK; + + switch (fmtbase) { + case ARCHIVE_FORMAT_CPIO: + switch (fmt) { + case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC: + case ARCHIVE_FORMAT_CPIO_SVR4_CRC: + res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO; + break; + default: + res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO; + break; + } + break; + case ARCHIVE_FORMAT_MTREE: + res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE; + break; + case ARCHIVE_FORMAT_TAR: + res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR; + break; + default: + res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR; + break; + } +} + +void +archive_entry_linkresolver_free(struct archive_entry_linkresolver *res) +{ + struct links_entry *le; + + if (res == NULL) + return; + + if (res->buckets != NULL) { + while ((le = next_entry(res)) != NULL) + archive_entry_free(le->entry); + free(res->buckets); + res->buckets = NULL; + } + free(res); +} + +void +archive_entry_linkify(struct archive_entry_linkresolver *res, + struct archive_entry **e, struct archive_entry **f) +{ + struct links_entry *le; + struct archive_entry *t; + + *f = NULL; /* Default: Don't return a second entry. */ + + if (*e == NULL) { + le = next_entry(res); + if (le != NULL) { + *e = le->entry; + le->entry = NULL; + } + return; + } - if (links_cache->buckets == NULL) + /* If it has only one link, then we're done. */ + if (archive_entry_nlink(*e) == 1) return; - for (i = 0; i < links_cache->number_buckets; i++) { - while (links_cache->buckets[i] != NULL) { - struct links_entry *lp = links_cache->buckets[i]->next; - if (links_cache->buckets[i]->name != NULL) - free(links_cache->buckets[i]->name); - free(links_cache->buckets[i]); - links_cache->buckets[i] = lp; + switch (res->strategy) { + case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR: + le = find_entry(res, *e); + if (le != NULL) { + archive_entry_set_size(*e, 0); + archive_entry_copy_hardlink(*e, + archive_entry_pathname(le->canonical)); + } else + insert_entry(res, *e); + return; + case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE: + le = find_entry(res, *e); + if (le != NULL) { + archive_entry_copy_hardlink(*e, + archive_entry_pathname(le->canonical)); + } else + insert_entry(res, *e); + return; + case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO: + /* This one is trivial. */ + return; + case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO: + le = find_entry(res, *e); + if (le != NULL) { + /* + * Put the new entry in le, return the + * old entry from le. + */ + t = *e; + *e = le->entry; + le->entry = t; + /* Make the old entry into a hardlink. */ + archive_entry_set_size(*e, 0); + archive_entry_copy_hardlink(*e, + archive_entry_pathname(le->canonical)); + /* If we ran out of links, return the + * final entry as well. */ + if (le->links == 0) { + *f = le->entry; + le->entry = NULL; + } + } else { + /* + * If we haven't seen it, tuck it away + * for future use. + */ + le = insert_entry(res, *e); + le->entry = *e; + *e = NULL; } + return; + default: + break; } - free(links_cache->buckets); - links_cache->buckets = NULL; + return; } -const char * -archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache, +static struct links_entry * +find_entry(struct archive_entry_linkresolver *res, struct archive_entry *entry) { - struct links_entry *le, **new_buckets; - int hash; - size_t i, new_size; + struct links_entry *le; + int hash, bucket; dev_t dev; ino_t ino; - int nlinks; - - /* Free a held name. */ - free(links_cache->last_name); - links_cache->last_name = NULL; + /* Free a held entry. */ + if (res->spare != NULL) { + archive_entry_free(res->spare->canonical); + archive_entry_free(res->spare->entry); + free(res->spare); + res->spare = NULL; + } /* If the links cache overflowed and got flushed, don't bother. */ - if (links_cache->buckets == NULL) + if (res->buckets == NULL) return (NULL); dev = archive_entry_dev(entry); ino = archive_entry_ino(entry); - nlinks = archive_entry_nlink(entry); - - /* An entry with one link can't be a hard link. */ - if (nlinks == 1) - return (NULL); - - /* If the links cache is getting too full, enlarge the hash table. */ - if (links_cache->number_entries > links_cache->number_buckets * 2) - { - /* Try to enlarge the bucket list. */ - new_size = links_cache->number_buckets * 2; - new_buckets = malloc(new_size * sizeof(struct links_entry *)); - - if (new_buckets != NULL) { - memset(new_buckets, 0, - new_size * sizeof(struct links_entry *)); - for (i = 0; i < links_cache->number_buckets; i++) { - while (links_cache->buckets[i] != NULL) { - /* Remove entry from old bucket. */ - le = links_cache->buckets[i]; - links_cache->buckets[i] = le->next; - - /* Add entry to new bucket. */ - hash = (le->dev ^ le->ino) % new_size; - - if (new_buckets[hash] != NULL) - new_buckets[hash]->previous = - le; - le->next = new_buckets[hash]; - le->previous = NULL; - new_buckets[hash] = le; - } - } - free(links_cache->buckets); - links_cache->buckets = new_buckets; - links_cache->number_buckets = new_size; - } - } + hash = dev ^ ino; /* Try to locate this entry in the links cache. */ - hash = ( dev ^ ino ) % links_cache->number_buckets; - for (le = links_cache->buckets[hash]; le != NULL; le = le->next) { - if (le->dev == dev && le->ino == ino) { + bucket = hash % res->number_buckets; + for (le = res->buckets[bucket]; le != NULL; le = le->next) { + if (le->hash == hash + && dev == archive_entry_dev(le->canonical) + && ino == archive_entry_ino(le->canonical)) { /* * Decrement link count each time and release * the entry if it hits zero. This saves @@ -177,46 +278,123 @@ archive_entry_linkresolve(struct archive_entry_linkresolver *links_cache, */ --le->links; if (le->links > 0) - return (le->name); - /* - * When we release the entry, save the name - * until the next call. - */ - links_cache->last_name = le->name; - /* - * Release the entry. - */ + return (le); + /* Remove it from this hash bucket. */ if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; - if (links_cache->buckets[hash] == le) - links_cache->buckets[hash] = le->next; - links_cache->number_entries--; - free(le); - return (links_cache->last_name); + if (res->buckets[bucket] == le) + res->buckets[bucket] = le->next; + res->number_entries--; + /* Defer freeing this entry. */ + res->spare = le; + return (le); + } + } + return (NULL); +} + +static struct links_entry * +next_entry(struct archive_entry_linkresolver *res) +{ + struct links_entry *le; + size_t bucket; + + /* Free a held entry. */ + if (res->spare != NULL) { + archive_entry_free(res->spare->canonical); + free(res->spare); + res->spare = NULL; + } + + /* If the links cache overflowed and got flushed, don't bother. */ + if (res->buckets == NULL) + return (NULL); + + /* Look for next non-empty bucket in the links cache. */ + for (bucket = 0; bucket < res->number_buckets; bucket++) { + le = res->buckets[bucket]; + if (le != NULL) { + /* Remove it from this hash bucket. */ + if (le->next != NULL) + le->next->previous = le->previous; + res->buckets[bucket] = le->next; + res->number_entries--; + /* Defer freeing this entry. */ + res->spare = le; + return (le); } } + return (NULL); +} + +static struct links_entry * +insert_entry(struct archive_entry_linkresolver *res, + struct archive_entry *entry) +{ + struct links_entry *le; + int hash, bucket; /* Add this entry to the links cache. */ le = malloc(sizeof(struct links_entry)); if (le == NULL) return (NULL); - le->name = strdup(archive_entry_pathname(entry)); - if (le->name == NULL) { - free(le); - return (NULL); - } + memset(le, 0, sizeof(*le)); + le->canonical = archive_entry_clone(entry); + + /* If the links cache is getting too full, enlarge the hash table. */ + if (res->number_entries > res->number_buckets * 2) + grow_hash(res); + + hash = archive_entry_dev(entry) ^ archive_entry_ino(entry); + bucket = hash % res->number_buckets; /* If we could allocate the entry, record it. */ - if (links_cache->buckets[hash] != NULL) - links_cache->buckets[hash]->previous = le; - links_cache->number_entries++; - le->next = links_cache->buckets[hash]; + if (res->buckets[bucket] != NULL) + res->buckets[bucket]->previous = le; + res->number_entries++; + le->next = res->buckets[bucket]; le->previous = NULL; - links_cache->buckets[hash] = le; - le->dev = dev; - le->ino = ino; - le->links = nlinks - 1; - return (NULL); + res->buckets[bucket] = le; + le->hash = hash; + le->links = archive_entry_nlink(entry) - 1; + return (le); +} + +static void +grow_hash(struct archive_entry_linkresolver *res) +{ + struct links_entry *le, **new_buckets; + size_t new_size; + size_t i, bucket; + + /* Try to enlarge the bucket list. */ + new_size = res->number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + if (new_buckets != NULL) { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < res->number_buckets; i++) { + while (res->buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = res->buckets[i]; + res->buckets[i] = le->next; + + /* Add entry to new bucket. */ + bucket = le->hash % new_size; + + if (new_buckets[bucket] != NULL) + new_buckets[bucket]->previous = + le; + le->next = new_buckets[bucket]; + le->previous = NULL; + new_buckets[bucket] = le; + } + } + free(res->buckets); + res->buckets = new_buckets; + res->number_buckets = new_size; + } } diff --git a/lib/libarchive/archive_entry_private.h b/lib/libarchive/archive_entry_private.h index 6eb973e..86a3ba1 100644 --- a/lib/libarchive/archive_entry_private.h +++ b/lib/libarchive/archive_entry_private.h @@ -28,17 +28,25 @@ #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED #define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED +#include "archive_string.h" + /* * Handle wide character (i.e., Unicode) and non-wide character * strings transparently. - * */ struct aes { - const char *aes_mbs; - char *aes_mbs_alloc; + struct archive_string aes_mbs; + struct archive_string aes_utf8; const wchar_t *aes_wcs; - wchar_t *aes_wcs_alloc; + /* Bitmap of which of the above are valid. Because we're lazy + * about malloc-ing and reusing the underlying storage, we + * can't rely on NULL pointers to indicate whether a string + * has been set. */ + int aes_set; +#define AES_SET_MBS 1 +#define AES_SET_UTF8 2 +#define AES_SET_WCS 4 }; struct ae_acl { @@ -128,8 +136,6 @@ struct archive_entry { dev_t aest_rdevminor; } ae_stat; - - /* * Use aes here so that we get transparent mbs<->wcs conversions. */ @@ -141,15 +147,23 @@ struct archive_entry { struct aes ae_pathname; /* Name of entry */ struct aes ae_symlink; /* symlink contents */ struct aes ae_uname; /* Name of owner */ + unsigned char ae_hardlinkset; + unsigned char ae_symlinkset; + + /* Not used within libarchive; useful for some clients. */ + struct aes ae_sourcepath; /* Path this entry is sourced from. */ + /* ACL support. */ struct ae_acl *acl_head; struct ae_acl *acl_p; int acl_state; /* See acl_next for details. */ wchar_t *acl_text_w; + /* extattr support. */ struct ae_xattr *xattr_head; struct ae_xattr *xattr_p; + /* Miscellaneous. */ char strmode[12]; }; diff --git a/lib/libarchive/archive_platform.h b/lib/libarchive/archive_platform.h index 035d27c..e349019 100644 --- a/lib/libarchive/archive_platform.h +++ b/lib/libarchive/archive_platform.h @@ -36,6 +36,9 @@ #ifndef ARCHIVE_PLATFORM_H_INCLUDED #define ARCHIVE_PLATFORM_H_INCLUDED +/* archive.h and archive_entry.h require this. */ +#define __LIBARCHIVE_BUILD 1 + #ifdef _WIN32 #include "config_windows.h" #include "archive_windows.h" diff --git a/lib/libarchive/archive_read.3 b/lib/libarchive/archive_read.3 index 43cfddf..591819c 100644 --- a/lib/libarchive/archive_read.3 +++ b/lib/libarchive/archive_read.3 @@ -56,6 +56,7 @@ .\" #endif .Nm archive_read_data_into_fd , .Nm archive_read_extract , +.Nm archive_read_extract2 , .Nm archive_read_extract_set_progress_callback , .Nm archive_read_close , .Nm archive_read_finish @@ -145,6 +146,12 @@ .Fa "struct archive_entry *" .Fa "int flags" .Fc +.Ft int +.Fo archive_read_extract2 +.Fa "struct archive *src" +.Fa "struct archive_entry *" +.Fa "struct archive *dest" +.Fc .Ft void .Fo archive_read_extract_set_progress_callback .Fa "struct archive *" @@ -314,6 +321,22 @@ The .Va flags argument is passed unmodified to .Xr archive_write_disk_set_options 3 . +.It Fn archive_read_extract2 +This is another version of +.Fn archive_read_extract +that allows you to provide your own restore object. +In particular, this allows you to override the standard lookup functions +using +.Xr archive_write_disk_set_group_lookup 3 , +and +.Xr archive_write_disk_set_user_lookup 3 . +Note that +.Fn archive_read_extract2 +does not accept a +.Va flags +argument; you should use +.Fn archive_write_disk_set_options +to set the restore options yourself. .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. diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index 5eb0d36..5ba8d22 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -82,34 +82,40 @@ get_extract(struct archive_read *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 = get_extract(a); + extract = get_extract((struct archive_read *)_a); if (extract == NULL) return (ARCHIVE_FATAL); + archive_write_disk_set_options(extract->ad, flags); + return (archive_read_extract2(_a, entry, extract->ad)); +} + +int +archive_read_extract2(struct archive *_a, struct archive_entry *entry, + struct archive *ad) +{ + struct archive_read *a = (struct archive_read *)_a; + int r, r2; /* 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, + archive_write_disk_set_skip_file(ad, a->skip_file_dev, a->skip_file_ino); - r = archive_write_header(a->extract->ad, entry); + r = archive_write_header(ad, entry); if (r < ARCHIVE_WARN) r = ARCHIVE_WARN; if (r != ARCHIVE_OK) /* If _write_header failed, copy the error. */ - archive_copy_error(&a->archive, extract->ad); + archive_copy_error(&a->archive, ad); else /* Otherwise, pour data into the entry. */ - r = copy_data(_a, a->extract->ad); - r2 = archive_write_finish_entry(a->extract->ad); + r = copy_data(_a, ad); + r2 = archive_write_finish_entry(ad); if (r2 < ARCHIVE_WARN) r2 = ARCHIVE_WARN; /* Use the first message. */ if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) - archive_copy_error(&a->archive, extract->ad); + archive_copy_error(&a->archive, ad); /* Use the worst error return. */ if (r2 < r) r = r2; diff --git a/lib/libarchive/archive_read_support_compression_bzip2.c b/lib/libarchive/archive_read_support_compression_bzip2.c index 6f56068..b08b6b5 100644 --- a/lib/libarchive/archive_read_support_compression_bzip2.c +++ b/lib/libarchive/archive_read_support_compression_bzip2.c @@ -116,17 +116,29 @@ bid(const void *buff, size_t len) if (buffer[3] < '1' || buffer[3] > '9') return (0); bits_checked += 5; + if (len < 5) + return (bits_checked); - /* - * Research Question: Can we do any more to verify that this - * really is BZip2 format?? For 99.9% of the time, the above - * test is sufficient, but it would be nice to do a more - * thorough check. It's especially troubling that the BZip2 - * signature begins with all ASCII characters; a tar archive - * whose first filename begins with 'BZh3' would potentially - * fool this logic. (It may also be possible to guard against - * such anomalies in archive_read_support_compression_none.) - */ + /* After BZh[1-9], there must be either a data block + * which begins with 0x314159265359 or an end-of-data + * marker of 0x177245385090. */ + + if (buffer[4] == 0x31) { + /* Verify the data block signature. */ + size_t s = len; + if (s > 10) s = 10; + if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0) + return (0); + bits_checked += 8 * (s - 4); + } else if (buffer[4] == 0x17) { + /* Verify the end-of-data marker. */ + size_t s = len; + if (s > 10) s = 10; + if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0) + return (0); + bits_checked += 8 * (s - 4); + } else + return (0); return (bits_checked); } diff --git a/lib/libarchive/archive_read_support_compression_program.c b/lib/libarchive/archive_read_support_compression_program.c index dfea568..5f6cb46 100644 --- a/lib/libarchive/archive_read_support_compression_program.c +++ b/lib/libarchive/archive_read_support_compression_program.c @@ -26,6 +26,24 @@ #include "archive_platform.h" __FBSDID("$FreeBSD$"); + +/* This capability is only available on POSIX systems. */ +#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) + +/* + * On non-Posix systems, allow the program to build, but choke if + * this function is actually invoked. + */ +int +archive_read_support_compression_program(struct archive *_a, const char *cmd) +{ + archive_set_error(_a, -1, + "External compression programs not supported on this platform"); + return (ARCHIVE_FATAL); +} + +#else + #ifdef HAVE_SYS_WAIT_H # include <sys/wait.h> #endif @@ -313,3 +331,5 @@ archive_decompressor_program_finish(struct archive_read *a) return (ARCHIVE_OK); } + +#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/archive_read_support_format_ar.c b/lib/libarchive/archive_read_support_format_ar.c index 9ce5160..175876e 100644 --- a/lib/libarchive/archive_read_support_format_ar.c +++ b/lib/libarchive/archive_read_support_format_ar.c @@ -350,12 +350,16 @@ archive_read_format_ar_read_header(struct archive_read *a, /* Parse the size of the name, adjust the file size. */ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); - if ((off_t)number > ar->entry_bytes_remaining) { + bsd_name_length = (size_t)number; + /* Guard against the filename + trailing NUL + * overflowing a size_t and against the filename size + * being larger than the entire entry. */ + if (number > (uint64_t)(bsd_name_length + 1) + || (off_t)bsd_name_length > ar->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad input file size"); return (ARCHIVE_FATAL); } - bsd_name_length = (size_t)number; ar->entry_bytes_remaining -= bsd_name_length; /* Adjust file size reported to client. */ archive_entry_set_size(entry, ar->entry_bytes_remaining); diff --git a/lib/libarchive/archive_read_support_format_iso9660.c b/lib/libarchive/archive_read_support_format_iso9660.c index 80bac08..6e0da0e 100644 --- a/lib/libarchive/archive_read_support_format_iso9660.c +++ b/lib/libarchive/archive_read_support_format_iso9660.c @@ -595,8 +595,11 @@ add_entry(struct iso9660 *iso9660, struct file_info *file) struct file_info **new_pending_files; int new_size = iso9660->pending_files_allocated * 2; - if (new_size < 1024) + if (iso9660->pending_files_allocated < 1024) new_size = 1024; + /* Overflow might keep us from growing the list. */ + if (new_size <= iso9660->pending_files_allocated) + __archive_errx(1, "Out of memory"); new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) __archive_errx(1, "Out of memory"); @@ -908,6 +911,11 @@ fprintf(stderr, " *** Discarding CE data.\n"); file->ce_size = 0; } + /* Don't waste time seeking for zero-length bodies. */ + if (file->size == 0) { + file->offset = iso9660->current_position; + } + /* If CE exists, find and read it now. */ if (file->ce_offset > 0) offset = file->ce_offset; @@ -1041,51 +1049,22 @@ isodate17(const unsigned char *v) return (time_from_tm(&tm)); } -/* - * timegm() converts a struct tm to a time_t, except it isn't standard, - * so I provide my own function here that (ideally) is just a wrapper - * for timegm(). - */ static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM + /* Use platform timegm() if available. */ return (timegm(t)); -#elif HAVE_STRUCT_TM_TM_GMTOFF - /* - * Unfortunately, timegm() isn't standard. The standard - * mktime() function is a close match, except that it uses - * local timezone instead of GMT. You can compensate for - * this by adding the timezone and DST offsets back in, at - * the cost of two calls to mktime(). - */ - mktime(t); /* Normalize the time and get the TZ offset. */ - t->tm_sec += t->tm_gmtoff; /* Try to adjust for the timezone and DST.*/ - if (t->tm_isdst) - t->tm_hour -= 1; - return (mktime(t)); /* Re-convert. */ -#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && defined(HAVE_TZSET) - /* No timegm() and no tm_gmtoff, let's try forcing mktime() to UTC. */ - time_t ret; - char *tz; - - /* Reset the timezone, remember the old one. */ - tz = getenv("TZ"); - setenv("TZ", "UTC 0", 1); - tzset(); - - ret = mktime(t); - - /* Restore the previous timezone. */ - if (tz) - setenv("TZ", tz, 1); - else - unsetenv("TZ"); - tzset(); - return ret; #else - /* <sigh> We have no choice but to use localtime instead of UTC. */ - return (mktime(t)); + /* Else use direct calculation using POSIX assumptions. */ + /* First, fix up tm_yday based on the year/month/day. */ + mktime(t); + /* Then we can compute timegm() from first principles. */ + return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + + ((t->tm_year - 69) / 4) * 86400 - + ((t->tm_year - 1) / 100) * 86400 + + ((t->tm_year + 299) / 400) * 86400); #endif } diff --git a/lib/libarchive/archive_read_support_format_mtree.c b/lib/libarchive/archive_read_support_format_mtree.c index 01d5a32..1d770ba 100644 --- a/lib/libarchive/archive_read_support_format_mtree.c +++ b/lib/libarchive/archive_read_support_format_mtree.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2008 Joerg Sonnenberger * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,11 +55,29 @@ __FBSDID("$FreeBSD$"); #define O_BINARY 0 #endif +#define MTREE_HAS_DEVICE 0x0001 +#define MTREE_HAS_FFLAGS 0x0002 +#define MTREE_HAS_GID 0x0004 +#define MTREE_HAS_GNAME 0x0008 +#define MTREE_HAS_MTIME 0x0010 +#define MTREE_HAS_NLINK 0x0020 +#define MTREE_HAS_PERM 0x0040 +#define MTREE_HAS_SIZE 0x0080 +#define MTREE_HAS_TYPE 0x0100 +#define MTREE_HAS_UID 0x0200 +#define MTREE_HAS_UNAME 0x0400 + +#define MTREE_HAS_OPTIONAL 0x0800 + +struct mtree_option { + struct mtree_option *next; + char *value; +}; + struct mtree_entry { struct mtree_entry *next; + struct mtree_option *options; char *name; - char *option_start; - char *option_end; char full; char used; }; @@ -77,18 +96,20 @@ struct mtree { struct archive_string current_dir; struct archive_string contents_name; + struct archive_entry_linkresolver *resolver; + off_t cur_size, cur_offset; }; static int cleanup(struct archive_read *); static int mtree_bid(struct archive_read *); static int parse_file(struct archive_read *, struct archive_entry *, - struct mtree *, struct mtree_entry *); + struct mtree *, struct mtree_entry *, int *); static void parse_escapes(char *, struct mtree_entry *); static int parse_line(struct archive_read *, struct archive_entry *, - struct mtree *, struct mtree_entry *); + struct mtree *, struct mtree_entry *, int *); static int parse_keyword(struct archive_read *, struct mtree *, - struct archive_entry *, char *, char *); + struct archive_entry *, struct mtree_option *, int *); static int read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset); static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); @@ -97,6 +118,19 @@ static int read_header(struct archive_read *, struct archive_entry *); static int64_t mtree_atol10(char **); static int64_t mtree_atol8(char **); +static int64_t mtree_atol(char **); + +static void +free_options(struct mtree_option *head) +{ + struct mtree_option *next; + + for (; head != NULL; head = next) { + next = head->next; + free(head->value); + free(head); + } +} int archive_read_support_format_mtree(struct archive *_a) @@ -129,21 +163,20 @@ cleanup(struct archive_read *a) struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); + p = mtree->entries; while (p != NULL) { q = p->next; free(p->name); - /* - * Note: option_start, option_end are pointers into - * the block that p->name points to. So we should - * not try to free them! - */ + free_options(p->options); free(p); p = q; } archive_string_free(&mtree->line); archive_string_free(&mtree->current_dir); archive_string_free(&mtree->contents_name); + archive_entry_linkresolver_free(mtree->resolver); + free(mtree->buff); free(mtree); (a->format->data) = NULL; @@ -184,21 +217,205 @@ mtree_bid(struct archive_read *a) /* * The extended mtree format permits multiple lines specifying - * attributes for each file. Practically speaking, that means we have + * attributes for each file. For those entries, only the last line + * is actually used. Practically speaking, that means we have * to read the entire mtree file into memory up front. + * + * The parsing is done in two steps. First, it is decided if a line + * changes the global defaults and if it is, processed accordingly. + * Otherwise, the options of the line are merged with the current + * global options. */ static int +add_option(struct archive_read *a, struct mtree_option **global, + const char *value, size_t len) +{ + struct mtree_option *option; + + if ((option = malloc(sizeof(*option))) == NULL) { + archive_set_error(&a->archive, errno, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + if ((option->value = malloc(len + 1)) == NULL) { + free(option); + archive_set_error(&a->archive, errno, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + memcpy(option->value, value, len); + option->value[len] = '\0'; + option->next = *global; + *global = option; + return (ARCHIVE_OK); +} + +static void +remove_option(struct mtree_option **global, const char *value, size_t len) +{ + struct mtree_option *iter, *last; + + last = NULL; + for (iter = *global; iter != NULL; last = iter, iter = iter->next) { + if (strncmp(iter->value, value, len) == 0 && + (iter->value[len] == '\0' || + iter->value[len] == '=')) + break; + } + if (iter == NULL) + return; + if (last == NULL) + *global = iter->next; + else + last->next = iter->next; + + free(iter->value); + free(iter); +} + +static int +process_global_set(struct archive_read *a, + struct mtree_option **global, const char *line) +{ + const char *next, *eq; + size_t len; + int r; + + line += 4; + for (;;) { + next = line + strspn(line, " \t\r\n"); + if (*next == '\0') + return (ARCHIVE_OK); + line = next; + next = line + strcspn(line, " \t\r\n"); + eq = strchr(line, '='); + if (eq > next) + len = next - line; + else + len = eq - line; + + remove_option(global, line, len); + r = add_option(a, global, line, next - line); + if (r != ARCHIVE_OK) + return (r); + line = next; + } +} + +static int +process_global_unset(struct archive_read *a, + struct mtree_option **global, const char *line) +{ + const char *next; + size_t len; + + line += 6; + if ((next = strchr(line, '=')) != NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "/unset shall not contain `='"); + return ARCHIVE_FATAL; + } + + for (;;) { + next = line + strspn(line, " \t\r\n"); + if (*next == '\0') + return (ARCHIVE_OK); + line = next; + len = strcspn(line, " \t\r\n"); + + if (len == 3 && strncmp(line, "all", 3) == 0) { + free_options(*global); + *global = NULL; + } else { + remove_option(global, line, len); + } + + line += len; + } +} + +static int +process_add_entry(struct archive_read *a, struct mtree *mtree, + struct mtree_option **global, const char *line, + struct mtree_entry **last_entry) +{ + struct mtree_entry *entry; + struct mtree_option *iter; + const char *next, *eq; + size_t len; + int r; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + archive_set_error(&a->archive, errno, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + entry->next = NULL; + entry->options = NULL; + entry->name = NULL; + entry->used = 0; + entry->full = 0; + + /* Add this entry to list. */ + if (*last_entry == NULL) + mtree->entries = entry; + else + (*last_entry)->next = entry; + *last_entry = entry; + + len = strcspn(line, " \t\r\n"); + if ((entry->name = malloc(len + 1)) == NULL) { + archive_set_error(&a->archive, errno, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + + memcpy(entry->name, line, len); + entry->name[len] = '\0'; + parse_escapes(entry->name, entry); + + line += len; + for (iter = *global; iter != NULL; iter = iter->next) { + r = add_option(a, &entry->options, iter->value, + strlen(iter->value)); + if (r != ARCHIVE_OK) + return (r); + } + + for (;;) { + next = line + strspn(line, " \t\r\n"); + if (*next == '\0') + return (ARCHIVE_OK); + line = next; + next = line + strcspn(line, " \t\r\n"); + eq = strchr(line, '='); + if (eq > next) + len = next - line; + else + len = eq - line; + + remove_option(&entry->options, line, len); + r = add_option(a, &entry->options, line, next - line); + if (r != ARCHIVE_OK) + return (r); + line = next; + } +} + +static int read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; + uintmax_t counter; char *p; - struct mtree_entry *mentry; - struct mtree_entry *last_mentry = NULL; + struct mtree_option *global; + struct mtree_entry *last_entry; + int r; mtree->archive_format = ARCHIVE_FORMAT_MTREE_V1; mtree->archive_format_name = "mtree"; - for (;;) { + global = NULL; + last_entry = NULL; + r = ARCHIVE_OK; + + for (counter = 1; ; ++counter) { len = readline(a, mtree, &p, 256); if (len == 0) { mtree->this_entry = mtree->entries; @@ -216,46 +433,27 @@ read_mtree(struct archive_read *a, struct mtree *mtree) continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; - mentry = malloc(sizeof(*mentry)); - if (mentry == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - memset(mentry, 0, sizeof(*mentry)); - /* Add this entry to list. */ - if (last_mentry == NULL) { - last_mentry = mtree->entries = mentry; - } else { - last_mentry->next = mentry; - } - last_mentry = mentry; + if (*p != '/') { + r = process_add_entry(a, mtree, &global, p, + &last_entry); + } else if (strncmp(p, "/set", 4) == 0) { + if (p[4] != ' ' && p[4] != '\t') + break; + r = process_global_set(a, &global, p); + } else if (strncmp(p, "/unset", 6) == 0) { + if (p[6] != ' ' && p[6] != '\t') + break; + r = process_global_unset(a, &global, p); + } else + break; - /* Copy line over onto heap. */ - mentry->name = malloc(len + 1); - if (mentry->name == NULL) { - free(mentry); - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - strcpy(mentry->name, p); - mentry->option_end = mentry->name + len; - /* Find end of name. */ - p = mentry->name; - while (*p != ' ' && *p != '\n' && *p != '\0') - ++p; - *p++ = '\0'; - parse_escapes(mentry->name, mentry); - /* Find start of options and record it. */ - while (p < mentry->option_end && (*p == ' ' || *p == '\t')) - ++p; - mentry->option_start = p; - /* Null terminate each separate option. */ - while (++p < mentry->option_end) - if (*p == ' ' || *p == '\t' || *p == '\n') - *p = '\0'; + if (r != ARCHIVE_OK) + return r; } + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Can't parse line %ju", counter); + return ARCHIVE_FATAL; } /* @@ -267,7 +465,7 @@ read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree; char *p; - int r; + int r, use_next; mtree = (struct mtree *)(a->format->data); @@ -277,6 +475,11 @@ read_header(struct archive_read *a, struct archive_entry *entry) } if (mtree->entries == NULL) { + mtree->resolver = archive_entry_linkresolver_new(); + if (mtree->resolver == NULL) + return ARCHIVE_FATAL; + archive_entry_linkresolver_set_strategy(mtree->resolver, + ARCHIVE_FORMAT_MTREE); r = read_mtree(a, mtree); if (r != ARCHIVE_OK) return (r); @@ -303,8 +506,10 @@ read_header(struct archive_read *a, struct archive_entry *entry) } } if (!mtree->this_entry->used) { - r = parse_file(a, entry, mtree, mtree->this_entry); - return (r); + use_next = 0; + r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); + if (use_next == 0) + return (r); } mtree->this_entry = mtree->this_entry->next; } @@ -317,11 +522,13 @@ read_header(struct archive_read *a, struct archive_entry *entry) */ static int parse_file(struct archive_read *a, struct archive_entry *entry, - struct mtree *mtree, struct mtree_entry *mentry) + struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { - struct stat st; + const char *path; + struct stat st_storage, *st; struct mtree_entry *mp; - int r = ARCHIVE_OK, r1; + struct archive_entry *sparse_entry; + int r = ARCHIVE_OK, r1, parsed_kws, mismatched_type; mentry->used = 1; @@ -330,7 +537,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry, archive_entry_set_size(entry, 0); /* Parse options from this line. */ - r = parse_line(a, entry, mtree, mentry); + parsed_kws = 0; + r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); @@ -349,7 +557,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry, && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; - r1 = parse_line(a, entry, mtree, mp); + r1 = parse_line(a, entry, mtree, mp, + &parsed_kws); if (r1 < r) r = r1; } @@ -381,49 +590,126 @@ parse_file(struct archive_read *a, struct archive_entry *entry, * disk.) */ mtree->fd = -1; - if (archive_strlen(&mtree->contents_name) > 0) { - mtree->fd = open(mtree->contents_name.s, + if (archive_strlen(&mtree->contents_name) > 0) + path = mtree->contents_name.s; + else + path = archive_entry_pathname(entry); + + if (archive_entry_filetype(entry) == AE_IFREG || + archive_entry_filetype(entry) == AE_IFDIR) { + mtree->fd = open(path, O_RDONLY | O_BINARY); - if (mtree->fd < 0) { + if (mtree->fd == -1 && + (errno != ENOENT || + archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, - "Can't open content=\"%s\"", - mtree->contents_name.s); + "Can't open %s", path); r = ARCHIVE_WARN; } - } else if (archive_entry_filetype(entry) == AE_IFREG) { - mtree->fd = open(archive_entry_pathname(entry), - O_RDONLY | O_BINARY); } - /* - * If there is a contents file on disk, use that size; - * otherwise leave it as-is (it might have been set from - * the mtree size= keyword). - */ + st = &st_storage; if (mtree->fd >= 0) { - if (fstat(mtree->fd, &st) != 0) { + if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, - "could not stat %s", - archive_entry_pathname(entry)); + "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; - } else if ((st.st_mode & S_IFMT) != S_IFREG) { - archive_set_error(&a->archive, errno, - "%s is not a regular file", - archive_entry_pathname(entry)); - r = ARCHIVE_WARN; + st = NULL; + } + } else if (lstat(path, st) == -1) { + st = NULL; + } + + /* + * If there is a contents file on disk, use that size; + * otherwise leave it as-is (it might have been set from + * the mtree size= keyword). + */ + if (st != NULL) { + mismatched_type = 0; + if ((st->st_mode & S_IFMT) == S_IFREG && + archive_entry_filetype(entry) != AE_IFREG) + mismatched_type = 1; + if ((st->st_mode & S_IFMT) == S_IFLNK && + archive_entry_filetype(entry) != AE_IFLNK) + mismatched_type = 1; + if ((st->st_mode & S_IFSOCK) == S_IFSOCK && + archive_entry_filetype(entry) != AE_IFSOCK) + mismatched_type = 1; + if ((st->st_mode & S_IFMT) == S_IFCHR && + archive_entry_filetype(entry) != AE_IFCHR) + mismatched_type = 1; + if ((st->st_mode & S_IFMT) == S_IFBLK && + archive_entry_filetype(entry) != AE_IFBLK) + mismatched_type = 1; + if ((st->st_mode & S_IFMT) == S_IFDIR && + archive_entry_filetype(entry) != AE_IFDIR) + mismatched_type = 1; + if ((st->st_mode & S_IFMT) == S_IFIFO && + archive_entry_filetype(entry) != AE_IFIFO) + mismatched_type = 1; + + if (mismatched_type) { + if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "mtree specification has different type for %s", + archive_entry_pathname(entry)); + r = ARCHIVE_WARN; + } else { + *use_next = 1; + } /* Don't hold a non-regular file open. */ close(mtree->fd); mtree->fd = -1; - } else { - archive_entry_set_size(entry, st.st_size); - archive_entry_set_ino(entry, st.st_ino); - archive_entry_set_dev(entry, st.st_dev); - archive_entry_set_nlink(entry, st.st_nlink); + st = NULL; + return r; + } + } + + if (st != NULL) { + if ((parsed_kws & MTREE_HAS_DEVICE) == 0 && + (archive_entry_filetype(entry) == AE_IFCHR || + archive_entry_filetype(entry) == AE_IFBLK)) + archive_entry_set_rdev(entry, st->st_rdev); + if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0) + archive_entry_set_gid(entry, st->st_gid); + if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0) + archive_entry_set_uid(entry, st->st_uid); + if ((parsed_kws & MTREE_HAS_MTIME) == 0) { +#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtimespec.tv_nsec); +#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtim.tv_nsec); +#else + archive_entry_set_mtime(entry, st->st_mtime, 0); +#endif } + if ((parsed_kws & MTREE_HAS_NLINK) == 0) + archive_entry_set_nlink(entry, st->st_nlink); + if ((parsed_kws & MTREE_HAS_PERM) == 0) + archive_entry_set_perm(entry, st->st_mode); + if ((parsed_kws & MTREE_HAS_SIZE) == 0) + archive_entry_set_size(entry, st->st_size); + archive_entry_set_ino(entry, st->st_ino); + archive_entry_set_dev(entry, st->st_dev); + + archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); + } else if (parsed_kws & MTREE_HAS_OPTIONAL) { + /* + * Couldn't open the entry, stat it or the on-disk type + * didn't match. If this entry is optional, just ignore it + * and read the next header entry. + */ + *use_next = 1; + return ARCHIVE_OK; } + mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; @@ -435,36 +721,82 @@ parse_file(struct archive_read *a, struct archive_entry *entry, */ static int parse_line(struct archive_read *a, struct archive_entry *entry, - struct mtree *mtree, struct mtree_entry *mp) + struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) { - char *p, *q; + struct mtree_option *iter; int r = ARCHIVE_OK, r1; - p = mp->option_start; - while (p < mp->option_end) { - q = p + strlen(p); - r1 = parse_keyword(a, mtree, entry, p, q); + for (iter = mp->options; iter != NULL; iter = iter->next) { + r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); if (r1 < r) r = r1; - p = q + 1; + } + if ((*parsed_kws & MTREE_HAS_TYPE) == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Missing type keyword in mtree specification"); + return (ARCHIVE_WARN); } return (r); } /* + * Device entries have one of the following forms: + * raw dev_t + * format,major,minor[,subdevice] + * + * Just use major and minor, no translation etc is done + * between formats. + */ +static int +parse_device(struct archive *a, struct archive_entry *entry, char *val) +{ + char *comma1, *comma2; + + comma1 = strchr(val, ','); + if (comma1 == NULL) { + archive_entry_set_dev(entry, mtree_atol10(&val)); + return (ARCHIVE_OK); + } + ++comma1; + comma2 = strchr(comma1, ','); + if (comma1 == NULL) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed device attribute"); + return (ARCHIVE_WARN); + } + ++comma2; + archive_entry_set_rdevmajor(entry, mtree_atol(&comma1)); + archive_entry_set_rdevminor(entry, mtree_atol(&comma2)); + return (ARCHIVE_OK); +} + +/* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, - struct archive_entry *entry, char *key, char *end) + struct archive_entry *entry, struct mtree_option *option, int *parsed_kws) { - char *val; + char *val, *key; + + key = option->value; - if (end == key) - return (ARCHIVE_OK); if (*key == '\0') return (ARCHIVE_OK); + if (strcmp(key, "optional") == 0) { + *parsed_kws |= MTREE_HAS_OPTIONAL; + return (ARCHIVE_OK); + } + if (strcmp(key, "ignore") == 0) { + /* + * The mtree processing is not recursive, so + * recursion will only happen for explicitly listed + * entries. + */ + return (ARCHIVE_OK); + } + val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -483,38 +815,93 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_strcpy(&mtree->contents_name, val); break; } + if (strcmp(key, "cksum") == 0) + break; + case 'd': + if (strcmp(key, "device") == 0) { + *parsed_kws |= MTREE_HAS_DEVICE; + return parse_device(&a->archive, entry, val); + } + case 'f': + if (strcmp(key, "flags") == 0) { + *parsed_kws |= MTREE_HAS_FFLAGS; + archive_entry_copy_fflags_text(entry, val); + break; + } case 'g': if (strcmp(key, "gid") == 0) { + *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "gname") == 0) { + *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } case 'l': if (strcmp(key, "link") == 0) { - archive_entry_set_link(entry, val); + archive_entry_copy_symlink(entry, val); break; } case 'm': + if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) + break; if (strcmp(key, "mode") == 0) { - if (val[0] == '0') { + if (val[0] >= '0' && val[0] <= '9') { + *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, mtree_atol8(&val)); - } else + } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic mode \"%s\" unsupported", val); + return ARCHIVE_WARN; + } break; } + case 'n': + if (strcmp(key, "nlink") == 0) { + *parsed_kws |= MTREE_HAS_NLINK; + archive_entry_set_nlink(entry, mtree_atol10(&val)); + break; + } + case 'r': + if (strcmp(key, "rmd160") == 0 || + strcmp(key, "rmd160digest") == 0) + break; case 's': + if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) + break; + if (strcmp(key, "sha256") == 0 || + strcmp(key, "sha256digest") == 0) + break; + if (strcmp(key, "sha384") == 0 || + strcmp(key, "sha384digest") == 0) + break; + if (strcmp(key, "sha512") == 0 || + strcmp(key, "sha512digest") == 0) + break; if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol10(&val)); break; } case 't': + if (strcmp(key, "tags") == 0) { + /* + * Comma delimited list of tags. + * Ignore the tags for now, but the interface + * should be extended to allow inclusion/exclusion. + */ + break; + } + if (strcmp(key, "time") == 0) { + *parsed_kws |= MTREE_HAS_MTIME; + archive_entry_set_mtime(entry, mtree_atol10(&val), 0); + break; + } if (strcmp(key, "type") == 0) { + *parsed_kws |= MTREE_HAS_TYPE; switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { @@ -554,16 +941,14 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_set_filetype(entry, mtree->filetype); break; } - if (strcmp(key, "time") == 0) { - archive_entry_set_mtime(entry, mtree_atol10(&val), 0); - break; - } case 'u': if (strcmp(key, "uid") == 0) { + *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "uname") == 0) { + *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } @@ -643,6 +1028,13 @@ parse_escapes(char *src, struct mtree_entry *mentry) char *dest = src; char c; + /* + * The current directory is somewhat special, it should be archived + * only once as it will confuse extraction otherwise. + */ + if (strcmp(src, ".") == 0) + mentry->full = 1; + while (*src != '\0') { c = *src++; if (c == '/' && mentry != NULL) @@ -725,6 +1117,66 @@ mtree_atol10(char **p) } /* + * Note that this implementation does not (and should not!) obey + * locale settings; you cannot simply substitute strtol here, since + * it does obey locale. + */ +static int64_t +mtree_atol16(char **p) +{ + int64_t l, limit, last_digit_limit; + int base, digit, sign; + + base = 16; + limit = INT64_MAX / base; + last_digit_limit = INT64_MAX % base; + + if (**p == '-') { + sign = -1; + ++(*p); + } else + sign = 1; + + l = 0; + if (**p >= '0' && **p <= '9') + digit = **p - '0'; + else if (**p >= 'a' && **p <= 'f') + digit = **p - 'a' + 10; + else if (**p >= 'A' && **p <= 'F') + digit = **p - 'A' + 10; + else + digit = -1; + while (digit >= 0 && digit < base) { + if (l > limit || (l == limit && digit > last_digit_limit)) { + l = UINT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + if (**p >= '0' && **p <= '9') + digit = **p - '0'; + else if (**p >= 'a' && **p <= 'f') + digit = **p - 'a' + 10; + else if (**p >= 'A' && **p <= 'F') + digit = **p - 'A' + 10; + else + digit = -1; + } + return (sign < 0) ? -l : l; +} + +static int64_t +mtree_atol(char **p) +{ + if (**p != '0') + return mtree_atol10(p); + if ((*p)[1] == 'x' || (*p)[1] == 'X') { + *p += 2; + return mtree_atol16(p); + } + return mtree_atol8(p); +} + +/* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index a0c4342..f25916b 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -145,6 +145,8 @@ struct sparse_block { struct tar { struct archive_string acl_text; struct archive_string entry_pathname; + /* For "GNU.sparse.name" and other similar path extensions. */ + struct archive_string entry_pathname_override; struct archive_string entry_linkpath; struct archive_string entry_uname; struct archive_string entry_gname; @@ -272,6 +274,7 @@ archive_read_format_tar_cleanup(struct archive_read *a) gnu_clear_sparse_list(tar); archive_string_free(&tar->acl_text); archive_string_free(&tar->entry_pathname); + archive_string_free(&tar->entry_pathname_override); archive_string_free(&tar->entry_linkpath); archive_string_free(&tar->entry_uname); archive_string_free(&tar->entry_gname); @@ -1174,7 +1177,6 @@ pax_header(struct archive_read *a, struct tar *tar, size_t attr_length, l, line_length; char *line, *p; char *key, *value; - wchar_t *wp; int err, err2; attr_length = strlen(attr); @@ -1182,6 +1184,7 @@ pax_header(struct archive_read *a, struct tar *tar, archive_string_empty(&(tar->entry_gname)); archive_string_empty(&(tar->entry_linkpath)); archive_string_empty(&(tar->entry_pathname)); + archive_string_empty(&(tar->entry_pathname_override)); archive_string_empty(&(tar->entry_uname)); err = ARCHIVE_OK; while (attr_length > 0) { @@ -1257,13 +1260,13 @@ pax_header(struct archive_read *a, struct tar *tar, if (tar->pax_hdrcharset_binary) archive_entry_copy_gname(entry, value); else { - wp = utf8_decode(tar, value, strlen(value)); - if (wp == NULL) { - archive_entry_copy_gname(entry, value); - if (err > ARCHIVE_WARN) - err = ARCHIVE_WARN; - } else - archive_entry_copy_gname_w(entry, wp); + if (!archive_entry_update_gname_utf8(entry, value)) { + err = ARCHIVE_WARN; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Gname in pax header can't " + "be converted to current locale."); + } } } if (archive_strlen(&(tar->entry_linkpath)) > 0) { @@ -1271,27 +1274,40 @@ pax_header(struct archive_read *a, struct tar *tar, if (tar->pax_hdrcharset_binary) archive_entry_copy_link(entry, value); else { - wp = utf8_decode(tar, value, strlen(value)); - if (wp == NULL) { - archive_entry_copy_link(entry, value); - if (err > ARCHIVE_WARN) - err = ARCHIVE_WARN; - } else - archive_entry_copy_link_w(entry, wp); + if (!archive_entry_update_link_utf8(entry, value)) { + err = ARCHIVE_WARN; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Linkname in pax header can't " + "be converted to current locale."); + } } } - if (archive_strlen(&(tar->entry_pathname)) > 0) { + /* + * Some extensions (such as the GNU sparse file extensions) + * deliberately store a synthetic name under the regular 'path' + * attribute and the real file name under a different attribute. + * Since we're supposed to not care about the order, we + * have no choice but to store all of the various filenames + * we find and figure it all out afterwards. This is the + * figuring out part. + */ + value = NULL; + if (archive_strlen(&(tar->entry_pathname_override)) > 0) + value = tar->entry_pathname_override.s; + else if (archive_strlen(&(tar->entry_pathname)) > 0) value = tar->entry_pathname.s; + if (value != NULL) { if (tar->pax_hdrcharset_binary) archive_entry_copy_pathname(entry, value); else { - wp = utf8_decode(tar, value, strlen(value)); - if (wp == NULL) { - archive_entry_copy_pathname(entry, value); - if (err > ARCHIVE_WARN) - err = ARCHIVE_WARN; - } else - archive_entry_copy_pathname_w(entry, wp); + if (!archive_entry_update_pathname_utf8(entry, value)) { + err = ARCHIVE_WARN; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname in pax header can't be " + "converted to current locale."); + } } } if (archive_strlen(&(tar->entry_uname)) > 0) { @@ -1299,13 +1315,13 @@ pax_header(struct archive_read *a, struct tar *tar, if (tar->pax_hdrcharset_binary) archive_entry_copy_uname(entry, value); else { - wp = utf8_decode(tar, value, strlen(value)); - if (wp == NULL) { - archive_entry_copy_uname(entry, value); - if (err > ARCHIVE_WARN) - err = ARCHIVE_WARN; - } else - archive_entry_copy_uname_w(entry, wp); + if (!archive_entry_update_uname_utf8(entry, value)) { + err = ARCHIVE_WARN; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Uname in pax header can't " + "be converted to current locale."); + } } } return (err); @@ -1415,11 +1431,13 @@ pax_attribute(struct tar *tar, struct archive_entry *entry, tar->sparse_gnu_pending = 1; } if (strcmp(key, "GNU.sparse.name") == 0) { - wp = utf8_decode(tar, value, strlen(value)); - if (wp != NULL) - archive_entry_copy_pathname_w(entry, wp); - else - archive_entry_copy_pathname(entry, value); + /* + * The real filename; when storing sparse + * files, GNU tar puts a synthesized name into + * the regular 'path' attribute in an attempt + * to limit confusion. ;-) + */ + archive_strcpy(&(tar->entry_pathname_override), value); } if (strcmp(key, "GNU.sparse.realsize") == 0) { tar->realsize = tar_atol10(value, strlen(value)); @@ -1455,9 +1473,7 @@ pax_attribute(struct tar *tar, struct archive_entry *entry, archive_entry_set_rdevminor(entry, tar_atol10(value, strlen(value))); } else if (strcmp(key, "SCHILY.fflags")==0) { - wp = utf8_decode(tar, value, strlen(value)); - /* TODO: if (wp == NULL) */ - archive_entry_copy_fflags_text_w(entry, wp); + archive_entry_copy_fflags_text(entry, value); } else if (strcmp(key, "SCHILY.dev")==0) { archive_entry_set_dev(entry, tar_atol10(value, strlen(value))); @@ -2287,7 +2303,7 @@ base64_decode(const char *s, size_t len, size_t *out_len) /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ - out = (char *)malloc((len * 3 + 3) / 4); + out = (char *)malloc(len - len / 4 + 1); if (out == NULL) { *out_len = 0; return (NULL); diff --git a/lib/libarchive/archive_read_support_format_zip.c b/lib/libarchive/archive_read_support_format_zip.c index f18c3c2..d7cd074 100644 --- a/lib/libarchive/archive_read_support_format_zip.c +++ b/lib/libarchive/archive_read_support_format_zip.c @@ -162,11 +162,9 @@ archive_read_support_format_zip(struct archive *_a) static int archive_read_format_zip_bid(struct archive_read *a) { - int bid = 0; const char *p; - - if (a->archive.archive_format == ARCHIVE_FORMAT_ZIP) - bid += 1; + const void *buff; + size_t bytes_avail; if ((p = __archive_read_ahead(a, 4)) == NULL) return (-1); @@ -184,9 +182,104 @@ archive_read_format_zip_bid(struct archive_read *a) || (p[2] == '0' && p[3] == '0')) return (30); } + + /* + * Attempt to handle self-extracting archives + * by noting a PE header and searching forward + * up to 64k for a 'PK\003\004' marker. + */ + if (p[0] == 'M' && p[1] == 'Z') { + /* + * TODO: Additional checks that this really is a PE + * file before we invoke the 128k lookahead below. + * No point in allocating a bigger lookahead buffer + * if we don't need to. + */ + /* + * TODO: Of course, the compression layer lookahead + * buffers aren't dynamically sized yet; they should be. + */ + bytes_avail = (a->decompressor->read_ahead)(a, &buff, 128*1024); + p = (const char *)buff; + + /* + * TODO: Optimize by jumping forward based on values + * in the PE header. Note that we don't need to be + * exact, but we mustn't skip too far. The search + * below will compensate if we undershoot. Skipping + * will also reduce the chance of false positives + * (which is not really all that high to begin with, + * so maybe skipping isn't really necessary). + */ + + while (p < bytes_avail + (const char *)buff) { + if (p[0] == 'P' && p[1] == 'K' /* "PK" signature */ + && p[2] == 3 && p[3] == 4 /* File entry */ + && p[8] == 8 /* compression == deflate */ + && p[9] == 0 /* High byte of compression */ + ) + { + return (30); + } + ++p; + } + } + return (0); } +/* + * Search forward for a "PK\003\004" file header. This handles the + * case of self-extracting archives, where there is an executable + * prepended to the ZIP archive. + */ +static int +skip_sfx(struct archive_read *a) +{ + const void *h; + const char *p, *q; + size_t skip, bytes; + + /* + * TODO: We should be able to skip forward by a bunch + * by lifting some values from the PE header. We don't + * need to be exact (we're still going to search forward + * to find the header), but it will speed things up and + * reduce the chance of a false positive. + */ + for (;;) { + bytes = (a->decompressor->read_ahead)(a, &h, 4096); + if (bytes < 4) + return (ARCHIVE_FATAL); + p = h; + q = p + bytes; + + /* + * Scan ahead until we find something that looks + * like the zip header. + */ + while (p + 4 < q) { + switch (p[3]) { + case '\004': + /* TODO: Additional verification here. */ + if (memcmp("PK\003\004", p, 4) == 0) { + skip = p - (const char *)h; + (a->decompressor->consume)(a, skip); + return (ARCHIVE_OK); + } + p += 4; + break; + case '\003': p += 1; break; + case 'K': p += 2; break; + case 'P': p += 3; break; + default: p += 4; break; + } + } + skip = p - (const char *)h; + (a->decompressor->consume)(a, skip); + } +} + static int archive_read_format_zip_read_header(struct archive_read *a, struct archive_entry *entry) @@ -194,6 +287,7 @@ archive_read_format_zip_read_header(struct archive_read *a, const void *h; const char *signature; struct zip *zip; + int r = ARCHIVE_OK, r1; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) @@ -209,6 +303,16 @@ archive_read_format_zip_read_header(struct archive_read *a, return (ARCHIVE_FATAL); signature = (const char *)h; + if (signature[0] == 'M' && signature[1] == 'Z') { + /* This is an executable? Must be self-extracting... */ + r = skip_sfx(a); + if (r < ARCHIVE_WARN) + return (r); + if ((h = __archive_read_ahead(a, 4)) == NULL) + return (ARCHIVE_FATAL); + signature = (const char *)h; + } + if (signature[0] != 'P' || signature[1] != 'K') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad ZIP file"); @@ -239,7 +343,10 @@ archive_read_format_zip_read_header(struct archive_read *a, if (signature[2] == '\003' && signature[3] == '\004') { /* Regular file entry. */ - return (zip_read_file_header(a, entry, zip)); + r1 = zip_read_file_header(a, entry, zip); + if (r1 != ARCHIVE_OK) + return (r1); + return (r); } if (signature[2] == '\005' && signature[3] == '\006') { diff --git a/lib/libarchive/archive_string.c b/lib/libarchive/archive_string.c index 8e1d11c..ba7605a 100644 --- a/lib/libarchive/archive_string.c +++ b/lib/libarchive/archive_string.c @@ -37,6 +37,17 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include <string.h> #endif +#ifdef HAVE_WCHAR_H +#include <wchar.h> +#endif + +#ifdef __sgi +/* + * The following prototype is missing on IRXI, + * even though the function is implemented in libc. + */ +size_t wcrtomb(char *, wchar_t, mbstate_t *); +#endif #include "archive_private.h" #include "archive_string.h" @@ -55,11 +66,15 @@ __archive_string_append(struct archive_string *as, const char *p, size_t s) void __archive_string_copy(struct archive_string *dest, struct archive_string *src) { - if (__archive_string_ensure(dest, src->length + 1) == NULL) - __archive_errx(1, "Out of memory"); - memcpy(dest->s, src->s, src->length); - dest->length = src->length; - dest->s[dest->length] = 0; + if (src->length == 0) + dest->length = 0; + else { + if (__archive_string_ensure(dest, src->length + 1) == NULL) + __archive_errx(1, "Out of memory"); + memcpy(dest->s, src->s, src->length); + dest->length = src->length; + dest->s[dest->length] = 0; + } } void @@ -67,21 +82,52 @@ __archive_string_free(struct archive_string *as) { as->length = 0; as->buffer_length = 0; - if (as->s != NULL) + if (as->s != NULL) { free(as->s); + as->s = NULL; + } } /* Returns NULL on any allocation failure. */ struct archive_string * __archive_string_ensure(struct archive_string *as, size_t s) { + /* If buffer is already big enough, don't reallocate. */ if (as->s && (s <= as->buffer_length)) return (as); + /* + * Growing the buffer at least exponentially ensures that + * append operations are always linear in the number of + * characters appended. Using a smaller growth rate for + * larger buffers reduces memory waste somewhat at the cost of + * a larger constant factor. + */ if (as->buffer_length < 32) + /* Start with a minimum 32-character buffer. */ as->buffer_length = 32; - while (as->buffer_length < s) + else if (as->buffer_length < 8192) + /* Buffers under 8k are doubled for speed. */ as->buffer_length *= 2; + else { + /* Buffers 8k and over grow by at least 25% each time. */ + size_t old_length = as->buffer_length; + as->buffer_length = (as->buffer_length * 5) / 4; + /* Be safe: If size wraps, release buffer and return NULL. */ + if (as->buffer_length < old_length) { + free(as->s); + as->s = NULL; + return (NULL); + } + } + /* + * The computation above is a lower limit to how much we'll + * grow the buffer. In any case, we have to grow it enough to + * hold the request. + */ + if (as->buffer_length < s) + as->buffer_length = s; + /* Now we can reallocate the buffer. */ as->s = (char *)realloc(as->s, as->buffer_length); if (as->s == NULL) return (NULL); @@ -124,3 +170,206 @@ __archive_strappend_int(struct archive_string *as, int d, int base) __archive_strappend_char(as, digits[d % base]); return (as); } + +/* + * Home-grown wcrtomb for UTF-8. + */ +static size_t +my_wcrtomb_utf8(char *p, wchar_t wc, mbstate_t *s) +{ + (void)s; /* UNUSED */ + + if (p == NULL) + return (0); + if (wc <= 0x7f) { + p[0] = (char)wc; + return (1); + } + if (wc <= 0x7ff) { + p[0] = 0xc0 | ((wc >> 6) & 0x1f); + p[1] = 0x80 | (wc & 0x3f); + return (2); + } + if (wc <= 0xffff) { + p[0] = 0xe0 | ((wc >> 12) & 0x0f); + p[1] = 0x80 | ((wc >> 6) & 0x3f); + p[2] = 0x80 | (wc & 0x3f); + return (3); + } + if (wc <= 0x1fffff) { + p[0] = 0xf0 | ((wc >> 18) & 0x07); + p[1] = 0x80 | ((wc >> 12) & 0x3f); + p[2] = 0x80 | ((wc >> 6) & 0x3f); + p[3] = 0x80 | (wc & 0x3f); + return (4); + } + /* Unicode has no codes larger than 0x1fffff. */ + /* + * Awkward point: UTF-8 <-> wchar_t conversions + * can actually fail. + */ + return ((size_t)-1); +} + +static int +my_wcstombs(struct archive_string *as, const wchar_t *w, + size_t (*func)(char *, wchar_t, mbstate_t *)) +{ + size_t n; + char *p; + mbstate_t shift_state; + char buff[256]; + + /* + * Convert one wide char at a time into 'buff', whenever that + * fills, append it to the string. + */ + p = buff; + wcrtomb(NULL, L'\0', &shift_state); + while (*w != L'\0') { + /* Flush the buffer when we have <=16 bytes free. */ + /* (No encoding has a single character >16 bytes.) */ + if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) { + *p = '\0'; + archive_strcat(as, buff); + p = buff; + } + n = (*func)(p, *w++, &shift_state); + if (n == (size_t)-1) + return (-1); + p += n; + } + *p = '\0'; + archive_strcat(as, buff); + return (0); +} + +/* + * Translates a wide character string into UTF-8 and appends + * to the archive_string. Note: returns NULL if conversion fails. + */ +struct archive_string * +__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w) +{ + if (my_wcstombs(as, w, my_wcrtomb_utf8)) + return (NULL); + return (as); +} + +/* + * Translates a wide character string into current locale character set + * and appends to the archive_string. Note: returns NULL if conversion + * fails. + * + * TODO: use my_wcrtomb_utf8 if !HAVE_WCRTOMB (add configure logic first!) + */ +struct archive_string * +__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w) +{ + if (my_wcstombs(as, w, wcrtomb)) + return (NULL); + return (as); +} + + +/* + * Home-grown mbrtowc for UTF-8. Some systems lack UTF-8 + * (or even lack mbrtowc()) and we need UTF-8 support for pax + * format. So please don't replace this with a call to the + * standard mbrtowc() function! + */ +static size_t +my_mbrtowc_utf8(wchar_t *pwc, const char *s, size_t n, mbstate_t *ps) +{ + int ch; + + /* + * This argument is here to make the prototype identical to the + * standard mbrtowc(), so I can build generic string processors + * that just accept a pointer to a suitable mbrtowc() function. + */ + (void)ps; /* UNUSED */ + + /* Standard behavior: a NULL value for 's' just resets shift state. */ + if (s == NULL) + return (0); + /* If length argument is zero, don't look at the first character. */ + if (n <= 0) + return ((size_t)-2); + + /* + * Decode 1-4 bytes depending on the value of the first byte. + */ + ch = (unsigned char)*s; + if (ch == 0) { + return (0); /* Standard: return 0 for end-of-string. */ + } + if ((ch & 0x80) == 0) { + *pwc = ch & 0x7f; + return (1); + } + if ((ch & 0xe0) == 0xc0) { + if (n < 2) + return ((size_t)-2); + if ((s[1] & 0xc0) != 0x80) return (size_t)-1; + *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); + return (2); + } + if ((ch & 0xf0) == 0xe0) { + if (n < 3) + return ((size_t)-2); + if ((s[1] & 0xc0) != 0x80) return (size_t)-1; + if ((s[2] & 0xc0) != 0x80) return (size_t)-1; + *pwc = ((ch & 0x0f) << 12) + | ((s[1] & 0x3f) << 6) + | (s[2] & 0x3f); + return (3); + } + if ((ch & 0xf8) == 0xf0) { + if (n < 4) + return ((size_t)-2); + if ((s[1] & 0xc0) != 0x80) return (size_t)-1; + if ((s[2] & 0xc0) != 0x80) return (size_t)-1; + if ((s[3] & 0xc0) != 0x80) return (size_t)-1; + *pwc = ((ch & 0x07) << 18) + | ((s[1] & 0x3f) << 12) + | ((s[2] & 0x3f) << 6) + | (s[3] & 0x3f); + return (4); + } + /* Invalid first byte. */ + return ((size_t)-1); +} + +/* + * Return a wide-character string by converting this archive_string + * from UTF-8. + */ +wchar_t * +__archive_string_utf8_w(struct archive_string *as) +{ + wchar_t *ws, *dest; + const char *src; + size_t n; + int err; + + ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t)); + if (ws == NULL) + __archive_errx(1, "Out of memory"); + err = 0; + dest = ws; + src = as->s; + while (*src != '\0') { + n = my_mbrtowc_utf8(dest, src, 8, NULL); + if (n == 0) + break; + if (n == (size_t)-1 || n == (size_t)-2) { + free(ws); + return (NULL); + } + dest++; + src += n; + } + *dest++ = L'\0'; + return (ws); +} diff --git a/lib/libarchive/archive_string.h b/lib/libarchive/archive_string.h index 271eae3..84f3669 100644 --- a/lib/libarchive/archive_string.h +++ b/lib/libarchive/archive_string.h @@ -33,6 +33,9 @@ #ifdef HAVE_STRING_H #include <string.h> #endif +#ifdef HAVE_WCHAR_H +#include <wchar.h> +#endif /* * Basic resizable/reusable string support a la Java's "StringBuffer." @@ -60,16 +63,22 @@ struct archive_string * __archive_strappend_char(struct archive_string *, char); #define archive_strappend_char __archive_strappend_char -/* Append a char to an archive_string using UTF8. */ -struct archive_string * -__archive_strappend_char_UTF8(struct archive_string *, int); -#define archive_strappend_char_UTF8 __archive_strappend_char_UTF8 - /* Append an integer in the specified base (2 <= base <= 16). */ struct archive_string * __archive_strappend_int(struct archive_string *as, int d, int base); #define archive_strappend_int __archive_strappend_int +/* Convert a wide-char string to UTF-8 and append the result. */ +struct archive_string * +__archive_strappend_w_utf8(struct archive_string *, const wchar_t *); +#define archive_strappend_w_utf8 __archive_strappend_w_utf8 + +/* Convert a wide-char string to current locale and append the result. */ +/* Returns NULL if conversion fails. */ +struct archive_string * +__archive_strappend_w_mbs(struct archive_string *, const wchar_t *); +#define archive_strappend_w_mbs __archive_strappend_w_mbs + /* Basic append operation. */ struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s); @@ -95,7 +104,7 @@ __archive_strncat(struct archive_string *, const char *, size_t); /* Copy a C string to an archive_string, resizing as necessary. */ #define archive_strcpy(as,p) \ - ((as)->length = 0, __archive_string_append((as), (p), strlen(p))) + ((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p))) /* Copy a C string to an archive_string with limit, resizing as necessary. */ #define archive_strncpy(as,p,l) \ @@ -119,4 +128,9 @@ void __archive_string_vsprintf(struct archive_string *, const char *, void __archive_string_sprintf(struct archive_string *, const char *, ...); #define archive_string_sprintf __archive_string_sprintf +/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it. + * Returns NULL if conversion failed in any way. */ +wchar_t *__archive_string_utf8_w(struct archive_string *as); + + #endif diff --git a/lib/libarchive/archive_util.c b/lib/libarchive/archive_util.c index cad14f4..a29be34 100644 --- a/lib/libarchive/archive_util.c +++ b/lib/libarchive/archive_util.c @@ -77,32 +77,10 @@ archive_version_number(void) return (ARCHIVE_VERSION_NUMBER); } -/* - * Format a version string of the form "libarchive x.y.z", where x, y, - * z are the correct parts of the version ID from - * archive_version_number(). - * - * I used to do all of this at build time in shell scripts but that - * proved to be a portability headache. - */ - const char * archive_version_string(void) { - static char buff[128]; - struct archive_string as; - int n; - - if (buff[0] == '\0') { - n = archive_version_number(); - memset(&as, 0, sizeof(as)); - archive_string_sprintf(&as, "libarchive %d.%d.%d", - n / 1000000, (n / 1000) % 1000, n % 1000); - strncpy(buff, as.s, sizeof(buff)); - buff[sizeof(buff) - 1] = '\0'; - archive_string_free(&as); - } - return (buff); + return (ARCHIVE_VERSION_STRING); } int diff --git a/lib/libarchive/archive_write.3 b/lib/libarchive/archive_write.3 index 060d000..2aa9cef 100644 --- a/lib/libarchive/archive_write.3 +++ b/lib/libarchive/archive_write.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 19, 2006 +.Dd May 11, 2008 .Dt archive_write 3 .Os .Sh NAME @@ -39,6 +39,7 @@ .Nm archive_write_set_bytes_per_block , .Nm archive_write_set_bytes_in_last_block , .Nm archive_write_set_compression_bzip2 , +.Nm archive_write_set_compression_compress , .Nm archive_write_set_compression_gzip , .Nm archive_write_set_compression_none , .Nm archive_write_set_compression_program , @@ -66,6 +67,8 @@ .Ft int .Fn archive_write_set_compression_bzip2 "struct archive *" .Ft int +.Fn archive_write_set_compression_compress "struct archive *" +.Ft int .Fn archive_write_set_compression_gzip "struct archive *" .Ft int .Fn archive_write_set_compression_none "struct archive *" @@ -197,6 +200,7 @@ the pax extended header for most normal files. In most cases, this will result in ordinary ustar archives. .It Xo .Fn archive_write_set_compression_bzip2 , +.Fn archive_write_set_compression_compress , .Fn archive_write_set_compression_gzip , .Fn archive_write_set_compression_none .Xc diff --git a/lib/libarchive/archive_write_disk.3 b/lib/libarchive/archive_write_disk.3 index 1bcd70d..5eaa027 100644 --- a/lib/libarchive/archive_write_disk.3 +++ b/lib/libarchive/archive_write_disk.3 @@ -170,6 +170,10 @@ Note that paths ending in .Pa .. always cause an error, regardless of this flag. .El +.It Cm ARCHIVE_EXTRACT_SPARSE +Scan data for blocks of NUL bytes and try to recreate them with holes. +This results in sparse files, independent of whether the archive format +supports or uses them. .It Xo .Fn archive_write_disk_set_group_lookup , .Fn archive_write_disk_set_user_lookup diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c index 394df80..8852715 100644 --- a/lib/libarchive/archive_write_disk.c +++ b/lib/libarchive/archive_write_disk.c @@ -187,6 +187,8 @@ struct archive_write_disk { /* UID/GID to use in restoring this entry. */ uid_t uid; gid_t gid; + /* Last offset written to disk. */ + off_t last_offset; }; /* @@ -242,6 +244,31 @@ 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 int +_archive_write_disk_lazy_stat(struct archive_write_disk *a) +{ + if (a->pst != NULL) { + /* Already have stat() data available. */ + return (ARCHIVE_OK); + } +#ifdef HAVE_FSTAT + if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { + a->pst = &a->st; + return (ARCHIVE_OK); + } +#endif + /* + * XXX At this point, symlinks should not be hit, otherwise + * XXX a race occured. Do we want to check explicitly for that? + */ + if (lstat(a->name, &a->st) == 0) { + a->pst = &a->st; + return (ARCHIVE_OK); + } + archive_set_error(&a->archive, errno, "Couldn't stat file"); + return (ARCHIVE_WARN); +} + static struct archive_vtable * archive_write_disk_vtable(void) { @@ -294,7 +321,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { r = _archive_write_finish_entry(&a->archive); - if (r != ARCHIVE_OK) + if (r == ARCHIVE_FATAL) return (r); } @@ -308,6 +335,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) } a->entry = archive_entry_clone(entry); a->fd = -1; + a->last_offset = 0; a->offset = 0; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); @@ -463,6 +491,7 @@ _archive_write_data_block(struct archive *_a, { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t bytes_written = 0; + ssize_t block_size, bytes_to_write; int r = ARCHIVE_OK; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -473,31 +502,53 @@ _archive_write_data_block(struct archive *_a, } 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; + if (a->flags & ARCHIVE_EXTRACT_SPARSE) { + if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + return (r); + block_size = a->pst->st_blksize; + } else + block_size = -1; + + if ((off_t)(offset + size) > a->filesize) { + size = (size_t)(a->filesize - a->offset); + archive_set_error(&a->archive, 0, + "Write request too large"); + r = ARCHIVE_WARN; } /* Write the data. */ - while (size > 0 && a->offset < a->filesize) { - if ((off_t)(a->offset + size) > a->filesize) { - size = (size_t)(a->filesize - a->offset); - archive_set_error(&a->archive, errno, - "Write request too large"); - r = ARCHIVE_WARN; - } + while (size > 0) { + if (block_size != -1) { + const char *buf; + + for (buf = buff; size; ++buf, --size, ++offset) { + if (*buf != '\0') + break; + } + if (size == 0) + break; + bytes_to_write = block_size - offset % block_size; + buff = buf; + } else + bytes_to_write = size; + /* Seek if necessary to the specified offset. */ + if (offset != a->last_offset) { + if (lseek(a->fd, offset, SEEK_SET) < 0) { + archive_set_error(&a->archive, errno, "Seek failed"); + return (ARCHIVE_FATAL); + } + } bytes_written = write(a->fd, buff, size); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } + buff = (const char *)buff + bytes_written; size -= bytes_written; - a->offset += bytes_written; + offset += bytes_written; + a->last_offset = a->offset = offset; } + a->offset = offset; return (r); } @@ -505,7 +556,6 @@ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; - off_t offset; int r; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -513,11 +563,10 @@ _archive_write_data(struct archive *_a, const void *buff, size_t size) if (a->fd < 0) return (ARCHIVE_OK); - offset = a->offset; r = _archive_write_data_block(_a, buff, size, a->offset); if (r < ARCHIVE_OK) return (r); - return (a->offset - offset); + return size; } static int @@ -533,6 +582,34 @@ _archive_write_finish_entry(struct archive *_a) return (ARCHIVE_OK); archive_clear_error(&a->archive); + if (a->last_offset != a->filesize && a->fd >= 0) { + if (ftruncate(a->fd, a->filesize) == -1 && + a->filesize == 0) { + archive_set_error(&a->archive, errno, + "File size could not be restored"); + return (ARCHIVE_FAILED); + } + /* + * Explicitly stat the file as some platforms might not + * implement the XSI option to extend files via ftruncate. + */ + a->pst = NULL; + if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + return (ret); + if (a->st.st_size != a->filesize) { + const char nul = '\0'; + if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) { + archive_set_error(&a->archive, errno, "Seek failed"); + return (ARCHIVE_FATAL); + } + if (write(a->fd, &nul, 1) < 0) { + archive_set_error(&a->archive, errno, + "Write to restore size failed"); + return (ARCHIVE_FATAL); + } + } + } + /* Restore metadata. */ /* @@ -723,11 +800,13 @@ restore_entry(struct archive_write_disk *a) * object isn't a dir. */ if (unlink(a->name) == 0) { - /* We removed it, we're done. */ + /* We removed it, reset cached stat. */ + a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ + a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, @@ -768,6 +847,7 @@ restore_entry(struct archive_write_disk *a) "Can't remove already-existing dir"); return (ARCHIVE_WARN); } + a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { @@ -807,6 +887,7 @@ restore_entry(struct archive_write_disk *a) "Can't unlink already-existing object"); return (ARCHIVE_WARN); } + a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { @@ -866,8 +947,18 @@ create_filesystem_object(struct archive_write_disk *a) * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. + * + * If the hardlink was successfully created and + * the archive doesn't have carry data for it, + * consider it to be non-authoritive for meta data. + * This is consistent with GNU tar and BSD pax. + * If the hardlink does carry data, let the last + * archive entry decide ownership. */ - if (r == 0 && a->filesize > 0) { + if (r == 0 && a->filesize == 0) { + a->todo = 0; + a->deferred = 0; + } if (r == 0 && a->filesize > 0) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); if (a->fd < 0) r = errno; @@ -1203,6 +1294,7 @@ check_symlinks(struct archive_write_disk *a) pn[0] = c; return (ARCHIVE_WARN); } + a->pst = NULL; /* * Even if we did remove it, a warning * is in order. The warning is silly, @@ -1226,6 +1318,7 @@ check_symlinks(struct archive_write_disk *a) pn[0] = c; return (ARCHIVE_WARN); } + a->pst = NULL; } else { archive_set_error(&a->archive, 0, "Cannot extract through symlink %s", @@ -1608,19 +1701,8 @@ set_mode(struct archive_write_disk *a, int mode) * 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 (a->fd >= 0 && fstat(a->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 ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { @@ -1783,6 +1865,8 @@ 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 r; + (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); @@ -1793,15 +1877,8 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, * 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); - } + if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; diff --git a/lib/libarchive/archive_write_set_compression_program.c b/lib/libarchive/archive_write_set_compression_program.c index 4f28a0f..b910ef6 100644 --- a/lib/libarchive/archive_write_set_compression_program.c +++ b/lib/libarchive/archive_write_set_compression_program.c @@ -27,6 +27,23 @@ __FBSDID("$FreeBSD$"); +/* This capability is only available on POSIX systems. */ +#if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) + +/* + * On non-Posix systems, allow the program to build, but choke if + * this function is actually invoked. + */ +int +archive_write_set_compression_program(struct archive *_a, const char *cmd) +{ + archive_set_error(_a, -1, + "External compression programs not supported on this platform"); + return (ARCHIVE_FATAL); +} + +#else + #ifdef HAVE_SYS_WAIT_H # include <sys/wait.h> #endif @@ -320,3 +337,5 @@ cleanup: free(state); return (ret); } + +#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/archive_write_set_format_ar.c b/lib/libarchive/archive_write_set_format_ar.c index 3473c46..e6a9d90 100644 --- a/lib/libarchive/archive_write_set_format_ar.c +++ b/lib/libarchive/archive_write_set_format_ar.c @@ -142,12 +142,15 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) struct ar_w *ar; const char *pathname; const char *filename; + int64_t size; ret = 0; append_fn = 0; ar = (struct ar_w *)a->format_data; ar->is_strtab = 0; filename = NULL; + size = archive_entry_size(entry); + /* * Reject files with empty name. @@ -285,8 +288,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_WARN); } append_fn = 1; - archive_entry_set_size(entry, - archive_entry_size(entry) + strlen(filename)); + size += strlen(filename); } } @@ -322,8 +324,7 @@ stat: } size: - if (format_decimal(archive_entry_size(entry), buff + AR_size_offset, - AR_size_size)) { + if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); return (ARCHIVE_WARN); @@ -333,7 +334,7 @@ size: if (ret != ARCHIVE_OK) return (ret); - ar->entry_bytes_remaining = archive_entry_size(entry); + ar->entry_bytes_remaining = size; ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index 6f05a99..b40aec5 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -386,7 +386,7 @@ archive_write_pax_header(struct archive_write *a, const char *p; char *t; const wchar_t *wp; - const char *suffix_start; + const char *suffix; int need_extension, r, ret; struct pax *pax; const char *hdrcharset = NULL; @@ -496,34 +496,73 @@ archive_write_pax_header(struct archive_write *a, if (hdrcharset != NULL) add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset); - /* - * Determining whether or not the name is too big is ugly - * because of the rules for dividing names between 'name' and - * 'prefix' fields. Here, I pick out the longest possible - * suffix, then test whether the remaining prefix is too long. - */ - if (strlen(path) <= 100) /* Short enough for just 'name' field */ - suffix_start = path; /* Record a zero-length prefix */ - else - /* Find the largest suffix that fits in 'name' field. */ - suffix_start = strchr(path + strlen(path) - 100 - 1, '/'); /* * If name is too long, or has non-ASCII characters, add * 'path' to pax extended attrs. (Note that an unconvertible * name must have non-ASCII characters.) */ - if (suffix_start == NULL || suffix_start - path > 155 - || path_w == NULL || has_non_ASCII(path_w)) { - if (path_w == NULL || hdrcharset != NULL) + if (path == NULL) { + /* We don't have a narrow version, so we have to store + * the wide version. */ + add_pax_attr_w(&(pax->pax_header), "path", path_w); + archive_entry_set_pathname(entry_main, "@WidePath"); + need_extension = 1; + } else if (has_non_ASCII(path_w)) { + /* We have non-ASCII characters. */ + if (path_w == NULL || hdrcharset != NULL) { /* Can't do UTF-8, so store it raw. */ add_pax_attr(&(pax->pax_header), "path", path); - else - add_pax_attr_w(&(pax->pax_header), "path", path_w); + } else { + /* Store UTF-8 */ + add_pax_attr_w(&(pax->pax_header), + "path", path_w); + } archive_entry_set_pathname(entry_main, build_ustar_entry_name(ustar_entry_name, path, strlen(path), NULL)); need_extension = 1; + } else { + /* We have an all-ASCII path; we'd like to just store + * it in the ustar header if it will fit. Yes, this + * duplicates some of the logic in + * write_set_format_ustar.c + */ + if (strlen(path) <= 100) { + /* Fits in the old 100-char tar name field. */ + } else { + /* Find largest suffix that will fit. */ + /* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */ + suffix = strchr(path + strlen(path) - 100 - 1, '/'); + /* Don't attempt an empty prefix. */ + if (suffix == path) + suffix = strchr(suffix + 1, '/'); + /* We can put it in the ustar header if it's + * all ASCII and it's either <= 100 characters + * or can be split at a '/' into a prefix <= + * 155 chars and a suffix <= 100 chars. (Note + * the strchr() above will return NULL exactly + * when the path can't be split.) + */ + if (suffix == NULL /* Suffix > 100 chars. */ + || suffix[1] == '\0' /* empty suffix */ + || suffix - path > 155) /* Prefix > 155 chars */ + { + if (path_w == NULL || hdrcharset != NULL) { + /* Can't do UTF-8, so store it raw. */ + add_pax_attr(&(pax->pax_header), + "path", path); + } else { + /* Store UTF-8 */ + add_pax_attr_w(&(pax->pax_header), + "path", path_w); + } + archive_entry_set_pathname(entry_main, + build_ustar_entry_name(ustar_entry_name, + path, strlen(path), NULL)); + need_extension = 1; + } + } } if (linkpath != NULL) { @@ -1215,6 +1254,8 @@ archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) static int has_non_ASCII(const wchar_t *wp) { + if (wp == NULL) + return (1); while (*wp != L'\0' && *wp < 128) wp++; return (*wp != L'\0'); diff --git a/lib/libarchive/archive_write_set_format_ustar.c b/lib/libarchive/archive_write_set_format_ustar.c index 2693f14..665d080 100644 --- a/lib/libarchive/archive_write_set_format_ustar.c +++ b/lib/libarchive/archive_write_set_format_ustar.c @@ -195,7 +195,7 @@ static int archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; - int ret; + int ret, ret2; struct ustar *ustar; ustar = (struct ustar *)a->format_data; @@ -206,7 +206,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); - if (AE_IFDIR == archive_entry_mode(entry)) { + if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; char *t; /* @@ -229,15 +229,17 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) } ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); - if (ret != ARCHIVE_OK) - return (ret); - ret = (a->compressor.write)(a, buff, 512); - if (ret != ARCHIVE_OK) + if (ret < ARCHIVE_WARN) return (ret); + ret2 = (a->compressor.write)(a, buff, 512); + if (ret2 < ARCHIVE_WARN) + return (ret2); + if (ret2 < ret) + ret = ret2; ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); - return (ARCHIVE_OK); + return (ret); } /* @@ -282,27 +284,33 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], /* Store in two pieces, splitting at a '/'. */ p = strchr(pp + strlen(pp) - USTAR_name_size - 1, '/'); /* - * If the separator we found is the first '/', find - * the next one. (This is a pathological case that - * occurs for paths of exactly 101 bytes that start with - * '/'; it occurs because the separating '/' is not - * stored explicitly and the reconstruction assumes that - * an empty prefix means there is no '/' separator.) + * Look for the next '/' if we chose the first character + * as the separator. (ustar format doesn't permit + * an empty prefix.) */ if (p == pp) p = strchr(p + 1, '/'); - /* - * If there is no path separator, or the prefix or - * remaining name are too large, return an error. - */ + /* Fail if the name won't fit. */ if (!p) { + /* No separator. */ + archive_set_error(&a->archive, ENAMETOOLONG, + "Pathname too long"); + ret = ARCHIVE_FAILED; + } else if (p[1] == '\0') { + /* + * The only feasible separator is a final '/'; + * this would result in a non-empty prefix and + * an empty name, which POSIX doesn't + * explicity forbid, but it just feels wrong. + */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } else if (p > pp + USTAR_prefix_size) { + /* Prefix is too long. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } else { /* Copy prefix and remainder to appropriate places */ memcpy(h + USTAR_prefix_offset, pp, p - pp); @@ -320,7 +328,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], if (copy_length > USTAR_linkname_size) { archive_set_error(&a->archive, ENAMETOOLONG, "Link contents too long"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; copy_length = USTAR_linkname_size; } memcpy(h + USTAR_linkname_offset, p, copy_length); @@ -332,7 +340,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], if (copy_length > USTAR_uname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Username too long"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; copy_length = USTAR_uname_size; } memcpy(h + USTAR_uname_offset, p, copy_length); @@ -344,7 +352,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], if (strlen(p) > USTAR_gname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Group name too long"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; copy_length = USTAR_gname_size; } memcpy(h + USTAR_gname_offset, p, copy_length); @@ -352,28 +360,28 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (archive_entry_filetype(entry) == AE_IFBLK @@ -382,14 +390,14 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset, USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } } @@ -409,7 +417,7 @@ __archive_write_format_header_ustar(struct archive_write *a, char h[512], archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); - ret = ARCHIVE_WARN; + ret = ARCHIVE_FAILED; } } diff --git a/lib/libarchive/config_freebsd.h b/lib/libarchive/config_freebsd.h index 6dddb0f..c92e2ed 100644 --- a/lib/libarchive/config_freebsd.h +++ b/lib/libarchive/config_freebsd.h @@ -51,6 +51,7 @@ #define HAVE_FCHFLAGS 1 #define HAVE_FCHMOD 1 #define HAVE_FCHOWN 1 +#define HAVE_FCNTL 1 #define HAVE_FCNTL_H 1 #define HAVE_FSEEKO 1 #define HAVE_FSTAT 1 @@ -70,6 +71,7 @@ #define HAVE_MKDIR 1 #define HAVE_MKFIFO 1 #define HAVE_MKNOD 1 +#define HAVE_PIPE 1 #define HAVE_POLL 1 #define HAVE_POLL_H 1 #define HAVE_PWD_H 1 @@ -102,6 +104,7 @@ #define HAVE_UTIME 1 #define HAVE_UTIMES 1 #define HAVE_UTIME_H 1 +#define HAVE_VFORK 1 #define HAVE_WCHAR_H 1 #define HAVE_WCSCPY 1 #define HAVE_WCSLEN 1 diff --git a/lib/libarchive/cpio.5 b/lib/libarchive/cpio.5 index 2f29833..02077d6 100644 --- a/lib/libarchive/cpio.5 +++ b/lib/libarchive/cpio.5 @@ -149,7 +149,7 @@ followed by the least-significant 16 bits. Each of the two 16 bit values are stored in machine-native byte order. .It Va namesize The number of bytes in the pathname that follows the header. -This count includes the trailing NULL byte. +This count includes the trailing NUL byte. .It Va filesize The size of the file. Note that this archive format is limited to @@ -162,8 +162,8 @@ above for a description of the storage of four-byte integers. The pathname immediately follows the fixed header. If the .Cm namesize -is odd, an additional NULL byte is added after the pathname. -The file data is then appended, padded with NULL +is odd, an additional NUL byte is added after the pathname. +The file data is then appended, padded with NUL bytes to an even length. .Pp Hardlinked files are not given special treatment; @@ -202,7 +202,7 @@ Unlike the old binary format, there is no additional padding after the pathname or file contents. If the files being archived are themselves entirely ASCII, then the resulting archive will be entirely ASCII, except for the -NULL byte that terminates the name field. +NUL byte that terminates the name field. .Ss New ASCII Format The "new" ASCII format uses 8-byte hexadecimal fields for all numbers and separates device numbers into separate fields @@ -237,7 +237,7 @@ This field is always set to zero by writers and ignored by readers. See the next section for more details. .El .Pp -The pathname is followed by NULL bytes so that the total size +The pathname is followed by NUL bytes so that the total size of the fixed header plus pathname is a multiple of four. Likewise, the file data is padded to a multiple of four bytes. Note that this format supports only 4 gigabyte files (unlike the @@ -322,4 +322,4 @@ by SCO under their license. The character format was adopted as part of .St -p1003.1-88 . -XXX when did "newc" appear? Who invented it? When did HP come out with their variant? When did Sun introduce ACLs and extended attributes? XXX
\ No newline at end of file +XXX when did "newc" appear? Who invented it? When did HP come out with their variant? When did Sun introduce ACLs and extended attributes? XXX diff --git a/lib/libarchive/filter_fork.c b/lib/libarchive/filter_fork.c index 41c20f0..dde6114 100644 --- a/lib/libarchive/filter_fork.c +++ b/lib/libarchive/filter_fork.c @@ -25,6 +25,9 @@ #include "archive_platform.h" +/* This capability is only available on POSIX systems. */ +#if defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) + __FBSDID("$FreeBSD$"); #if defined(HAVE_POLL) @@ -137,3 +140,5 @@ __archive_check_child(int in, int out) sleep(1); #endif } + +#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */ diff --git a/lib/libarchive/libarchive-formats.5 b/lib/libarchive/libarchive-formats.5 index 7cc64ec..daf5738 100644 --- a/lib/libarchive/libarchive-formats.5 +++ b/lib/libarchive/libarchive-formats.5 @@ -259,6 +259,14 @@ There are two common variants: the GNU format derived from SVR4, and the BSD format, which first appeared in 4.4BSD. Libarchive provides read and write support for both variants. +.Ss mtree +Libarchive can read files in +.Xr mtree 5 +format. This format is not a true archive format, but rather a description +of a file hierarchy. When requested, libarchive obtains the contents of +the files described by the +.Xr mtree 5 +format from files on disk instead. .Sh SEE ALSO .Xr ar 1 , .Xr cpio 1 , diff --git a/lib/libarchive/tar.5 b/lib/libarchive/tar.5 index ff52fd5..c9a3c88 100644 --- a/lib/libarchive/tar.5 +++ b/lib/libarchive/tar.5 @@ -221,7 +221,7 @@ field with several new type values: .Bl -tag -width indent -compact .It Dq 0 Regular file. -NULL should be treated as a synonym, for compatibility purposes. +NUL should be treated as a synonym, for compatibility purposes. .It Dq 1 Hard link. .It Dq 2 @@ -258,7 +258,7 @@ by readers. .It Va magic Contains the magic value .Dq ustar -followed by a NULL byte to indicate that this is a POSIX standard archive. +followed by a NUL byte to indicate that this is a POSIX standard archive. Full compliance requires the uname and gname fields be properly set. .It Va version Version. @@ -285,7 +285,7 @@ character to the regular name field to obtain the full pathname. .El .Pp Note that all unused bytes must be set to -.Dv NULL . +.Dv NUL . .Pp Field termination is specified slightly differently by POSIX than by previous implementations. @@ -295,14 +295,14 @@ The and .Va gname fields must have a trailing -.Dv NULL . +.Dv NUL . The .Va pathname , .Va linkname , and .Va prefix fields must have a trailing -.Dv NULL +.Dv NUL unless they fill the entire field. (In particular, it is possible to store a 256-character pathname if it happens to have a @@ -310,7 +310,7 @@ happens to have a as the 156th character.) POSIX requires numeric fields to be zero-padded in the front, and allows them to be terminated with either space or -.Dv NULL +.Dv NUL characters. .Pp Currently, most tar implementations comply with the ustar diff --git a/lib/libarchive/test/Makefile b/lib/libarchive/test/Makefile index c64a6ee..f718ae3 100644 --- a/lib/libarchive/test/Makefile +++ b/lib/libarchive/test/Makefile @@ -18,6 +18,7 @@ TESTS= \ test_empty_write.c \ test_entry.c \ test_entry_strmode.c \ + test_link_resolver.c \ test_pax_filename_encoding.c \ test_read_compress_program.c \ test_read_data_large.c \ @@ -38,6 +39,7 @@ TESTS= \ test_read_format_mtree.c \ test_read_format_pax_bz2.c \ test_read_format_tar.c \ + test_read_format_tar_empty_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ test_read_format_tz.c \ @@ -48,6 +50,7 @@ TESTS= \ test_read_truncated.c \ test_tar_filenames.c \ test_tar_large.c \ + test_ustar_filenames.c \ test_write_compress_program.c \ test_write_compress.c \ test_write_disk.c \ @@ -61,6 +64,7 @@ TESTS= \ test_write_format_cpio_empty.c \ test_write_format_shar_empty.c \ test_write_format_tar.c \ + test_write_format_tar_ustar.c \ test_write_format_tar_empty.c \ test_write_open_memory.c @@ -68,12 +72,10 @@ TESTS= \ # Build the test program using all libarchive sources + the test sources. SRCS= ${LA_SRCS} \ ${TESTS} \ - list.h \ + ${.OBJDIR}/list.h \ main.c \ read_open_memory.c -CLEANFILES+= list.h archive.h - NO_MAN=yes PROG=libarchive_test @@ -82,12 +84,7 @@ DPADD=${LIBBZ2} ${LIBZ} CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" LDADD= -lz -lbz2 CFLAGS+= -static -g -CFLAGS+= -I${.OBJDIR} -CFLAGS+= -I${.CURDIR} CFLAGS+= -I${LA_SRCDIR} -# Without this, libarchive source files find archive.h in LA_SRCDIR, -# which may not be the same as archive.h in the test dir. -CFLAGS+= -I- # Uncomment to link against dmalloc LDADD+= -L/usr/local/lib -ldmalloc @@ -96,20 +93,13 @@ WARNS=6 # Build libarchive_test and run it. check test: libarchive_test - ./libarchive_test -k -r ${.CURDIR} - -INCS=archive.h list.h - -# Build archive.h, but in our .OBJDIR, not libarchive's -# This keeps libarchive_test and libarchive builds completely separate. -archive.h: ${LA_SRCDIR}/archive.h.in ${LA_SRCDIR}/Makefile - cd ${LA_SRCDIR} && unset MAKEOBJDIRPREFIX && MAKEOBJDIR=${.OBJDIR} make archive.h + ./libarchive_test -v -r ${.CURDIR} # list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines -list.h: ${TESTS} Makefile +${.OBJDIR}/list.h: ${TESTS} Makefile (cd ${.CURDIR}; cat ${TESTS}) | grep DEFINE_TEST > list.h -CLEANFILES += *.out *.o *.core *~ list.h archive.h +CLEANFILES += *.out *.o *.core *~ list.h cleantest: -chmod -R +w /tmp/libarchive_test.* diff --git a/lib/libarchive/test/main.c b/lib/libarchive/test/main.c index b72a516..d518b8c 100644 --- a/lib/libarchive/test/main.c +++ b/lib/libarchive/test/main.c @@ -63,10 +63,14 @@ extern char *optarg; extern int optind; #endif -/* Default is to crash and try to force a core dump on failure. */ -static int dump_on_failure = 1; +/* Enable core dump on failure. */ +static int dump_on_failure = 0; +/* Default is to remove temp dirs for successful tests. */ +static int keep_temp_files = 0; /* Default is to print some basic information about each test. */ static int quiet_flag = 0; +/* Default is to summarize repeated failures. */ +static int verbose = 0; /* Cumulative count of component failures. */ static int failures = 0; /* Cumulative count of skipped component tests. */ @@ -242,7 +246,7 @@ test_assert(const char *file, int line, int value, const char *condition, void * return (value); } failures ++; - if (previous_failures(file, line)) + if (!verbose && previous_failures(file, line)) return (value); fprintf(stderr, "%s:%d: Assertion failed\n", file, line); fprintf(stderr, " Condition: %s\n", condition); @@ -261,7 +265,7 @@ test_assert_equal_int(const char *file, int line, return (1); } failures ++; - if (previous_failures(file, line)) + if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n", file, line); @@ -271,6 +275,30 @@ test_assert_equal_int(const char *file, int line, return (0); } +static void strdump(const char *p) +{ + if (p == NULL) { + fprintf(stderr, "(null)"); + return; + } + fprintf(stderr, "\""); + while (*p != '\0') { + unsigned int c = 0xff & *p++; + switch (c) { + case '\a': fprintf(stderr, "\a"); break; + case '\b': fprintf(stderr, "\b"); break; + case '\n': fprintf(stderr, "\n"); break; + case '\r': fprintf(stderr, "\r"); break; + default: + if (c >= 32 && c < 127) + fprintf(stderr, "%c", c); + else + fprintf(stderr, "\\x%02X", c); + } + } + fprintf(stderr, "\""); +} + /* assertEqualString() displays the values of the two strings. */ int test_assert_equal_string(const char *file, int line, @@ -289,16 +317,41 @@ test_assert_equal_string(const char *file, int line, return (1); } failures ++; - if (previous_failures(file, line)) + if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n", file, line); - fprintf(stderr, " %s = \"%s\"\n", e1, v1); - fprintf(stderr, " %s = \"%s\"\n", e2, v2); + fprintf(stderr, " %s = ", e1); + strdump(v1); + fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1)); + fprintf(stderr, " %s = ", e2); + strdump(v2); + fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2)); report_failure(extra); return (0); } +static void wcsdump(const wchar_t *w) +{ + if (w == NULL) { + fprintf(stderr, "(null)"); + return; + } + fprintf(stderr, "\""); + while (*w != L'\0') { + unsigned int c = *w++; + if (c >= 32 && c < 127) + fprintf(stderr, "%c", c); + else if (c < 256) + fprintf(stderr, "\\x%02X", c); + else if (c < 0x10000) + fprintf(stderr, "\\u%04X", c); + else + fprintf(stderr, "\\U%08X", c); + } + fprintf(stderr, "\""); +} + /* assertEqualWString() displays the values of the two strings. */ int test_assert_equal_wstring(const char *file, int line, @@ -307,17 +360,31 @@ test_assert_equal_wstring(const char *file, int line, void *extra) { ++assertions; - if (wcscmp(v1, v2) == 0) { + if (v1 == NULL) { + if (v2 == NULL) { + msg[0] = '\0'; + return (1); + } + } else if (v2 == NULL) { + if (v1 == NULL) { + msg[0] = '\0'; + return (1); + } + } else if (wcscmp(v1, v2) == 0) { msg[0] = '\0'; return (1); } failures ++; - if (previous_failures(file, line)) + if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n", file, line); - fwprintf(stderr, L" %s = \"%ls\"\n", e1, v1); - fwprintf(stderr, L" %s = \"%ls\"\n", e2, v2); + fprintf(stderr, " %s = ", e1); + wcsdump(v1); + fprintf(stderr, "\n"); + fprintf(stderr, " %s = ", e2); + wcsdump(v2); + fprintf(stderr, "\n"); report_failure(extra); return (0); } @@ -378,7 +445,7 @@ test_assert_equal_mem(const char *file, int line, return (1); } failures ++; - if (previous_failures(file, line)) + if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n", file, line); @@ -410,12 +477,13 @@ test_assert_empty_file(const char *f1fmt, ...) if (stat(f1, &st) != 0) { fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1); report_failure(NULL); + return (0); } if (st.st_size == 0) return (1); failures ++; - if (previous_failures(test_filename, test_line)) + if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1); @@ -462,7 +530,7 @@ test_assert_equal_file(const char *f1, const char *f2pattern, ...) break; } failures ++; - if (previous_failures(test_filename, test_line)) + if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: Files are not identical\n", test_filename, test_line); @@ -633,6 +701,12 @@ static int test_run(int i, const char *tmpdir) (*tests[i].func)(); /* Summarize the results of this test. */ summarize(); + /* If there were no failures, we can remove the work dir. */ + if (failures == failures_before) { + if (!keep_temp_files && chdir(tmpdir) == 0) { + systemf("rm -rf %s", tests[i].name); + } + } /* Return appropriate status. */ return (failures == failures_before ? 0 : 1); } @@ -646,8 +720,9 @@ static void usage(const char *program) printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); - printf(" -k Keep running after failures.\n"); - printf(" Default: Core dump after any failure.\n"); + printf(" -d Dump core after any failure, for debugging.\n"); + printf(" -k Keep all temp files.\n"); + printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p <path> Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); @@ -655,6 +730,7 @@ static void usage(const char *program) printf(" -q Quiet.\n"); printf(" -r <dir> Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); + printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); @@ -747,9 +823,9 @@ int main(int argc, char **argv) testprog = getenv(ENVBASE); #endif - /* Allow -k to be controlled through the environment. */ - if (getenv(ENVBASE "_KEEP_GOING") != NULL) - dump_on_failure = 0; + /* Allow -d to be controlled through the environment. */ + if (getenv(ENVBASE "_DEBUG") != NULL) + dump_on_failure = 1; /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); @@ -757,10 +833,13 @@ int main(int argc, char **argv) /* * Parse options. */ - while ((opt = getopt(argc, argv, "kp:qr:")) != -1) { + while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) { switch (opt) { + case 'd': + dump_on_failure = 1; + break; case 'k': - dump_on_failure = 0; + keep_temp_files = 1; break; case 'p': #ifdef PROGRAM @@ -775,6 +854,9 @@ int main(int argc, char **argv) case 'r': refdir = optarg; break; + case 'v': + verbose = 1; + break; case '?': default: usage(progname); @@ -823,6 +905,7 @@ int main(int argc, char **argv) --p; *p = '\0'; } + systemf("rm %s/refdir", tmpdir); } /* @@ -878,5 +961,9 @@ int main(int argc, char **argv) free(refdir_alloc); + /* If the final tmpdir is empty, we can remove it. */ + /* This should be the usual case when all tests succeed. */ + rmdir(tmpdir); + return (tests_failed); } diff --git a/lib/libarchive/test/test_acl_pax.c b/lib/libarchive/test/test_acl_pax.c index eacad6a..a40256b 100644 --- a/lib/libarchive/test/test_acl_pax.c +++ b/lib/libarchive/test/test_acl_pax.c @@ -332,14 +332,10 @@ acl_match(struct acl_t *acl, int type, int permset, int tag, int qual, const cha return (1); if (qual != acl->qual) return (0); - if (name == NULL) { - if (acl->name == NULL || acl->name[0] == '\0') - return (1); - } - if (acl->name == NULL) { - if (name[0] == '\0') - return (1); - } + if (name == NULL) + return (acl->name == NULL || acl->name[0] == '\0'); + if (acl->name == NULL) + return (name == NULL || name[0] == '\0'); return (0 == strcmp(name, acl->name)); } diff --git a/lib/libarchive/test/test_archive_api_feature.c b/lib/libarchive/test/test_archive_api_feature.c index ba9dd44..3324aad 100644 --- a/lib/libarchive/test/test_archive_api_feature.c +++ b/lib/libarchive/test/test_archive_api_feature.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_archive_api_feature) { char buff[128]; + const char *p; /* This is the (hopefully) final versioning API. */ assertEqualInt(ARCHIVE_VERSION_NUMBER, archive_version_number()); @@ -35,7 +36,17 @@ DEFINE_TEST(test_archive_api_feature) archive_version_number() / 1000000, (archive_version_number() / 1000) % 1000, archive_version_number() % 1000); - assertEqualString(buff, archive_version_string()); + failure("Version string is: %s, computed is: %s", + archive_version_string(), buff); + assert(memcmp(buff, archive_version_string(), strlen(buff)) == 0); + if (strlen(buff) < strlen(archive_version_string())) { + p = archive_version_string() + strlen(buff); + failure("Version string is: %s", archive_version_string()); + assert(*p == 'a' || *p == 'b' || *p == 'c' || *p == 'd'); + ++p; + failure("Version string is: %s", archive_version_string()); + assert(*p == '\0'); + } /* This is all scheduled to disappear in libarchive 3.0 */ #if ARCHIVE_VERSION_NUMBER < 3000000 diff --git a/lib/libarchive/test/test_entry.c b/lib/libarchive/test/test_entry.c index 638af48..352c99f 100644 --- a/lib/libarchive/test/test_entry.c +++ b/lib/libarchive/test/test_entry.c @@ -52,6 +52,8 @@ DEFINE_TEST(test_entry) const void *xval; /* For xattr tests. */ size_t xsize; /* For xattr tests. */ int c; + wchar_t wc; + long l; assert((e = archive_entry_new()) != NULL); @@ -146,7 +148,7 @@ DEFINE_TEST(test_entry) archive_entry_copy_link_w(e, L"link3"); assertEqualString(archive_entry_hardlink(e), NULL); assertEqualString(archive_entry_symlink(e), "link3"); - /* Arbitrarily override hardlink if both hardlink and symlink set. */ + /* Arbitrarily override symlink if both hardlink and symlink set. */ archive_entry_set_hardlink(e, "hardlink"); archive_entry_set_symlink(e, "symlink"); archive_entry_set_link(e, "link"); @@ -230,6 +232,11 @@ DEFINE_TEST(test_entry) assertEqualString(archive_entry_fflags_text(e), "uappnd,nouchg,nodump,noopaque,uunlnk"); /* TODO: Test archive_entry_copy_fflags_text_w() */ + /* Test archive_entry_copy_fflags_text() */ + archive_entry_copy_fflags_text(e, "nouappnd, nouchg, dump,uunlnk"); + archive_entry_fflags(e, &set, &clear); + assertEqualInt(16, set); + assertEqualInt(7, clear); #endif /* See test_acl_basic.c for tests of ACL set/get consistency. */ @@ -726,8 +733,10 @@ DEFINE_TEST(test_entry) /* * Exercise the character-conversion logic, if we can. */ - failure("Can't exercise charset-conversion logic."); - if (assert(NULL != setlocale(LC_ALL, "de_DE.UTF-8"))) { + if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) { + skipping("Can't exercise charset-conversion logic without" + " a suitable locale."); + } else { /* A filename that cannot be converted to wide characters. */ archive_entry_copy_pathname(e, "abc\314\214mno\374xyz"); failure("Converting invalid chars to Unicode should fail."); @@ -756,6 +765,26 @@ DEFINE_TEST(test_entry) assert(NULL == archive_entry_symlink_w(e)); } + l = 0x12345678L; + wc = (wchar_t)l; /* Wide character too big for UTF-8. */ + if (NULL == setlocale(LC_ALL, "C") || (long)wc != l) { + skipping("Testing charset conversion failure requires 32-bit wchar_t and support for \"C\" locale."); + } else { + /* + * Build the string L"xxx\U12345678yyy\u5678zzz" without + * using C99 \u#### syntax, which isn't uniformly + * supported. (GCC 3.4.6, for instance, defaults to + * "c89 plus GNU extensions.") + */ + wcscpy(wbuff, L"xxxAyyyBzzz"); + wbuff[3] = 0x12345678; + wbuff[7] = 0x5678; + /* A wide filename that cannot be converted to narrow. */ + archive_entry_copy_pathname_w(e, wbuff); + failure("Converting wide characters from Unicode should fail."); + assertEqualString(NULL, archive_entry_pathname(e)); + } + /* Release the experimental entry. */ archive_entry_free(e); } diff --git a/lib/libarchive/test/test_link_resolver.c b/lib/libarchive/test/test_link_resolver.c new file mode 100644 index 0000000..a51e4a4 --- /dev/null +++ b/lib/libarchive/test/test_link_resolver.c @@ -0,0 +1,187 @@ +/*- + * 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 void test_linkify_tar(void) +{ + struct archive_entry *entry, *e2; + struct archive_entry_linkresolver *resolver; + + /* Initialize the resolver. */ + assert(NULL != (resolver = archive_entry_linkresolver_new())); + archive_entry_linkresolver_set_strategy(resolver, + ARCHIVE_FORMAT_TAR_USTAR); + + /* Create an entry with only 1 link and try to linkify it. */ + assert(NULL != (entry = archive_entry_new())); + archive_entry_set_pathname(entry, "test1"); + archive_entry_set_ino(entry, 1); + archive_entry_set_dev(entry, 2); + archive_entry_set_nlink(entry, 1); + archive_entry_set_size(entry, 10); + archive_entry_linkify(resolver, &entry, &e2); + + /* Shouldn't have been changed. */ + assert(e2 == NULL); + assertEqualInt(10, archive_entry_size(entry)); + assertEqualString("test1", archive_entry_pathname(entry)); + + /* Now, try again with an entry that has 2 links. */ + archive_entry_set_pathname(entry, "test2"); + archive_entry_set_nlink(entry, 2); + archive_entry_set_ino(entry, 2); + archive_entry_linkify(resolver, &entry, &e2); + /* Shouldn't be altered, since it wasn't seen before. */ + assert(e2 == NULL); + assertEqualString("test2", archive_entry_pathname(entry)); + assertEqualString(NULL, archive_entry_hardlink(entry)); + assertEqualInt(10, archive_entry_size(entry)); + + /* Match again and make sure it does get altered. */ + archive_entry_linkify(resolver, &entry, &e2); + assert(e2 == NULL); + assertEqualString("test2", archive_entry_pathname(entry)); + assertEqualString("test2", archive_entry_hardlink(entry)); + assertEqualInt(0, archive_entry_size(entry)); + + + archive_entry_free(entry); + archive_entry_linkresolver_free(resolver); +} + +static void test_linkify_old_cpio(void) +{ + struct archive_entry *entry, *e2; + struct archive_entry_linkresolver *resolver; + + /* Initialize the resolver. */ + assert(NULL != (resolver = archive_entry_linkresolver_new())); + archive_entry_linkresolver_set_strategy(resolver, + ARCHIVE_FORMAT_CPIO_POSIX); + + /* Create an entry with 2 link and try to linkify it. */ + assert(NULL != (entry = archive_entry_new())); + archive_entry_set_pathname(entry, "test1"); + archive_entry_set_ino(entry, 1); + archive_entry_set_dev(entry, 2); + archive_entry_set_nlink(entry, 2); + archive_entry_set_size(entry, 10); + archive_entry_linkify(resolver, &entry, &e2); + + /* Shouldn't have been changed. */ + assert(e2 == NULL); + assertEqualInt(10, archive_entry_size(entry)); + assertEqualString("test1", archive_entry_pathname(entry)); + + /* Still shouldn't be matched. */ + archive_entry_linkify(resolver, &entry, &e2); + assert(e2 == NULL); + assertEqualString("test1", archive_entry_pathname(entry)); + assertEqualString(NULL, archive_entry_hardlink(entry)); + assertEqualInt(10, archive_entry_size(entry)); + + archive_entry_free(entry); + archive_entry_linkresolver_free(resolver); +} + +static void test_linkify_new_cpio(void) +{ + struct archive_entry *entry, *e2; + struct archive_entry_linkresolver *resolver; + + /* Initialize the resolver. */ + assert(NULL != (resolver = archive_entry_linkresolver_new())); + archive_entry_linkresolver_set_strategy(resolver, + ARCHIVE_FORMAT_CPIO_SVR4_NOCRC); + + /* Create an entry with only 1 link and try to linkify it. */ + assert(NULL != (entry = archive_entry_new())); + archive_entry_set_pathname(entry, "test1"); + archive_entry_set_ino(entry, 1); + archive_entry_set_dev(entry, 2); + archive_entry_set_nlink(entry, 1); + archive_entry_set_size(entry, 10); + archive_entry_linkify(resolver, &entry, &e2); + + /* Shouldn't have been changed. */ + assert(e2 == NULL); + assertEqualInt(10, archive_entry_size(entry)); + assertEqualString("test1", archive_entry_pathname(entry)); + + /* Now, try again with an entry that has 3 links. */ + archive_entry_set_pathname(entry, "test2"); + archive_entry_set_nlink(entry, 3); + archive_entry_set_ino(entry, 2); + archive_entry_linkify(resolver, &entry, &e2); + + /* First time, it just gets swallowed. */ + assert(entry == NULL); + assert(e2 == NULL); + + /* Match again. */ + assert(NULL != (entry = archive_entry_new())); + archive_entry_set_pathname(entry, "test3"); + archive_entry_set_ino(entry, 2); + archive_entry_set_dev(entry, 2); + archive_entry_set_nlink(entry, 2); + archive_entry_set_size(entry, 10); + archive_entry_linkify(resolver, &entry, &e2); + + /* Should get back "test2" and nothing else. */ + assertEqualString("test2", archive_entry_pathname(entry)); + assertEqualInt(0, archive_entry_size(entry)); + archive_entry_free(entry); + assert(NULL == e2); + archive_entry_free(e2); /* This should be a no-op. */ + + /* Match a third time. */ + assert(NULL != (entry = archive_entry_new())); + archive_entry_set_pathname(entry, "test4"); + archive_entry_set_ino(entry, 2); + archive_entry_set_dev(entry, 2); + archive_entry_set_nlink(entry, 3); + archive_entry_set_size(entry, 10); + archive_entry_linkify(resolver, &entry, &e2); + + /* Should get back "test3". */ + assertEqualString("test3", archive_entry_pathname(entry)); + assertEqualInt(0, archive_entry_size(entry)); + + /* Since "test4" was the last link, should get it back also. */ + assertEqualString("test4", archive_entry_pathname(e2)); + assertEqualInt(10, archive_entry_size(e2)); + + archive_entry_free(entry); + archive_entry_free(e2); + archive_entry_linkresolver_free(resolver); +} + +DEFINE_TEST(test_link_resolver) +{ + test_linkify_tar(); + test_linkify_old_cpio(); + test_linkify_new_cpio(); +} diff --git a/lib/libarchive/test/test_pax_filename_encoding.c b/lib/libarchive/test/test_pax_filename_encoding.c index fae1197..c63f06f 100644 --- a/lib/libarchive/test/test_pax_filename_encoding.c +++ b/lib/libarchive/test/test_pax_filename_encoding.c @@ -34,24 +34,20 @@ __FBSDID("$FreeBSD$"); * stored and restored correctly, regardless of the encodings. */ -DEFINE_TEST(test_pax_filename_encoding) +/* + * Read a manually-created archive that has filenames that are + * stored in binary instead of UTF-8 and verify that we get + * the right filename returned and that we get a warning only + * if the header isn't marked as binary. + */ +DEFINE_TEST(test_pax_filename_encoding_1) { static const char testname[] = "test_pax_filename_encoding.tar.gz"; - char buff[65536]; /* * \314\214 is a valid 2-byte UTF-8 sequence. * \374 is invalid in UTF-8. */ char filename[] = "abc\314\214mno\374xyz"; - char longname[] = "abc\314\214mno\374xyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" - ; - size_t used; struct archive *a; struct archive_entry *entry; @@ -69,8 +65,7 @@ DEFINE_TEST(test_pax_filename_encoding) * in it, but the header is not marked as hdrcharset=BINARY, so that * requires a warning. */ - failure("An invalid UTF8 pathname in a pax archive should be read\n" - " without conversion but with a warning"); + failure("Invalid UTF8 in a pax archive pathname should cause a warning"); assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); assertEqualString(filename, archive_entry_pathname(entry)); /* @@ -82,15 +77,39 @@ DEFINE_TEST(test_pax_filename_encoding) assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry)); assertEqualString(filename, archive_entry_pathname(entry)); archive_read_finish(a); +} + +/* + * Set the locale and write a pathname containing invalid characters. + * This should work; the underlying implementation should automatically + * fall back to storing the pathname in binary. + */ +DEFINE_TEST(test_pax_filename_encoding_2) +{ + char filename[] = "abc\314\214mno\374xyz"; + struct archive *a; + struct archive_entry *entry; + char buff[65536]; + char longname[] = "abc\314\214mno\374xyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + "/abc\314\214mno\374xyz/abcdefghijklmnopqrstuvwxyz" + ; + size_t used; /* * We need a starting locale which has invalid sequences. * de_DE.UTF-8 seems to be commonly supported. */ /* If it doesn't exist, just warn and return. */ - failure("We need a suitable locale for the encoding tests."); - if (!assert(NULL != setlocale(LC_ALL, "de_DE.UTF-8"))) + if (NULL == setlocale(LC_ALL, "de_DE.UTF-8")) { + skipping("invalid encoding tests require a suitable locale;" + " de_DE.UTF-8 not available on this system"); return; + } assert((a = archive_write_new()) != NULL); assertEqualIntA(a, 0, archive_write_set_format_pax(a)); @@ -159,3 +178,120 @@ DEFINE_TEST(test_pax_filename_encoding) assertEqualInt(0, archive_read_finish(a)); } +/* + * Create an entry starting from a wide-character Unicode pathname, + * read it back into "C" locale, which doesn't support the name. + * TODO: Figure out the "right" behavior here. + */ +DEFINE_TEST(test_pax_filename_encoding_3) +{ + wchar_t badname[] = L"xxxAyyyBzzz"; + const char badname_utf8[] = "xxx\xE1\x88\xB4yyy\xE5\x99\xB8zzz"; + struct archive *a; + struct archive_entry *entry; + char buff[65536]; + size_t used; + + badname[3] = 0x1234; + badname[7] = 0x5678; + + /* If it doesn't exist, just warn and return. */ + if (NULL == setlocale(LC_ALL, "C")) { + skipping("Can't set \"C\" locale, so can't exercise " + "certain character-conversion failures"); + return; + } + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_pax(a)); + assertEqualIntA(a, 0, archive_write_set_compression_none(a)); + assertEqualIntA(a, 0, archive_write_set_bytes_per_block(a, 0)); + assertEqualInt(0, + archive_write_open_memory(a, buff, sizeof(buff), &used)); + + assert((entry = archive_entry_new()) != NULL); + /* Set pathname to non-convertible wide value. */ + archive_entry_copy_pathname_w(entry, badname); + archive_entry_set_filetype(entry, AE_IFREG); + assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + assert((entry = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(entry, L"abc"); + /* Set gname to non-convertible wide value. */ + archive_entry_copy_gname_w(entry, badname); + archive_entry_set_filetype(entry, AE_IFREG); + assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + assert((entry = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(entry, L"abc"); + /* Set uname to non-convertible wide value. */ + archive_entry_copy_uname_w(entry, badname); + archive_entry_set_filetype(entry, AE_IFREG); + assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + assert((entry = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(entry, L"abc"); + /* Set hardlink to non-convertible wide value. */ + archive_entry_copy_hardlink_w(entry, badname); + archive_entry_set_filetype(entry, AE_IFREG); + assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + assert((entry = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(entry, L"abc"); + /* Set symlink to non-convertible wide value. */ + archive_entry_copy_symlink_w(entry, badname); + archive_entry_set_filetype(entry, AE_IFLNK); + assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + assertEqualInt(0, archive_write_close(a)); + assertEqualInt(0, archive_write_finish(a)); + + /* + * Now read the entries back. + */ + + assert((a = archive_read_new()) != NULL); + assertEqualInt(0, archive_read_support_format_tar(a)); + assertEqualInt(0, archive_read_open_memory(a, buff, used)); + + failure("A non-convertible pathname should cause a warning."); + assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); + assertEqualWString(badname, archive_entry_pathname_w(entry)); + failure("If native locale can't convert, we should get UTF-8 back."); + assertEqualString(badname_utf8, archive_entry_pathname(entry)); + + failure("A non-convertible gname should cause a warning."); + assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); + assertEqualWString(badname, archive_entry_gname_w(entry)); + failure("If native locale can't convert, we should get UTF-8 back."); + assertEqualString(badname_utf8, archive_entry_gname(entry)); + + failure("A non-convertible uname should cause a warning."); + assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); + assertEqualWString(badname, archive_entry_uname_w(entry)); + failure("If native locale can't convert, we should get UTF-8 back."); + assertEqualString(badname_utf8, archive_entry_uname(entry)); + + failure("A non-convertible hardlink should cause a warning."); + assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); + assertEqualWString(badname, archive_entry_hardlink_w(entry)); + failure("If native locale can't convert, we should get UTF-8 back."); + assertEqualString(badname_utf8, archive_entry_hardlink(entry)); + + failure("A non-convertible symlink should cause a warning."); + assertEqualInt(ARCHIVE_WARN, archive_read_next_header(a, &entry)); + assertEqualWString(badname, archive_entry_symlink_w(entry)); + assertEqualWString(NULL, archive_entry_hardlink_w(entry)); + failure("If native locale can't convert, we should get UTF-8 back."); + assertEqualString(badname_utf8, archive_entry_symlink(entry)); + + assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &entry)); + + assertEqualInt(0, archive_read_close(a)); + assertEqualInt(0, archive_read_finish(a)); +} diff --git a/lib/libarchive/test/test_read_format_mtree.c b/lib/libarchive/test/test_read_format_mtree.c index fa20c35..861fbc7 100644 --- a/lib/libarchive/test/test_read_format_mtree.c +++ b/lib/libarchive/test/test_read_format_mtree.c @@ -32,18 +32,18 @@ static unsigned char archive[] = { "dir type=dir\n" " file\\040with\\040space type=file uid=18\n" " ..\n" - "file\\04with\\040space\n" + "file\\04with\\040space type=file\n" "dir2 type=dir\n" " dir3a type=dir\n" - " indir3a\n" + " indir3a type=file\n" "dir2/fullindir2 type=file mode=0777\n" " ..\n" - " indir2\n" + " indir2 type=file\n" " dir3b type=dir\n" - " indir3b\n" + " indir3b type=file\n" " ..\n" " ..\n" - "notindir\n" + "notindir type=file\n" "dir2/fullindir2 mode=0644\n" }; diff --git a/lib/libarchive/test/test_read_format_tar_empty_filename.c b/lib/libarchive/test/test_read_format_tar_empty_filename.c new file mode 100644 index 0000000..d9ba69b --- /dev/null +++ b/lib/libarchive/test/test_read_format_tar_empty_filename.c @@ -0,0 +1,66 @@ +/*- + * 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$"); + +/* + * Tar entries with empty filenames are unusual, but shouldn't crash us. + */ +DEFINE_TEST(test_read_format_tar_empty_filename) +{ + char name[] = "test_read_format_tar_empty_filename.tar"; + struct archive_entry *ae; + struct archive *a; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + extract_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240)); + + /* Read first entry. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("", archive_entry_pathname(ae)); + assertEqualInt(1208628157, archive_entry_mtime(ae)); + assertEqualInt(1000, archive_entry_uid(ae)); + assertEqualString("tim", archive_entry_uname(ae)); + assertEqualInt(0, archive_entry_gid(ae)); + assertEqualString("wheel", archive_entry_gname(ae)); + assertEqualInt(040775, archive_entry_mode(ae)); + + /* Verify the end-of-archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify that the format detection worked. */ + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_NONE); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); +#if ARCHIVE_API_VERSION > 1 + assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); +#else + archive_read_finish(a); +#endif +} diff --git a/lib/libarchive/test/test_read_format_tar_empty_filename.tar.uu b/lib/libarchive/test/test_read_format_tar_empty_filename.tar.uu new file mode 100644 index 0000000..7a34c82 --- /dev/null +++ b/lib/libarchive/test/test_read_format_tar_empty_filename.tar.uu @@ -0,0 +1,38 @@ +begin 644 test_compat_tar_1.tar +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#<W-2``,#`Q-S4P(``P,#`P,#`@`#`P,#`P,#`P,#`P +M(#$Q,#`R-#,Q-C<U(#`Q,3`P,0`@-0`````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````!U<W1A<@`P,'1I;0`` +M````````````````````````````````````=VAE96P````````````````` +M```````````````````P,#`P,#`@`#`P,#`P,"`````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +&```````` +` +end diff --git a/lib/libarchive/test/test_tar_filenames.c b/lib/libarchive/test/test_tar_filenames.c index 254917e..e96d2ec 100644 --- a/lib/libarchive/test/test_tar_filenames.c +++ b/lib/libarchive/test/test_tar_filenames.c @@ -40,19 +40,22 @@ test_filename(const char *prefix, int dlen, int flen) struct archive_entry *ae; struct archive *a; size_t used; - size_t prefix_length = 0; - unsigned i = 0; + char *p; + int i; + p = filename; if (prefix) { strcpy(filename, prefix); - i = prefix_length = strlen(prefix); + p += strlen(p); } - for (; i < prefix_length + dlen; i++) - filename[i] = 'a'; - filename[i++] = '/'; - for (; i < prefix_length + dlen + flen + 1; i++) - filename[i] = 'b'; - filename[i++] = '\0'; + if (dlen > 0) { + for (i = 0; i < dlen; i++) + *p++ = 'a'; + *p++ = '/'; + } + for (i = 0; i < flen; i++) + *p++ = 'b'; + *p = '\0'; strcpy(dirname, filename); @@ -160,15 +163,22 @@ DEFINE_TEST(test_tar_filenames) int dlen, flen; /* Repeat the following for a variety of dir/file lengths. */ - for (dlen = 40; dlen < 60; dlen++) { - for (flen = 40; flen < 60; flen++) { + for (dlen = 45; dlen < 55; dlen++) { + for (flen = 45; flen < 55; flen++) { + test_filename(NULL, dlen, flen); + test_filename("/", dlen, flen); + } + } + + for (dlen = 0; dlen < 140; dlen += 10) { + for (flen = 98; flen < 102; flen++) { test_filename(NULL, dlen, flen); test_filename("/", dlen, flen); } } for (dlen = 140; dlen < 160; dlen++) { - for (flen = 90; flen < 110; flen++) { + for (flen = 95; flen < 105; flen++) { test_filename(NULL, dlen, flen); test_filename("/", dlen, flen); } diff --git a/lib/libarchive/test/test_tar_large.c b/lib/libarchive/test/test_tar_large.c index 73113cd..b0a277f 100644 --- a/lib/libarchive/test/test_tar_large.c +++ b/lib/libarchive/test/test_tar_large.c @@ -242,6 +242,11 @@ DEFINE_TEST(test_tar_large) archive_entry_copy_pathname(ae, namebuff); archive_entry_set_mode(ae, S_IFREG | 0755); filesize = tests[i]; + + if (filesize < 0) { + skipping("32-bit off_t doesn't permit testing of very large files."); + return; + } archive_entry_set_size(ae, filesize); assertA(0 == archive_write_header(a, ae)); diff --git a/lib/libarchive/test/test_ustar_filenames.c b/lib/libarchive/test/test_ustar_filenames.c new file mode 100644 index 0000000..88b4b2c --- /dev/null +++ b/lib/libarchive/test/test_ustar_filenames.c @@ -0,0 +1,183 @@ +/*- + * 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$"); + +/* + * Exercise various lengths of filenames in ustar archives. + */ + +static void +test_filename(const char *prefix, int dlen, int flen) +{ + char buff[8192]; + char filename[400]; + char dirname[400]; + struct archive_entry *ae; + struct archive *a; + size_t used; + int separator = 0; + int i = 0; + + if (prefix != NULL) { + strcpy(filename, prefix); + i = strlen(prefix); + } + if (dlen > 0) { + for (; i < dlen; i++) + filename[i] = 'a'; + filename[i++] = '/'; + separator = 1; + } + for (; i < dlen + flen + separator; i++) + filename[i] = 'b'; + filename[i++] = '\0'; + + strcpy(dirname, filename); + + /* Create a new archive in memory. */ + assert((a = archive_write_new()) != NULL); + assertA(0 == archive_write_set_format_ustar(a)); + assertA(0 == archive_write_set_compression_none(a)); + assertA(0 == archive_write_set_bytes_per_block(a,0)); + assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); + + /* + * Write a file to it. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, filename); + archive_entry_set_mode(ae, S_IFREG | 0755); + failure("dlen=%d, flen=%d", dlen, flen); + if (flen > 100) { + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); + } else { + assertEqualIntA(a, 0, archive_write_header(a, ae)); + } + archive_entry_free(ae); + + /* + * Write a dir to it (without trailing '/'). + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, dirname); + archive_entry_set_mode(ae, S_IFDIR | 0755); + failure("dlen=%d, flen=%d", dlen, flen); + if (flen >= 100) { + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); + } else { + assertEqualIntA(a, 0, archive_write_header(a, ae)); + } + archive_entry_free(ae); + + /* Tar adds a '/' to directory names. */ + strcat(dirname, "/"); + + /* + * Write a dir to it (with trailing '/'). + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, dirname); + archive_entry_set_mode(ae, S_IFDIR | 0755); + failure("dlen=%d, flen=%d", dlen, flen); + if (flen >= 100) { + assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); + } else { + assertEqualIntA(a, 0, archive_write_header(a, ae)); + } + archive_entry_free(ae); + + /* Close out the archive. */ + assertA(0 == archive_write_close(a)); + assertA(0 == archive_write_finish(a)); + + /* + * 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)); + + if (flen <= 100) { + /* Read the file and check the filename. */ + assertA(0 == archive_read_next_header(a, &ae)); + failure("dlen=%d, flen=%d", dlen, flen); + assertEqualString(filename, archive_entry_pathname(ae)); + assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); + } + + /* + * Read the two dirs and check the names. + * + * Both dirs should read back with the same name, since + * tar should add a trailing '/' to any dir that doesn't + * already have one. + */ + if (flen <= 99) { + assertA(0 == archive_read_next_header(a, &ae)); + assert((S_IFDIR | 0755) == archive_entry_mode(ae)); + failure("dlen=%d, flen=%d", dlen, flen); + assertEqualString(dirname, archive_entry_pathname(ae)); + } + + if (flen <= 99) { + assertA(0 == archive_read_next_header(a, &ae)); + assert((S_IFDIR | 0755) == archive_entry_mode(ae)); + assertEqualString(dirname, archive_entry_pathname(ae)); + } + + /* Verify the end of the archive. */ + failure("This fails if entries were written that should not have been written. dlen=%d, flen=%d", dlen, flen); + assertEqualInt(1, archive_read_next_header(a, &ae)); + assert(0 == archive_read_close(a)); + assert(0 == archive_read_finish(a)); +} + +DEFINE_TEST(test_ustar_filenames) +{ + int dlen, flen; + + /* Try a bunch of different file/dir lengths that add up + * to just a little less or a little more than 100 bytes. + * This exercises the code that splits paths between ustar + * filename and prefix fields. + */ + for (dlen = 5; dlen < 70; dlen += 5) { + for (flen = 100 - dlen - 5; flen < 100 - dlen + 5; flen++) { + test_filename(NULL, dlen, flen); + test_filename("/", dlen, flen); + } + } + + /* Probe the 100-char limit for paths with no '/'. */ + for (flen = 90; flen < 110; flen++) { + test_filename(NULL, 0, flen); + test_filename("/", dlen, flen); + } + + /* XXXX TODO Probe the 100-char limit with a dir prefix. */ + /* XXXX TODO Probe the 255-char total limit. */ +} diff --git a/lib/libarchive/test/test_write_disk_hardlink.c b/lib/libarchive/test/test_write_disk_hardlink.c index 108fcbd..76969d1 100644 --- a/lib/libarchive/test/test_write_disk_hardlink.c +++ b/lib/libarchive/test/test_write_disk_hardlink.c @@ -29,6 +29,10 @@ __FBSDID("$FreeBSD$"); /* * Exercise hardlink recreation. + * + * File permissions are chosen so that the authoritive entry + * has the correct permission and the non-authoritive versions + * are just writeable files. */ DEFINE_TEST(test_write_disk_hardlink) { @@ -64,7 +68,7 @@ DEFINE_TEST(test_write_disk_hardlink) /* Link. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link1b"); - archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_mode(ae, S_IFREG | 0600); archive_entry_set_size(ae, 0); archive_entry_copy_hardlink(ae, "link1a"); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); @@ -80,7 +84,7 @@ DEFINE_TEST(test_write_disk_hardlink) /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link2a"); - archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_mode(ae, S_IFREG | 0600); archive_entry_set_size(ae, sizeof(data)); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); @@ -106,10 +110,14 @@ DEFINE_TEST(test_write_disk_hardlink) /* Regular file. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, "link3a"); - archive_entry_set_mode(ae, S_IFREG | 0755); + archive_entry_set_mode(ae, S_IFREG | 0600); archive_entry_set_size(ae, 0); assertEqualIntA(ad, 0, archive_write_header(ad, ae)); - assertEqualInt(0, archive_write_data(ad, data, sizeof(data))); +#if ARCHIVE_VERSION_NUMBER < 3000000 + assertEqualInt(ARCHIVE_WARN, archive_write_data(ad, data, 1)); +#else + assertEqualInt(-1, archive_write_data(ad, data, 1)); +#endif assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); diff --git a/lib/libarchive/test/test_write_format_ar.c b/lib/libarchive/test/test_write_format_ar.c index cc49b28..09d6baa 100644 --- a/lib/libarchive/test/test_write_format_ar.c +++ b/lib/libarchive/test/test_write_format_ar.c @@ -30,7 +30,7 @@ __FBSDID("$FreeBSD$"); char buff[4096]; char buff2[64]; -static unsigned char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n"; +static char strtab[] = "abcdefghijklmn.o/\nggghhhjjjrrrttt.o/\niiijjjdddsssppp.o/\n"; DEFINE_TEST(test_write_format_ar) { @@ -162,6 +162,7 @@ DEFINE_TEST(test_write_format_ar) archive_entry_set_filetype(ae, AE_IFREG); archive_entry_set_size(ae, 5); assertA(0 == archive_write_header(a, ae)); + assertA(5 == archive_entry_size(ae)); assertA(5 == archive_write_data(a, "12345", 7)); archive_entry_free(ae); diff --git a/lib/libarchive/test/test_write_format_tar_ustar.c b/lib/libarchive/test/test_write_format_tar_ustar.c new file mode 100644 index 0000000..53eb07c --- /dev/null +++ b/lib/libarchive/test/test_write_format_tar_ustar.c @@ -0,0 +1,342 @@ +/*- + * 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 int +is_null(const char *p, size_t l) +{ + while (l > 0) { + if (*p != '\0') + return (0); + --l; + ++p; + } + return (1); +} + +/* Verify the contents, then erase them to NUL bytes. */ +/* Tar requires all "unused" bytes be set to NUL; this allows us + * to easily verify that by invoking is_null() over the entire header + * after verifying each field. */ +#define myAssertEqualMem(a,b,s) assertEqualMem(a, b, s); memset(a, 0, s) + +/* + * Detailed verification that 'ustar' archives are written with + * the correct format. + */ +DEFINE_TEST(test_write_format_tar_ustar) +{ + struct archive *a; + struct archive_entry *entry; + char *buff, *e; + size_t buffsize = 100000; + size_t used; + int i; + char f99[100]; + char f100[101]; + char f256[257]; + + for (i = 0; i < 99; ++i) + f99[i] = 'a' + i % 26; + f99[99] = '\0'; + + for (i = 0; i < 100; ++i) + f100[i] = 'A' + i % 26; + f100[100] = '\0'; + + for (i = 0; i < 256; ++i) + f256[i] = 'A' + i % 26; + f256[155] = '/'; + f256[256] = '\0'; + + buff = malloc(buffsize); + + /* Create a new archive in memory. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_ustar(a)); + assertEqualIntA(a, 0, archive_write_set_compression_none(a)); + assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used)); + + /* + * Add various files to it. + * TODO: Extend this to cover more filetypes. + */ + + /* "file" with 10 bytes of content */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 1, 10); + archive_entry_set_pathname(entry, "file"); + archive_entry_set_mode(entry, S_IFREG | 0664); + archive_entry_set_size(entry, 10); + archive_entry_set_uid(entry, 80); + archive_entry_set_gid(entry, 90); + archive_entry_set_dev(entry, 12); + archive_entry_set_ino(entry, 89); + archive_entry_set_nlink(entry, 2); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + assertEqualIntA(a, 10, archive_write_data(a, "1234567890", 10)); + + /* Hardlink to "file" with 10 bytes of content */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 1, 10); + archive_entry_set_pathname(entry, "linkfile"); + archive_entry_set_mode(entry, S_IFREG | 0664); + /* TODO: Put this back and fix the bug. */ + /* archive_entry_set_size(entry, 10); */ + archive_entry_set_uid(entry, 80); + archive_entry_set_gid(entry, 90); + archive_entry_set_dev(entry, 12); + archive_entry_set_ino(entry, 89); + archive_entry_set_nlink(entry, 2); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + /* Write of data to dir should fail == zero bytes get written. */ + assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10)); + + /* "dir" */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 2, 20); + archive_entry_set_pathname(entry, "dir"); + archive_entry_set_mode(entry, S_IFDIR | 0775); + archive_entry_set_size(entry, 10); + archive_entry_set_nlink(entry, 2); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + /* Write of data to dir should fail == zero bytes get written. */ + assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10)); + + /* "symlink" pointing to "file" */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 3, 30); + archive_entry_set_pathname(entry, "symlink"); + archive_entry_set_mode(entry, S_IFLNK | 0664); + archive_entry_set_symlink(entry,"file"); + archive_entry_set_size(entry, 0); + archive_entry_set_uid(entry, 88); + archive_entry_set_gid(entry, 98); + archive_entry_set_dev(entry, 12); + archive_entry_set_ino(entry, 90); + archive_entry_set_nlink(entry, 1); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + /* Write of data to symlink should fail == zero bytes get written. */ + assertEqualIntA(a, 0, archive_write_data(a, "1234567890", 10)); + + /* file with 99-char filename. */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 1, 10); + archive_entry_set_pathname(entry, f99); + archive_entry_set_mode(entry, S_IFREG | 0664); + archive_entry_set_size(entry, 0); + archive_entry_set_uid(entry, 82); + archive_entry_set_gid(entry, 93); + archive_entry_set_dev(entry, 102); + archive_entry_set_ino(entry, 7); + archive_entry_set_nlink(entry, 1); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + + /* file with 100-char filename. */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 1, 10); + archive_entry_set_pathname(entry, f100); + archive_entry_set_mode(entry, S_IFREG | 0664); + archive_entry_set_size(entry, 0); + archive_entry_set_uid(entry, 82); + archive_entry_set_gid(entry, 93); + archive_entry_set_dev(entry, 102); + archive_entry_set_ino(entry, 7); + archive_entry_set_nlink(entry, 1); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + + /* file with 256-char filename. */ + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_mtime(entry, 1, 10); + archive_entry_set_pathname(entry, f256); + archive_entry_set_mode(entry, S_IFREG | 0664); + archive_entry_set_size(entry, 0); + archive_entry_set_uid(entry, 82); + archive_entry_set_gid(entry, 93); + archive_entry_set_dev(entry, 102); + archive_entry_set_ino(entry, 7); + archive_entry_set_nlink(entry, 1); + assertEqualIntA(a, 0, archive_write_header(a, entry)); + archive_entry_free(entry); + + assert(0 == archive_write_finish(a)); + + /* + * Verify the archive format. + */ + e = buff; + + /* "file" */ + myAssertEqualMem(e + 0, "file", 5); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000120 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000132 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000012 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "010034\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "0", 1); /* linkflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + myAssertEqualMem(e + 512, "1234567890", 10); + assert(is_null(e + 512, 512)); + e += 1024; + + /* hardlink to "file" */ + myAssertEqualMem(e + 0, "linkfile", 9); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000120 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000132 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "010707\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "0", 1); /* linkflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* "dir" */ + myAssertEqualMem(e + 0, "dir/", 4); /* Filename */ + myAssertEqualMem(e + 100, "000775 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000000 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000000 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000002 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "007747\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "5", 1); /* typeflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* "symlink" pointing to "file" */ + myAssertEqualMem(e + 0, "symlink", 8); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000130 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000142 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000003 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "011446\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "2", 1); /* linkflag */ + myAssertEqualMem(e + 157, "file", 5); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* File with 99-char filename */ + myAssertEqualMem(e + 0, f99, 100); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000122 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000135 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "034242\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "0", 1); /* linkflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* File with 100-char filename */ + myAssertEqualMem(e + 0, f100, 100); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000122 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000135 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "026230\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "0", 1); /* linkflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, "", 1); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* File with 256-char filename */ + myAssertEqualMem(e + 0, f256 + 156, 100); /* Filename */ + myAssertEqualMem(e + 100, "000664 ", 8); /* mode */ + myAssertEqualMem(e + 108, "000122 ", 8); /* uid */ + myAssertEqualMem(e + 116, "000135 ", 8); /* gid */ + myAssertEqualMem(e + 124, "00000000000 ", 12); /* size */ + myAssertEqualMem(e + 136, "00000000001 ", 12); /* mtime */ + myAssertEqualMem(e + 148, "055570\0 ", 8); /* checksum */ + myAssertEqualMem(e + 156, "0", 1); /* linkflag */ + myAssertEqualMem(e + 157, "", 1); /* linkname */ + myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */ + myAssertEqualMem(e + 265, "", 1); /* uname */ + myAssertEqualMem(e + 297, "", 1); /* gname */ + myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */ + myAssertEqualMem(e + 337, "000000 ", 8); /* devminor */ + myAssertEqualMem(e + 345, f256, 155); /* prefix */ + assert(is_null(e + 0, 512)); + e += 512; + + /* TODO: Verify other types of entries. */ + + /* Last entry is end-of-archive marker. */ + assert(is_null(e, 1024)); + e += 1024; + + assertEqualInt(used, e - buff); + + free(buff); +} |