diff options
author | Martin Matuska <mm@FreeBSD.org> | 2016-05-11 10:19:44 +0000 |
---|---|---|
committer | Martin Matuska <mm@FreeBSD.org> | 2016-05-11 10:19:44 +0000 |
commit | 4e579f6c9425c995106fbf72667f4ec7e5d5db85 (patch) | |
tree | d298bb9fa3cf4f58e328e696a77fadf758a534eb /libarchive | |
parent | 8c8f03ca5b7f1f0d07c23e7c354a45b7c95335f0 (diff) | |
download | src-4e579f6c9425c995106fbf72667f4ec7e5d5db85.tar.gz src-4e579f6c9425c995106fbf72667f4ec7e5d5db85.zip |
Update vendor/libarchive/dist to git commit 61c56e5 (post 3.2.0)vendor/libarchive/3.2.0
Notes
Notes:
svn path=/vendor/libarchive/dist/; revision=299425
svn path=/vendor/libarchive/3.2.0/; revision=299528; tag=vendor/libarchive/3.2.0
Diffstat (limited to 'libarchive')
318 files changed, 54545 insertions, 4012 deletions
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index ecb0409bd9d8..4cc9a2ca2c17 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -18,8 +18,10 @@ SET(libarchive_SOURCES archive_cmdline.c archive_cmdline_private.h archive_crc32.h - archive_crypto.c - archive_crypto_private.h + archive_cryptor.c + archive_cryptor_private.h + archive_digest.c + archive_digest_private.h archive_endian.h archive_entry.c archive_entry.h @@ -32,9 +34,14 @@ SET(libarchive_SOURCES archive_entry_strmode.c archive_entry_xattr.c archive_getdate.c + archive_getdate.h + archive_hmac.c + archive_hmac_private.h archive_match.c archive_options.c archive_options_private.h + archive_pack_dev.h + archive_pack_dev.c archive_pathmatch.c archive_pathmatch.h archive_platform.h @@ -42,9 +49,12 @@ SET(libarchive_SOURCES archive_ppmd7.c archive_ppmd7_private.h archive_private.h + archive_random.c + archive_random_private.h archive_rb.c archive_rb.h archive_read.c + archive_read_add_passphrase.c archive_read_append_filter.c archive_read_data_into_fd.c archive_read_disk_entry_from_file.c @@ -52,6 +62,7 @@ SET(libarchive_SOURCES archive_read_disk_private.h archive_read_disk_set_standard_lookup.c archive_read_extract.c + archive_read_extract2.c archive_read_open_fd.c archive_read_open_file.c archive_read_open_filename.c @@ -65,6 +76,7 @@ SET(libarchive_SOURCES archive_read_support_filter_gzip.c archive_read_support_filter_grzip.c archive_read_support_filter_lrzip.c + archive_read_support_filter_lz4.c archive_read_support_filter_lzop.c archive_read_support_filter_none.c archive_read_support_filter_program.c @@ -84,6 +96,7 @@ SET(libarchive_SOURCES archive_read_support_format_rar.c archive_read_support_format_raw.c archive_read_support_format_tar.c + archive_read_support_format_warc.c archive_read_support_format_xar.c archive_read_support_format_zip.c archive_string.c @@ -110,6 +123,7 @@ SET(libarchive_SOURCES archive_write_add_filter_grzip.c archive_write_add_filter_gzip.c archive_write_add_filter_lrzip.c + archive_write_add_filter_lz4.c archive_write_add_filter_lzop.c archive_write_add_filter_none.c archive_write_add_filter_program.c @@ -121,18 +135,24 @@ SET(libarchive_SOURCES archive_write_set_format_by_name.c archive_write_set_format_cpio.c archive_write_set_format_cpio_newc.c + archive_write_set_format_filter_by_ext.c archive_write_set_format_gnutar.c archive_write_set_format_iso9660.c archive_write_set_format_mtree.c archive_write_set_format_pax.c + archive_write_set_format_raw.c archive_write_set_format_shar.c archive_write_set_format_ustar.c archive_write_set_format_v7tar.c + archive_write_set_format_warc.c archive_write_set_format_xar.c archive_write_set_format_zip.c archive_write_set_options.c + archive_write_set_passphrase.c + archive_xxhash.h filter_fork_posix.c filter_fork.h + xxhash.c ) # Man pages @@ -145,12 +165,14 @@ SET(libarchive_MANS archive_entry_stat.3 archive_entry_time.3 archive_read.3 + archive_read_add_passphrase.3 archive_read_disk.3 archive_read_set_options.3 archive_util.3 archive_write.3 archive_write_disk.3 archive_write_set_options.3 + archive_write_set_passphrase.3 cpio.5 libarchive.3 libarchive_internals.3 @@ -175,6 +197,7 @@ SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION}) # archive_static is a static library ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS}) +TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS}) SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS LIBARCHIVE_STATIC) # On Posix systems, libarchive.so and libarchive.a can co-exist. @@ -182,12 +205,14 @@ IF(NOT WIN32 OR CYGWIN) SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive) ENDIF(NOT WIN32 OR CYGWIN) -# How to install the libraries -INSTALL(TARGETS archive archive_static - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) -INSTALL_MAN(${libarchive_MANS}) -INSTALL(FILES ${include_HEADERS} DESTINATION include) +IF(ENABLE_INSTALL) + # How to install the libraries + INSTALL(TARGETS archive archive_static + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + INSTALL_MAN(${libarchive_MANS}) + INSTALL(FILES ${include_HEADERS} DESTINATION include) +ENDIF() add_subdirectory(test) diff --git a/libarchive/archive.h b/libarchive/archive.h index f56bc38e5b43..59e9ef15eb60 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -28,9 +28,20 @@ #ifndef ARCHIVE_H_INCLUDED #define ARCHIVE_H_INCLUDED +/* + * The version number is expressed as a single integer that makes it + * easy to compare versions at build time: for version a.b.c, the + * version number is printf("%d%03d%03d",a,b,c). For example, if you + * know your application requires version 2.12.108 or later, you can + * assert that ARCHIVE_VERSION_NUMBER >= 2012108. + */ +/* Note: Compiler will complain if this does not match archive_entry.h! */ +#define ARCHIVE_VERSION_NUMBER 3002000 + #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ #include <stdio.h> /* For FILE * */ +#include <time.h> /* For time_t */ /* * Note: archive.h is for use outside of libarchive; the configuration @@ -41,29 +52,53 @@ */ #if defined(__BORLANDC__) && __BORLANDC__ >= 0x560 # include <stdint.h> -#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) +#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) # include <inttypes.h> #endif -/* Get appropriate definitions of standard POSIX-style types. */ -/* These should match the types used in 'struct stat' */ -#if defined(_WIN32) && !defined(__CYGWIN__) -# define __LA_INT64_T __int64 -# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) -# define __LA_SSIZE_T ssize_t -# elif defined(_WIN64) -# define __LA_SSIZE_T __int64 -# else -# define __LA_SSIZE_T long +/* Get appropriate definitions of 64-bit integer */ +#if !defined(__LA_INT64_T_DEFINED) +/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */ +# if ARCHIVE_VERSION_NUMBER < 4000000 +#define __LA_INT64_T la_int64_t # endif -#else +#define __LA_INT64_T_DEFINED +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) +typedef __int64 la_int64_t; +# else # include <unistd.h> /* ssize_t */ -# if defined(_SCO_DS) -# define __LA_INT64_T long long +# if defined(_SCO_DS) || defined(__osf__) +typedef long long la_int64_t; +# else +typedef int64_t la_int64_t; +# endif +# endif +#endif + +/* The la_ssize_t should match the type used in 'struct stat' */ +#if !defined(__LA_SSIZE_T_DEFINED) +/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */ +# if ARCHIVE_VERSION_NUMBER < 4000000 +#define __LA_SSIZE_T la_ssize_t +# endif +#define __LA_SSIZE_T_DEFINED +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) +# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_) +typedef ssize_t la_ssize_t; +# elif defined(_WIN64) +typedef __int64 la_ssize_t; +# else +typedef long la_ssize_t; +# endif # else -# define __LA_INT64_T int64_t +# include <unistd.h> /* ssize_t */ +typedef ssize_t la_ssize_t; # endif -# define __LA_SSIZE_T ssize_t +#endif + +/* Large file support for Android */ +#ifdef __ANDROID__ +#include "android_lf.h" #endif /* @@ -115,24 +150,34 @@ extern "C" { * header and library are very different, you should expect some * strangeness. Don't do that. */ - -/* - * The version number is expressed as a single integer that makes it - * easy to compare versions at build time: for version a.b.c, the - * version number is printf("%d%03d%03d",a,b,c). For example, if you - * know your application requires version 2.12.108 or later, you can - * assert that ARCHIVE_VERSION_NUMBER >= 2012108. - */ -/* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3001002 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_STRING "libarchive 3.1.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.2.0" +#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); +/* + * Detailed textual name/version of the library and its dependencies. + * This has the form: + * "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..." + * the list of libraries described here will vary depending on how + * libarchive was compiled. + */ +__LA_DECL const char * archive_version_details(void); + +/* + * Returns NULL if libarchive was compiled without the associated library. + * Otherwise, returns the version number that libarchive was compiled + * against. + */ +__LA_DECL const char * archive_zlib_version(void); +__LA_DECL const char * archive_liblzma_version(void); +__LA_DECL const char * archive_bzlib_version(void); +__LA_DECL const char * archive_liblz4_version(void); + /* Declare our basic types. */ struct archive; struct archive_entry; @@ -173,7 +218,7 @@ struct archive_entry; */ /* Returns pointer and size of next block of data from archive. */ -typedef __LA_SSIZE_T archive_read_callback(struct archive *, +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. @@ -181,18 +226,18 @@ typedef __LA_SSIZE_T archive_read_callback(struct archive *, * If you do skip fewer bytes than requested, libarchive will invoke your * read callback and discard data as necessary to make up the full skip. */ -typedef __LA_INT64_T archive_skip_callback(struct archive *, - void *_client_data, __LA_INT64_T request); +typedef la_int64_t archive_skip_callback(struct archive *, + void *_client_data, la_int64_t request); /* Seeks to specified location in the file and returns the position. * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h. * Return ARCHIVE_FATAL if the seek fails for any reason. */ -typedef __LA_INT64_T archive_seek_callback(struct archive *, - void *_client_data, __LA_INT64_T offset, int whence); +typedef la_int64_t archive_seek_callback(struct archive *, + void *_client_data, la_int64_t offset, int whence); /* Returns size actually written, zero on EOF, -1 on error. */ -typedef __LA_SSIZE_T archive_write_callback(struct archive *, +typedef la_ssize_t archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); @@ -208,6 +253,13 @@ typedef int archive_switch_callback(struct archive *, void *_client_data1, void *_client_data2); /* + * Returns a passphrase used for encryption or decryption, NULL on nothing + * to do and give it up. + */ +typedef const char *archive_passphrase_callback(struct archive *, + void *_client_data); + +/* * Codes to identify various stream filters. */ #define ARCHIVE_FILTER_NONE 0 @@ -223,6 +275,7 @@ typedef int archive_switch_callback(struct archive *, void *_client_data1, #define ARCHIVE_FILTER_LRZIP 10 #define ARCHIVE_FILTER_LZOP 11 #define ARCHIVE_FILTER_GRZIP 12 +#define ARCHIVE_FILTER_LZ4 13 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE @@ -284,6 +337,31 @@ typedef int archive_switch_callback(struct archive *, void *_client_data1, #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 #define ARCHIVE_FORMAT_7ZIP 0xE0000 +#define ARCHIVE_FORMAT_WARC 0xF0000 + +/* + * Codes returned by archive_read_format_capabilities(). + * + * This list can be extended with values between 0 and 0xffff. + * The original purpose of this list was to let different archive + * format readers expose their general capabilities in terms of + * encryption. + */ +#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */ +#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */ +#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */ + +/* + * Codes returned by archive_read_has_encrypted_entries(). + * + * In case the archive does not support encryption detection at all + * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader + * for some other reason (e.g. not enough bytes read) cannot say if + * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW + * is returned. + */ +#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2 +#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1 /*- * Basic outline for reading an archive: @@ -342,6 +420,7 @@ __LA_DECL int archive_read_support_filter_compress(struct archive *); __LA_DECL int archive_read_support_filter_gzip(struct archive *); __LA_DECL int archive_read_support_filter_grzip(struct archive *); __LA_DECL int archive_read_support_filter_lrzip(struct archive *); +__LA_DECL int archive_read_support_filter_lz4(struct archive *); __LA_DECL int archive_read_support_filter_lzip(struct archive *); __LA_DECL int archive_read_support_filter_lzma(struct archive *); __LA_DECL int archive_read_support_filter_lzop(struct archive *); @@ -369,8 +448,17 @@ __LA_DECL int archive_read_support_format_mtree(struct archive *); __LA_DECL int archive_read_support_format_rar(struct archive *); __LA_DECL int archive_read_support_format_raw(struct archive *); __LA_DECL int archive_read_support_format_tar(struct archive *); +__LA_DECL int archive_read_support_format_warc(struct archive *); __LA_DECL int archive_read_support_format_xar(struct archive *); +/* archive_read_support_format_zip() enables both streamable and seekable + * zip readers. */ __LA_DECL int archive_read_support_format_zip(struct archive *); +/* Reads Zip archives as stream from beginning to end. Doesn't + * correctly handle SFX ZIP files or ZIP archives that have been modified + * in-place. */ +__LA_DECL int archive_read_support_format_zip_streamable(struct archive *); +/* Reads starting from central directory; requires seekable input. */ +__LA_DECL int archive_read_support_format_zip_seekable(struct archive *); /* Functions to manually set the format and filters to be used. This is * useful to bypass the bidding process when the format and filters to use @@ -441,9 +529,9 @@ __LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size) __LA_DEPRECATED; /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, - void * buff, size_t size); + const void * buff, size_t size); /* A more involved version that is only used for internal testing. */ -__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, +__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ __LA_DECL int archive_read_open_fd(struct archive *, int _fd, @@ -464,14 +552,40 @@ __LA_DECL int archive_read_next_header2(struct archive *, * Retrieve the byte offset in UNCOMPRESSED data where last-read * header started. */ -__LA_DECL __LA_INT64_T archive_read_header_position(struct archive *); +__LA_DECL la_int64_t archive_read_header_position(struct archive *); + +/* + * Returns 1 if the archive contains at least one encrypted entry. + * If the archive format not support encryption at all + * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. + * If for any other reason (e.g. not enough data read so far) + * we cannot say whether there are encrypted entries, then + * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned. + * In general, this function will return values below zero when the + * reader is uncertain or totally uncapable of encryption support. + * When this function returns 0 you can be sure that the reader + * supports encryption detection but no encrypted entries have + * been found yet. + * + * NOTE: If the metadata/header of an archive is also encrypted, you + * cannot rely on the number of encrypted entries. That is why this + * function does not return the number of encrypted entries but# + * just shows that there are some. + */ +__LA_DECL int archive_read_has_encrypted_entries(struct archive *); + +/* + * Returns a bitmask of capabilities that are supported by the archive format reader. + * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned. + */ +__LA_DECL int archive_read_format_capabilities(struct archive *); /* Read data from the body of an entry. Similar to read(2). */ -__LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, +__LA_DECL la_ssize_t archive_read_data(struct archive *, void *, size_t); /* Seek within the body of an entry. Similar to lseek(2). */ -__LA_DECL __LA_INT64_T archive_seek_data(struct archive *, __LA_INT64_T, int); +__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int); /* * A zero-copy version of archive_read_data that also exposes the file offset @@ -480,7 +594,7 @@ __LA_DECL __LA_INT64_T archive_seek_data(struct archive *, __LA_INT64_T, int); * be strictly increasing and that returned blocks will not overlap. */ __LA_DECL int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, __LA_INT64_T *offset); + const void **buff, size_t *size, la_int64_t *offset); /*- * Some convenience functions that are built on archive_read_data: @@ -510,6 +624,14 @@ __LA_DECL int archive_read_set_option(struct archive *_a, __LA_DECL int archive_read_set_options(struct archive *_a, const char *opts); +/* + * Add a decryption passphrase. + */ +__LA_DECL int archive_read_add_passphrase(struct archive *, const char *); +__LA_DECL int archive_read_set_passphrase_callback(struct archive *, + void *client_data, archive_passphrase_callback *); + + /*- * Convenience function to recreate the current entry (whose header * has just been read) on disk. @@ -562,6 +684,10 @@ __LA_DECL int archive_read_set_options(struct archive *_a, /* Default: Do not use HFS+ compression if it was not compressed. */ /* This has no effect except on Mac OS v10.6 or later. */ #define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000) +/* Default: Do not reject entries with absolute paths */ +#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000) +/* Default: Do not clear no-change flags when unlinking object */ +#define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); @@ -573,7 +699,7 @@ __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, /* 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. */ __LA_DECL void archive_read_extract_set_skip_file(struct archive *, - __LA_INT64_T, __LA_INT64_T); + la_int64_t, la_int64_t); /* Close the file and release most resources. */ __LA_DECL int archive_read_close(struct archive *); @@ -612,7 +738,7 @@ __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. */ __LA_DECL int archive_write_set_skip_file(struct archive *, - __LA_INT64_T, __LA_INT64_T); + la_int64_t, la_int64_t); #if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL int archive_write_set_compression_bzip2(struct archive *) @@ -643,6 +769,7 @@ __LA_DECL int archive_write_add_filter_compress(struct archive *); __LA_DECL int archive_write_add_filter_grzip(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); __LA_DECL int archive_write_add_filter_lrzip(struct archive *); +__LA_DECL int archive_write_add_filter_lz4(struct archive *); __LA_DECL int archive_write_add_filter_lzip(struct archive *); __LA_DECL int archive_write_add_filter_lzma(struct archive *); __LA_DECL int archive_write_add_filter_lzop(struct archive *); @@ -670,12 +797,16 @@ __LA_DECL int archive_write_set_format_mtree_classic(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __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_raw(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_set_format_v7tar(struct archive *); +__LA_DECL int archive_write_set_format_warc(struct archive *); __LA_DECL int archive_write_set_format_xar(struct archive *); __LA_DECL int archive_write_set_format_zip(struct archive *); +__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename); +__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext); __LA_DECL int archive_write_zip_set_compression_deflate(struct archive *); __LA_DECL int archive_write_zip_set_compression_store(struct archive *); __LA_DECL int archive_write_open(struct archive *, void *, @@ -700,12 +831,12 @@ __LA_DECL int archive_write_open_memory(struct archive *, */ __LA_DECL int archive_write_header(struct archive *, struct archive_entry *); -__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, +__LA_DECL la_ssize_t archive_write_data(struct archive *, const void *, size_t); /* This interface is currently only available for archive_write_disk handles. */ -__LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, - const void *, size_t, __LA_INT64_T); +__LA_DECL la_ssize_t archive_write_data_block(struct archive *, + const void *, size_t, la_int64_t); __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); @@ -740,6 +871,13 @@ __LA_DECL int archive_write_set_option(struct archive *_a, __LA_DECL int archive_write_set_options(struct archive *_a, const char *opts); +/* + * Set a encryption passphrase. + */ +__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p); +__LA_DECL int archive_write_set_passphrase_callback(struct archive *, + void *client_data, archive_passphrase_callback *); + /*- * ARCHIVE_WRITE_DISK API * @@ -759,7 +897,7 @@ __LA_DECL int archive_write_set_options(struct archive *_a, __LA_DECL struct archive *archive_write_disk_new(void); /* This file will not be overwritten. */ __LA_DECL int archive_write_disk_set_skip_file(struct archive *, - __LA_INT64_T, __LA_INT64_T); + la_int64_t, la_int64_t); /* Set flags to control how the next item gets created. * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */ __LA_DECL int archive_write_disk_set_options(struct archive *, @@ -789,14 +927,14 @@ __LA_DECL int archive_write_disk_set_standard_lookup(struct archive *); */ __LA_DECL int archive_write_disk_set_group_lookup(struct archive *, void * /* private_data */, - __LA_INT64_T (*)(void *, const char *, __LA_INT64_T), + la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); __LA_DECL int archive_write_disk_set_user_lookup(struct archive *, void * /* private_data */, - __LA_INT64_T (*)(void *, const char *, __LA_INT64_T), + la_int64_t (*)(void *, const char *, la_int64_t), void (* /* cleanup */)(void *)); -__LA_DECL __LA_INT64_T archive_write_disk_gid(struct archive *, const char *, __LA_INT64_T); -__LA_DECL __LA_INT64_T archive_write_disk_uid(struct archive *, const char *, __LA_INT64_T); +__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t); +__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t); /* * ARCHIVE_READ_DISK API @@ -817,19 +955,19 @@ __LA_DECL int archive_read_disk_entry_from_file(struct archive *, struct archive_entry *, int /* fd */, const struct stat *); /* Look up gname for gid or uname for uid. */ /* Default implementations are very, very stupid. */ -__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_INT64_T); -__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_INT64_T); +__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t); +__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t); /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the * results for performance. */ __LA_DECL int archive_read_disk_set_standard_lookup(struct archive *); /* You can install your own lookups if you like. */ __LA_DECL int archive_read_disk_set_gname_lookup(struct archive *, void * /* private_data */, - const char *(* /* lookup_fn */)(void *, __LA_INT64_T), + const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, void * /* private_data */, - const char *(* /* lookup_fn */)(void *, __LA_INT64_T), + const char *(* /* lookup_fn */)(void *, la_int64_t), void (* /* cleanup_fn */)(void *)); /* Start traversal. */ __LA_DECL int archive_read_disk_open(struct archive *, const char *); @@ -859,8 +997,10 @@ __LA_DECL int archive_read_disk_set_atime_restored(struct archive *); /* Default: Skip a mac resource fork file whose prefix is "._" because of * using copyfile. */ #define ARCHIVE_READDISK_MAC_COPYFILE (0x0004) -/* Default: Do not traverse mount points. */ +/* Default: Traverse mount points. */ #define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008) +/* Default: Xattrs are read from disk. */ +#define ARCHIVE_READDISK_NO_XATTR (0x0010) __LA_DECL int archive_read_disk_set_behavior(struct archive *, int flags); @@ -879,6 +1019,10 @@ __LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data); +/* Simplified cleanup interface; + * This calls archive_read_free() or archive_write_free() as needed. */ +__LA_DECL int archive_free(struct archive *); + /* * Accessor functions to read/set various information in * the struct archive object: @@ -889,7 +1033,7 @@ __LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *, * last filter, which is always the pseudo-filter that wraps the * client callbacks. */ __LA_DECL int archive_filter_count(struct archive *); -__LA_DECL __LA_INT64_T archive_filter_bytes(struct archive *, int); +__LA_DECL la_int64_t archive_filter_bytes(struct archive *, int); __LA_DECL int archive_filter_code(struct archive *, int); __LA_DECL const char * archive_filter_name(struct archive *, int); @@ -897,10 +1041,10 @@ __LA_DECL const char * archive_filter_name(struct archive *, int); /* These don't properly handle multiple filters, so are deprecated and * will eventually be removed. */ /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */ -__LA_DECL __LA_INT64_T archive_position_compressed(struct archive *) +__LA_DECL la_int64_t archive_position_compressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */ -__LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *) +__LA_DECL la_int64_t archive_position_uncompressed(struct archive *) __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ __LA_DECL const char *archive_compression_name(struct archive *) @@ -1016,8 +1160,8 @@ __LA_DECL int archive_match_exclude_entry(struct archive *, __LA_DECL int archive_match_owner_excluded(struct archive *, struct archive_entry *); /* Add inclusion uid, gid, uname and gname. */ -__LA_DECL int archive_match_include_uid(struct archive *, __LA_INT64_T); -__LA_DECL int archive_match_include_gid(struct archive *, __LA_INT64_T); +__LA_DECL int archive_match_include_uid(struct archive *, la_int64_t); +__LA_DECL int archive_match_include_gid(struct archive *, la_int64_t); __LA_DECL int archive_match_include_uname(struct archive *, const char *); __LA_DECL int archive_match_include_uname_w(struct archive *, const wchar_t *); @@ -1025,6 +1169,10 @@ __LA_DECL int archive_match_include_gname(struct archive *, const char *); __LA_DECL int archive_match_include_gname_w(struct archive *, const wchar_t *); +/* Utility functions */ +/* Convenience function to sort a NULL terminated list of strings */ +__LA_DECL int archive_utility_string_sort(char **); + #ifdef __cplusplus } #endif @@ -1032,9 +1180,4 @@ __LA_DECL int archive_match_include_gname_w(struct archive *, /* These are meaningless outside of this header. */ #undef __LA_DECL -/* These need to remain defined because they're used in the - * callback type definitions. XXX Fix this. This is ugly. XXX */ -/* #undef __LA_INT64_T */ -/* #undef __LA_SSIZE_T */ - #endif /* !ARCHIVE_H_INCLUDED */ diff --git a/libarchive/archive_cryptor.c b/libarchive/archive_cryptor.c new file mode 100644 index 000000000000..0be30c601a94 --- /dev/null +++ b/libarchive/archive_cryptor.c @@ -0,0 +1,448 @@ +/*- +* Copyright (c) 2014 Michihiro NAKAJIMA +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "archive_platform.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include "archive.h" +#include "archive_cryptor_private.h" + +/* + * On systems that do not support any recognized crypto libraries, + * this file will normally define no usable symbols. + * + * But some compilers and linkers choke on empty object files, so + * define a public symbol that will always exist. This could + * be removed someday if this file gains another always-present + * symbol definition. + */ +int __libarchive_cryptor_build_hack(void) { + return 0; +} + +#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) +{ + CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw, + pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds, + derived_key, derived_key_len); + return 0; +} + +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#ifdef _MSC_VER +#pragma comment(lib, "Bcrypt.lib") +#endif + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) +{ + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg; + + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, + MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); + if (!BCRYPT_SUCCESS(status)) + return -1; + + status = BCryptDeriveKeyPBKDF2(hAlg, + (PUCHAR)(uintptr_t)pw, (ULONG)pw_len, + (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds, + (PUCHAR)derived_key, (ULONG)derived_key_len, 0); + + BCryptCloseAlgorithmProvider(hAlg, 0); + + return (BCRYPT_SUCCESS(status)) ? 0: -1; +} + +#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H) + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) { + pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds, + salt_len, salt, derived_key_len, derived_key); + return 0; +} + +#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1) + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) { + + PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds, + derived_key_len, derived_key); + return 0; +} + +#else + +/* Stub */ +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) { + (void)pw; /* UNUSED */ + (void)pw_len; /* UNUSED */ + (void)salt; /* UNUSED */ + (void)salt_len; /* UNUSED */ + (void)rounds; /* UNUSED */ + (void)derived_key; /* UNUSED */ + (void)derived_key_len; /* UNUSED */ + return -1; /* UNSUPPORTED */ +} + +#endif + +#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto +# if MAC_OS_X_VERSION_MAX_ALLOWED < 1090 +# define kCCAlgorithmAES kCCAlgorithmAES128 +# endif + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + CCCryptorStatus r; + + ctx->key_len = key_len; + memcpy(ctx->key, key, key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + ctx->encr_pos = AES_BLOCK_SIZE; + r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES, + ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx); + return (r == kCCSuccess)? 0: -1; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + CCCryptorRef ref = ctx->ctx; + CCCryptorStatus r; + + r = CCCryptorReset(ref, NULL); + if (r != kCCSuccess) + return -1; + r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf, + AES_BLOCK_SIZE, NULL); + return (r == kCCSuccess)? 0: -1; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + memset(ctx->key, 0, ctx->key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + return 0; +} + +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_KEY_HANDLE hKey; + DWORD keyObj_len, aes_key_len; + PBYTE keyObj; + ULONG result; + NTSTATUS status; + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + + ctx->hAlg = NULL; + ctx->hKey = NULL; + ctx->keyObj = NULL; + switch (key_len) { + case 16: aes_key_len = 128; break; + case 24: aes_key_len = 192; break; + case 32: aes_key_len = 256; break; + default: return -1; + } + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if (!BCRYPT_SUCCESS(status)) + return -1; + status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths, + sizeof(key_lengths), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + if (key_lengths.dwMinLength > aes_key_len + || key_lengths.dwMaxLength < aes_key_len) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len, + sizeof(keyObj_len), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len); + if (keyObj == NULL) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, + (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, keyObj); + return -1; + } + status = BCryptGenerateSymmetricKey(hAlg, &hKey, + keyObj, keyObj_len, + (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, keyObj); + return -1; + } + + ctx->hAlg = hAlg; + ctx->hKey = hKey; + ctx->keyObj = keyObj; + ctx->keyObj_len = keyObj_len; + ctx->encr_pos = AES_BLOCK_SIZE; + + return 0; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + NTSTATUS status; + ULONG result; + + status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE, + NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE, + &result, 0); + return BCRYPT_SUCCESS(status) ? 0 : -1; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + + if (ctx->hAlg != NULL) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + ctx->hAlg = NULL; + BCryptDestroyKey(ctx->hKey); + ctx->hKey = NULL; + HeapFree(GetProcessHeap(), 0, ctx->keyObj); + ctx->keyObj = NULL; + } + memset(ctx, 0, sizeof(*ctx)); + return 0; +} + +#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + ctx->key_len = key_len; + memcpy(ctx->key, key, key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + ctx->encr_pos = AES_BLOCK_SIZE; + memset(&ctx->ctx, 0, sizeof(ctx->ctx)); + return 0; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key); + aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce); + return 0; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + return 0; +} + +#elif defined(HAVE_LIBCRYPTO) + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + + switch (key_len) { + case 16: ctx->type = EVP_aes_128_ecb(); break; + case 24: ctx->type = EVP_aes_192_ecb(); break; + case 32: ctx->type = EVP_aes_256_ecb(); break; + default: ctx->type = NULL; return -1; + } + + ctx->key_len = key_len; + memcpy(ctx->key, key, key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + ctx->encr_pos = AES_BLOCK_SIZE; + EVP_CIPHER_CTX_init(&ctx->ctx); + return 0; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + int outl = 0; + int r; + + r = EVP_EncryptInit_ex(&ctx->ctx, ctx->type, NULL, ctx->key, NULL); + if (r == 0) + return -1; + r = EVP_EncryptUpdate(&ctx->ctx, ctx->encr_buf, &outl, ctx->nonce, + AES_BLOCK_SIZE); + if (r == 0 || outl != AES_BLOCK_SIZE) + return -1; + return 0; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + EVP_CIPHER_CTX_cleanup(&ctx->ctx); + memset(ctx->key, 0, ctx->key_len); + memset(ctx->nonce, 0, sizeof(ctx->nonce)); + return 0; +} + +#else + +#define ARCHIVE_CRYPTOR_STUB +/* Stub */ +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + (void)ctx; /* UNUSED */ + (void)key; /* UNUSED */ + (void)key_len; /* UNUSED */ + return -1; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + (void)ctx; /* UNUSED */ + return -1; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + (void)ctx; /* UNUSED */ + return 0; +} + +#endif + +#ifdef ARCHIVE_CRYPTOR_STUB +static int +aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, + size_t in_len, uint8_t * const out, size_t *out_len) +{ + (void)ctx; /* UNUSED */ + (void)in; /* UNUSED */ + (void)in_len; /* UNUSED */ + (void)out; /* UNUSED */ + (void)out_len; /* UNUSED */ + aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */ + return -1; +} + +#else +static void +aes_ctr_increase_counter(archive_crypto_ctx *ctx) +{ + uint8_t *const nonce = ctx->nonce; + int j; + + for (j = 0; j < 8; j++) { + if (++nonce[j]) + break; + } +} + +static int +aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, + size_t in_len, uint8_t * const out, size_t *out_len) +{ + uint8_t *const ebuf = ctx->encr_buf; + unsigned pos = ctx->encr_pos; + unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len); + unsigned i; + + for (i = 0; i < max; ) { + if (pos == AES_BLOCK_SIZE) { + aes_ctr_increase_counter(ctx); + if (aes_ctr_encrypt_counter(ctx) != 0) + return -1; + while (max -i >= AES_BLOCK_SIZE) { + for (pos = 0; pos < AES_BLOCK_SIZE; pos++) + out[i+pos] = in[i+pos] ^ ebuf[pos]; + i += AES_BLOCK_SIZE; + aes_ctr_increase_counter(ctx); + if (aes_ctr_encrypt_counter(ctx) != 0) + return -1; + } + pos = 0; + if (i >= max) + break; + } + out[i] = in[i] ^ ebuf[pos++]; + i++; + } + ctx->encr_pos = pos; + *out_len = i; + + return 0; +} +#endif /* ARCHIVE_CRYPTOR_STUB */ + + +const struct archive_cryptor __archive_cryptor = +{ + &pbkdf2_sha1, + &aes_ctr_init, + &aes_ctr_update, + &aes_ctr_release, + &aes_ctr_init, + &aes_ctr_update, + &aes_ctr_release, +}; diff --git a/libarchive/archive_cryptor_private.h b/libarchive/archive_cryptor_private.h new file mode 100644 index 000000000000..37eaad369dc7 --- /dev/null +++ b/libarchive/archive_cryptor_private.h @@ -0,0 +1,163 @@ +/*- +* Copyright (c) 2014 Michihiro NAKAJIMA +* 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. +*/ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED +#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED + +/* + * On systems that do not support any recognized crypto libraries, + * the archive_cryptor.c file will normally define no usable symbols. + * + * But some compilers and linkers choke on empty object files, so + * define a public symbol that will always exist. This could + * be removed someday if this file gains another always-present + * symbol definition. + */ +int __libarchive_cryptor_build_hack(void); + +#ifdef __APPLE__ +# include <AvailabilityMacros.h> +# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 +# define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto +# endif +#endif + +#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto +#include <CommonCrypto/CommonCryptor.h> +#include <CommonCrypto/CommonKeyDerivation.h> +#define AES_BLOCK_SIZE 16 +#define AES_MAX_KEY_SIZE kCCKeySizeAES256 + +typedef struct { + CCCryptorRef ctx; + uint8_t key[AES_MAX_KEY_SIZE]; + unsigned key_len; + uint8_t nonce[AES_BLOCK_SIZE]; + uint8_t encr_buf[AES_BLOCK_SIZE]; + unsigned encr_pos; +} archive_crypto_ctx; + +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#include <Bcrypt.h> + +/* Common in other bcrypt implementations, but missing from VS2008. */ +#ifndef BCRYPT_SUCCESS +#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) +#endif + +#define AES_MAX_KEY_SIZE 32 +#define AES_BLOCK_SIZE 16 +typedef struct { + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_KEY_HANDLE hKey; + PBYTE keyObj; + DWORD keyObj_len; + uint8_t nonce[AES_BLOCK_SIZE]; + uint8_t encr_buf[AES_BLOCK_SIZE]; + unsigned encr_pos; +} archive_crypto_ctx; + +#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H) +#if defined(HAVE_NETTLE_PBKDF2_H) +#include <nettle/pbkdf2.h> +#endif +#include <nettle/aes.h> + +typedef struct { + struct aes_ctx ctx; + uint8_t key[AES_MAX_KEY_SIZE]; + unsigned key_len; + uint8_t nonce[AES_BLOCK_SIZE]; + uint8_t encr_buf[AES_BLOCK_SIZE]; + unsigned encr_pos; +} archive_crypto_ctx; + +#elif defined(HAVE_LIBCRYPTO) +#include <openssl/evp.h> +#define AES_BLOCK_SIZE 16 +#define AES_MAX_KEY_SIZE 32 + +typedef struct { + EVP_CIPHER_CTX ctx; + const EVP_CIPHER *type; + uint8_t key[AES_MAX_KEY_SIZE]; + unsigned key_len; + uint8_t nonce[AES_BLOCK_SIZE]; + uint8_t encr_buf[AES_BLOCK_SIZE]; + unsigned encr_pos; +} archive_crypto_ctx; + +#else + +#define AES_BLOCK_SIZE 16 +#define AES_MAX_KEY_SIZE 32 +typedef int archive_crypto_ctx; + +#endif + +/* defines */ +#define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\ + __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len) + +#define archive_decrypto_aes_ctr_init(ctx, key, key_len) \ + __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len) +#define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \ + __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) +#define archive_decrypto_aes_ctr_release(ctx) \ + __archive_cryptor.decrypto_aes_ctr_release(ctx) + +#define archive_encrypto_aes_ctr_init(ctx, key, key_len) \ + __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len) +#define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \ + __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) +#define archive_encrypto_aes_ctr_release(ctx) \ + __archive_cryptor.encrypto_aes_ctr_release(ctx) + +/* Minimal interface to cryptographic functionality for internal use in + * libarchive */ +struct archive_cryptor +{ + /* PKCS5 PBKDF2 HMAC-SHA1 */ + int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len); + /* AES CTR mode(little endian version) */ + int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t); + int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *, + size_t, uint8_t *, size_t *); + int (*decrypto_aes_ctr_release)(archive_crypto_ctx *); + int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t); + int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *, + size_t, uint8_t *, size_t *); + int (*encrypto_aes_ctr_release)(archive_crypto_ctx *); +}; + +extern const struct archive_cryptor __archive_cryptor; + +#endif diff --git a/libarchive/archive_crypto.c b/libarchive/archive_digest.c index 85aba3ae27cd..f009d317aeef 100644 --- a/libarchive/archive_crypto.c +++ b/libarchive/archive_digest.c @@ -28,7 +28,7 @@ #include "archive_platform.h" #include "archive.h" -#include "archive_crypto_private.h" +#include "archive_digest_private.h" /* In particular, force the configure probe to break if it tries * to test a combination of OpenSSL and libmd. */ @@ -1216,8 +1216,8 @@ __archive_stub_sha512final(archive_sha512_ctx *ctx, void *md) #endif -/* NOTE: Crypto functions are set based on availability and by the following - * order of preference. +/* NOTE: Message Digest functions are set based on availability and by the + * following order of preference. * 1. libc * 2. libc2 * 3. libc3 @@ -1227,7 +1227,7 @@ __archive_stub_sha512final(archive_sha512_ctx *ctx, void *md) * 7. libmd * 8. Windows API */ -const struct archive_crypto __archive_crypto = +const struct archive_digest __archive_digest = { /* MD5 */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) @@ -1412,7 +1412,7 @@ const struct archive_crypto __archive_crypto = #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE) &__archive_nettle_sha512init, &__archive_nettle_sha512update, - &__archive_nettle_sha512final, + &__archive_nettle_sha512final #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) &__archive_openssl_sha512init, &__archive_openssl_sha512update, diff --git a/libarchive/archive_crypto_private.h b/libarchive/archive_digest_private.h index f8b1fb3c3f54..77fad5806fdd 100644 --- a/libarchive/archive_crypto_private.h +++ b/libarchive/archive_digest_private.h @@ -264,11 +264,11 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_MD5 #endif #define archive_md5_init(ctx)\ - __archive_crypto.md5init(ctx) + __archive_digest.md5init(ctx) #define archive_md5_final(ctx, md)\ - __archive_crypto.md5final(ctx, md) + __archive_digest.md5final(ctx, md) #define archive_md5_update(ctx, buf, n)\ - __archive_crypto.md5update(ctx, buf, n) + __archive_digest.md5update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\ @@ -276,11 +276,11 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_RMD160 #endif #define archive_rmd160_init(ctx)\ - __archive_crypto.rmd160init(ctx) + __archive_digest.rmd160init(ctx) #define archive_rmd160_final(ctx, md)\ - __archive_crypto.rmd160final(ctx, md) + __archive_digest.rmd160final(ctx, md) #define archive_rmd160_update(ctx, buf, n)\ - __archive_crypto.rmd160update(ctx, buf, n) + __archive_digest.rmd160update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \ @@ -291,11 +291,11 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_SHA1 #endif #define archive_sha1_init(ctx)\ - __archive_crypto.sha1init(ctx) + __archive_digest.sha1init(ctx) #define archive_sha1_final(ctx, md)\ - __archive_crypto.sha1final(ctx, md) + __archive_digest.sha1final(ctx, md) #define archive_sha1_update(ctx, buf, n)\ - __archive_crypto.sha1update(ctx, buf, n) + __archive_digest.sha1update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\ @@ -308,11 +308,11 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_SHA256 #endif #define archive_sha256_init(ctx)\ - __archive_crypto.sha256init(ctx) + __archive_digest.sha256init(ctx) #define archive_sha256_final(ctx, md)\ - __archive_crypto.sha256final(ctx, md) + __archive_digest.sha256final(ctx, md) #define archive_sha256_update(ctx, buf, n)\ - __archive_crypto.sha256update(ctx, buf, n) + __archive_digest.sha256update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\ @@ -324,11 +324,11 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_SHA384 #endif #define archive_sha384_init(ctx)\ - __archive_crypto.sha384init(ctx) + __archive_digest.sha384init(ctx) #define archive_sha384_final(ctx, md)\ - __archive_crypto.sha384final(ctx, md) + __archive_digest.sha384final(ctx, md) #define archive_sha384_update(ctx, buf, n)\ - __archive_crypto.sha384update(ctx, buf, n) + __archive_digest.sha384update(ctx, buf, n) #if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\ @@ -341,14 +341,14 @@ typedef unsigned char archive_sha512_ctx; #define ARCHIVE_HAS_SHA512 #endif #define archive_sha512_init(ctx)\ - __archive_crypto.sha512init(ctx) + __archive_digest.sha512init(ctx) #define archive_sha512_final(ctx, md)\ - __archive_crypto.sha512final(ctx, md) + __archive_digest.sha512final(ctx, md) #define archive_sha512_update(ctx, buf, n)\ - __archive_crypto.sha512update(ctx, buf, n) + __archive_digest.sha512update(ctx, buf, n) -/* Minimal interface to crypto functionality for internal use in libarchive */ -struct archive_crypto +/* Minimal interface to digest functionality for internal use in libarchive */ +struct archive_digest { /* Message Digest */ int (*md5init)(archive_md5_ctx *ctx); @@ -371,6 +371,6 @@ struct archive_crypto int (*sha512final)(archive_sha512_ctx *, void *); }; -extern const struct archive_crypto __archive_crypto; +extern const struct archive_digest __archive_digest; #endif diff --git a/libarchive/archive_endian.h b/libarchive/archive_endian.h index 68123b0dd6a3..1c48563b137d 100644 --- a/libarchive/archive_endian.h +++ b/libarchive/archive_endian.h @@ -44,10 +44,16 @@ * - Watcom C++ in C code. (For any version?) * - SGI MIPSpro * - Microsoft Visual C++ 6.0 (supposedly newer versions too) + * - IBM VisualAge 6 (XL v6) + * - Sun WorkShop C (SunPro) before 5.9 */ #if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__) #define inline -#elif defined(_MSC_VER) +#elif defined(__IBMC__) && __IBMC__ < 700 +#define inline +#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590 +#define inline +#elif defined(_MSC_VER) || defined(__osf__) #define inline __inline #endif @@ -58,7 +64,13 @@ archive_be16dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; - return ((p[0] << 8) | p[1]); + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p0 << 8) | p1); } static inline uint32_t @@ -66,7 +78,15 @@ archive_be32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; - return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p3 = p[3]; + unsigned int p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p0 << 24) | (p1 << 16) | (p2 << 8) | p3); } static inline uint64_t @@ -82,7 +102,13 @@ archive_le16dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; - return ((p[1] << 8) | p[0]); + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p1 << 8) | p0); } static inline uint32_t @@ -90,7 +116,15 @@ archive_le32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; - return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); + /* Store into unsigned temporaries before left shifting, to avoid + promotion to signed int and then left shifting into the sign bit, + which is undefined behaviour. */ + unsigned int p3 = p[3]; + unsigned int p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p3 << 24) | (p2 << 16) | (p1 << 8) | p0); } static inline uint64_t diff --git a/libarchive/archive_entry.3 b/libarchive/archive_entry.3 index f77f385f2e2c..f5e22af85122 100644 --- a/libarchive/archive_entry.3 +++ b/libarchive/archive_entry.3 @@ -131,11 +131,11 @@ be discarded in favor of the new data. .\" .Sh RETURN VALUES .\" .Sh ERRORS .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry_acl 3 , .Xr archive_entry_paths 3 , .Xr archive_entry_perms 3 , .Xr archive_entry_time 3 +.Xr libarchive 3 , .Sh HISTORY The .Nm libarchive diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 386e51d473e8..4ac196608938 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -201,6 +201,9 @@ archive_entry_clone(struct archive_entry *entry) entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); + /* Copy encryption status */ + entry2->encryption = entry->encryption; + /* Copy ACL data over. */ archive_acl_copy(&entry2->acl, &entry->acl); @@ -415,6 +418,18 @@ archive_entry_gname(struct archive_entry *entry) return (NULL); } +const char * +archive_entry_gname_utf8(struct archive_entry *entry) +{ + const char *p; + if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) + return (p); + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); +} + + const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { @@ -447,6 +462,20 @@ archive_entry_hardlink(struct archive_entry *entry) return (NULL); } +const char * +archive_entry_hardlink_utf8(struct archive_entry *entry) +{ + const char *p; + if ((entry->ae_set & AE_SET_HARDLINK) == 0) + return (NULL); + if (archive_mstring_get_utf8( + entry->archive, &entry->ae_hardlink, &p) == 0) + return (p); + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); +} + const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { @@ -533,6 +562,18 @@ archive_entry_pathname(struct archive_entry *entry) return (NULL); } +const char * +archive_entry_pathname_utf8(struct archive_entry *entry) +{ + const char *p; + if (archive_mstring_get_utf8( + entry->archive, &entry->ae_pathname, &p) == 0) + return (p); + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); +} + const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { @@ -634,6 +675,20 @@ archive_entry_symlink(struct archive_entry *entry) return (NULL); } +const char * +archive_entry_symlink_utf8(struct archive_entry *entry) +{ + const char *p; + if ((entry->ae_set & AE_SET_SYMLINK) == 0) + return (NULL); + if (archive_mstring_get_utf8( + entry->archive, &entry->ae_symlink, &p) == 0) + return (p); + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); +} + const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { @@ -677,6 +732,17 @@ archive_entry_uname(struct archive_entry *entry) return (NULL); } +const char * +archive_entry_uname_utf8(struct archive_entry *entry) +{ + const char *p; + if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0) + return (p); + if (errno == ENOMEM) + __archive_errx(1, "No memory"); + return (NULL); +} + const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { @@ -695,6 +761,24 @@ _archive_entry_uname_l(struct archive_entry *entry, return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc)); } +int +archive_entry_is_data_encrypted(struct archive_entry *entry) +{ + return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA); +} + +int +archive_entry_is_metadata_encrypted(struct archive_entry *entry) +{ + return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA); +} + +int +archive_entry_is_encrypted(struct archive_entry *entry) +{ + return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA)); +} + /* * Functions to set archive_entry properties. */ @@ -748,6 +832,12 @@ archive_entry_set_gname(struct archive_entry *entry, const char *name) } void +archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name) +{ + archive_mstring_copy_utf8(&entry->ae_gname, name); +} + +void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); @@ -804,6 +894,16 @@ archive_entry_set_hardlink(struct archive_entry *entry, const char *target) } void +archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) +{ + archive_mstring_copy_utf8(&entry->ae_hardlink, target); + if (target != NULL) + entry->ae_set |= AE_SET_HARDLINK; + else + entry->ae_set &= ~AE_SET_HARDLINK; +} + +void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); @@ -941,6 +1041,15 @@ archive_entry_set_link(struct archive_entry *entry, const char *target) archive_mstring_copy_mbs(&entry->ae_hardlink, target); } +void +archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) +{ + if (entry->ae_set & AE_SET_SYMLINK) + archive_mstring_copy_utf8(&entry->ae_symlink, target); + else + archive_mstring_copy_utf8(&entry->ae_hardlink, target); +} + /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) @@ -1031,6 +1140,12 @@ archive_entry_set_pathname(struct archive_entry *entry, const char *name) } void +archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name) +{ + archive_mstring_copy_utf8(&entry->ae_pathname, name); +} + +void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); @@ -1131,6 +1246,16 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) } void +archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) +{ + archive_mstring_copy_utf8(&entry->ae_symlink, linkname); + if (linkname != NULL) + entry->ae_set |= AE_SET_SYMLINK; + else + entry->ae_set &= ~AE_SET_SYMLINK; +} + +void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); @@ -1194,6 +1319,12 @@ archive_entry_set_uname(struct archive_entry *entry, const char *name) } void +archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name) +{ + archive_mstring_copy_utf8(&entry->ae_uname, name); +} + +void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); @@ -1216,6 +1347,26 @@ archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) return (0); } +void +archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted) +{ + if (is_encrypted) { + entry->encryption |= AE_ENCRYPTION_DATA; + } else { + entry->encryption &= ~AE_ENCRYPTION_DATA; + } +} + +void +archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted) +{ + if (is_encrypted) { + entry->encryption |= AE_ENCRYPTION_METADATA; + } else { + entry->encryption &= ~AE_ENCRYPTION_METADATA; + } +} + int _archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) @@ -1588,19 +1739,23 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { + size_t length; /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; + length = end - start; for (flag = flags; flag->name != NULL; flag++) { - if (memcmp(start, flag->name, end - start) == 0) { + size_t flag_length = strlen(flag->name); + if (length == flag_length + && memcmp(start, flag->name, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; - } else if (memcmp(start, flag->name + 2, end - start) - == 0) { + } else if (length == flag_length - 2 + && memcmp(start, flag->name + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; @@ -1652,19 +1807,23 @@ ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { + size_t length; /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; + length = end - start; for (flag = flags; flag->wname != NULL; flag++) { - if (wmemcmp(start, flag->wname, end - start) == 0) { + size_t flag_length = wcslen(flag->wname); + if (length == flag_length + && wmemcmp(start, flag->wname, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; - } else if (wmemcmp(start, flag->wname + 2, end - start) - == 0) { + } else if (length == flag_length - 2 + && wmemcmp(start, flag->wname + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index a9050652e6a4..423c8d3ff7b1 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -29,7 +29,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3001002 +#define ARCHIVE_VERSION_NUMBER 3002000 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -48,14 +48,20 @@ #endif /* Get a suitable 64-bit integer type. */ -#if defined(_WIN32) && !defined(__CYGWIN__) -# define __LA_INT64_T __int64 -#else -#include <unistd.h> -# if defined(_SCO_DS) -# define __LA_INT64_T long long +#if !defined(__LA_INT64_T_DEFINED) +# if ARCHIVE_VERSION_NUMBER < 4000000 +#define __LA_INT64_T la_int64_t +# endif +#define __LA_INT64_T_DEFINED +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__) +typedef __int64 la_int64_t; # else -# define __LA_INT64_T int64_t +#include <unistd.h> +# if defined(_SCO_DS) || defined(__osf__) +typedef long long la_int64_t; +# else +typedef int64_t la_int64_t; +# endif # endif #endif @@ -63,12 +69,17 @@ #if ARCHIVE_VERSION_NUMBER >= 3999000 /* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */ # define __LA_MODE_T int -#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) +#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__) # define __LA_MODE_T unsigned short #else # define __LA_MODE_T mode_t #endif +/* Large file support for Android */ +#ifdef __ANDROID__ +#include "android_lf.h" +#endif + /* * On Windows, define LIBARCHIVE_STATIC if you're building or using a * .lib. The default here assumes you're building a DLL. Only @@ -206,13 +217,15 @@ __LA_DECL void archive_entry_fflags(struct archive_entry *, unsigned long * /* set */, unsigned long * /* clear */); __LA_DECL const char *archive_entry_fflags_text(struct archive_entry *); -__LA_DECL __LA_INT64_T archive_entry_gid(struct archive_entry *); +__LA_DECL la_int64_t archive_entry_gid(struct archive_entry *); __LA_DECL const char *archive_entry_gname(struct archive_entry *); +__LA_DECL const char *archive_entry_gname_utf8(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 char *archive_entry_hardlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *); -__LA_DECL __LA_INT64_T archive_entry_ino(struct archive_entry *); -__LA_DECL __LA_INT64_T archive_entry_ino64(struct archive_entry *); +__LA_DECL la_int64_t archive_entry_ino(struct archive_entry *); +__LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *); __LA_DECL int archive_entry_ino_is_set(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *); __LA_DECL time_t archive_entry_mtime(struct archive_entry *); @@ -220,6 +233,7 @@ __LA_DECL long archive_entry_mtime_nsec(struct archive_entry *); __LA_DECL int archive_entry_mtime_is_set(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 char *archive_entry_pathname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *); __LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *); __LA_DECL dev_t archive_entry_rdev(struct archive_entry *); @@ -227,14 +241,19 @@ __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 const wchar_t *archive_entry_sourcepath_w(struct archive_entry *); -__LA_DECL __LA_INT64_T archive_entry_size(struct archive_entry *); +__LA_DECL la_int64_t archive_entry_size(struct archive_entry *); __LA_DECL int archive_entry_size_is_set(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 char *archive_entry_symlink_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); -__LA_DECL __LA_INT64_T archive_entry_uid(struct archive_entry *); +__LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); +__LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *); +__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *); +__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *); +__LA_DECL int archive_entry_is_encrypted(struct archive_entry *); /* * Set fields in an archive_entry. @@ -266,18 +285,21 @@ __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 *); -__LA_DECL void archive_entry_set_gid(struct archive_entry *, __LA_INT64_T); +__LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_gname_utf8(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_set_hardlink_utf8(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 int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); -__LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INT64_T); -__LA_DECL void archive_entry_set_ino64(struct archive_entry *, __LA_INT64_T); +__LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t); +__LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_link_utf8(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 *); @@ -286,6 +308,7 @@ __LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long); __LA_DECL void archive_entry_unset_mtime(struct archive_entry *); __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_set_pathname_utf8(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 *); @@ -293,19 +316,23 @@ __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 *, __LA_INT64_T); +__LA_DECL void archive_entry_set_size(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink_utf8(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 int archive_entry_update_symlink_utf8(struct archive_entry *, const char *); -__LA_DECL void archive_entry_set_uid(struct archive_entry *, __LA_INT64_T); +__LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t); __LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_uname_utf8(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 *); +__LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted); +__LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted); /* * Routines to bulk copy fields to/from a platform-native "struct * stat." Libarchive used to just store a struct stat inside of each @@ -514,7 +541,7 @@ __LA_DECL int archive_entry_xattr_next(struct archive_entry *, __LA_DECL void archive_entry_sparse_clear(struct archive_entry *); __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, - __LA_INT64_T /* offset */, __LA_INT64_T /* length */); + la_int64_t /* offset */, la_int64_t /* length */); /* * To retrieve the xattr list, first "reset", then repeatedly ask for the @@ -524,7 +551,7 @@ __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, __LA_DECL int archive_entry_sparse_count(struct archive_entry *); __LA_DECL int archive_entry_sparse_reset(struct archive_entry *); __LA_DECL int archive_entry_sparse_next(struct archive_entry *, - __LA_INT64_T * /* offset */, __LA_INT64_T * /* length */); + la_int64_t * /* offset */, la_int64_t * /* length */); /* * Utility to match up hardlinks. diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3 index f5c337733ce8..5aff99682352 100644 --- a/libarchive/archive_entry_acl.3 +++ b/libarchive/archive_entry_acl.3 @@ -226,8 +226,8 @@ The returned long string is valid until the next call to or .Fn archive_entry_acl_text_w . .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry 3 +.Xr libarchive 3 , .Sh BUGS .Dv ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID and diff --git a/libarchive/archive_entry_copy_stat.c b/libarchive/archive_entry_copy_stat.c index 37d4d6edbd86..ac83868e8f8a 100644 --- a/libarchive/archive_entry_copy_stat.c +++ b/libarchive/archive_entry_copy_stat.c @@ -44,6 +44,10 @@ archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st) archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec); archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); +#elif HAVE_STRUCT_STAT_ST_MTIME_NSEC + archive_entry_set_atime(entry, st->st_atime, st->st_atime_nsec); + archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_nsec); + archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_atime(entry, st->st_atime, st->st_atime_n); archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n); diff --git a/libarchive/archive_entry_paths.3 b/libarchive/archive_entry_paths.3 index 51c8b8c8e6d8..fd22cf7e20c1 100644 --- a/libarchive/archive_entry_paths.3 +++ b/libarchive/archive_entry_paths.3 @@ -149,5 +149,5 @@ It doesn't have a corresponding get accessor function. is an alias for .Fn archive_entry_copy_XXX . .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry 3 +.Xr libarchive 3 , diff --git a/libarchive/archive_entry_perms.3 b/libarchive/archive_entry_perms.3 index 5b7b5d955a8d..340c5ea72486 100644 --- a/libarchive/archive_entry_perms.3 +++ b/libarchive/archive_entry_perms.3 @@ -194,11 +194,11 @@ every name that is recognized. .Xr strtofflags 3 , which stops parsing at the first unrecognized name.) .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry 3 , .Xr archive_entry_acl 3 , .Xr archive_read_disk 3 , .Xr archive_write_disk 3 +.Xr libarchive 3 , .Sh BUGS The platform types .Vt uid_t diff --git a/libarchive/archive_entry_private.h b/libarchive/archive_entry_private.h index e3547c3e3187..c69233e68bd3 100644 --- a/libarchive/archive_entry_private.h +++ b/libarchive/archive_entry_private.h @@ -154,6 +154,11 @@ struct archive_entry { /* Not used within libarchive; useful for some clients. */ struct archive_mstring ae_sourcepath; /* Path this entry is sourced from. */ +#define AE_ENCRYPTION_NONE 0 +#define AE_ENCRYPTION_DATA 1 +#define AE_ENCRYPTION_METADATA 2 + char encryption; + void *mac_metadata; size_t mac_metadata_size; diff --git a/libarchive/archive_entry_sparse.c b/libarchive/archive_entry_sparse.c index 10c54474a379..fed74f5121d7 100644 --- a/libarchive/archive_entry_sparse.c +++ b/libarchive/archive_entry_sparse.c @@ -58,7 +58,7 @@ archive_entry_sparse_add_entry(struct archive_entry *entry, if (offset < 0 || length < 0) /* Invalid value */ return; - if (offset + length < 0 || + if (offset > INT64_MAX - length || offset + length > archive_entry_size(entry)) /* A value of "length" parameter is too large. */ return; diff --git a/libarchive/archive_entry_stat.3 b/libarchive/archive_entry_stat.3 index 84a4ea12a0ad..26611e4c62e9 100644 --- a/libarchive/archive_entry_stat.3 +++ b/libarchive/archive_entry_stat.3 @@ -226,7 +226,7 @@ and are used by .Xr archive_entry_linkify 3 to find hardlinks. -The pair of device and inode is suppossed to identify hardlinked files. +The pair of device and inode is supposed to identify hardlinked files. .Pp The device major and minor number can be obtained independently using .Fn archive_entry_devmajor @@ -267,8 +267,8 @@ platforms. Some archive formats use the combined form, while other formats use the split form. .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry_acl 3 , .Xr archive_entry_perms 3 , .Xr archive_entry_time 3 , +.Xr libarchive 3 , .Xr stat 2 diff --git a/libarchive/archive_entry_time.3 b/libarchive/archive_entry_time.3 index 17c658a65817..186452159370 100644 --- a/libarchive/archive_entry_time.3 +++ b/libarchive/archive_entry_time.3 @@ -113,8 +113,8 @@ The current state can be queried using .Fn XXX_is_set . Unset time fields have a second and nanosecond field of 0. .Sh SEE ALSO -.Xr archive 3 , .Xr archive_entry 3 +.Xr libarchive 3 , .Sh HISTORY The .Nm libarchive diff --git a/libarchive/archive_entry_xattr.c b/libarchive/archive_entry_xattr.c index a3efe7ca8d9f..05eb90f1afd1 100644 --- a/libarchive/archive_entry_xattr.c +++ b/libarchive/archive_entry_xattr.c @@ -98,7 +98,10 @@ archive_entry_xattr_add_entry(struct archive_entry *entry, /* XXX Error XXX */ return; - xp->name = strdup(name); + if ((xp->name = strdup(name)) == NULL) + /* XXX Error XXX */ + return; + if ((xp->value = malloc(size)) != NULL) { memcpy(xp->value, value, size); xp->size = size; diff --git a/libarchive/archive_getdate.c b/libarchive/archive_getdate.c index f8b5a28d583d..beb0cba2ed3b 100644 --- a/libarchive/archive_getdate.c +++ b/libarchive/archive_getdate.c @@ -38,8 +38,8 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <time.h> -/* This file defines a single public function. */ -time_t __archive_get_date(time_t now, char *); +#define __LIBARCHIVE_BUILD 1 +#include "archive_getdate.h" /* Basic time units. */ #define EPOCH 1970 @@ -369,8 +369,8 @@ relunitphrase(struct gdstate *gds) && gds->tokenp[1].token == tSEC_UNIT) { /* "1 day" */ gds->HaveRel++; - gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; - gds->tokenp += 3; + gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value; + gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == '-' @@ -403,7 +403,7 @@ relunitphrase(struct gdstate *gds) /* "now", "tomorrow" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value; - ++gds->tokenp; + gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tMONTH_UNIT) { @@ -782,7 +782,7 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) * Tokenizer. */ static int -nexttoken(char **in, time_t *value) +nexttoken(const char **in, time_t *value) { char c; char buff[64]; @@ -809,7 +809,7 @@ nexttoken(char **in, time_t *value) /* Try the next token in the word table first. */ /* This allows us to match "2nd", for example. */ { - char *src = *in; + const char *src = *in; const struct LEXICON *tp; unsigned i = 0; @@ -894,7 +894,7 @@ difftm (struct tm *a, struct tm *b) * TODO: tokens[] array should be dynamically sized. */ time_t -__archive_get_date(time_t now, char *p) +__archive_get_date(time_t now, const char *p) { struct token tokens[256]; struct gdstate _gds; @@ -1022,10 +1022,11 @@ int main(int argc, char **argv) { time_t d; + time_t now = time(NULL); while (*++argv != NULL) { (void)printf("Input: %s\n", *argv); - d = get_date(*argv); + d = get_date(now, *argv); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else diff --git a/libarchive/archive_getdate.h b/libarchive/archive_getdate.h new file mode 100644 index 000000000000..666ff5ff78b9 --- /dev/null +++ b/libarchive/archive_getdate.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2003-2015 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifndef ARCHIVE_GETDATE_H_INCLUDED +#define ARCHIVE_GETDATE_H_INCLUDED + +#include <time.h> + +time_t __archive_get_date(time_t now, const char *); + +#endif diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c new file mode 100644 index 000000000000..7857c0ff3568 --- /dev/null +++ b/libarchive/archive_hmac.c @@ -0,0 +1,248 @@ +/*- +* Copyright (c) 2014 Michihiro NAKAJIMA +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "archive_platform.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include "archive.h" +#include "archive_hmac_private.h" + +/* + * On systems that do not support any recognized crypto libraries, + * the archive_hmac.c file is expected to define no usable symbols. + * + * But some compilers and linkers choke on empty object files, so + * define a public symbol that will always exist. This could + * be removed someday if this file gains another always-present + * symbol definition. + */ +int __libarchive_hmac_build_hack(void) { + return 0; +} + + +#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len); + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + CCHmacUpdate(ctx, data, data_len); +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + CCHmacFinal(ctx, out); + *out_len = 20; +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; + DWORD hash_len; + PBYTE hash; + ULONG result; + NTSTATUS status; + + ctx->hAlg = NULL; + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, + MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); + if (!BCRYPT_SUCCESS(status)) + return -1; + status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len, + sizeof(hash_len), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len); + if (hash == NULL) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptCreateHash(hAlg, &hHash, NULL, 0, + (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, hash); + return -1; + } + + ctx->hAlg = hAlg; + ctx->hHash = hHash; + ctx->hash_len = hash_len; + ctx->hash = hash; + + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0); +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0); + if (ctx->hash_len == *out_len) + memcpy(out, ctx->hash, *out_len); +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + if (ctx->hAlg != NULL) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + HeapFree(GetProcessHeap(), 0, ctx->hash); + ctx->hAlg = NULL; + } +} + +#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + hmac_sha1_set_key(ctx, key_len, key); + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + hmac_sha1_update(ctx, data_len, data); +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + hmac_sha1_digest(ctx, (unsigned)*out_len, out); +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +#elif defined(HAVE_LIBCRYPTO) + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + HMAC_CTX_init(ctx); + HMAC_Init(ctx, key, key_len, EVP_sha1()); + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + HMAC_Update(ctx, data, data_len); +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + unsigned int len = (unsigned int)*out_len; + HMAC_Final(ctx, out, &len); + *out_len = len; +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + HMAC_CTX_cleanup(ctx); + memset(ctx, 0, sizeof(*ctx)); +} + +#else + +/* Stub */ +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + (void)ctx;/* UNUSED */ + (void)key;/* UNUSED */ + (void)key_len;/* UNUSED */ + return -1; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + (void)ctx;/* UNUSED */ + (void)data;/* UNUSED */ + (void)data_len;/* UNUSED */ +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + (void)ctx;/* UNUSED */ + (void)out;/* UNUSED */ + (void)out_len;/* UNUSED */ +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + (void)ctx;/* UNUSED */ +} + +#endif + +const struct archive_hmac __archive_hmac = { + &__hmac_sha1_init, + &__hmac_sha1_update, + &__hmac_sha1_final, + &__hmac_sha1_cleanup, +}; diff --git a/libarchive/archive_hmac_private.h b/libarchive/archive_hmac_private.h new file mode 100644 index 000000000000..64de743cb329 --- /dev/null +++ b/libarchive/archive_hmac_private.h @@ -0,0 +1,106 @@ +/*- +* Copyright (c) 2014 Michihiro NAKAJIMA +* 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. +*/ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED +#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED + +/* + * On systems that do not support any recognized crypto libraries, + * the archive_hmac.c file is expected to define no usable symbols. + * + * But some compilers and linkers choke on empty object files, so + * define a public symbol that will always exist. This could + * be removed someday if this file gains another always-present + * symbol definition. + */ +int __libarchive_hmac_build_hack(void); + +#ifdef __APPLE__ +# include <AvailabilityMacros.h> +# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +# define ARCHIVE_HMAC_USE_Apple_CommonCrypto +# endif +#endif + +#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto +#include <CommonCrypto/CommonHMAC.h> + +typedef CCHmacContext archive_hmac_sha1_ctx; + +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#include <bcrypt.h> + +typedef struct { + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; + DWORD hash_len; + PBYTE hash; + +} archive_hmac_sha1_ctx; + +#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H) +#include <nettle/hmac.h> + +typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx; + +#elif defined(HAVE_LIBCRYPTO) +#include <openssl/hmac.h> + +typedef HMAC_CTX archive_hmac_sha1_ctx; + +#else + +typedef int archive_hmac_sha1_ctx; + +#endif + + +/* HMAC */ +#define archive_hmac_sha1_init(ctx, key, key_len)\ + __archive_hmac.__hmac_sha1_init(ctx, key, key_len) +#define archive_hmac_sha1_update(ctx, data, data_len)\ + __archive_hmac.__hmac_sha1_update(ctx, data, data_len) +#define archive_hmac_sha1_final(ctx, out, out_len)\ + __archive_hmac.__hmac_sha1_final(ctx, out, out_len) +#define archive_hmac_sha1_cleanup(ctx)\ + __archive_hmac.__hmac_sha1_cleanup(ctx) + + +struct archive_hmac { + /* HMAC */ + int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *, + size_t); + void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *, + size_t); + void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *); + void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *); +}; + +extern const struct archive_hmac __archive_hmac; +#endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index 6b6be9cb20b5..4c41badf1f86 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" #include "archive_entry.h" +#include "archive_getdate.h" #include "archive_pathmatch.h" #include "archive_rb.h" #include "archive_string.h" @@ -184,7 +185,6 @@ static int time_excluded(struct archive_match *, struct archive_entry *); static int validate_time_flag(struct archive *, int, const char *); -time_t __archive_get_date(time_t now, const char *); #define get_date __archive_get_date static const struct archive_rb_tree_ops rb_ops_mbs = { @@ -580,6 +580,7 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, return (ARCHIVE_FATAL); } r = archive_read_support_format_raw(ar); + r = archive_read_support_format_empty(ar); if (r != ARCHIVE_OK) { archive_copy_error(&(a->archive), ar); archive_read_free(ar); @@ -596,9 +597,13 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, } r = archive_read_next_header(ar, &ae); if (r != ARCHIVE_OK) { - archive_copy_error(&(a->archive), ar); archive_read_free(ar); - return (r); + if (r == ARCHIVE_EOF) { + return (ARCHIVE_OK); + } else { + archive_copy_error(&(a->archive), ar); + return (r); + } } archive_string_init(&as); @@ -1152,7 +1157,7 @@ set_timefilter_pathname_mbs(struct archive_match *a, int timetype, { /* NOTE: stat() on Windows cannot handle nano seconds. */ HANDLE h; - WIN32_FIND_DATA d; + WIN32_FIND_DATAA d; if (path == NULL || *path == '\0') { archive_set_error(&(a->archive), EINVAL, "pathname is empty"); diff --git a/libarchive/archive_options.c b/libarchive/archive_options.c index 8af623931f01..dbf3e80e9024 100644 --- a/libarchive/archive_options.c +++ b/libarchive/archive_options.c @@ -42,9 +42,9 @@ _archive_set_option(struct archive *a, archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn); - mp = m != NULL && m[0] == '\0' ? NULL : m; - op = o != NULL && o[0] == '\0' ? NULL : o; - vp = v != NULL && v[0] == '\0' ? NULL : v; + mp = (m != NULL && m[0] != '\0') ? m : NULL; + op = (o != NULL && o[0] != '\0') ? o : NULL; + vp = (v != NULL && v[0] != '\0') ? v : NULL; if (op == NULL && vp == NULL) return (ARCHIVE_OK); diff --git a/libarchive/archive_pack_dev.c b/libarchive/archive_pack_dev.c new file mode 100644 index 000000000000..6b7b4726d6ea --- /dev/null +++ b/libarchive/archive_pack_dev.c @@ -0,0 +1,329 @@ +/* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Originally from NetBSD's mknod(8) source. */ + +#include "archive_platform.h" + +#if HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif +#if !defined(lint) +__RCSID("$NetBSD$"); +#endif /* not lint */ + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "archive_pack_dev.h" + +static pack_t pack_netbsd; +static pack_t pack_freebsd; +static pack_t pack_8_8; +static pack_t pack_12_20; +static pack_t pack_14_18; +static pack_t pack_8_24; +static pack_t pack_bsdos; +static int compare_format(const void *, const void *); + +static const char iMajorError[] = "invalid major number"; +static const char iMinorError[] = "invalid minor number"; +static const char tooManyFields[] = "too many fields for format"; + +/* This is blatantly stolen from libarchive/archive_entry.c, + * in an attempt to get this to play nice on MinGW... */ +#if !defined(HAVE_MAJOR) && !defined(major) +/* Replacement for major/minor/makedev. */ +#define major(x) ((int)(0x00ff & ((x) >> 8))) +#define minor(x) ((int)(0xffff00ff & (x))) +#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) +#endif + +/* Play games to come up with a suitable makedev() definition. */ +#ifdef __QNXNTO__ +/* QNX. <sigh> */ +#include <sys/netmgr.h> +#define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) +#elif defined makedev +/* There's a "makedev" macro. */ +#define apd_makedev(maj, min) makedev((maj), (min)) +#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) +/* Windows. <sigh> */ +#define apd_makedev(maj, min) mkdev((maj), (min)) +#else +/* There's a "makedev" function. */ +#define apd_makedev(maj, min) makedev((maj), (min)) +#endif + +/* exported */ +dev_t +pack_native(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = apd_makedev(numbers[0], numbers[1]); + if ((unsigned long)major(dev) != numbers[0]) + *error = iMajorError; + else if ((unsigned long)minor(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +static dev_t +pack_netbsd(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if ((unsigned long)major_netbsd(dev) != numbers[0]) + *error = iMajorError; + else if ((unsigned long)minor_netbsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +static dev_t +pack_freebsd(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if ((unsigned long)major_freebsd(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_freebsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +static dev_t +pack_8_8(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if ((unsigned long)major_8_8(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_8_8(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +static dev_t +pack_12_20(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((unsigned long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +static dev_t +pack_14_18(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if ((unsigned long)major_14_18(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_14_18(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +static dev_t +pack_8_24(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if ((unsigned long)major_8_24(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_8_24(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +static dev_t +pack_bsdos(int n, unsigned long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((unsigned long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if ((unsigned long)major_12_12_8(dev) != numbers[0]) + *error = iMajorError; + if ((unsigned long)unit_12_12_8(dev) != numbers[1]) + *error = "invalid unit number"; + if ((unsigned long)subunit_12_12_8(dev) != numbers[2]) + *error = "invalid subunit number"; + } else + *error = tooManyFields; + return (dev); +} + + + /* list of formats and pack functions */ + /* this list must be sorted lexically */ +static struct format { + const char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +static int +compare_format(const void *key, const void *element) +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +pack_t * +pack_find(const char *name) +{ + struct format *format; + + format = bsearch(name, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + return (NULL); + return (format->pack); +} diff --git a/libarchive/archive_pack_dev.h b/libarchive/archive_pack_dev.h new file mode 100644 index 000000000000..749fd3d2cb65 --- /dev/null +++ b/libarchive/archive_pack_dev.h @@ -0,0 +1,49 @@ +/* $NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* Originally from NetBSD's mknod(8) source. */ + +#ifndef _PACK_DEV_H +#define _PACK_DEV_H + +typedef dev_t pack_t(int, unsigned long [], const char **); + +pack_t *pack_find(const char *); +pack_t pack_native; + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +#endif /* _PACK_DEV_H */ diff --git a/libarchive/archive_pathmatch.c b/libarchive/archive_pathmatch.c index 505252a18f38..619e2b622a3c 100644 --- a/libarchive/archive_pathmatch.c +++ b/libarchive/archive_pathmatch.c @@ -394,8 +394,8 @@ __archive_pathmatch(const char *p, const char *s, int flags) if (*p == '/' && *s != '/') return (0); - /* Certain patterns and file names anchor implicitly. */ - if (*p == '*' || *p == '/' || *p == '/') { + /* Certain patterns anchor implicitly. */ + if (*p == '*' || *p == '/') { while (*p == '/') ++p; while (*s == '/') @@ -434,8 +434,8 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags) if (*p == L'/' && *s != L'/') return (0); - /* Certain patterns and file names anchor implicitly. */ - if (*p == L'*' || *p == L'/' || *p == L'/') { + /* Certain patterns anchor implicitly. */ + if (*p == L'*' || *p == L'/') { while (*p == L'/') ++p; while (*s == L'/') diff --git a/libarchive/archive_platform.h b/libarchive/archive_platform.h index ce2f482ba042..b06c3cd28fa6 100644 --- a/libarchive/archive_platform.h +++ b/libarchive/archive_platform.h @@ -66,15 +66,18 @@ * headers as required. */ -/* Get a real definition for __FBSDID if we can */ +/* Get a real definition for __FBSDID or __RCSID if we can */ #if HAVE_SYS_CDEFS_H #include <sys/cdefs.h> #endif -/* If not, define it so as to avoid dangling semicolons. */ +/* If not, define them so as to avoid dangling semicolons. */ #ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif +#ifndef __RCSID +#define __RCSID(a) struct _undefined_hack +#endif /* Try to get standard C99-style integer type definitions. */ #if HAVE_INTTYPES_H @@ -114,6 +117,12 @@ #if !HAVE_DECL_UINT32_MAX #define UINT32_MAX (~(uint32_t)0) #endif +#if !HAVE_DECL_INT32_MAX +#define INT32_MAX ((int32_t)(UINT32_MAX >> 1)) +#endif +#if !HAVE_DECL_INT32_MIN +#define INT32_MIN ((int32_t)(~INT32_MAX)) +#endif #if !HAVE_DECL_UINT64_MAX #define UINT64_MAX (~(uint64_t)0) #endif @@ -123,6 +132,15 @@ #if !HAVE_DECL_INT64_MIN #define INT64_MIN ((int64_t)(~INT64_MAX)) #endif +#if !HAVE_DECL_UINTMAX_MAX +#define UINTMAX_MAX (~(uintmax_t)0) +#endif +#if !HAVE_DECL_INTMAX_MAX +#define INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1)) +#endif +#if !HAVE_DECL_INTMAX_MIN +#define INTMAX_MIN ((intmax_t)(~INTMAX_MAX)) +#endif /* * If this platform has <sys/acl.h>, acl_create(), acl_init(), diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index 30d472fcda0f..4b4be9796dfc 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -119,6 +119,23 @@ struct archive { unsigned current_codepage; /* Current ACP(ANSI CodePage). */ unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */ struct archive_string_conv *sconv; + + /* + * Used by archive_read_data() to track blocks and copy + * data to client buffers, filling gaps with zero bytes. + */ + const char *read_data_block; + int64_t read_data_offset; + int64_t read_data_output_offset; + size_t read_data_remaining; + + /* + * Used by formats/filters to determine the amount of data + * requested from a call to archive_read_data(). This is only + * useful when the format/filter has seek support. + */ + char read_data_is_posix_read; + size_t read_data_requested; }; /* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */ @@ -139,6 +156,8 @@ int __archive_mktemp(const char *tmpdir); int __archive_clean(struct archive *); +void __archive_reset_read_data(struct archive *); + #define err_combine(a,b) ((a) < (b) ? (a) : (b)) #if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER <= 1300) diff --git a/libarchive/archive_random.c b/libarchive/archive_random.c new file mode 100644 index 000000000000..a20b9b111510 --- /dev/null +++ b/libarchive/archive_random.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) + +#ifdef HAVE_FCNTL +#include <fcntl.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#endif + +static void arc4random_buf(void *, size_t); + +#endif /* HAVE_ARC4RANDOM_BUF */ + +#include "archive.h" +#include "archive_random_private.h" + +#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) +#include <wincrypt.h> +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +/* + * Random number generator function. + * This simply calls arc4random_buf function if the platform provides it. + */ + +int +archive_random(void *buf, size_t nbytes) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + HCRYPTPROV hProv; + BOOL success; + + success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT); + if (!success && GetLastError() == NTE_BAD_KEYSET) { + success = CryptAcquireContext(&hProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_NEWKEYSET); + } + if (success) { + success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf); + CryptReleaseContext(hProv, 0); + if (success) + return ARCHIVE_OK; + } + /* TODO: Does this case really happen? */ + return ARCHIVE_FAILED; +#else + arc4random_buf(buf, nbytes); + return ARCHIVE_OK; +#endif +} + +#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) + +/* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */ +/* + * Copyright (c) 1996, David Mazieres <dm@uun.org> + * Copyright (c) 2008, Damien Miller <djm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Arc4 random number generator for OpenBSD. + * + * This code is derived from section 17.1 of Applied Cryptography, + * second edition, which describes a stream cipher allegedly + * compatible with RSA Labs "RC4" cipher (the actual description of + * which is a trade secret). The same algorithm is used as a stream + * cipher called "arcfour" in Tatu Ylonen's ssh package. + * + * RC4 is a registered trademark of RSA Laboratories. + */ + +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +struct arc4_stream { + uint8_t i; + uint8_t j; + uint8_t s[256]; +}; + +#define RANDOMDEV "/dev/urandom" +#define KEYSIZE 128 +#ifdef HAVE_PTHREAD_H +static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; +#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx); +#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx); +#else +#define _ARC4_LOCK() +#define _ARC4_UNLOCK() +#endif + +static int rs_initialized; +static struct arc4_stream rs; +static pid_t arc4_stir_pid; +static int arc4_count; + +static inline uint8_t arc4_getbyte(void); +static void arc4_stir(void); + +static inline void +arc4_init(void) +{ + int n; + + for (n = 0; n < 256; n++) + rs.s[n] = n; + rs.i = 0; + rs.j = 0; +} + +static inline void +arc4_addrandom(u_char *dat, int datlen) +{ + int n; + uint8_t si; + + rs.i--; + for (n = 0; n < 256; n++) { + rs.i = (rs.i + 1); + si = rs.s[rs.i]; + rs.j = (rs.j + si + dat[n % datlen]); + rs.s[rs.i] = rs.s[rs.j]; + rs.s[rs.j] = si; + } + rs.j = rs.i; +} + +static void +arc4_stir(void) +{ + int done, fd, i; + struct { + struct timeval tv; + pid_t pid; + u_char rnd[KEYSIZE]; + } rdat; + + if (!rs_initialized) { + arc4_init(); + rs_initialized = 1; + } + done = 0; + fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0); + if (fd >= 0) { + if (read(fd, &rdat, KEYSIZE) == KEYSIZE) + done = 1; + (void)close(fd); + } + if (!done) { + (void)gettimeofday(&rdat.tv, NULL); + rdat.pid = getpid(); + /* We'll just take whatever was on the stack too... */ + } + + arc4_addrandom((u_char *)&rdat, KEYSIZE); + + /* + * Discard early keystream, as per recommendations in: + * "(Not So) Random Shuffles of RC4" by Ilya Mironov. + */ + for (i = 0; i < 1024; i++) + (void)arc4_getbyte(); + arc4_count = 1600000; +} + +static void +arc4_stir_if_needed(void) +{ + pid_t pid = getpid(); + + if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) { + arc4_stir_pid = pid; + arc4_stir(); + } +} + +static inline uint8_t +arc4_getbyte(void) +{ + uint8_t si, sj; + + rs.i = (rs.i + 1); + si = rs.s[rs.i]; + rs.j = (rs.j + si); + sj = rs.s[rs.j]; + rs.s[rs.i] = sj; + rs.s[rs.j] = si; + return (rs.s[(si + sj) & 0xff]); +} + +static void +arc4random_buf(void *_buf, size_t n) +{ + u_char *buf = (u_char *)_buf; + _ARC4_LOCK(); + arc4_stir_if_needed(); + while (n--) { + if (--arc4_count <= 0) + arc4_stir(); + buf[n] = arc4_getbyte(); + } + _ARC4_UNLOCK(); +} + +#endif /* !HAVE_ARC4RANDOM_BUF */ diff --git a/libarchive/archive_random_private.h b/libarchive/archive_random_private.h new file mode 100644 index 000000000000..c414779f8d44 --- /dev/null +++ b/libarchive/archive_random_private.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * 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. + */ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED +#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED + +/* Random number generator. */ +int archive_random(void *buf, size_t nbytes); + +#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_read.3 b/libarchive/archive_read.3 index a29cc1ea6915..d37e7327cb5e 100644 --- a/libarchive/archive_read.3 +++ b/libarchive/archive_read.3 @@ -130,7 +130,7 @@ which provides a slightly more efficient interface. You may prefer to use the higher-level .Fn archive_read_data_skip , which reads and discards the data for this entry, -.Fn archive_read_data_to_file , +.Fn archive_read_data_into_fd , which copies the data to the provided file descriptor, or .Fn archive_read_extract , which recreates the specified entry on disk and copies data @@ -186,7 +186,7 @@ list_archive(const char *name) free(mydata); } -ssize_t +la_ssize_t myread(struct archive *a, void *client_data, const void **buff) { struct mydata *mydata = client_data; diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index eb8a5b0bc444..0bbacc8f185b 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -101,16 +101,17 @@ archive_read_new(void) { struct archive_read *a; - a = (struct archive_read *)malloc(sizeof(*a)); + a = (struct archive_read *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_READ_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new2(&a->archive); a->archive.vtable = archive_read_vtable(); + a->passphrases.last = &a->passphrases.first; + return (&a->archive); } @@ -194,10 +195,12 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) ask = skip_limit; get = (self->archive->client.skipper) (&self->archive->archive, self->data, ask); - if (get == 0) + total += get; + if (get == 0 || get == request) return (total); + if (get > request) + return ARCHIVE_FATAL; request -= get; - total += get; } } else if (self->archive->client.seeker != NULL && request > 64 * 1024) { @@ -230,8 +233,11 @@ client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence) * other libarchive code that assumes a successful forward * seek means it can also seek backwards. */ - if (self->archive->client.seeker == NULL) + if (self->archive->client.seeker == NULL) { + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Current client reader does not support seeking a device"); return (ARCHIVE_FAILED); + } return (self->archive->client.seeker)(&self->archive->archive, self->data, offset, whence); } @@ -454,7 +460,7 @@ archive_read_open1(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter, *tmp; - int slot, e; + int slot, e = ARCHIVE_OK; unsigned int i; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, @@ -541,16 +547,20 @@ archive_read_open1(struct archive *_a) * it wants to handle this stream. Repeat until we've finished * building the pipeline. */ + +/* We won't build a filter pipeline with more stages than this. */ +#define MAX_NUMBER_FILTERS 25 + static int choose_filters(struct archive_read *a) { - int number_bidders, i, bid, best_bid, n; + int number_bidders, i, bid, best_bid, number_filters; struct archive_read_filter_bidder *bidder, *best_bidder; struct archive_read_filter *filter; ssize_t avail; int r; - for (n = 0; n < 25; ++n) { + for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) { number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); best_bid = 0; @@ -661,16 +671,14 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) break; } - a->read_data_output_offset = 0; - a->read_data_remaining = 0; - a->read_data_is_posix_read = 0; - a->read_data_requested = 0; + __archive_reset_read_data(&a->archive); + a->data_start_node = a->client.cursor; /* EOF always wins; otherwise return the worst error. */ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; } -int +static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; @@ -750,6 +758,59 @@ archive_read_header_position(struct archive *_a) } /* + * Returns 1 if the archive contains at least one encrypted entry. + * If the archive format not support encryption at all + * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. + * If for any other reason (e.g. not enough data read so far) + * we cannot say whether there are encrypted entries, then + * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned. + * In general, this function will return values below zero when the + * reader is uncertain or totally uncapable of encryption support. + * When this function returns 0 you can be sure that the reader + * supports encryption detection but no encrypted entries have + * been found yet. + * + * NOTE: If the metadata/header of an archive is also encrypted, you + * cannot rely on the number of encrypted entries. That is why this + * function does not return the number of encrypted entries but# + * just shows that there are some. + */ +int +archive_read_has_encrypted_entries(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + int format_supports_encryption = archive_read_format_capabilities(_a) + & (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); + + if (!_a || !format_supports_encryption) { + /* Format in general doesn't support encryption */ + return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; + } + + /* A reader potentially has read enough data now. */ + if (a->format && a->format->has_encrypted_entries) { + return (a->format->has_encrypted_entries)(a); + } + + /* For any other reason we cannot say how many entries are there. */ + return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; +} + +/* + * Returns a bitmask of capabilities that are supported by the archive format reader. + * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned. + */ +int +archive_read_format_capabilities(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if (a && a->format && a->format->format_capabilties) { + return (a->format->format_capabilties)(a); + } + return ARCHIVE_READ_FORMAT_CAPS_NONE; +} + +/* * Read data from an archive entry, using a read(2)-style interface. * This is a convenience routine that just calls * archive_read_data_block and copies the results into the client @@ -763,7 +824,7 @@ archive_read_header_position(struct archive *_a) ssize_t archive_read_data(struct archive *_a, void *buff, size_t s) { - struct archive_read *a = (struct archive_read *)_a; + struct archive *a = (struct archive *)_a; char *dest; const void *read_buf; size_t bytes_read; @@ -778,7 +839,7 @@ archive_read_data(struct archive *_a, void *buff, size_t s) read_buf = a->read_data_block; a->read_data_is_posix_read = 1; a->read_data_requested = s; - r = _archive_read_data_block(&a->archive, &read_buf, + r = archive_read_data_block(a, &read_buf, &a->read_data_remaining, &a->read_data_offset); a->read_data_block = read_buf; if (r == ARCHIVE_EOF) @@ -793,7 +854,7 @@ archive_read_data(struct archive *_a, void *buff, size_t s) } if (a->read_data_offset < a->read_data_output_offset) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Encountered out-of-order sparse blocks"); return (ARCHIVE_RETRY); } @@ -837,6 +898,21 @@ archive_read_data(struct archive *_a, void *buff, size_t s) } /* + * Reset the read_data_* variables, used for starting a new entry. + */ +void __archive_reset_read_data(struct archive * a) +{ + a->read_data_output_offset = 0; + a->read_data_remaining = 0; + a->read_data_is_posix_read = 0; + a->read_data_requested = 0; + + /* extra resets, from rar.c */ + a->read_data_block = NULL; + a->read_data_offset = 0; +} + +/* * Skip over all remaining data in this entry. */ int @@ -903,7 +979,7 @@ _archive_read_data_block(struct archive *_a, if (a->format->read_data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " - "No format_read_data_block function registered"); + "No format->read_data function registered"); return (ARCHIVE_FATAL); } @@ -990,6 +1066,7 @@ static int _archive_read_free(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; + struct archive_read_passphrase *p; int i, n; int slots; int r = ARCHIVE_OK; @@ -1027,9 +1104,20 @@ _archive_read_free(struct archive *_a) } } + /* Release passphrase list. */ + p = a->passphrases.first; + while (p != NULL) { + struct archive_read_passphrase *np = p->next; + + /* A passphrase should be cleaned. */ + memset(p->passphrase, 0, strlen(p->passphrase)); + free(p->passphrase); + free(p); + p = np; + } + archive_string_free(&a->archive.error_string); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a->client.dataset); @@ -1073,7 +1161,7 @@ static const char * _archive_filter_name(struct archive *_a, int n) { struct archive_read_filter *f = get_filter(_a, n); - return f == NULL ? NULL : f->name; + return f != NULL ? f->name : NULL; } static int64_t @@ -1097,7 +1185,9 @@ __archive_read_register_format(struct archive_read *a, int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), int (*read_data_skip)(struct archive_read *), int64_t (*seek_data)(struct archive_read *, int64_t, int), - int (*cleanup)(struct archive_read *)) + int (*cleanup)(struct archive_read *), + int (*format_capabilities)(struct archive_read *), + int (*has_encrypted_entries)(struct archive_read *)) { int i, number_slots; @@ -1120,6 +1210,8 @@ __archive_read_register_format(struct archive_read *a, a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; a->formats[i].name = name; + a->formats[i].format_capabilties = format_capabilities; + a->formats[i].has_encrypted_entries = has_encrypted_entries; return (ARCHIVE_OK); } } @@ -1562,10 +1654,9 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, client->dataset[++cursor].begin_position = r; } offset -= client->dataset[cursor].begin_position; - if (offset < 0) - offset = 0; - else if (offset > client->dataset[cursor].total_size - 1) - offset = client->dataset[cursor].total_size - 1; + if (offset < 0 + || offset > client->dataset[cursor].total_size) + return ARCHIVE_FATAL; if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0) return r; break; diff --git a/libarchive/archive_read_add_passphrase.3 b/libarchive/archive_read_add_passphrase.3 new file mode 100644 index 000000000000..8b242ea79b1e --- /dev/null +++ b/libarchive/archive_read_add_passphrase.3 @@ -0,0 +1,74 @@ +.\" Copyright (c) 2014 Michihiro NAKAJIMA +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 14, 2014 +.Dt ARCHIVE_READ_ADD_PASSPHRASE 3 +.Os +.Sh NAME +.Nm archive_read_add_passphrase , +.Nm archive_read_set_passphrase_callback +.Nd functions for reading encrypted archives +.Sh LIBRARY +Streaming Archive Library (libarchive, -larchive) +.Sh SYNOPSIS +.In archive.h +.Ft int +.Fo archive_read_add_passphrase +.Fa "struct archive *" +.Fa "const char *passphrase" +.Fc +.Ft int +.Fo archive_read_set_passphrase_callback +.Fa "struct archive *" +.Fa "void *client_data" +.Fa "archive_passphrase_callback *" +.Fc +.Sh DESCRIPTION +.Bl -tag -width indent +.It Fn archive_read_add_passphrase +Register passphrases for reading an encryption archive. +If +.Ar passphrase +is +.Dv NULL +or empty, this function will do nothing and +.Cm ARCHIVE_FAILED +will be returned. +Otherwise, +.Cm ARCHIVE_OK +will be returned. +.It Fn archive_read_set_passphrase_callback +Register callback function that will be invoked to get a passphrase +for decrption after trying all passphrases registered by the +.Fn archive_read_add_passphrase +function failed. +.El +.\" .Sh ERRORS +.Sh SEE ALSO +.Xr tar 1 , +.Xr libarchive 3 , +.Xr archive_read 3 , +.Xr archive_read_set_options 3 diff --git a/libarchive/archive_read_add_passphrase.c b/libarchive/archive_read_add_passphrase.c new file mode 100644 index 000000000000..f67f1ebc6e27 --- /dev/null +++ b/libarchive/archive_read_add_passphrase.c @@ -0,0 +1,186 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include "archive_read_private.h" + +static void +add_passphrase_to_tail(struct archive_read *a, + struct archive_read_passphrase *p) +{ + *a->passphrases.last = p; + a->passphrases.last = &p->next; + p->next = NULL; +} + +static struct archive_read_passphrase * +remove_passphrases_from_head(struct archive_read *a) +{ + struct archive_read_passphrase *p; + + p = a->passphrases.first; + if (p != NULL) + a->passphrases.first = p->next; + return (p); +} + +static void +insert_passphrase_to_head(struct archive_read *a, + struct archive_read_passphrase *p) +{ + p->next = a->passphrases.first; + a->passphrases.first = p; +} + +static struct archive_read_passphrase * +new_read_passphrase(struct archive_read *a, const char *passphrase) +{ + struct archive_read_passphrase *p; + + p = malloc(sizeof(*p)); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (NULL); + } + p->passphrase = strdup(passphrase); + if (p->passphrase == NULL) { + free(p); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (NULL); + } + return (p); +} + +int +archive_read_add_passphrase(struct archive *_a, const char *passphrase) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_passphrase *p; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_add_passphrase"); + + if (passphrase == NULL || passphrase[0] == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Empty passphrase is unacceptable"); + return (ARCHIVE_FAILED); + } + + p = new_read_passphrase(a, passphrase); + if (p == NULL) + return (ARCHIVE_FATAL); + add_passphrase_to_tail(a, p); + + return (ARCHIVE_OK); +} + +int +archive_read_set_passphrase_callback(struct archive *_a, void *client_data, + archive_passphrase_callback *cb) +{ + struct archive_read *a = (struct archive_read *)_a; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_passphrase_callback"); + + a->passphrases.callback = cb; + a->passphrases.client_data = client_data; + return (ARCHIVE_OK); +} + +/* + * Call this in advance when you start to get a passphrase for decryption + * for a entry. + */ +void +__archive_read_reset_passphrase(struct archive_read *a) +{ + + a->passphrases.candiate = -1; +} + +/* + * Get a passphrase for decryption. + */ +const char * +__archive_read_next_passphrase(struct archive_read *a) +{ + struct archive_read_passphrase *p; + const char *passphrase; + + if (a->passphrases.candiate < 0) { + /* Count out how many passphrases we have. */ + int cnt = 0; + + for (p = a->passphrases.first; p != NULL; p = p->next) + cnt++; + a->passphrases.candiate = cnt; + p = a->passphrases.first; + } else if (a->passphrases.candiate > 1) { + /* Rotate a passphrase list. */ + a->passphrases.candiate--; + p = remove_passphrases_from_head(a); + add_passphrase_to_tail(a, p); + /* Pick a new passphrase candiate up. */ + p = a->passphrases.first; + } else if (a->passphrases.candiate == 1) { + /* This case is that all cadiates failed to decryption. */ + a->passphrases.candiate = 0; + if (a->passphrases.first->next != NULL) { + /* Rotate a passphrase list. */ + p = remove_passphrases_from_head(a); + add_passphrase_to_tail(a, p); + } + p = NULL; + } else /* There is no passphrase candaite. */ + p = NULL; + + if (p != NULL) + passphrase = p->passphrase; + else if (a->passphrases.callback != NULL) { + /* Get a passphrase through a call-back function + * since we tried all passphrases out or we don't + * have it. */ + passphrase = a->passphrases.callback(&a->archive, + a->passphrases.client_data); + if (passphrase != NULL) { + p = new_read_passphrase(a, passphrase); + if (p == NULL) + return (NULL); + insert_passphrase_to_head(a, p); + a->passphrases.candiate = 1; + } + } else + passphrase = NULL; + + return (passphrase); +} diff --git a/libarchive/archive_read_append_filter.c b/libarchive/archive_read_append_filter.c index 017d7c68a4b3..3a0d4d68d89a 100644 --- a/libarchive/archive_read_append_filter.c +++ b/libarchive/archive_read_append_filter.c @@ -43,7 +43,7 @@ archive_read_append_filter(struct archive *_a, int code) struct archive_read_filter *filter; struct archive_read *a = (struct archive_read *)_a; - r1 = r2 = (ARCHIVE_OK); + r2 = (ARCHIVE_OK); switch (code) { case ARCHIVE_FILTER_NONE: @@ -85,6 +85,10 @@ archive_read_append_filter(struct archive *_a, int code) strcpy(str, "rpm"); r1 = archive_read_support_filter_rpm(_a); break; + case ARCHIVE_FILTER_LZ4: + strcpy(str, "lz4"); + r1 = archive_read_support_filter_lz4(_a); + break; case ARCHIVE_FILTER_LZIP: strcpy(str, "lzip"); r1 = archive_read_support_filter_lzip(_a); diff --git a/libarchive/archive_read_data.3 b/libarchive/archive_read_data.3 index bf0578ce3779..c1bc15d7cc8c 100644 --- a/libarchive/archive_read_data.3 +++ b/libarchive/archive_read_data.3 @@ -37,7 +37,7 @@ Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h -.Ft ssize_t +.Ft la_ssize_t .Fn archive_read_data "struct archive *" "void *buff" "size_t len" .Ft int .Fo archive_read_data_block diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index e984aaadbeab..74fe353d9d34 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -251,9 +251,11 @@ archive_read_disk_entry_from_file(struct archive *_a, #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = setup_acls(a, entry, &fd); - r1 = setup_xattrs(a, entry, &fd); - if (r1 < r) - r = r1; + if (!a->suppress_xattr) { + r1 = setup_xattrs(a, entry, &fd); + if (r1 < r) + r = r1; + } if (a->enable_copyfile) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) @@ -399,7 +401,7 @@ setup_mac_metadata(struct archive_read_disk *a, #endif -#if defined(HAVE_POSIX_ACL) && defined(ACL_TYPE_NFS4) +#ifdef HAVE_POSIX_ACL static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); @@ -419,6 +421,7 @@ setup_acls(struct archive_read_disk *a, archive_entry_acl_clear(entry); +#ifdef ACL_TYPE_NFS4 /* Try NFS4 ACL first. */ if (*fd >= 0) acl = acl_get_fd(*fd); @@ -447,6 +450,7 @@ setup_acls(struct archive_read_disk *a, acl_free(acl); return (ARCHIVE_OK); } +#endif /* Retrieve access ACL from file. */ if (*fd >= 0) @@ -492,6 +496,7 @@ static struct { {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, +#ifdef ACL_TYPE_NFS4 {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, @@ -508,8 +513,10 @@ static struct { {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} +#endif }; +#ifdef ACL_TYPE_NFS4 static struct { int archive_inherit; int platform_inherit; @@ -519,21 +526,25 @@ static struct { {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY} }; - +#endif static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int default_entry_acl_type) { acl_tag_t acl_tag; +#ifdef ACL_TYPE_NFS4 acl_entry_type_t acl_type; acl_flagset_t acl_flagset; + int brand, r; +#endif acl_entry_t acl_entry; acl_permset_t acl_permset; - int brand, i, r, entry_acl_type; + int i, entry_acl_type; int s, ae_id, ae_tag, ae_perm; const char *ae_name; +#ifdef ACL_TYPE_NFS4 // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 // Make sure the "brand" on this ACL is consistent // with the default_entry_acl_type bits provided. @@ -560,6 +571,7 @@ translate_acl(struct archive_read_disk *a, return ARCHIVE_FAILED; break; } +#endif s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); @@ -592,9 +604,11 @@ translate_acl(struct archive_read_disk *a, case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; break; +#ifdef ACL_TYPE_NFS4 case ACL_EVERYONE: ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; break; +#endif default: /* Skip types that libarchive can't support. */ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); @@ -605,6 +619,7 @@ translate_acl(struct archive_read_disk *a, // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for // non-NFSv4 ACLs entry_acl_type = default_entry_acl_type; +#ifdef ACL_TYPE_NFS4 r = acl_get_entry_type_np(acl_entry, &acl_type); if (r == 0) { switch (acl_type) { @@ -634,9 +649,10 @@ translate_acl(struct archive_read_disk *a, ae_perm |= acl_inherit_map[i].archive_inherit; } +#endif acl_get_permset(acl_entry, &acl_permset); - for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { + for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { /* * acl_get_perm() is spelled differently on different * platforms; see above. @@ -1030,7 +1046,7 @@ setup_sparse(struct archive_read_disk *a, struct fiemap *fm; struct fiemap_extent *fe; int64_t size; - int count, do_fiemap; + int count, do_fiemap, iters; int exit_sts = ARCHIVE_OK; if (archive_entry_filetype(entry) != AE_IFREG @@ -1067,7 +1083,7 @@ setup_sparse(struct archive_read_disk *a, fm->fm_extent_count = count; do_fiemap = 1; size = archive_entry_size(entry); - for (;;) { + for (iters = 0; ; ++iters) { int i, r; r = ioctl(*fd, FS_IOC_FIEMAP, fm); @@ -1077,8 +1093,13 @@ setup_sparse(struct archive_read_disk *a, * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */ goto exit_setup_sparse; } - if (fm->fm_mapped_extents == 0) + if (fm->fm_mapped_extents == 0) { + if (iters == 0) { + /* Fully sparse file; insert a zero-length "data" entry */ + archive_entry_sparse_add_entry(entry, 0, 0); + } break; + } fe = fm->fm_extents; for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { @@ -1123,6 +1144,7 @@ setup_sparse(struct archive_read_disk *a, off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */ off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */ int exit_sts = ARCHIVE_OK; + int check_fully_sparse = 0; if (archive_entry_filetype(entry) != AE_IFREG || archive_entry_size(entry) <= 0 @@ -1175,8 +1197,14 @@ setup_sparse(struct archive_read_disk *a, while (off_s < size) { off_s = lseek(*fd, off_s, SEEK_DATA); if (off_s == (off_t)-1) { - if (errno == ENXIO) - break;/* no more hole */ + if (errno == ENXIO) { + /* no more hole */ + if (archive_entry_sparse_count(entry) == 0) { + /* Potentially a fully-sparse file. */ + check_fully_sparse = 1; + } + break; + } archive_set_error(&a->archive, errno, "lseek(SEEK_HOLE) failed"); exit_sts = ARCHIVE_FAILED; @@ -1200,6 +1228,14 @@ setup_sparse(struct archive_read_disk *a, off_e - off_s); off_s = off_e; } + + if (check_fully_sparse) { + if (lseek(*fd, 0, SEEK_HOLE) == 0 && + lseek(*fd, 0, SEEK_END) == size) { + /* Fully sparse file; insert a zero-length "data" entry */ + archive_entry_sparse_add_entry(entry, 0, 0); + } + } exit_setup_sparse: lseek(*fd, initial_off, SEEK_SET); return (exit_sts); diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 1787d864ca92..22a1f14c0e03 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -356,6 +356,8 @@ static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); +static int _archive_read_next_header(struct archive *, + struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); @@ -377,6 +379,7 @@ archive_read_disk_vtable(void) av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; + av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } @@ -459,6 +462,7 @@ archive_read_disk_new(void) a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); + a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->enable_copyfile = 1; @@ -491,6 +495,7 @@ _archive_read_free(struct archive *_a) if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); + archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); @@ -609,6 +614,10 @@ archive_read_disk_set_behavior(struct archive *_a, int flags) a->traverse_mount_points = 0; else a->traverse_mount_points = 1; + if (flags & ARCHIVE_READDISK_NO_XATTR) + a->suppress_xattr = 1; + else + a->suppress_xattr = 0; return (r); } @@ -708,6 +717,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, int r; ssize_t bytes; size_t buffbytes; + int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); @@ -789,6 +799,9 @@ _archive_read_data_block(struct archive *_a, const void **buff, if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; + if (t->current_sparse->length == 0) + empty_sparse_region = 1; + /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? @@ -819,7 +832,11 @@ _archive_read_data_block(struct archive *_a, const void **buff, } } else bytes = 0; - if (bytes == 0) { + /* + * Return an EOF unless we've read a leading empty sparse region, which + * is used to represent fully-sparse files. + */ + if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; @@ -1081,6 +1098,17 @@ next_entry(struct archive_read_disk *a, struct tree *t, } static int +_archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +{ + int ret; + struct archive_read_disk *a = (struct archive_read_disk *)_a; + *entryp = NULL; + ret = _archive_read_next_header2(_a, a->entry); + *entryp = a->entry; + return ret; +} + +static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; @@ -1148,6 +1176,7 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) break; } + __archive_reset_read_data(&a->archive); return (r); } @@ -1555,6 +1584,7 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else +# if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { @@ -1564,6 +1594,9 @@ setup_current_filesystem(struct archive_read_disk *a) nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); +# else + nm = -1; +# endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else @@ -1660,7 +1693,9 @@ setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; +#if defined(HAVE_STATVFS) struct statvfs svfs; +#endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { @@ -1677,7 +1712,9 @@ setup_current_filesystem(struct archive_read_disk *a) "openat failed"); return (ARCHIVE_FAILED); } +#if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ +#endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); @@ -1687,14 +1724,18 @@ setup_current_filesystem(struct archive_read_disk *a) archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } +#if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); +#endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS +#if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); +#endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); @@ -1703,7 +1744,9 @@ setup_current_filesystem(struct archive_read_disk *a) archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } +#if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); +#endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); @@ -1716,10 +1759,17 @@ setup_current_filesystem(struct archive_read_disk *a) return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ +#if defined(HAVE_STATVFS) t->current_filesystem->xfer_align = svfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = svfs.f_bsize; t->current_filesystem->incr_xfer_size = svfs.f_bsize; +#else + t->current_filesystem->xfer_align = sfs.f_frsize; + t->current_filesystem->max_xfer_size = -1; + t->current_filesystem->min_xfer_size = sfs.f_bsize; + t->current_filesystem->incr_xfer_size = sfs.f_bsize; +#endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: @@ -1744,7 +1794,11 @@ setup_current_filesystem(struct archive_read_disk *a) } #if defined(ST_NOATIME) +#if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) +#else + if (sfs.f_flag & ST_NOATIME) +#endif t->current_filesystem->noatime = 1; else #endif @@ -1973,7 +2027,7 @@ tree_dup(int fd) static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { - new_fd = fcntl(fd, F_DUPFD_CLOEXEC); + new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h index e5af16b9161b..2569321da544 100644 --- a/libarchive/archive_read_disk_private.h +++ b/libarchive/archive_read_disk_private.h @@ -39,6 +39,9 @@ struct archive_entry; struct archive_read_disk { struct archive archive; + /* Reused by archive_read_next_header() */ + struct archive_entry *entry; + /* * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid, * following an old BSD convention. 'L' follows all symlinks, @@ -68,6 +71,8 @@ struct archive_read_disk { int enable_copyfile; /* Set 1 if users request to traverse mount points. */ int traverse_mount_points; + /* Set 1 if users want to suppress xattr information. */ + int suppress_xattr; const char * (*lookup_gname)(void *private, int64_t gid); void (*cleanup_gname)(void *private); diff --git a/libarchive/archive_read_disk_set_standard_lookup.c b/libarchive/archive_read_disk_set_standard_lookup.c index 3bc52c70dbcb..d6b2d55b8d7b 100644 --- a/libarchive/archive_read_disk_set_standard_lookup.c +++ b/libarchive/archive_read_disk_set_standard_lookup.c @@ -83,7 +83,7 @@ static const char * lookup_uname_helper(struct name_cache *, id_t uid); * a simple cache to accelerate such lookups---into the archive_read_disk * object. This is in a separate file because getpwuid()/getgrgid() * can pull in a LOT of library code (including NIS/LDAP functions, which - * pull in DNS resolveers, etc). This can easily top 500kB, which makes + * pull in DNS resolvers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 9c5420d80e77..566d264e9a41 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -288,6 +288,8 @@ static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); +static int _archive_read_next_header(struct archive *, + struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); @@ -310,6 +312,7 @@ archive_read_disk_vtable(void) av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; + av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } @@ -393,6 +396,7 @@ archive_read_disk_new(void) a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); + a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->enable_copyfile = 1; @@ -422,6 +426,7 @@ _archive_read_free(struct archive *_a) if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); + archive_entry_free(a->entry); a->archive.magic = 0; free(a); return (r); @@ -929,7 +934,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, else flags |= FILE_FLAG_SEQUENTIAL_SCAN; t->entry_fh = CreateFileW(tree_current_access_path(t), - GENERIC_READ, 0, NULL, OPEN_EXISTING, flags, NULL); + GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL); if (t->entry_fh == INVALID_HANDLE_VALUE) { archive_set_error(&a->archive, errno, "Couldn't open %ls", tree_current_path(a->tree)); @@ -945,6 +950,17 @@ next_entry(struct archive_read_disk *a, struct tree *t, } static int +_archive_read_next_header(struct archive *_a, struct archive_entry **entryp) +{ + int ret; + struct archive_read_disk *a = (struct archive_read_disk *)_a; + *entryp = NULL; + ret = _archive_read_next_header2(_a, a->entry); + *entryp = a->entry; + return ret; +} + +static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; @@ -1000,6 +1016,7 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) break; } + __archive_reset_read_data(&a->archive); return (r); } @@ -1569,7 +1586,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time) t->stack->flags = needsFirstVisit; /* * Debug flag for Direct IO(No buffering) or Async IO. - * Those dependant on environment variable switches + * Those dependent on environment variable switches * will be removed until next release. */ { @@ -1851,8 +1868,6 @@ entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, break; case L'C': case L'c': if (((p[2] == L'M' || p[2] == L'm' ) && - (p[3] == L'D' || p[3] == L'd' )) || - ((p[2] == L'M' || p[2] == L'm' ) && (p[3] == L'D' || p[3] == L'd' ))) mode |= S_IXUSR | S_IXGRP | S_IXOTH; break; @@ -1886,7 +1901,7 @@ tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st, if (sim_lstat && tree_current_is_physical_link(t)) flag |= FILE_FLAG_OPEN_REPARSE_POINT; - h = CreateFileW(tree_current_access_path(t), 0, 0, NULL, + h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); @@ -2115,7 +2130,7 @@ archive_read_disk_entry_from_file(struct archive *_a, } else desiredAccess = GENERIC_READ; - h = CreateFileW(path, desiredAccess, 0, NULL, + h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); @@ -2162,7 +2177,7 @@ archive_read_disk_entry_from_file(struct archive *_a, if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); } else { - h = CreateFileW(path, GENERIC_READ, 0, NULL, + h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { la_dosmaperr(GetLastError()); diff --git a/libarchive/archive_read_extract.c b/libarchive/archive_read_extract.c index 795f2abea847..b7973fa8e006 100644 --- a/libarchive/archive_read_extract.c +++ b/libarchive/archive_read_extract.c @@ -26,158 +26,35 @@ #include "archive_platform.h" __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $"); -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif #ifdef HAVE_ERRNO_H #include <errno.h> #endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" -#include "archive_write_disk_private.h" - -struct extract { - struct archive *ad; /* archive_write_disk object */ - - /* Progress function invoked during extract. */ - void (*extract_progress)(void *); - void *extract_progress_user_data; -}; - -static int archive_read_extract_cleanup(struct archive_read *); -static int copy_data(struct archive *ar, struct archive *aw); -static struct extract *get_extract(struct archive_read *); - -static struct extract * -get_extract(struct archive_read *a) -{ - /* If we haven't initialized, do it now. */ - /* This also sets up a lot of global state. */ - if (a->extract == NULL) { - a->extract = (struct extract *)malloc(sizeof(*a->extract)); - if (a->extract == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't extract"); - return (NULL); - } - memset(a->extract, 0, sizeof(*a->extract)); - a->extract->ad = archive_write_disk_new(); - if (a->extract->ad == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't extract"); - return (NULL); - } - archive_write_disk_set_standard_lookup(a->extract->ad); - a->cleanup_archive_extract = archive_read_extract_cleanup; - } - return (a->extract); -} int archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags) { - struct extract *extract; + struct archive_read_extract *extract; + struct archive_read * a = (struct archive_read *)_a; - extract = get_extract((struct archive_read *)_a); + extract = __archive_read_get_extract(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. */ - if (a->skip_file_set) - archive_write_disk_set_skip_file(ad, - a->skip_file_dev, a->skip_file_ino); - 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, ad); - else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) - /* Otherwise, pour data into the entry. */ - 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, ad); - /* Use the worst error return. */ - if (r2 < r) - r = r2; - return (r); -} - -void -archive_read_extract_set_progress_callback(struct archive *_a, - void (*progress_func)(void *), void *user_data) -{ - struct archive_read *a = (struct archive_read *)_a; - struct extract *extract = get_extract(a); - if (extract != NULL) { - extract->extract_progress = progress_func; - extract->extract_progress_user_data = user_data; - } -} - -static int -copy_data(struct archive *ar, struct archive *aw) -{ - int64_t offset; - const void *buff; - struct extract *extract; - size_t size; - int r; - - extract = get_extract((struct archive_read *)ar); - if (extract == NULL) - return (ARCHIVE_FATAL); - for (;;) { - r = archive_read_data_block(ar, &buff, &size, &offset); - if (r == ARCHIVE_EOF) - return (ARCHIVE_OK); - if (r != ARCHIVE_OK) - return (r); - r = (int)archive_write_data_block(aw, buff, size, offset); - if (r < ARCHIVE_WARN) - r = ARCHIVE_WARN; - if (r != ARCHIVE_OK) { - archive_set_error(ar, archive_errno(aw), - "%s", archive_error_string(aw)); - return (r); + /* If we haven't initialized the archive_write_disk object, do it now. */ + if (extract->ad == NULL) { + extract->ad = archive_write_disk_new(); + if (extract->ad == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't extract"); + return (ARCHIVE_FATAL); } - if (extract->extract_progress) - (extract->extract_progress) - (extract->extract_progress_user_data); + archive_write_disk_set_standard_lookup(extract->ad); } -} -/* - * Cleanup function for archive_extract. - */ -static int -archive_read_extract_cleanup(struct archive_read *a) -{ - int ret = ARCHIVE_OK; - - ret = archive_write_free(a->extract->ad); - free(a->extract); - a->extract = NULL; - return (ret); + archive_write_disk_set_options(extract->ad, flags); + return (archive_read_extract2(&a->archive, entry, extract->ad)); } diff --git a/libarchive/archive_read_extract2.c b/libarchive/archive_read_extract2.c new file mode 100644 index 000000000000..62a46bef01d2 --- /dev/null +++ b/libarchive/archive_read_extract2.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +static int copy_data(struct archive *ar, struct archive *aw); +static int archive_read_extract_cleanup(struct archive_read *); + + +/* Retrieve an extract object without initialising the associated + * archive_write_disk object. + */ +struct archive_read_extract * +__archive_read_get_extract(struct archive_read *a) +{ + if (a->extract == NULL) { + a->extract = (struct archive_read_extract *)malloc(sizeof(*a->extract)); + if (a->extract == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't extract"); + return (NULL); + } + memset(a->extract, 0, sizeof(*a->extract)); + a->cleanup_archive_extract = archive_read_extract_cleanup; + } + return (a->extract); +} + +/* + * Cleanup function for archive_extract. + */ +static int +archive_read_extract_cleanup(struct archive_read *a) +{ + int ret = ARCHIVE_OK; + + if (a->extract->ad != NULL) { + ret = archive_write_free(a->extract->ad); + } + free(a->extract); + a->extract = NULL; + return (ret); +} + +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. */ + if (a->skip_file_set) + archive_write_disk_set_skip_file(ad, + a->skip_file_dev, a->skip_file_ino); + 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, ad); + else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) + /* Otherwise, pour data into the entry. */ + 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, ad); + /* Use the worst error return. */ + if (r2 < r) + r = r2; + return (r); +} + +void +archive_read_extract_set_progress_callback(struct archive *_a, + void (*progress_func)(void *), void *user_data) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_extract *extract = __archive_read_get_extract(a); + if (extract != NULL) { + extract->extract_progress = progress_func; + extract->extract_progress_user_data = user_data; + } +} + +static int +copy_data(struct archive *ar, struct archive *aw) +{ + int64_t offset; + const void *buff; + struct archive_read_extract *extract; + size_t size; + int r; + + extract = __archive_read_get_extract((struct archive_read *)ar); + if (extract == NULL) + return (ARCHIVE_FATAL); + for (;;) { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return (ARCHIVE_OK); + if (r != ARCHIVE_OK) + return (r); + r = (int)archive_write_data_block(aw, buff, size, offset); + if (r < ARCHIVE_WARN) + r = ARCHIVE_WARN; + if (r < ARCHIVE_OK) { + archive_set_error(ar, archive_errno(aw), + "%s", archive_error_string(aw)); + return (r); + } + if (extract->extract_progress) + (extract->extract_progress) + (extract->extract_progress_user_data); + } +} diff --git a/libarchive/archive_read_filter.3 b/libarchive/archive_read_filter.3 index 8761127d96db..7f020e3734b8 100644 --- a/libarchive/archive_read_filter.3 +++ b/libarchive/archive_read_filter.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 2, 2012 +.Dd August 14, 2014 .Dt ARCHIVE_READ_FILTER 3 .Os .Sh NAME @@ -32,8 +32,11 @@ .Nm archive_read_support_filter_bzip2 , .Nm archive_read_support_filter_compress , .Nm archive_read_support_filter_gzip , +.Nm archive_read_support_filter_lz4 , .Nm archive_read_support_filter_lzma , .Nm archive_read_support_filter_none , +.Nm archive_read_support_filter_rpm , +.Nm archive_read_support_filter_uu , .Nm archive_read_support_filter_xz , .Nm archive_read_support_filter_program , .Nm archive_read_support_filter_program_signature @@ -50,12 +53,24 @@ Streaming Archive Library (libarchive, -larchive) .Ft int .Fn archive_read_support_filter_compress "struct archive *" .Ft int +.Fn archive_read_support_filter_grzip "struct archive *" +.Ft int .Fn archive_read_support_filter_gzip "struct archive *" .Ft int +.Fn archive_read_support_filter_lrzip "struct archive *" +.Ft int +.Fn archive_read_support_filter_lz4 "struct archive *" +.Ft int .Fn archive_read_support_filter_lzma "struct archive *" .Ft int +.Fn archive_read_support_filter_lzop "struct archive *" +.Ft int .Fn archive_read_support_filter_none "struct archive *" .Ft int +.Fn archive_read_support_filter_rpm "struct archive *" +.Ft int +.Fn archive_read_support_filter_uu "struct archive *" +.Ft int .Fn archive_read_support_filter_xz "struct archive *" .Ft int .Fo archive_read_support_filter_program @@ -75,9 +90,15 @@ Streaming Archive Library (libarchive, -larchive) .It Xo .Fn archive_read_support_filter_bzip2 , .Fn archive_read_support_filter_compress , +.Fn archive_read_support_filter_grzip , .Fn archive_read_support_filter_gzip , +.Fn archive_read_support_filter_lrzip , +.Fn archive_read_support_filter_lz4 , .Fn archive_read_support_filter_lzma , +.Fn archive_read_support_filter_lzop , .Fn archive_read_support_filter_none , +.Fn archive_read_support_filter_rpm , +.Fn archive_read_support_filter_uu , .Fn archive_read_support_filter_xz .Xc Enables auto-detection code and decompression support for the diff --git a/libarchive/archive_read_open.3 b/libarchive/archive_read_open.3 index 30a740bef13f..4d8272cac873 100644 --- a/libarchive/archive_read_open.3 +++ b/libarchive/archive_read_open.3 @@ -130,14 +130,14 @@ objects can be found in the overview manual page for The callback functions must match the following prototypes: .Bl -item -offset indent .It -.Ft typedef ssize_t +.Ft typedef la_ssize_t .Fo archive_read_callback .Fa "struct archive *" .Fa "void *client_data" .Fa "const void **buffer" .Fc .It -.Ft typedef off_t +.Ft typedef la_int64_t .Fo archive_skip_callback .Fa "struct archive *" .Fa "void *client_data" diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c index e0f95bf41b65..f59cd07fe6c0 100644 --- a/libarchive/archive_read_open_fd.c +++ b/libarchive/archive_read_open_fd.c @@ -59,6 +59,7 @@ struct read_fd_data { static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); +static int64_t file_seek(struct archive *, void *, int64_t request, int); static int64_t file_skip(struct archive *, void *, int64_t request); int @@ -102,6 +103,7 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size) archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); + archive_read_set_seek_callback(a, file_seek); archive_read_set_close_callback(a, file_close); archive_read_set_callback_data(a, mine); return (archive_read_open1(a)); @@ -170,6 +172,33 @@ file_skip(struct archive *a, void *client_data, int64_t request) return (-1); } +/* + * TODO: Store the offset and use it in the read callback. + */ +static int64_t +file_seek(struct archive *a, void *client_data, int64_t request, int whence) +{ + struct read_fd_data *mine = (struct read_fd_data *)client_data; + int64_t r; + + /* We use off_t here because lseek() is declared that way. */ + /* See above for notes about when off_t is less than 64 bits. */ + r = lseek(mine->fd, request, whence); + if (r >= 0) + return r; + + if (errno == ESPIPE) { + archive_set_error(a, errno, + "A file descriptor(%d) is not seekable(PIPE)", mine->fd); + return (ARCHIVE_FAILED); + } else { + /* If the input is corrupted or truncated, fail. */ + archive_set_error(a, errno, + "Error seeking in a file descriptor(%d)", mine->fd); + return (ARCHIVE_FATAL); + } +} + static int file_close(struct archive *a, void *client_data) { diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index 3a33c258ee70..bfe933bf32e9 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -83,8 +83,9 @@ archive_read_open_FILE(struct archive *a, FILE *f) mine->f = f; /* * If we can't fstat() the file, it may just be that it's not - * a file. (FILE * objects can wrap many kinds of I/O - * streams, some of which don't support fileno()).) + * a file. (On some platforms, FILE * objects can wrap I/O + * streams that don't support fileno()). As a result, fileno() + * should be used cautiously.) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); @@ -150,7 +151,10 @@ file_skip(struct archive *a, void *client_data, int64_t request) skip = max_skip; } -#if HAVE_FSEEKO +#ifdef __ANDROID__ + /* fileno() isn't safe on all platforms ... see above. */ + if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0) +#elif HAVE_FSEEKO if (fseeko(mine->f, skip, SEEK_CUR) != 0) #elif HAVE__FSEEKI64 if (_fseeki64(mine->f, skip, SEEK_CUR) != 0) diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index fefcd904d2c4..5611aa85aa45 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -103,7 +103,9 @@ int archive_read_open_filename(struct archive *a, const char *filename, size_t block_size) { - const char *filenames[2] = { filename, NULL }; + const char *filenames[2]; + filenames[0] = filename; + filenames[1] = NULL; return archive_read_open_filenames(a, filenames, block_size); } @@ -176,7 +178,7 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, #else /* * POSIX system does not support a wchar_t interface for - * open() system call, so we have to translate a whcar_t + * open() system call, so we have to translate a wchar_t * filename to multi-byte one and use it. */ struct archive_string fn; diff --git a/libarchive/archive_read_open_memory.c b/libarchive/archive_read_open_memory.c index bcc7d6f78e30..ff935a708f59 100644 --- a/libarchive/archive_read_open_memory.c +++ b/libarchive/archive_read_open_memory.c @@ -41,9 +41,9 @@ __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/ */ struct read_memory_data { - unsigned char *start; - unsigned char *p; - unsigned char *end; + const unsigned char *start; + const unsigned char *p; + const unsigned char *end; ssize_t read_size; }; @@ -54,7 +54,7 @@ static int64_t memory_read_skip(struct archive *, void *, int64_t request); static ssize_t memory_read(struct archive *, void *, const void **buff); int -archive_read_open_memory(struct archive *a, void *buff, size_t size) +archive_read_open_memory(struct archive *a, const void *buff, size_t size) { return archive_read_open_memory2(a, buff, size, size); } @@ -65,7 +65,7 @@ archive_read_open_memory(struct archive *a, void *buff, size_t size) * test harnesses can exercise block operations inside the library. */ int -archive_read_open_memory2(struct archive *a, void *buff, +archive_read_open_memory2(struct archive *a, const void *buff, size_t size, size_t read_size) { struct read_memory_data *mine; @@ -76,7 +76,7 @@ archive_read_open_memory2(struct archive *a, void *buff, return (ARCHIVE_FATAL); } memset(mine, 0, sizeof(*mine)); - mine->start = mine->p = (unsigned char *)buff; + mine->start = mine->p = (const unsigned char *)buff; mine->end = mine->start + size; mine->read_size = read_size; archive_read_set_open_callback(a, memory_read_open); diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 8a6c859a8a14..9b61a5380a2e 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -26,8 +26,10 @@ */ #ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif +#endif #ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED #define ARCHIVE_READ_PRIVATE_H_INCLUDED @@ -141,6 +143,18 @@ struct archive_read_client { int64_t position; struct archive_read_data_node *dataset; }; +struct archive_read_passphrase { + char *passphrase; + struct archive_read_passphrase *next; +}; + +struct archive_read_extract { + struct archive *ad; /* archive_write_disk object */ + + /* Progress function invoked during extract. */ + void (*extract_progress)(void *); + void *extract_progress_user_data; +}; struct archive_read { struct archive archive; @@ -152,28 +166,11 @@ struct archive_read { int64_t skip_file_dev; int64_t skip_file_ino; - /* - * Used by archive_read_data() to track blocks and copy - * data to client buffers, filling gaps with zero bytes. - */ - const char *read_data_block; - int64_t read_data_offset; - int64_t read_data_output_offset; - size_t read_data_remaining; - - /* - * Used by formats/filters to determine the amount of data - * requested from a call to archive_read_data(). This is only - * useful when the format/filter has seek support. - */ - char read_data_is_posix_read; - size_t read_data_requested; - /* Callbacks to open/read/write/close client archive streams. */ struct archive_read_client client; /* Registered filter bidders. */ - struct archive_read_filter_bidder bidders[14]; + struct archive_read_filter_bidder bidders[16]; /* Last filter in chain */ struct archive_read_filter *filter; @@ -207,26 +204,41 @@ struct archive_read { int (*read_data_skip)(struct archive_read *); int64_t (*seek_data)(struct archive_read *, int64_t, int); int (*cleanup)(struct archive_read *); + int (*format_capabilties)(struct archive_read *); + int (*has_encrypted_entries)(struct archive_read *); } formats[16]; struct archive_format_descriptor *format; /* Active format. */ /* * Various information needed by archive_extract. */ - struct extract *extract; + struct archive_read_extract *extract; int (*cleanup_archive_extract)(struct archive_read *); + + /* + * Decryption passphrase. + */ + struct { + struct archive_read_passphrase *first; + struct archive_read_passphrase **last; + int candiate; + archive_passphrase_callback *callback; + void *client_data; + } passphrases; }; int __archive_read_register_format(struct archive_read *a, - void *format_data, - const char *name, - int (*bid)(struct archive_read *, int), - int (*options)(struct archive_read *, const char *, const char *), - int (*read_header)(struct archive_read *, struct archive_entry *), - int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), - int (*read_data_skip)(struct archive_read *), - int64_t (*seek_data)(struct archive_read *, int64_t, int), - int (*cleanup)(struct archive_read *)); + void *format_data, + const char *name, + int (*bid)(struct archive_read *, int), + int (*options)(struct archive_read *, const char *, const char *), + int (*read_header)(struct archive_read *, struct archive_entry *), + int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), + int (*read_data_skip)(struct archive_read *), + int64_t (*seek_data)(struct archive_read *, int64_t, int), + int (*cleanup)(struct archive_read *), + int (*format_capabilities)(struct archive_read *), + int (*has_encrypted_entries)(struct archive_read *)); int __archive_read_get_bidder(struct archive_read *a, struct archive_read_filter_bidder **bidder); @@ -241,4 +253,12 @@ int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); int __archive_read_program(struct archive_read_filter *, const char *); void __archive_read_free_filters(struct archive_read *); int __archive_read_close_filters(struct archive_read *); +struct archive_read_extract *__archive_read_get_extract(struct archive_read *); + + +/* + * Get a decryption passphrase. + */ +void __archive_read_reset_passphrase(struct archive_read *a); +const char * __archive_read_next_passphrase(struct archive_read *a); #endif diff --git a/libarchive/archive_read_set_options.3 b/libarchive/archive_read_set_options.3 index 6fe9f90f8f38..1a251cefecd1 100644 --- a/libarchive/archive_read_set_options.3 +++ b/libarchive/archive_read_set_options.3 @@ -193,6 +193,28 @@ Defaults to enabled, use .Cm !rockridge to disable. .El +.It Format tar +.Bl -tag -compact -width indent +.It Cm compat-2x +Libarchive 2.x incorrectly encoded Unicode filenames on +some platforms. +This option mimics the libarchive 2.x filename handling +so that such archives can be read correctly. +.It Cm hdrcharset +The value is used as a character set name that will be +used when translating filenames. +.It Cm mac-ext +Support Mac OS metadata extension that records data in special +files beginning with a period and underscore. +Defaults to enabled on Mac OS, disabled on other platforms. +Use +.Cm !mac-ext +to disable. +.It Cm read_concatenated_archives +Ignore zeroed blocks in the archive, which occurs when multiple tar archives +have been concatenated together. Without this option, only the contents of +the first concatenated archive would be read. +.El .El .\" .Sh ERRORS diff --git a/libarchive/archive_read_set_options.c b/libarchive/archive_read_set_options.c index 793f8f73e53e..2e2eea69058d 100644 --- a/libarchive/archive_read_set_options.c +++ b/libarchive/archive_read_set_options.c @@ -76,18 +76,20 @@ archive_set_format_option(struct archive *_a, const char *m, const char *o, const char *v) { struct archive_read *a = (struct archive_read *)_a; - struct archive_format_descriptor *format; size_t i; - int r, rv = ARCHIVE_WARN; + int r, rv = ARCHIVE_WARN, matched_modules = 0; for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) { - format = &a->formats[i]; - if (format == NULL || format->options == NULL || - format->name == NULL) + struct archive_format_descriptor *format = &a->formats[i]; + + if (format->options == NULL || format->name == NULL) /* This format does not support option. */ continue; - if (m != NULL && strcmp(format->name, m) != 0) - continue; + if (m != NULL) { + if (strcmp(format->name, m) != 0) + continue; + ++matched_modules; + } a->format = format; r = format->options(a, o, v); @@ -96,16 +98,13 @@ archive_set_format_option(struct archive *_a, const char *m, const char *o, if (r == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - if (m != NULL) - return (r); - if (r == ARCHIVE_OK) rv = ARCHIVE_OK; } /* If the format name didn't match, return a special code for * _archive_set_option[s]. */ - if (rv == ARCHIVE_WARN && m != NULL) - rv = ARCHIVE_WARN - 1; + if (m != NULL && matched_modules == 0) + return ARCHIVE_WARN - 1; return (rv); } @@ -116,7 +115,7 @@ archive_set_filter_option(struct archive *_a, const char *m, const char *o, struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *filter; struct archive_read_filter_bidder *bidder; - int r, rv = ARCHIVE_WARN; + int r, rv = ARCHIVE_WARN, matched_modules = 0; for (filter = a->filter; filter != NULL; filter = filter->upstream) { bidder = filter->bidder; @@ -125,24 +124,24 @@ archive_set_filter_option(struct archive *_a, const char *m, const char *o, if (bidder->options == NULL) /* This bidder does not support option */ continue; - if (m != NULL && strcmp(filter->name, m) != 0) - continue; + if (m != NULL) { + if (strcmp(filter->name, m) != 0) + continue; + ++matched_modules; + } r = bidder->options(bidder, o, v); if (r == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - if (m != NULL) - return (r); - if (r == ARCHIVE_OK) rv = ARCHIVE_OK; } /* If the filter name didn't match, return a special code for * _archive_set_option[s]. */ - if (rv == ARCHIVE_WARN && m != NULL) - rv = ARCHIVE_WARN - 1; + if (m != NULL && matched_modules == 0) + return ARCHIVE_WARN - 1; return (rv); } diff --git a/libarchive/archive_read_support_filter_all.c b/libarchive/archive_read_support_filter_all.c index b778cfb79215..68c53de41f2d 100644 --- a/libarchive/archive_read_support_filter_all.c +++ b/libarchive/archive_read_support_filter_all.c @@ -69,6 +69,8 @@ archive_read_support_filter_all(struct archive *a) archive_read_support_filter_lzop(a); /* The decode code always uses "grzip -d" command-line. */ archive_read_support_filter_grzip(a); + /* Lz4 falls back to "lz4 -d" command-line program. */ + archive_read_support_filter_lz4(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c index 3f5d1f37eab3..e05132dbfaec 100644 --- a/libarchive/archive_read_support_filter_compress.c +++ b/libarchive/archive_read_support_filter_compress.c @@ -185,19 +185,22 @@ compress_bidder_bid(struct archive_read_filter_bidder *self, (void)self; /* UNUSED */ - buffer = __archive_read_filter_ahead(filter, 2, &avail); + /* Shortest valid compress file is 3 bytes. */ + buffer = __archive_read_filter_ahead(filter, 3, &avail); if (buffer == NULL) return (0); bits_checked = 0; + /* First two bytes are the magic value */ if (buffer[0] != 0x1F || buffer[1] != 0x9D) return (0); - bits_checked += 16; - - /* - * TODO: Verify more. - */ + /* Third byte holds compression parameters. */ + if (buffer[2] & 0x20) /* Reserved bit, must be zero. */ + return (0); + if (buffer[2] & 0x40) /* Reserved bit, must be zero. */ + return (0); + bits_checked += 18; return (bits_checked); } @@ -239,7 +242,13 @@ compress_bidder_init(struct archive_read_filter *self) (void)getbits(self, 8); /* Skip first signature byte. */ (void)getbits(self, 8); /* Skip second signature byte. */ + /* Get compression parameters. */ code = getbits(self, 8); + if ((code & 0x1f) > 16) { + archive_set_error(&self->archive->archive, -1, + "Invalid compressed data"); + return (ARCHIVE_FATAL); + } state->maxcode_bits = code & 0x1f; state->maxcode = (1 << state->maxcode_bits); state->use_reset_code = code & 0x80; @@ -368,7 +377,8 @@ next_code(struct archive_read_filter *self) return (next_code(self)); } - if (code > state->free_ent) { + if (code > state->free_ent + || (code == state->free_ent && state->oldcode < 0)) { /* An invalid code is a fatal error. */ archive_set_error(&(self->archive->archive), -1, "Invalid compressed data"); diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c new file mode 100644 index 000000000000..e877917b940b --- /dev/null +++ b/libarchive/archive_read_support_filter_lz4.c @@ -0,0 +1,737 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_LZ4_H +#include <lz4.h> +#endif + +#include "archive.h" +#include "archive_endian.h" +#include "archive_private.h" +#include "archive_read_private.h" +#include "archive_xxhash.h" + +#define LZ4_MAGICNUMBER 0x184d2204 +#define LZ4_SKIPPABLED 0x184d2a50 +#define LZ4_LEGACY 0x184c2102 + +#if defined(HAVE_LIBLZ4) +struct private_data { + enum { SELECT_STREAM, + READ_DEFAULT_STREAM, + READ_DEFAULT_BLOCK, + READ_LEGACY_STREAM, + READ_LEGACY_BLOCK, + } stage; + struct { + unsigned block_independence:1; + unsigned block_checksum:3; + unsigned stream_size:1; + unsigned stream_checksum:1; + unsigned preset_dictionary:1; + int block_maximum_size; + } flags; + int64_t stream_size; + uint32_t dict_id; + char *out_block; + size_t out_block_size; + + /* Bytes read but not yet consumed via __archive_read_consume() */ + size_t unconsumed; + size_t decoded_size; + void *xxh32_state; + + char valid; /* True = decompressor is initialized */ + char eof; /* True = found end of compressed data. */ +}; + +#define LEGACY_BLOCK_SIZE (8 * 1024 * 1024) + +/* Lz4 filter */ +static ssize_t lz4_filter_read(struct archive_read_filter *, const void **); +static int lz4_filter_close(struct archive_read_filter *); +#endif + +/* + * Note that we can detect lz4 archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * error messages.) So the bid framework here gets compiled even + * if liblz4 is unavailable. + */ +static int lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); +static int lz4_reader_init(struct archive_read_filter *); +static int lz4_reader_free(struct archive_read_filter_bidder *); +#if defined(HAVE_LIBLZ4) +static ssize_t lz4_filter_read_default_stream(struct archive_read_filter *, + const void **); +static ssize_t lz4_filter_read_legacy_stream(struct archive_read_filter *, + const void **); +#endif + +int +archive_read_support_filter_lz4(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *reader; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_lz4"); + + if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->name = "lz4"; + reader->bid = lz4_reader_bid; + reader->init = lz4_reader_init; + reader->options = NULL; + reader->free = lz4_reader_free; +#if defined(HAVE_LIBLZ4) + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external lz4 program"); + return (ARCHIVE_WARN); +#endif +} + +static int +lz4_reader_free(struct archive_read_filter_bidder *self){ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + */ +static int +lz4_reader_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *buffer; + ssize_t avail; + int bits_checked; + uint32_t number; + + (void)self; /* UNUSED */ + + /* Minimal lz4 archive is 11 bytes. */ + buffer = __archive_read_filter_ahead(filter, 11, &avail); + if (buffer == NULL) + return (0); + + /* First four bytes must be LZ4 magic numbers. */ + bits_checked = 0; + if ((number = archive_le32dec(buffer)) == LZ4_MAGICNUMBER) { + unsigned char flag, BD; + + bits_checked += 32; + /* Next follows a stream descriptor. */ + /* Descriptor Flags. */ + flag = buffer[4]; + /* A version number must be "01". */ + if (((flag & 0xc0) >> 6) != 1) + return (0); + /* A reserved bit must be "0". */ + if (flag & 2) + return (0); + bits_checked += 8; + BD = buffer[5]; + /* A block maximum size shuld be more than 3. */ + if (((BD & 0x70) >> 4) < 4) + return (0); + /* Reserved bits must be "0". */ + if (BD & ~0x70) + return (0); + bits_checked += 8; + } else if (number == LZ4_LEGACY) { + bits_checked += 32; + } + + return (bits_checked); +} + +#if !defined(HAVE_LIBLZ4) + +/* + * If we don't have the library on this system, we can't actually do the + * decompression. We can, however, still detect compressed archives + * and emit a useful message. + */ +static int +lz4_reader_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "lz4 -d -q"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_FILTER_LZ4; + self->name = "lz4"; + return (r); +} + + +#else + +/* + * Setup the callbacks. + */ +static int +lz4_reader_init(struct archive_read_filter *self) +{ + struct private_data *state; + + self->code = ARCHIVE_FILTER_LZ4; + self->name = "lz4"; + + state = (struct private_data *)calloc(sizeof(*state), 1); + if (state == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lz4 decompression"); + return (ARCHIVE_FATAL); + } + + self->data = state; + state->stage = SELECT_STREAM; + self->read = lz4_filter_read; + self->skip = NULL; /* not supported */ + self->close = lz4_filter_close; + + return (ARCHIVE_OK); +} + +static int +lz4_allocate_out_block(struct archive_read_filter *self) +{ + struct private_data *state = (struct private_data *)self->data; + size_t out_block_size = state->flags.block_maximum_size; + void *out_block; + + if (!state->flags.block_independence) + out_block_size += 64 * 1024; + if (state->out_block_size < out_block_size) { + free(state->out_block); + out_block = (unsigned char *)malloc(out_block_size); + state->out_block_size = out_block_size; + if (out_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lz4 decompression"); + return (ARCHIVE_FATAL); + } + state->out_block = out_block; + } + if (!state->flags.block_independence) + memset(state->out_block, 0, 64 * 1024); + return (ARCHIVE_OK); +} + +static int +lz4_allocate_out_block_for_legacy(struct archive_read_filter *self) +{ + struct private_data *state = (struct private_data *)self->data; + size_t out_block_size = LEGACY_BLOCK_SIZE; + void *out_block; + + if (state->out_block_size < out_block_size) { + free(state->out_block); + out_block = (unsigned char *)malloc(out_block_size); + state->out_block_size = out_block_size; + if (out_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lz4 decompression"); + return (ARCHIVE_FATAL); + } + state->out_block = out_block; + } + return (ARCHIVE_OK); +} + +/* + * Return the next block of decompressed data. + */ +static ssize_t +lz4_filter_read(struct archive_read_filter *self, const void **p) +{ + struct private_data *state = (struct private_data *)self->data; + ssize_t ret; + + if (state->eof) { + *p = NULL; + return (0); + } + + __archive_read_filter_consume(self->upstream, state->unconsumed); + state->unconsumed = 0; + + switch (state->stage) { + case SELECT_STREAM: + break; + case READ_DEFAULT_STREAM: + case READ_LEGACY_STREAM: + /* Reading a lz4 stream already failed. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Invalid sequence."); + return (ARCHIVE_FATAL); + case READ_DEFAULT_BLOCK: + ret = lz4_filter_read_default_stream(self, p); + if (ret != 0 || state->stage != SELECT_STREAM) + return ret; + break; + case READ_LEGACY_BLOCK: + ret = lz4_filter_read_legacy_stream(self, p); + if (ret != 0 || state->stage != SELECT_STREAM) + return ret; + break; + default: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Program error."); + return (ARCHIVE_FATAL); + break; + } + + while (state->stage == SELECT_STREAM) { + const char *read_buf; + + /* Read a magic number. */ + read_buf = __archive_read_filter_ahead(self->upstream, 4, + NULL); + if (read_buf == NULL) { + state->eof = 1; + *p = NULL; + return (0); + } + uint32_t number = archive_le32dec(read_buf); + __archive_read_filter_consume(self->upstream, 4); + if (number == LZ4_MAGICNUMBER) + return lz4_filter_read_default_stream(self, p); + else if (number == LZ4_LEGACY) + return lz4_filter_read_legacy_stream(self, p); + else if ((number & ~0xF) == LZ4_SKIPPABLED) { + read_buf = __archive_read_filter_ahead( + self->upstream, 4, NULL); + if (read_buf == NULL) { + archive_set_error( + &self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Malformed lz4 data"); + return (ARCHIVE_FATAL); + } + uint32_t skip_bytes = archive_le32dec(read_buf); + __archive_read_filter_consume(self->upstream, + 4 + skip_bytes); + } else { + /* Ignore following unrecognized data. */ + state->eof = 1; + *p = NULL; + return (0); + } + } + state->eof = 1; + *p = NULL; + return (0); +} + +static int +lz4_filter_read_descriptor(struct archive_read_filter *self) +{ + struct private_data *state = (struct private_data *)self->data; + const char *read_buf; + ssize_t bytes_remaining; + ssize_t descriptor_bytes; + unsigned char flag, bd; + unsigned int chsum, chsum_verifier; + + /* Make sure we have 2 bytes for flags. */ + read_buf = __archive_read_filter_ahead(self->upstream, 2, + &bytes_remaining); + if (read_buf == NULL) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "truncated lz4 input"); + return (ARCHIVE_FATAL); + } + + /* + Parse flags. + */ + flag = (unsigned char)read_buf[0]; + /* Verify version number. */ + if ((flag & 0xc0) != 1<<6) + goto malformed_error; + /* A reserved bit must be zero. */ + if (flag & 0x02) + goto malformed_error; + state->flags.block_independence = (flag & 0x20) != 0; + state->flags.block_checksum = (flag & 0x10)?4:0; + state->flags.stream_size = (flag & 0x08) != 0; + state->flags.stream_checksum = (flag & 0x04) != 0; + state->flags.preset_dictionary = (flag & 0x01) != 0; + + /* BD */ + bd = (unsigned char)read_buf[1]; + /* Reserved bits must be zero. */ + if (bd & 0x8f) + goto malformed_error; + /* Get a maxinum block size. */ + switch (read_buf[1] >> 4) { + case 4: /* 64 KB */ + state->flags.block_maximum_size = 64 * 1024; + break; + case 5: /* 256 KB */ + state->flags.block_maximum_size = 256 * 1024; + break; + case 6: /* 1 MB */ + state->flags.block_maximum_size = 1024 * 1024; + break; + case 7: /* 4 MB */ + state->flags.block_maximum_size = 4 * 1024 * 1024; + break; + default: + goto malformed_error; + } + + /* Read the whole descriptor in a stream block. */ + descriptor_bytes = 3; + if (state->flags.stream_size) + descriptor_bytes += 8; + if (state->flags.preset_dictionary) + descriptor_bytes += 4; + if (bytes_remaining < descriptor_bytes) { + read_buf = __archive_read_filter_ahead(self->upstream, + descriptor_bytes, &bytes_remaining); + if (read_buf == NULL) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "truncated lz4 input"); + return (ARCHIVE_FATAL); + } + } + /* Check if a descriptor is corrupted */ + chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0); + chsum = (chsum >> 8) & 0xff; + chsum_verifier = read_buf[descriptor_bytes-1] & 0xff; + if (chsum != chsum_verifier) + goto malformed_error; + + __archive_read_filter_consume(self->upstream, descriptor_bytes); + + /* Make sure we have an enough buffer for uncompressed data. */ + if (lz4_allocate_out_block(self) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + if (state->flags.stream_checksum) + state->xxh32_state = __archive_xxhash.XXH32_init(0); + + state->decoded_size = 0; + /* Success */ + return (ARCHIVE_OK); +malformed_error: + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "malformed lz4 data"); + return (ARCHIVE_FATAL); +} + +static ssize_t +lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) +{ + struct private_data *state = (struct private_data *)self->data; + ssize_t compressed_size; + const char *read_buf; + ssize_t bytes_remaining; + int checksum_size; + ssize_t uncompressed_size; + size_t prefix64k; + + *p = NULL; + + /* Make sure we have 4 bytes for a block size. */ + read_buf = __archive_read_filter_ahead(self->upstream, 4, + &bytes_remaining); + if (read_buf == NULL) + goto truncated_error; + compressed_size = archive_le32dec(read_buf); + if ((compressed_size & ~(1 << 31)) > state->flags.block_maximum_size) + goto malformed_error; + /* A compressed size == 0 means the end of stream blocks. */ + if (compressed_size == 0) { + __archive_read_filter_consume(self->upstream, 4); + return 0; + } + + checksum_size = state->flags.block_checksum; + /* Check if the block is uncompressed. */ + if (compressed_size & (1 << 31)) { + compressed_size &= ~(1 << 31); + uncompressed_size = compressed_size; + } else + uncompressed_size = 0;/* Unknown yet. */ + + /* + Unfortunately, lz4 decompression API requires a whole block + for its decompression speed, so we read a whole block and allocate + a huge buffer used for decoded data. + */ + read_buf = __archive_read_filter_ahead(self->upstream, + 4 + compressed_size + checksum_size, &bytes_remaining); + if (read_buf == NULL) + goto truncated_error; + + /* Optional process, checking a block sum. */ + if (checksum_size) { + unsigned int chsum = __archive_xxhash.XXH32( + read_buf + 4, (int)compressed_size, 0); + unsigned int chsum_block = + archive_le32dec(read_buf + 4 + compressed_size); + if (chsum != chsum_block) + goto malformed_error; + } + + + /* If the block is uncompressed, there is nothing to do. */ + if (uncompressed_size) { + /* Prepare a prefix 64k block for next block. */ + if (!state->flags.block_independence) { + prefix64k = 64 * 1024; + if (uncompressed_size < (ssize_t)prefix64k) { + memcpy(state->out_block + + prefix64k - uncompressed_size, + read_buf + 4, + uncompressed_size); + memset(state->out_block, 0, + prefix64k - uncompressed_size); + } else { + memcpy(state->out_block, + read_buf + 4 + + uncompressed_size - prefix64k, + prefix64k); + } + state->decoded_size = 0; + } + state->unconsumed = 4 + uncompressed_size + checksum_size; + *p = read_buf + 4; + return uncompressed_size; + } + + /* + Decompress a block data. + */ + if (state->flags.block_independence) { + prefix64k = 0; + uncompressed_size = LZ4_decompress_safe(read_buf + 4, + state->out_block, (int)compressed_size, + state->flags.block_maximum_size); + } else { + prefix64k = 64 * 1024; + if (state->decoded_size) { + if (state->decoded_size < prefix64k) { + memmove(state->out_block + + prefix64k - state->decoded_size, + state->out_block + prefix64k, + state->decoded_size); + memset(state->out_block, 0, + prefix64k - state->decoded_size); + } else { + memmove(state->out_block, + state->out_block + state->decoded_size, + prefix64k); + } + } +#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 + uncompressed_size = LZ4_decompress_safe_usingDict( + read_buf + 4, + state->out_block + prefix64k, (int)compressed_size, + state->flags.block_maximum_size, + state->out_block, + prefix64k); +#else + uncompressed_size = LZ4_decompress_safe_withPrefix64k( + read_buf + 4, + state->out_block + prefix64k, (int)compressed_size, + state->flags.block_maximum_size); +#endif + } + + /* Check if an error happend in decompression process. */ + if (uncompressed_size < 0) { + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, "lz4 decompression failed"); + return (ARCHIVE_FATAL); + } + + state->unconsumed = 4 + compressed_size + checksum_size; + *p = state->out_block + prefix64k; + state->decoded_size = uncompressed_size; + return uncompressed_size; + +malformed_error: + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "malformed lz4 data"); + return (ARCHIVE_FATAL); +truncated_error: + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "truncated lz4 input"); + return (ARCHIVE_FATAL); +} + +static ssize_t +lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p) +{ + struct private_data *state = (struct private_data *)self->data; + const char *read_buf; + ssize_t bytes_remaining; + ssize_t ret; + + if (state->stage == SELECT_STREAM) { + state->stage = READ_DEFAULT_STREAM; + /* First, read a desciprtor. */ + if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK) + return (ret); + state->stage = READ_DEFAULT_BLOCK; + } + /* Decompress a block. */ + ret = lz4_filter_read_data_block(self, p); + + /* If the end of block is detected, change the filter status + to read next stream. */ + if (ret == 0 && *p == NULL) + state->stage = SELECT_STREAM; + + /* Optional process, checking a stream sum. */ + if (state->flags.stream_checksum) { + if (state->stage == SELECT_STREAM) { + unsigned int checksum; + unsigned int checksum_stream; + read_buf = __archive_read_filter_ahead(self->upstream, + 4, &bytes_remaining); + if (read_buf == NULL) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "truncated lz4 input"); + return (ARCHIVE_FATAL); + } + checksum = archive_le32dec(read_buf); + __archive_read_filter_consume(self->upstream, 4); + checksum_stream = __archive_xxhash.XXH32_digest( + state->xxh32_state); + state->xxh32_state = NULL; + if (checksum != checksum_stream) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "lz4 stream cheksum error"); + return (ARCHIVE_FATAL); + } + } else if (ret > 0) + __archive_xxhash.XXH32_update(state->xxh32_state, + *p, (int)ret); + } + return (ret); +} + +static ssize_t +lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p) +{ + struct private_data *state = (struct private_data *)self->data; + int compressed; + const char *read_buf; + ssize_t ret; + + *p = NULL; + ret = lz4_allocate_out_block_for_legacy(self); + if (ret != ARCHIVE_OK) + return ret; + + /* Make sure we have 4 bytes for a block size. */ + read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (read_buf == NULL) { + if (state->stage == SELECT_STREAM) { + state->stage = READ_LEGACY_STREAM; + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "truncated lz4 input"); + return (ARCHIVE_FATAL); + } + state->stage = SELECT_STREAM; + return 0; + } + state->stage = READ_LEGACY_BLOCK; + compressed = archive_le32dec(read_buf); + if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) { + state->stage = SELECT_STREAM; + return 0; + } + + /* Make sure we have a whole block. */ + read_buf = __archive_read_filter_ahead(self->upstream, + 4 + compressed, NULL); + ret = LZ4_decompress_safe(read_buf + 4, state->out_block, + compressed, (int)state->out_block_size); + if (ret < 0) { + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, "lz4 decompression failed"); + return (ARCHIVE_FATAL); + } + *p = state->out_block; + state->unconsumed = 4 + compressed; + return ret; +} + +/* + * Clean up the decompressor. + */ +static int +lz4_filter_close(struct archive_read_filter *self) +{ + struct private_data *state; + int ret = ARCHIVE_OK; + + state = (struct private_data *)self->data; + free(state->xxh32_state); + free(state->out_block); + free(state); + return (ret); +} + +#endif /* HAVE_LIBLZ4 */ diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c index 713af31e99f0..44ac9964ae11 100644 --- a/libarchive/archive_read_support_filter_lzop.c +++ b/libarchive/archive_read_support_filter_lzop.c @@ -242,10 +242,18 @@ consume_header(struct archive_read_filter *self) if (version >= 0x940) { unsigned level = *p++; - if (method == 1 && level == 0) level = 3; - if (method == 2 && level == 0) level = 1; - if (method == 3 && level == 0) level = 9; - if (level < 1 && level > 9) { +#if 0 + unsigned default_level[] = {0, 3, 1, 9}; +#endif + if (level == 0) + /* Method is 1..3 here due to check above. */ +#if 0 /* Avoid an error Clang Static Analyzer claims + "Value stored to 'level' is never read". */ + level = default_level[method]; +#else + ;/* NOP */ +#endif + else if (level > 9) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Invalid level"); return (ARCHIVE_FAILED); diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c index 471771b6f469..787a619f2f3a 100644 --- a/libarchive/archive_read_support_filter_uu.c +++ b/libarchive/archive_read_support_filter_uu.c @@ -509,7 +509,7 @@ read_more: return (ARCHIVE_FATAL); } llen = len; - if (nl == 0) { + if ((nl == 0) && (uudecode->state != ST_UUEND)) { /* * Save remaining data which does not contain * NL('\n','\r'). @@ -527,6 +527,7 @@ read_more: self->upstream, ravail); goto read_more; } + used += len; break; } switch (uudecode->state) { diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c index 15824b1d0974..4e0a95feeb0f 100644 --- a/libarchive/archive_read_support_filter_xz.c +++ b/libarchive/archive_read_support_filter_xz.c @@ -601,7 +601,7 @@ lzip_init(struct archive_read_filter *self) return (ARCHIVE_FATAL); } ret = lzma_raw_decoder(&(state->stream), filters); -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 free(filters[0].options); #endif if (ret != LZMA_OK) { @@ -627,7 +627,7 @@ lzip_tail(struct archive_read_filter *self) f = __archive_read_filter_ahead(self->upstream, tail, &avail_in); if (f == NULL && avail_in < 0) return (ARCHIVE_FATAL); - if (avail_in < tail) { + if (f == NULL || avail_in < tail) { archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Remaining data is less bytes"); return (ARCHIVE_FAILED); diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 194b8d51c96a..90901acb710f 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -69,7 +69,11 @@ __FBSDID("$FreeBSD$"); #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 -#define _7Z_CRYPTO 0x06F10701 +#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */ +#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */ +#define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */ + + #define _7Z_X86 0x03030103 #define _7Z_X86_BCJ2 0x0303011B #define _7Z_POWERPC 0x03030205 @@ -104,6 +108,7 @@ __FBSDID("$FreeBSD$"); #define kMTime 0x14 #define kAttributes 0x15 #define kEncodedHeader 0x17 +#define kDummy 0x19 struct _7z_digests { unsigned char *defineds; @@ -322,8 +327,18 @@ struct _7zip { struct archive_string_conv *sconv; char format_name[64]; + + /* Custom value that is non-zero if this archive contains encrypted entries. */ + int has_encrypted_entries; }; +/* Maximum entry size. This limitation prevents reading intentional + * corrupted 7-zip files on assuming there are not so many entries in + * the files. */ +#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) + +static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); +static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); static int archive_read_format_7zip_cleanup(struct archive_read *); static int archive_read_format_7zip_read_data(struct archive_read *, @@ -401,6 +416,13 @@ archive_read_support_format_7zip(struct archive *_a) return (ARCHIVE_FATAL); } + /* + * Until enough data has been read, we cannot tell about + * any encrypted entries yet. + */ + zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + + r = __archive_read_register_format(a, zip, "7zip", @@ -410,7 +432,9 @@ archive_read_support_format_7zip(struct archive *_a) archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, NULL, - archive_read_format_7zip_cleanup); + archive_read_format_7zip_cleanup, + archive_read_support_format_7zip_capabilities, + archive_read_format_7zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); @@ -418,6 +442,27 @@ archive_read_support_format_7zip(struct archive *_a) } static int +archive_read_support_format_7zip_capabilities(struct archive_read * a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); +} + + +static int +archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) +{ + if (_a && _a->format) { + struct _7zip * zip = (struct _7zip *)_a->format->data; + if (zip) { + return zip->has_encrypted_entries; + } + } + return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; +} + +static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { const char *p; @@ -476,7 +521,7 @@ check_7zip_header_in_sfx(const char *p) switch ((unsigned char)p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) - return (6); + return (6); /* * Test the CRC because its extraction code has 7-Zip * Magic Code, so we should do this in order not to @@ -484,15 +529,15 @@ check_7zip_header_in_sfx(const char *p) */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) - return (6); + return (6); /* Hit the header! */ return (0); - case 0x37: return (5); - case 0x7A: return (4); - case 0xBC: return (3); - case 0xAF: return (2); - case 0x27: return (1); - default: return (6); + case 0x37: return (5); + case 0x7A: return (4); + case 0xBC: return (3); + case 0xAF: return (2); + case 0x27: return (1); + default: return (6); } } @@ -568,6 +613,19 @@ archive_read_format_7zip_read_header(struct archive_read *a, struct _7zip *zip = (struct _7zip *)a->format->data; struct _7zip_entry *zip_entry; int r, ret = ARCHIVE_OK; + struct _7z_folder *folder = 0; + uint64_t fidx = 0; + + /* + * It should be sufficient to call archive_read_next_header() for + * a reader to determine if an entry is encrypted or not. If the + * encryption of an entry is only detectable when calling + * archive_read_data(), so be it. We'll do the same check there + * as well. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; if (a->archive.archive_format_name == NULL) @@ -588,7 +646,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, } zip_entry = zip->entry; - if (zip->entries_remaining <= 0) + if (zip->entries_remaining <= 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; @@ -604,6 +662,32 @@ archive_read_format_7zip_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } + /* Figure out if the entry is encrypted by looking at the folder + that is associated to the current 7zip entry. If the folder + has a coder with a _7Z_CRYPTO codec then the folder is encrypted. + Hence the entry must also be encrypted. */ + if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) { + folder = &(zip->si.ci.folders[zip_entry->folderIndex]); + for (fidx=0; folder && fidx<folder->numCoders; fidx++) { + switch(folder->coders[fidx].codec) { + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: { + archive_entry_set_is_data_encrypted(entry, 1); + zip->has_encrypted_entries = 1; + break; + } + } + } + } + + /* Now that we've checked for encryption, if there were still no + * encrypted entries found we can say for sure that there are none. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->sconv) != 0) { @@ -707,6 +791,10 @@ archive_read_format_7zip_read_data(struct archive_read *a, zip = (struct _7zip *)(a->format->data); + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if (zip->pack_stream_bytes_unconsumed) read_consume(a); @@ -969,7 +1057,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, { lzma_options_delta delta_opt; lzma_filter filters[LZMA_FILTERS_MAX]; -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 lzma_filter *ff; #endif int fi = 0; @@ -994,7 +1082,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, * for BCJ+LZMA. If we were able to tell the uncompressed * size to liblzma when using lzma_raw_decoder() liblzma * could correctly deal with BCJ+LZMA. But unfortunately - * there is no way to do that. + * there is no way to do that. * Discussion about this can be found at XZ Utils forum. */ if (coder2 != NULL) { @@ -1056,7 +1144,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 ff = &filters[fi]; #endif r = lzma_properties_decode(&filters[fi], NULL, @@ -1070,7 +1158,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); -#if LZMA_VERSION < 50000030 +#if LZMA_VERSION < 50010000 free(ff->options); #endif if (r != LZMA_OK) { @@ -1113,7 +1201,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", - detail == NULL ? "??" : detail); + detail != NULL ? detail : "??"); zip->bzstream_valid = 0; return (ARCHIVE_FAILED); } @@ -1203,6 +1291,17 @@ init_decompression(struct archive_read *a, struct _7zip *zip, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: + if (a->entry) { + archive_entry_set_is_metadata_encrypted(a->entry, 1); + archive_entry_set_is_data_encrypted(a->entry, 1); + zip->has_encrypted_entries = 1; + } + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); + return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown codec ID: %lX", zip->codec); @@ -1426,7 +1525,7 @@ decompress(struct archive_read *a, struct _7zip *zip, do { int sym; - + sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &(zip->ppmd7_context), &(zip->range_dec.p)); if (sym < 0) { @@ -1570,7 +1669,7 @@ parse_7zip_uint64(struct archive_read *a, uint64_t *val) mask >>= 1; continue; } - *val += (avail & (mask -1)) << (8 * i); + *val += ((uint64_t)(avail & (mask -1))) << (8 * i); break; } return (0); @@ -1670,7 +1769,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) return (-1); if (pi->numPackStreams == 0) return (-1); - if (1000000 < pi->numPackStreams) + if (UMAX_ENTRY < pi->numPackStreams) return (-1); /* @@ -1799,12 +1898,12 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) if (parse_7zip_uint64( a, &(f->coders[i].numInStreams)) < 0) return (-1); - if (1000000 < f->coders[i].numInStreams) + if (UMAX_ENTRY < f->coders[i].numInStreams) return (-1); if (parse_7zip_uint64( a, &(f->coders[i].numOutStreams)) < 0) return (-1); - if (1000000 < f->coders[i].numOutStreams) + if (UMAX_ENTRY < f->coders[i].numOutStreams) return (-1); } @@ -1844,11 +1943,11 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) for (i = 0; i < f->numBindPairs; i++) { if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); - if (1000000 < f->bindPairs[i].inIndex) + if (UMAX_ENTRY < f->bindPairs[i].inIndex) return (-1); if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); - if (1000000 < f->bindPairs[i].outIndex) + if (UMAX_ENTRY < f->bindPairs[i].outIndex) return (-1); } @@ -1874,7 +1973,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) for (i = 0; i < f->numPackedStreams; i++) { if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) return (-1); - if (1000000 < f->packedStreams[i]) + if (UMAX_ENTRY < f->packedStreams[i]) return (-1); } } @@ -1916,8 +2015,8 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) */ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) goto failed; - if (1000000 < ci->numFolders) - return (-1); + if (UMAX_ENTRY < ci->numFolders) + return (-1); /* * Read External. @@ -1938,9 +2037,18 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) case 1: if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) return (-1); - if (1000000 < ci->dataStreamIndex) + if (UMAX_ENTRY < ci->dataStreamIndex) return (-1); + if (ci->numFolders > 0) { + archive_set_error(&a->archive, -1, + "Malformed 7-Zip archive"); + goto failed; + } break; + default: + archive_set_error(&a->archive, -1, + "Malformed 7-Zip archive"); + goto failed; } if ((p = header_bytes(a, 1)) == NULL) @@ -2043,7 +2151,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); - if (1000000 < f[i].numUnpackStreams) + if (UMAX_ENTRY < f[i].numUnpackStreams) return (-1); unpack_streams += (size_t)f[i].numUnpackStreams; } @@ -2292,8 +2400,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); - if (1000000 < zip->numFiles) - return (-1); + if (UMAX_ENTRY < zip->numFiles) + return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) @@ -2452,6 +2560,9 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, } break; } + case kDummy: + if (ll == 0) + break; default: if (header_bytes(a, ll) == NULL) return (-1); @@ -2591,7 +2702,7 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type) if (*p) { if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) goto failed; - if (1000000 < h->dataIndex) + if (UMAX_ENTRY < h->dataIndex) goto failed; } @@ -2755,6 +2866,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, zip->header_crc32 = 0; zip->header_is_encoded = 0; zip->header_is_being_read = 1; + zip->has_encrypted_entries = 0; check_header_crc = 1; if ((p = header_bytes(a, 1)) == NULL) { @@ -3170,7 +3282,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (r); /* - * Skip the bytes we alrady has skipped in skip_stream(). + * Skip the bytes we alrady has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; @@ -3235,16 +3347,36 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, * Check coder types. */ for (i = 0; i < folder->numCoders; i++) { - if (folder->coders[i].codec == _7Z_CRYPTO) { - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "The %s is encrypted, " - "but currently not supported", cname); - return (ARCHIVE_FATAL); + switch(folder->coders[i].codec) { + case _7Z_CRYPTO_MAIN_ZIP: + case _7Z_CRYPTO_RAR_29: + case _7Z_CRYPTO_AES_256_SHA_256: { + /* For entry that is associated with this folder, mark + it as encrypted (data+metadata). */ + zip->has_encrypted_entries = 1; + if (a->entry) { + archive_entry_set_is_data_encrypted(a->entry, 1); + archive_entry_set_is_metadata_encrypted(a->entry, 1); + } + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_MISC, + "The %s is encrypted, " + "but currently not supported", cname); + return (ARCHIVE_FATAL); + } + case _7Z_X86_BCJ2: { + found_bcj2++; + break; + } } - if (folder->coders[i].codec == _7Z_X86_BCJ2) - found_bcj2++; } + /* Now that we've checked for encryption, if there were still no + * encrypted entries found we can say for sure that there are none. + */ + if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, diff --git a/libarchive/archive_read_support_format_all.c b/libarchive/archive_read_support_format_all.c index 53fe6fa391a2..2127ebd33538 100644 --- a/libarchive/archive_read_support_format_all.c +++ b/libarchive/archive_read_support_format_all.c @@ -61,6 +61,7 @@ archive_read_support_format_all(struct archive *a) archive_read_support_format_mtree(a); archive_read_support_format_tar(a); archive_read_support_format_xar(a); + archive_read_support_format_warc(a); /* * Install expensive bidders last. By doing them last, we diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 40be18c0cce2..4b5b66bd50a6 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -122,7 +122,9 @@ archive_read_support_format_ar(struct archive *_a) archive_read_format_ar_read_data, archive_read_format_ar_skip, NULL, - archive_read_format_ar_cleanup); + archive_read_format_ar_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) { free(ar); @@ -178,7 +180,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { archive_set_error(&a->archive, EINVAL, "Incorrect file header signature"); - return (ARCHIVE_WARN); + return (ARCHIVE_FATAL); } /* Copy filename into work buffer. */ @@ -237,8 +239,15 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, * and are not terminated in '/', so we don't trim anything * that starts with '/'.) */ - if (filename[0] != '/' && *p == '/') + if (filename[0] != '/' && p > filename && *p == '/') { *p = '\0'; + } + + if (p < filename) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Found entry with empty filename"); + return (ARCHIVE_FATAL); + } /* * '//' is the GNU filename table. @@ -260,12 +269,12 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, if (entry_size == 0) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); - return (ARCHIVE_WARN); + return (ARCHIVE_FATAL); } if (ar->strtab != NULL) { archive_set_error(&a->archive, EINVAL, "More than one string tables exist"); - return (ARCHIVE_WARN); + return (ARCHIVE_FATAL); } /* Read the filename table into memory. */ @@ -309,11 +318,11 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, */ if (ar->strtab == NULL || number > ar->strtab_size) { archive_set_error(&a->archive, EINVAL, - "Can't find long filename for entry"); + "Can't find long filename for GNU/SVR4 archive entry"); archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ ar_parse_common_header(ar, entry, h); - return (ARCHIVE_WARN); + return (ARCHIVE_FATAL); } archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); @@ -571,7 +580,7 @@ bad_string_table: "Invalid string table"); free(ar->strtab); ar->strtab = NULL; - return (ARCHIVE_WARN); + return (ARCHIVE_FATAL); } static uint64_t diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index 3c9f94ca6ba3..fc70684afa04 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -383,7 +383,9 @@ archive_read_support_format_cab(struct archive *_a) archive_read_format_cab_read_data, archive_read_format_cab_read_data_skip, NULL, - archive_read_format_cab_cleanup); + archive_read_format_cab_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(cab); diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 1dabc4786d28..c2ca85bd3ad9 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -243,7 +243,9 @@ archive_read_support_format_cpio(struct archive *_a) archive_read_format_cpio_read_data, archive_read_format_cpio_skip, NULL, - archive_read_format_cpio_cleanup); + archive_read_format_cpio_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(cpio); diff --git a/libarchive/archive_read_support_format_empty.c b/libarchive/archive_read_support_format_empty.c index 366073820975..c641eb9b150e 100644 --- a/libarchive/archive_read_support_format_empty.c +++ b/libarchive/archive_read_support_format_empty.c @@ -54,6 +54,8 @@ archive_read_support_format_empty(struct archive *_a) archive_read_format_empty_read_data, NULL, NULL, + NULL, + NULL, NULL); return (r); diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index 914bc71dfed5..6934ceefe9db 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -387,7 +387,7 @@ static int archive_read_format_iso9660_read_data(struct archive_read *, static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); -static const char *build_pathname(struct archive_string *, struct file_info *); +static const char *build_pathname(struct archive_string *, struct file_info *, int); static int build_pathname_utf16be(unsigned char *, size_t, size_t *, struct file_info *); #if DEBUG @@ -478,7 +478,9 @@ archive_read_support_format_iso9660(struct archive *_a) archive_read_format_iso9660_read_data, archive_read_format_iso9660_read_data_skip, NULL, - archive_read_format_iso9660_cleanup); + archive_read_format_iso9660_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) { free(iso9660); @@ -1223,6 +1225,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname is too long"); + return (ARCHIVE_FATAL); } r = archive_entry_copy_pathname_l(entry, @@ -1245,9 +1248,16 @@ archive_read_format_iso9660_read_header(struct archive_read *a, rd_r = ARCHIVE_WARN; } } else { - archive_string_empty(&iso9660->pathname); - archive_entry_set_pathname(entry, - build_pathname(&iso9660->pathname, file)); + const char *path = build_pathname(&iso9660->pathname, file, 0); + if (path == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname is too long"); + return (ARCHIVE_FATAL); + } else { + archive_string_empty(&iso9660->pathname); + archive_entry_set_pathname(entry, path); + } } iso9660->entry_bytes_remaining = file->size; @@ -1742,12 +1752,12 @@ parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec) { struct iso9660 *iso9660; - struct file_info *file; + struct file_info *file, *filep; size_t name_len; const unsigned char *rr_start, *rr_end; const unsigned char *p; size_t dr_len; - uint64_t fsize; + uint64_t fsize, offset; int32_t location; int flags; @@ -1791,6 +1801,16 @@ parse_file_info(struct archive_read *a, struct file_info *parent, return (NULL); } + /* Sanity check that this entry does not create a cycle. */ + offset = iso9660->logical_block_size * (uint64_t)location; + for (filep = parent; filep != NULL; filep = filep->parent) { + if (filep->offset == offset) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Directory structure contains loop"); + return (NULL); + } + } + /* Create a new file entry and copy data from the ISO dir record. */ file = (struct file_info *)calloc(1, sizeof(*file)); if (file == NULL) { @@ -1799,7 +1819,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, return (NULL); } file->parent = parent; - file->offset = iso9660->logical_block_size * (uint64_t)location; + file->offset = offset; file->size = fsize; file->mtime = isodate7(isodirrec + DR_date_offset); file->ctime = file->atime = file->mtime; @@ -3145,29 +3165,39 @@ static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM - /* Use platform timegm() if available. */ - return (timegm(t)); + /* Use platform timegm() if available. */ + return (timegm(t)); #elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); + return (_mkgmtime64(t)); #else - /* Else use direct calculation using POSIX assumptions. */ - /* First, fix up tm_yday based on the year/month/day. */ - if (mktime(t) == (time_t)-1) - return ((time_t)-1); - /* 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); + /* Else use direct calculation using POSIX assumptions. */ + /* First, fix up tm_yday based on the year/month/day. */ + if (mktime(t) == (time_t)-1) + return ((time_t)-1); + /* 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 } static const char * -build_pathname(struct archive_string *as, struct file_info *file) +build_pathname(struct archive_string *as, struct file_info *file, int depth) { + // Plain ISO9660 only allows 8 dir levels; if we get + // to 1000, then something is very, very wrong. + if (depth > 1000) { + return NULL; + } if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { - build_pathname(as, file->parent); + if (build_pathname(as, file->parent, depth + 1) == NULL) { + return NULL; + } archive_strcat(as, "/"); } if (archive_strlen(&file->name) == 0) diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index f702949fb8c1..c359d83ef97a 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008-2012 Michihiro NAKAJIMA + * Copyright (c) 2008-2014 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -82,9 +82,6 @@ struct lzh_dec { /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; - /* The remaining bytes that we have not copied decoded data from - * the window to an output buffer. */ - int w_remaining; /* * Bit stream reader. @@ -140,10 +137,10 @@ struct lzh_dec { struct lzh_stream { const unsigned char *next_in; - int64_t avail_in; + int avail_in; int64_t total_in; - unsigned char *next_out; - int64_t avail_out; + const unsigned char *ref_ptr; + int avail_out; int64_t total_out; struct lzh_dec *ds; }; @@ -198,9 +195,6 @@ struct lha { char end_of_entry_cleanup; char entry_is_compressed; - unsigned char *uncompressed_buffer; - size_t uncompressed_buffer_size; - char format_name[64]; struct lzh_stream strm; @@ -214,41 +208,6 @@ struct lha { #define H_LEVEL_OFFSET 20 /* Header Level. */ #define H_SIZE 22 /* Minimum header size. */ -static const uint16_t crc16tbl[256] = { - 0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241, - 0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440, - 0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40, - 0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841, - 0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40, - 0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41, - 0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641, - 0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040, - 0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240, - 0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441, - 0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41, - 0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840, - 0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41, - 0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40, - 0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640, - 0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041, - 0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240, - 0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441, - 0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41, - 0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840, - 0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41, - 0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40, - 0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640, - 0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041, - 0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241, - 0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440, - 0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40, - 0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841, - 0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40, - 0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41, - 0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641, - 0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040 -}; - static int archive_read_format_lha_bid(struct archive_read *, int); static int archive_read_format_lha_options(struct archive_read *, const char *, const char *); @@ -279,6 +238,7 @@ static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, size_t *, int64_t *); +static void lha_crc16_init(void); static uint16_t lha_crc16(uint16_t, const void *, size_t); static int lzh_decode_init(struct lzh_stream *, const char *); static void lzh_decode_free(struct lzh_stream *); @@ -320,7 +280,9 @@ archive_read_support_format_lha(struct archive *_a) archive_read_format_lha_read_data, archive_read_format_lha_read_data_skip, NULL, - archive_read_format_lha_cleanup); + archive_read_format_lha_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(lha); @@ -518,6 +480,8 @@ archive_read_format_lha_read_header(struct archive_read *a, const char *signature; int err; + lha_crc16_init(); + a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; @@ -1230,13 +1194,15 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, archive_string_empty(&lha->filename); break; } + if (extdheader[0] == '\0') + goto invalid; archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; case EXT_DIRECTORY: - if (datasize == 0) + if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ - break; + goto invalid; archive_strncpy(&lha->dirname, (const char *)extdheader, datasize); @@ -1376,6 +1342,26 @@ invalid: } static int +lha_end_of_entry(struct archive_read *a) +{ + struct lha *lha = (struct lha *)(a->format->data); + int r = ARCHIVE_EOF; + + if (!lha->end_of_entry_cleanup) { + if ((lha->setflag & CRC_IS_SET) && + lha->crc != lha->entry_crc_calculated) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "LHa data CRC error"); + r = ARCHIVE_WARN; + } + + /* End-of-entry cleanup done. */ + lha->end_of_entry_cleanup = 1; + } + return (r); +} + +static int archive_read_format_lha_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { @@ -1388,22 +1374,10 @@ archive_read_format_lha_read_data(struct archive_read *a, lha->entry_unconsumed = 0; } if (lha->end_of_entry) { - if (!lha->end_of_entry_cleanup) { - if ((lha->setflag & CRC_IS_SET) && - lha->crc != lha->entry_crc_calculated) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "LHa data CRC error"); - return (ARCHIVE_WARN); - } - - /* End-of-entry cleanup done. */ - lha->end_of_entry_cleanup = 1; - } *offset = lha->entry_offset; *size = 0; *buff = NULL; - return (ARCHIVE_EOF); + return (lha_end_of_entry(a)); } if (lha->entry_is_compressed) @@ -1475,18 +1449,6 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, ssize_t bytes_avail; int r; - /* If the buffer hasn't been allocated, allocate it now. */ - if (lha->uncompressed_buffer == NULL) { - lha->uncompressed_buffer_size = 64 * 1024; - lha->uncompressed_buffer - = (unsigned char *)malloc(lha->uncompressed_buffer_size); - if (lha->uncompressed_buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, - "No memory for lzh decompression"); - return (ARCHIVE_FATAL); - } - } - /* If we haven't yet read any data, initialize the decompressor. */ if (!lha->decompress_init) { r = lzh_decode_init(&(lha->strm), lha->method); @@ -1532,12 +1494,9 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; - lha->strm.avail_in = bytes_avail; + lha->strm.avail_in = (int)bytes_avail; lha->strm.total_in = 0; - if (lha->strm.avail_out == 0) { - lha->strm.next_out = lha->uncompressed_buffer; - lha->strm.avail_out = lha->uncompressed_buffer_size; - } + lha->strm.avail_out = 0; r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining); switch (r) { @@ -1554,10 +1513,10 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, lha->entry_unconsumed = lha->strm.total_in; lha->entry_bytes_remaining -= lha->strm.total_in; - if (lha->strm.avail_out == 0 || lha->end_of_entry) { + if (lha->strm.avail_out) { *offset = lha->entry_offset; - *size = lha->strm.next_out - lha->uncompressed_buffer; - *buff = lha->uncompressed_buffer; + *size = lha->strm.avail_out; + *buff = lha->strm.ref_ptr; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, *size); lha->entry_offset += *size; @@ -1565,6 +1524,8 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, *offset = lha->entry_offset; *size = 0; *buff = NULL; + if (lha->end_of_entry) + return (lha_end_of_entry(a)); } return (ARCHIVE_OK); } @@ -1609,7 +1570,6 @@ archive_read_format_lha_cleanup(struct archive_read *a) struct lha *lha = (struct lha *)(a->format->data); lzh_decode_free(&(lha->strm)); - free(lha->uncompressed_buffer); archive_string_free(&(lha->dirname)); archive_string_free(&(lha->filename)); archive_string_free(&(lha->uname)); @@ -1700,50 +1660,87 @@ lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) return (sum); } -#define CRC16(crc, v) do { \ - (crc) = crc16tbl[((crc) ^ v) & 0xFF] ^ ((crc) >> 8); \ -} while (0) +static uint16_t crc16tbl[2][256]; +static void +lha_crc16_init(void) +{ + unsigned int i; + static int crc16init = 0; + + if (crc16init) + return; + crc16init = 1; + + for (i = 0; i < 256; i++) { + unsigned int j; + uint16_t crc = (uint16_t)i; + for (j = 8; j; j--) + crc = (crc >> 1) ^ ((crc & 1) * 0xA001); + crc16tbl[0][i] = crc; + } + + for (i = 0; i < 256; i++) { + crc16tbl[1][i] = (crc16tbl[0][i] >> 8) + ^ crc16tbl[0][crc16tbl[0][i] & 0xff]; + } +} static uint16_t lha_crc16(uint16_t crc, const void *pp, size_t len) { - const unsigned char *buff = (const unsigned char *)pp; - - while (len >= 8) { - CRC16(crc, *buff++); CRC16(crc, *buff++); - CRC16(crc, *buff++); CRC16(crc, *buff++); - CRC16(crc, *buff++); CRC16(crc, *buff++); - CRC16(crc, *buff++); CRC16(crc, *buff++); - len -= 8; + const unsigned char *p = (const unsigned char *)pp; + const uint16_t *buff; + const union { + uint32_t i; + char c[4]; + } u = { 0x01020304 }; + + if (len == 0) + return crc; + + /* Process unaligned address. */ + if (((uintptr_t)p) & (uintptr_t)0x1) { + crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; + len--; } - switch (len) { - case 7: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 6: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 5: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 4: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 3: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 2: - CRC16(crc, *buff++); - /* FALL THROUGH */ - case 1: - CRC16(crc, *buff); - /* FALL THROUGH */ - case 0: - break; + buff = (const uint16_t *)p; + /* + * Modern C compiler such as GCC does not unroll automatically yet + * without unrolling pragma, and Clang is so. So we should + * unroll this loop for its performance. + */ + for (;len >= 8; len -= 8) { + /* This if statement expects compiler optimization will + * remove the stament which will not be executed. */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ +# define bswap16(x) _byteswap_ushort(x) +#elif (defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8) \ + || defined(__clang__) +# define bswap16(x) __builtin_bswap16(x) +#else +# define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) +#endif +#define CRC16W do { \ + if(u.c[0] == 1) { /* Big endian */ \ + crc ^= bswap16(*buff); buff++; \ + } else \ + crc ^= *buff++; \ + crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\ +} while (0) + CRC16W; + CRC16W; + CRC16W; + CRC16W; +#undef CRC16W +#undef bswap16 } - return (crc); -} + p = (const unsigned char *)buff; + for (;len; len--) { + crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; + } + return crc; +} /* * Initialize LZHUF decoder. @@ -1782,18 +1779,18 @@ lzh_decode_init(struct lzh_stream *strm, const char *method) return (ARCHIVE_FAILED);/* Not supported. */ } ds->error = ARCHIVE_FATAL; - w_size = ds->w_size; - ds->w_size = 1U << w_bits; + /* Expand a window size up to 128 KiB for decompressing process + * performance whatever its original window size is. */ + ds->w_size = 1U << 17; ds->w_mask = ds->w_size -1; - if (ds->w_buff == NULL || w_size != ds->w_size) { - free(ds->w_buff); + if (ds->w_buff == NULL) { ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); } - memset(ds->w_buff, 0x20, ds->w_size); + w_size = 1U << w_bits; + memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size); ds->w_pos = 0; - ds->w_remaining = 0; ds->state = 0; ds->pos_pt_len_size = w_bits + 1; ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4; @@ -1880,9 +1877,10 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) int n = CACHE_BITS - br->cache_avail; for (;;) { - switch (n >> 3) { - case 8: - if (strm->avail_in >= 8) { + const int x = n >> 3; + if (strm->avail_in >= x) { + switch (x) { + case 8: br->cache_buffer = ((uint64_t)strm->next_in[0]) << 56 | ((uint64_t)strm->next_in[1]) << 48 | @@ -1896,10 +1894,7 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); - } - break; - case 7: - if (strm->avail_in >= 7) { + case 7: br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)strm->next_in[0]) << 48 | @@ -1913,10 +1908,7 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) strm->avail_in -= 7; br->cache_avail += 7 * 8; return (1); - } - break; - case 6: - if (strm->avail_in >= 6) { + case 6: br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | @@ -1929,14 +1921,13 @@ lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); + case 0: + /* We have enough compressed data in + * the cache buffer.*/ + return (1); + default: + break; } - break; - case 0: - /* We have enough compressed data in - * the cache buffer.*/ - return (1); - default: - break; } if (strm->avail_in == 0) { /* There is not enough compressed data to fill up the @@ -1991,7 +1982,7 @@ static int lzh_decode(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; - int64_t avail_in; + int avail_in; int r; if (ds->error) @@ -2008,35 +1999,12 @@ lzh_decode(struct lzh_stream *strm, int last) return (r); } -static int -lzh_copy_from_window(struct lzh_stream *strm, struct lzh_dec *ds) +static void +lzh_emit_window(struct lzh_stream *strm, size_t s) { - size_t copy_bytes; - - if (ds->w_remaining == 0 && ds->w_pos > 0) { - if (ds->w_pos - ds->copy_pos <= strm->avail_out) - copy_bytes = ds->w_pos - ds->copy_pos; - else - copy_bytes = (size_t)strm->avail_out; - memcpy(strm->next_out, - ds->w_buff + ds->copy_pos, copy_bytes); - ds->copy_pos += (int)copy_bytes; - } else { - if (ds->w_remaining <= strm->avail_out) - copy_bytes = ds->w_remaining; - else - copy_bytes = (size_t)strm->avail_out; - memcpy(strm->next_out, - ds->w_buff + ds->w_size - ds->w_remaining, copy_bytes); - ds->w_remaining -= (int)copy_bytes; - } - strm->next_out += copy_bytes; - strm->avail_out -= copy_bytes; - strm->total_out += copy_bytes; - if (strm->avail_out == 0) - return (0); - else - return (1); + strm->ref_ptr = strm->ds->w_buff; + strm->avail_out = (int)s; + strm->total_out += s; } static int @@ -2071,8 +2039,9 @@ lzh_read_blocks(struct lzh_stream *strm, int last) goto failed; } if (ds->w_pos > 0) { - if (!lzh_copy_from_window(strm, ds)) - return (ARCHIVE_OK); + lzh_emit_window(strm, ds->w_pos); + ds->w_pos = 0; + return (ARCHIVE_OK); } /* End of compressed data; we have completely * handled all compressed data. */ @@ -2289,10 +2258,6 @@ lzh_decode_blocks(struct lzh_stream *strm, int last) int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits; int state = ds->state; - if (ds->w_remaining > 0) { - if (!lzh_copy_from_window(strm, ds)) - goto next_data; - } for (;;) { switch (state) { case ST_GET_LITERAL: @@ -2347,9 +2312,8 @@ lzh_decode_blocks(struct lzh_stream *strm, int last) w_buff[w_pos] = c; if (++w_pos >= w_size) { w_pos = 0; - ds->w_remaining = w_size; - if (!lzh_copy_from_window(strm, ds)) - goto next_data; + lzh_emit_window(strm, w_size); + goto next_data; } } /* 'c' is the length of a match pattern we have @@ -2427,25 +2391,26 @@ lzh_decode_blocks(struct lzh_stream *strm, int last) d = w_buff + w_pos; s = w_buff + copy_pos; - for (li = 0; li < l; li++) + for (li = 0; li < l-1;) { + d[li] = s[li];li++; + d[li] = s[li];li++; + } + if (li < l) d[li] = s[li]; } - w_pos = (w_pos + l) & w_mask; - if (w_pos == 0) { - ds->w_remaining = w_size; - if (!lzh_copy_from_window(strm, ds)) { - if (copy_len <= l) - state = ST_GET_LITERAL; - else { - state = ST_COPY_DATA; - ds->copy_len = - copy_len - l; - ds->copy_pos = - (copy_pos + l) - & w_mask; - } - goto next_data; + w_pos += l; + if (w_pos == w_size) { + w_pos = 0; + lzh_emit_window(strm, w_size); + if (copy_len <= l) + state = ST_GET_LITERAL; + else { + state = ST_COPY_DATA; + ds->copy_len = copy_len - l; + ds->copy_pos = + (copy_pos + l) & w_mask; } + goto next_data; } if (copy_len <= l) /* A copy of current pattern ended. */ @@ -2505,14 +2470,80 @@ lzh_huffman_free(struct huffman *hf) free(hf->tree); } +static char bitlen_tbl[0x400] = { + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0 +}; static int lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end) { struct lzh_dec *ds = strm->ds; - struct lzh_br * br = &(ds->br); + struct lzh_br *br = &(ds->br); int c, i; - for (i = start; i < end;) { + for (i = start; i < end; ) { /* * bit pattern the number we need * 000 -> 0 @@ -2528,17 +2559,13 @@ lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end) if (!lzh_br_read_ahead(strm, br, 3)) return (i); if ((c = lzh_br_bits(br, 3)) == 7) { - int d; if (!lzh_br_read_ahead(strm, br, 13)) return (i); - d = lzh_br_bits(br, 13); - while (d & 0x200) { - c++; - d <<= 1; - } - if (c > 16) + c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF]; + if (c) + lzh_br_consume(br, c - 3); + else return (-1);/* Invalid data. */ - lzh_br_consume(br, c - 3); } else lzh_br_consume(br, 3); ds->pt.bitlen[i++] = c; @@ -2601,7 +2628,7 @@ lzh_make_huffman_table(struct huffman *hf) } } if (maxbits > HTBL_BITS) { - int htbl_max; + unsigned htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; @@ -2645,8 +2672,40 @@ lzh_make_huffman_table(struct huffman *hf) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); - while (--cnt >= 0) - p[cnt] = (uint16_t)i; + if (cnt > 7) { + uint16_t *pc; + + cnt -= 8; + pc = &p[cnt]; + pc[0] = (uint16_t)i; + pc[1] = (uint16_t)i; + pc[2] = (uint16_t)i; + pc[3] = (uint16_t)i; + pc[4] = (uint16_t)i; + pc[5] = (uint16_t)i; + pc[6] = (uint16_t)i; + pc[7] = (uint16_t)i; + if (cnt > 7) { + cnt -= 8; + memcpy(&p[cnt], pc, + 8 * sizeof(uint16_t)); + pc = &p[cnt]; + while (cnt > 15) { + cnt -= 16; + memcpy(&p[cnt], pc, + 16 * sizeof(uint16_t)); + } + } + if (cnt) + memcpy(p, pc, cnt * sizeof(uint16_t)); + } else { + while (cnt > 1) { + p[--cnt] = (uint16_t)i; + p[--cnt] = (uint16_t)i; + } + if (cnt) + p[--cnt] = (uint16_t)i; + } continue; } @@ -2740,7 +2799,7 @@ lzh_decode_huffman(struct huffman *hf, unsigned rbits) * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; - if (c < hf->len_avail) + if (c < hf->len_avail || hf->len_avail == 0) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzh_decode_huffman_tree(hf, rbits, c)); diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index c4e7021a869b..81d96521138e 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" +#include "archive_pack_dev.h" #ifndef O_BINARY #define O_BINARY 0 @@ -103,6 +104,7 @@ struct mtree { struct archive_entry_linkresolver *resolver; int64_t cur_size; + char checkfs; }; static int bid_keycmp(const char *, const char *, ssize_t); @@ -137,16 +139,22 @@ get_time_t_max(void) #if defined(TIME_T_MAX) return TIME_T_MAX; #else - static time_t t; - time_t a; - if (t == 0) { - a = 1; - while (a > t) { - t = a; - a = a * 2 + 1; + /* ISO C allows time_t to be a floating-point type, + but POSIX requires an integer type. The following + should work on any system that follows the POSIX + conventions. */ + if (((time_t)0) < ((time_t)-1)) { + /* Time_t is unsigned */ + return (~(time_t)0); + } else { + /* Time_t is signed. */ + /* Assume it's the same as int64_t or int32_t */ + if (sizeof(time_t) == sizeof(int64_t)) { + return (time_t)INT64_MAX; + } else { + return (time_t)INT32_MAX; } } - return t; #endif } @@ -156,23 +164,43 @@ get_time_t_min(void) #if defined(TIME_T_MIN) return TIME_T_MIN; #else - /* 't' will hold the minimum value, which will be zero (if - * time_t is unsigned) or -2^n (if time_t is signed). */ - static int computed; - static time_t t; - time_t a; - if (computed == 0) { - a = (time_t)-1; - while (a < t) { - t = a; - a = a * 2; - } - computed = 1; + if (((time_t)0) < ((time_t)-1)) { + /* Time_t is unsigned */ + return (time_t)0; + } else { + /* Time_t is signed. */ + if (sizeof(time_t) == sizeof(int64_t)) { + return (time_t)INT64_MIN; + } else { + return (time_t)INT32_MIN; + } } - return t; #endif } +static int +archive_read_format_mtree_options(struct archive_read *a, + const char *key, const char *val) +{ + struct mtree *mtree; + + mtree = (struct mtree *)(a->format->data); + if (strcmp(key, "checkfs") == 0) { + /* Allows to read information missing from the mtree from the file system */ + if (val == NULL || val[0] == 0) { + mtree->checkfs = 0; + } else { + mtree->checkfs = 1; + } + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + static void free_options(struct mtree_option *head) { @@ -205,7 +233,7 @@ archive_read_support_format_mtree(struct archive *_a) mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", - mtree_bid, NULL, read_header, read_data, skip, NULL, cleanup); + mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(mtree); @@ -367,7 +395,7 @@ bid_keyword(const char *p, ssize_t len) "gid", "gname", NULL }; static const char *keys_il[] = { - "ignore", "link", NULL + "ignore", "inode", "link", NULL }; static const char *keys_m[] = { "md5", "md5digest", "mode", NULL @@ -376,7 +404,7 @@ bid_keyword(const char *p, ssize_t len) "nlink", "nochange", "optional", NULL }; static const char *keys_r[] = { - "rmd160", "rmd160digest", NULL + "resdevice", "rmd160", "rmd160digest", NULL }; static const char *keys_s[] = { "sha1", "sha1digest", @@ -507,32 +535,34 @@ bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; - ssize_t ll = len; + ssize_t ll; const char *pp = p; + const char * const pp_end = pp + len; *last_is_path = 0; /* * Skip the path-name which is quoted. */ - while (ll > 0 && *pp != ' ' &&*pp != '\t' && *pp != '\r' && - *pp != '\n') { + for (;pp < pp_end; ++pp) { if (!safe_char[*(const unsigned char *)pp]) { - f = 0; + if (*pp != ' ' && *pp != '\t' && *pp != '\r' + && *pp != '\n') + f = 0; break; } - ++pp; - --ll; - ++f; + f = 1; } + ll = pp_end - pp; + /* If a path-name was not found at the first, try to check - * a mtree format ``NetBSD's mtree -D'' creates, which - * places the path-name at the last. */ + * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates, + * which places the path-name at the last. */ if (f == 0) { const char *pb = p + len - nl; int name_len = 0; int slash; - /* Do not accept multi lines for form D. */ + /* The form D accepts only a single line for an entry. */ if (pb-2 >= p && pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) return (-1); @@ -826,8 +856,8 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq, *name, *end; - size_t len; - int r; + size_t name_len, len; + int r, i; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); @@ -847,43 +877,48 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, *last_entry = entry; if (is_form_d) { - /* - * This form places the file name as last parameter. - */ - name = line + line_len -1; + /* Filename is last item on line. */ + /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { - if (*name != '\r' && *name != '\n' && - *name != '\t' && *name != ' ') + char last_character = line[line_len - 1]; + if (last_character == '\r' + || last_character == '\n' + || last_character == '\t' + || last_character == ' ') { + line_len--; + } else { break; - name--; - line_len--; + } } - len = 0; - while (line_len > 0) { - if (*name == '\r' || *name == '\n' || - *name == '\t' || *name == ' ') { - name++; - break; + /* Name starts after the last whitespace separator */ + name = line; + for (i = 0; i < line_len; i++) { + if (line[i] == '\r' + || line[i] == '\n' + || line[i] == '\t' + || line[i] == ' ') { + name = line + i + 1; } - name--; - line_len--; - len++; } + name_len = line + line_len - name; end = name; } else { - len = strcspn(line, " \t\r\n"); + /* Filename is first item on line */ + name_len = strcspn(line, " \t\r\n"); name = line; - line += len; + line += name_len; end = line + line_len; } + /* name/name_len is the name within the line. */ + /* line..end brackets the entire line except the name */ - if ((entry->name = malloc(len + 1)) == NULL) { + if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } - memcpy(entry->name, name, len); - entry->name[len] = '\0'; + memcpy(entry->name, name, name_len); + entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); for (iter = *global; iter != NULL; iter = iter->next) { @@ -1031,7 +1066,8 @@ read_header(struct archive_read *a, struct archive_entry *entry) } if (!mtree->this_entry->used) { use_next = 0; - r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); + r = parse_file(a, entry, mtree, mtree->this_entry, + &use_next); if (use_next == 0) return (r); } @@ -1103,162 +1139,168 @@ parse_file(struct archive_read *a, struct archive_entry *entry, mtree->current_dir.length = n; } - /* - * Try to open and stat the file to get the real size - * and other file info. It would be nice to avoid - * this here so that getting a listing of an mtree - * wouldn't require opening every referenced contents - * file. But then we wouldn't know the actual - * contents size, so I don't see a really viable way - * around this. (Also, we may want to someday pull - * other unspecified info from the contents file on - * disk.) - */ - mtree->fd = -1; - 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 | O_CLOEXEC); - __archive_ensure_cloexec_flag(mtree->fd); - if (mtree->fd == -1 && - (errno != ENOENT || - archive_strlen(&mtree->contents_name) > 0)) { - archive_set_error(&a->archive, errno, - "Can't open %s", path); - r = ARCHIVE_WARN; + if (mtree->checkfs) { + /* + * Try to open and stat the file to get the real size + * and other file info. It would be nice to avoid + * this here so that getting a listing of an mtree + * wouldn't require opening every referenced contents + * file. But then we wouldn't know the actual + * contents size, so I don't see a really viable way + * around this. (Also, we may want to someday pull + * other unspecified info from the contents file on + * disk.) + */ + mtree->fd = -1; + 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 | O_CLOEXEC); + __archive_ensure_cloexec_flag(mtree->fd); + if (mtree->fd == -1 && + (errno != ENOENT || + archive_strlen(&mtree->contents_name) > 0)) { + archive_set_error(&a->archive, errno, + "Can't open %s", path); + r = ARCHIVE_WARN; + } } - } - st = &st_storage; - if (mtree->fd >= 0) { - if (fstat(mtree->fd, st) == -1) { - archive_set_error(&a->archive, errno, - "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; + st = &st_storage; + if (mtree->fd >= 0) { + if (fstat(mtree->fd, st) == -1) { + archive_set_error(&a->archive, errno, + "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; + st = NULL; + } + } else if (lstat(path, st) == -1) { st = NULL; } - } else if (lstat(path, st) == -1) { - st = NULL; - } - /* - * Check for a mismatch between the type in the specification and - * the type of the contents object on disk. - */ - if (st != NULL) { - if ( - ((st->st_mode & S_IFMT) == S_IFREG && - archive_entry_filetype(entry) == AE_IFREG) + /* + * Check for a mismatch between the type in the specification + * and the type of the contents object on disk. + */ + if (st != NULL) { + if (((st->st_mode & S_IFMT) == S_IFREG && + archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK - || ((st->st_mode & S_IFMT) == S_IFLNK && - archive_entry_filetype(entry) == AE_IFLNK) + ||((st->st_mode & S_IFMT) == S_IFLNK && + archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK - || ((st->st_mode & S_IFSOCK) == S_IFSOCK && - archive_entry_filetype(entry) == AE_IFSOCK) + ||((st->st_mode & S_IFSOCK) == S_IFSOCK && + archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR - || ((st->st_mode & S_IFMT) == S_IFCHR && - archive_entry_filetype(entry) == AE_IFCHR) + ||((st->st_mode & S_IFMT) == S_IFCHR && + archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK - || ((st->st_mode & S_IFMT) == S_IFBLK && - archive_entry_filetype(entry) == AE_IFBLK) + ||((st->st_mode & S_IFMT) == S_IFBLK && + archive_entry_filetype(entry) == AE_IFBLK) #endif - || ((st->st_mode & S_IFMT) == S_IFDIR && - archive_entry_filetype(entry) == AE_IFDIR) + ||((st->st_mode & S_IFMT) == S_IFDIR && + archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO - || ((st->st_mode & S_IFMT) == S_IFIFO && - archive_entry_filetype(entry) == AE_IFIFO) + ||((st->st_mode & S_IFMT) == S_IFIFO && + archive_entry_filetype(entry) == AE_IFIFO) #endif - ) { - /* Types match. */ - } else { - /* Types don't match; bail out gracefully. */ - if (mtree->fd >= 0) - close(mtree->fd); - mtree->fd = -1; - if (parsed_kws & MTREE_HAS_OPTIONAL) { - /* It's not an error for an optional entry - to not match disk. */ - *use_next = 1; - } else if (r == ARCHIVE_OK) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "mtree specification has different type for %s", - archive_entry_pathname(entry)); - r = ARCHIVE_WARN; + ) { + /* Types match. */ + } else { + /* Types don't match; bail out gracefully. */ + if (mtree->fd >= 0) + close(mtree->fd); + mtree->fd = -1; + if (parsed_kws & MTREE_HAS_OPTIONAL) { + /* It's not an error for an optional + * entry to not match disk. */ + *use_next = 1; + } else if (r == ARCHIVE_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "mtree specification has different" + " type for %s", + archive_entry_pathname(entry)); + r = ARCHIVE_WARN; + } + return (r); } - return r; } - } - /* - * If there is a contents file on disk, pick some of the metadata - * from that file. For most of these, we only set it from the contents - * if it wasn't already parsed from the specification. - */ - if (st != NULL) { - if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 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 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 0) - archive_entry_set_gid(entry, st->st_gid); - if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 0) - archive_entry_set_uid(entry, st->st_uid); - if ((parsed_kws & MTREE_HAS_MTIME) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { + /* + * If there is a contents file on disk, pick some of the + * metadata from that file. For most of these, we only + * set it from the contents if it wasn't already parsed + * from the specification. + */ + if (st != NULL) { + if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 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 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 0) + archive_entry_set_gid(entry, st->st_gid); + if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) + == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 0) + archive_entry_set_uid(entry, st->st_uid); + if ((parsed_kws & MTREE_HAS_MTIME) == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC - archive_entry_set_mtime(entry, st->st_mtime, - st->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); + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N - archive_entry_set_mtime(entry, st->st_mtime, - st->st_mtime_n); + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME - archive_entry_set_mtime(entry, st->st_mtime, - st->st_umtime*1000); + archive_entry_set_mtime(entry, st->st_mtime, + st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC - archive_entry_set_mtime(entry, st->st_mtime, - st->st_mtime_usec*1000); + archive_entry_set_mtime(entry, st->st_mtime, + st->st_mtime_usec*1000); #else - archive_entry_set_mtime(entry, st->st_mtime, 0); + archive_entry_set_mtime(entry, st->st_mtime, 0); #endif + } + if ((parsed_kws & MTREE_HAS_NLINK) == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 0) + archive_entry_set_nlink(entry, st->st_nlink); + if ((parsed_kws & MTREE_HAS_PERM) == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 0) + archive_entry_set_perm(entry, st->st_mode); + if ((parsed_kws & MTREE_HAS_SIZE) == 0 || + (parsed_kws & MTREE_HAS_NOCHANGE) != 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; } - if ((parsed_kws & MTREE_HAS_NLINK) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 0) - archive_entry_set_nlink(entry, st->st_nlink); - if ((parsed_kws & MTREE_HAS_PERM) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 0) - archive_entry_set_perm(entry, st->st_mode); - if ((parsed_kws & MTREE_HAS_SIZE) == 0 || - (parsed_kws & MTREE_HAS_NOCHANGE) != 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); @@ -1292,33 +1334,82 @@ parse_line(struct archive_read *a, struct archive_entry *entry, /* * 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. + * - raw dev_t + * - format,major,minor[,subdevice] + * When parsing succeeded, `pdev' will contain the appropriate dev_t value. */ -static int -parse_device(struct archive *a, struct archive_entry *entry, char *val) + +/* strsep() is not in C90, but strcspn() is. */ +/* Taken from http://unixpapa.com/incnote/string.html */ +static char * +la_strsep(char **sp, char *sep) { - char *comma1, *comma2; + char *p, *s; + if (sp == NULL || *sp == NULL || **sp == '\0') + return(NULL); + s = *sp; + p = s + strcspn(s, sep); + if (*p != '\0') + *p++ = '\0'; + *sp = p; + return(s); +} - comma1 = strchr(val, ','); - if (comma1 == NULL) { - archive_entry_set_dev(entry, (dev_t)mtree_atol10(&val)); - return (ARCHIVE_OK); - } - ++comma1; - comma2 = strchr(comma1, ','); - if (comma2 == NULL) { - archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, - "Malformed device attribute"); - return (ARCHIVE_WARN); +static int +parse_device(dev_t *pdev, struct archive *a, char *val) +{ +#define MAX_PACK_ARGS 3 + unsigned long numbers[MAX_PACK_ARGS]; + char *p, *dev; + int argc; + pack_t *pack; + dev_t result; + const char *error = NULL; + + memset(pdev, 0, sizeof(*pdev)); + if ((dev = strchr(val, ',')) != NULL) { + /* + * Device's major/minor are given in a specified format. + * Decode and pack it accordingly. + */ + *dev++ = '\0'; + if ((pack = pack_find(val)) == NULL) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown format `%s'", val); + return ARCHIVE_WARN; + } + argc = 0; + while ((p = la_strsep(&dev, ",")) != NULL) { + if (*p == '\0') { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "Missing number"); + return ARCHIVE_WARN; + } + numbers[argc++] = (unsigned long)mtree_atol(&p); + if (argc > MAX_PACK_ARGS) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "Too many arguments"); + return ARCHIVE_WARN; + } + } + if (argc < 2) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "Not enough arguments"); + return ARCHIVE_WARN; + } + result = (*pack)(argc, numbers, &error); + if (error != NULL) { + archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, + "%s", error); + return ARCHIVE_WARN; + } + } else { + /* file system raw value. */ + result = (dev_t)mtree_atol(&val); } - ++comma2; - archive_entry_set_rdevmajor(entry, (dev_t)mtree_atol(&comma1)); - archive_entry_set_rdevminor(entry, (dev_t)mtree_atol(&comma2)); - return (ARCHIVE_OK); + *pdev = result; + return ARCHIVE_OK; +#undef MAX_PACK_ARGS } /* @@ -1374,8 +1465,16 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, break; case 'd': if (strcmp(key, "device") == 0) { + /* stat(2) st_rdev field, e.g. the major/minor IDs + * of a char/block special file */ + int r; + dev_t dev; + *parsed_kws |= MTREE_HAS_DEVICE; - return parse_device(&a->archive, entry, val); + r = parse_device(&dev, &a->archive, val); + if (r == ARCHIVE_OK) + archive_entry_set_rdev(entry, dev); + return r; } case 'f': if (strcmp(key, "flags") == 0) { @@ -1394,6 +1493,11 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_copy_gname(entry, val); break; } + case 'i': + if (strcmp(key, "inode") == 0) { + archive_entry_set_ino(entry, mtree_atol10(&val)); + break; + } case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); @@ -1423,6 +1527,17 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, break; } case 'r': + if (strcmp(key, "resdevice") == 0) { + /* stat(2) st_dev field, e.g. the device ID where the + * inode resides */ + int r; + dev_t dev; + + r = parse_device(&dev, &a->archive, val); + if (r == ARCHIVE_OK) + archive_entry_set_dev(entry, dev); + return r; + } if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; @@ -1455,7 +1570,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); - long ns; + long ns = 0; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol10(&val); @@ -1483,32 +1598,38 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, } case 'c': if (strcmp(val, "char") == 0) { - archive_entry_set_filetype(entry, AE_IFCHR); + archive_entry_set_filetype(entry, + AE_IFCHR); break; } case 'd': if (strcmp(val, "dir") == 0) { - archive_entry_set_filetype(entry, AE_IFDIR); + archive_entry_set_filetype(entry, + AE_IFDIR); break; } case 'f': if (strcmp(val, "fifo") == 0) { - archive_entry_set_filetype(entry, AE_IFIFO); + archive_entry_set_filetype(entry, + AE_IFIFO); break; } if (strcmp(val, "file") == 0) { - archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_set_filetype(entry, + AE_IFREG); break; } case 'l': if (strcmp(val, "link") == 0) { - archive_entry_set_filetype(entry, AE_IFLNK); + archive_entry_set_filetype(entry, + AE_IFLNK); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unrecognized file type \"%s\"; assuming \"file\"", val); + "Unrecognized file type \"%s\"; " + "assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } @@ -1535,7 +1656,8 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, } static int -read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) +read_data(struct archive_read *a, const void **buff, size_t *size, + int64_t *offset) { size_t bytes_to_read; ssize_t bytes_read; @@ -1661,6 +1783,10 @@ parse_escapes(char *src, struct mtree_entry *mentry) c = '\v'; ++src; break; + case '\\': + c = '\\'; + ++src; + break; } } *dest++ = c; @@ -1798,14 +1924,14 @@ mtree_atol(char **p) * point to first character of line. */ static ssize_t -readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) +readline(struct archive_read *a, struct mtree *mtree, char **start, + ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; const void *t; - const char *s; - void *p; + void *nl; char *u; /* Accumulate line in a line buffer. */ @@ -1816,11 +1942,10 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); - s = t; /* Start of line? */ - p = memchr(t, '\n', bytes_read); - /* If we found '\n', trim the read. */ - if (p != NULL) { - bytes_read = 1 + ((const char *)p) - s; + nl = memchr(t, '\n', bytes_read); + /* If we found '\n', trim the read to end exactly there. */ + if (nl != NULL) { + bytes_read = ((const char *)nl) - ((const char *)t) + 1; } if (total_size + bytes_read + 1 > limit) { archive_set_error(&a->archive, @@ -1834,38 +1959,34 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limi "Can't allocate working buffer"); return (ARCHIVE_FATAL); } + /* Append new bytes to string. */ memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; - /* Null terminate. */ mtree->line.s[total_size] = '\0'; - /* If we found an unescaped '\n', clean up and return. */ + for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { + /* Ends with unescaped newline. */ *start = mtree->line.s; return total_size; - } - if (u[0] == '#') { - if (p == NULL) + } else if (u[0] == '#') { + /* Ends with comment sequence #...\n */ + if (nl == NULL) { + /* But we've not found the \n yet */ break; - *start = mtree->line.s; - return total_size; - } - if (u[0] != '\\') - continue; - if (u[1] == '\\') { - ++u; - continue; - } - if (u[1] == '\n') { - memmove(u, u + 1, - total_size - (u - mtree->line.s) + 1); - --total_size; - ++u; - break; + } + } else if (u[0] == '\\') { + if (u[1] == '\n') { + /* Trim escaped newline. */ + total_size -= 2; + mtree->line.s[total_size] = '\0'; + break; + } else if (u[1] != '\0') { + /* Skip the two-char escape sequence */ + ++u; + } } - if (u[1] == '\0') - break; } find_off = u - mtree->line.s; } diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c index 99c57a0fc0eb..6450aac82785 100644 --- a/libarchive/archive_read_support_format_rar.c +++ b/libarchive/archive_read_support_format_rar.c @@ -186,6 +186,7 @@ struct huffman_code { struct huffman_tree_node *tree; int numentries; + int numallocatedentries; int minlength; int maxlength; int tablesize; @@ -225,6 +226,7 @@ struct rar mode_t mode; char *filename; char *filename_save; + size_t filename_save_size; size_t filename_allocated; /* File header optional entries */ @@ -304,8 +306,15 @@ struct rar ssize_t avail_in; const unsigned char *next_in; } br; + + /* + * Custom field to denote that this archive contains encrypted entries + */ + int has_encrypted_entries; }; +static int archive_read_support_format_rar_capabilities(struct archive_read *); +static int archive_read_format_rar_has_encrypted_entries(struct archive_read *); static int archive_read_format_rar_bid(struct archive_read *, int); static int archive_read_format_rar_options(struct archive_read *, const char *, const char *); @@ -646,6 +655,12 @@ archive_read_support_format_rar(struct archive *_a) } memset(rar, 0, sizeof(*rar)); + /* + * Until enough data has been read, we cannot tell about + * any encrypted entries yet. + */ + rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + r = __archive_read_register_format(a, rar, "rar", @@ -655,7 +670,9 @@ archive_read_support_format_rar(struct archive *_a) archive_read_format_rar_read_data, archive_read_format_rar_read_data_skip, archive_read_format_rar_seek_data, - archive_read_format_rar_cleanup); + archive_read_format_rar_cleanup, + archive_read_support_format_rar_capabilities, + archive_read_format_rar_has_encrypted_entries); if (r != ARCHIVE_OK) free(rar); @@ -663,6 +680,27 @@ archive_read_support_format_rar(struct archive *_a) } static int +archive_read_support_format_rar_capabilities(struct archive_read * a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA + | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); +} + +static int +archive_read_format_rar_has_encrypted_entries(struct archive_read *_a) +{ + if (_a && _a->format) { + struct rar * rar = (struct rar *)_a->format->data; + if (rar) { + return rar->has_encrypted_entries; + } + } + return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; +} + + +static int archive_read_format_rar_bid(struct archive_read *a, int best_bid) { const char *p; @@ -755,7 +793,7 @@ archive_read_format_rar_options(struct archive_read *a, { struct rar *rar; int ret = ARCHIVE_FAILED; - + rar = (struct rar *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) @@ -790,6 +828,7 @@ archive_read_format_rar_read_header(struct archive_read *a, char head_type; int ret; unsigned flags; + unsigned long crc32_expected; a->archive.archive_format = ARCHIVE_FORMAT_RAR; if (a->archive.archive_format_name == NULL) @@ -797,6 +836,17 @@ archive_read_format_rar_read_header(struct archive_read *a, rar = (struct rar *)(a->format->data); + /* + * It should be sufficient to call archive_read_next_header() for + * a reader to determine if an entry is encrypted or not. If the + * encryption of an entry is only detectable when calling + * archive_read_data(), so be it. We'll do the same check there + * as well. + */ + if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + rar->has_encrypted_entries = 0; + } + /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if * this fails. */ @@ -857,9 +907,14 @@ archive_read_format_rar_read_header(struct archive_read *a, sizeof(rar->reserved2)); } + /* Main header is password encrytped, so we cannot read any + file names or any other info about files from the header. */ if (rar->main_flags & MHD_PASSWORD) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_entry_set_is_metadata_encrypted(entry, 1); + archive_entry_set_is_data_encrypted(entry, 1); + rar->has_encrypted_entries = 1; + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); return (ARCHIVE_FATAL); } @@ -886,36 +941,50 @@ archive_read_format_rar_read_header(struct archive_read *a, skip = archive_le16dec(p + 5); if (skip < 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid header size"); + "Invalid header size too small"); return (ARCHIVE_FATAL); } - if (skip > 7) { - if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) - return (ARCHIVE_FATAL); - p = h; - } if (flags & HD_ADD_SIZE_PRESENT) { if (skip < 7 + 4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid header size"); + "Invalid header size too small"); return (ARCHIVE_FATAL); } - skip += archive_le32dec(p + 7); if ((h = __archive_read_ahead(a, skip, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; + skip += archive_le32dec(p + 7); } - crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); - if ((crc32_val & 0xffff) != archive_le16dec(p)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Header CRC error"); - return (ARCHIVE_FATAL); + /* Skip over the 2-byte CRC at the beginning of the header. */ + crc32_expected = archive_le16dec(p); + __archive_read_consume(a, 2); + skip -= 2; + + /* Skim the entire header and compute the CRC. */ + crc32_val = 0; + while (skip > 0) { + size_t to_read = skip; + ssize_t did_read; + if (to_read > 32 * 1024) { + to_read = 32 * 1024; + } + if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) { + return (ARCHIVE_FATAL); + } + p = h; + crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read); + __archive_read_consume(a, did_read); + skip -= did_read; + } + if ((crc32_val & 0xffff) != crc32_expected) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Header CRC error"); + return (ARCHIVE_FATAL); } - __archive_read_consume(a, skip); if (head_type == ENDARC_HEAD) - return (ARCHIVE_EOF); + return (ARCHIVE_EOF); break; case NEWSUB_HEAD: @@ -938,14 +1007,18 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, struct rar *rar = (struct rar *)(a->format->data); int ret; + if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + rar->has_encrypted_entries = 0; + } + if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } + *buff = NULL; if (rar->entry_eof || rar->offset_seek >= rar->unp_size) { - *buff = NULL; *size = 0; *offset = rar->offset; if (*offset < rar->unp_size) @@ -957,7 +1030,7 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, { case COMPRESS_METHOD_STORE: ret = read_data_stored(a, buff, size, offset); - break; + break; case COMPRESS_METHOD_FASTEST: case COMPRESS_METHOD_FAST: @@ -967,13 +1040,13 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, ret = read_data_compressed(a, buff, size, offset); if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); - break; + break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported compression method for RAR file."); ret = ARCHIVE_FATAL; - break; + break; } return (ret); } @@ -1145,10 +1218,8 @@ archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, ret -= rar->dbo[0].start_offset; /* Always restart reading the file after a seek */ - a->read_data_block = NULL; - a->read_data_offset = 0; - a->read_data_output_offset = 0; - a->read_data_remaining = 0; + __archive_reset_read_data(&a->archive); + rar->bytes_unconsumed = 0; rar->offset = 0; @@ -1290,9 +1361,14 @@ read_header(struct archive_read *a, struct archive_entry *entry, if (rar->file_flags & FHD_PASSWORD) { + archive_entry_set_is_data_encrypted(entry, 1); + rar->has_encrypted_entries = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "RAR encryption support unavailable."); - return (ARCHIVE_FATAL); + /* Since it is only the data part itself that is encrypted we can at least + extract information about the currently processed entry and don't need + to return ARCHIVE_FATAL here. */ + /*return (ARCHIVE_FATAL);*/ } if (rar->file_flags & FHD_LARGE) @@ -1377,7 +1453,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, flagbyte = *(p + offset++); flagbits = 8; } - + flagbits -= 2; switch((flagbyte >> flagbits) & 3) { @@ -1469,6 +1545,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, /* Split file in multivolume RAR. No more need to process header. */ if (rar->filename_save && + filename_size == rar->filename_save_size && !memcmp(rar->filename, rar->filename_save, filename_size + 1)) { __archive_read_consume(a, header_size - 7); @@ -1498,6 +1575,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, rar->filename_save = (char*)realloc(rar->filename_save, filename_size + 1); memcpy(rar->filename_save, rar->filename, filename_size + 1); + rar->filename_save_size = filename_size; /* Set info for seeking */ free(rar->dbo); @@ -2345,6 +2423,8 @@ create_code(struct archive_read *a, struct huffman_code *code, { int i, j, codebits = 0, symbolsleft = numsymbols; + code->numentries = 0; + code->numallocatedentries = 0; if (new_node(code) < 0) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for node data."); @@ -2473,11 +2553,17 @@ static int new_node(struct huffman_code *code) { void *new_tree; - - new_tree = realloc(code->tree, (code->numentries + 1) * sizeof(*code->tree)); - if (new_tree == NULL) - return (-1); - code->tree = (struct huffman_tree_node *)new_tree; + if (code->numallocatedentries == code->numentries) { + int new_num_entries = 256; + if (code->numentries > 0) { + new_num_entries = code->numentries * 2; + } + new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree)); + if (new_tree == NULL) + return (-1); + code->tree = (struct huffman_tree_node *)new_tree; + code->numallocatedentries = new_num_entries; + } code->tree[code->numentries].branches[0] = -1; code->tree[code->numentries].branches[1] = -2; return 1; @@ -2611,7 +2697,7 @@ expand(struct archive_read *a, int64_t end) if ((symbol = read_next_symbol(a, &rar->maincode)) < 0) return (ARCHIVE_FATAL); rar->output_last_match = 0; - + if (symbol < 256) { lzss_emit_literal(rar, symbol); @@ -2834,8 +2920,8 @@ rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) int ret; if (avail) { - if (a->read_data_is_posix_read && *avail > (ssize_t)a->read_data_requested) - *avail = a->read_data_requested; + if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested) + *avail = a->archive.read_data_requested; if (*avail > rar->bytes_remaining) *avail = (ssize_t)rar->bytes_remaining; if (*avail < 0) diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c index 843497878a36..efa2c6a33c7e 100644 --- a/libarchive/archive_read_support_format_raw.c +++ b/libarchive/archive_read_support_format_raw.c @@ -78,7 +78,9 @@ archive_read_support_format_raw(struct archive *_a) archive_read_format_raw_read_data, archive_read_format_raw_read_data_skip, NULL, - archive_read_format_raw_cleanup); + archive_read_format_raw_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(info); return (r); diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index c6f405da6e3f..b0521a627ce3 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -151,6 +151,8 @@ struct tar { struct archive_string_conv *sconv_default; int init_default_conversion; int compat_2x; + int process_mac_extensions; + int read_concatenated_archives; }; static int archive_block_is_null(const char *p); @@ -200,7 +202,7 @@ static int archive_read_format_tar_read_header(struct archive_read *, struct archive_entry *); static int checksum(struct archive_read *, const void *); static int pax_attribute(struct archive_read *, struct tar *, - struct archive_entry *, char *key, char *value); + struct archive_entry *, const char *key, const char *value); static int pax_header(struct archive_read *, struct tar *, struct archive_entry *, char *attr); static void pax_time(const char *, int64_t *sec, long *nanos); @@ -241,6 +243,10 @@ archive_read_support_format_tar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_read_support_format_tar"); tar = (struct tar *)calloc(1, sizeof(*tar)); +#ifdef HAVE_COPYFILE_H + /* Set this by default on Mac OS. */ + tar->process_mac_extensions = 1; +#endif if (tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); @@ -254,7 +260,9 @@ archive_read_support_format_tar(struct archive *_a) archive_read_format_tar_read_data, archive_read_format_tar_skip, NULL, - archive_read_format_tar_cleanup); + archive_read_format_tar_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(tar); @@ -368,7 +376,7 @@ archive_read_format_tar_options(struct archive_read *a, tar = (struct tar *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle UTF-8 filnames as libarchive 2.x */ - tar->compat_2x = (val != NULL)?1:0; + tar->compat_2x = (val != NULL && val[0] != 0); tar->init_default_conversion = tar->compat_2x; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { @@ -385,6 +393,12 @@ archive_read_format_tar_options(struct archive_read *a, ret = ARCHIVE_FATAL; } return (ret); + } else if (strcmp(key, "mac-ext") == 0) { + tar->process_mac_extensions = (val != NULL && val[0] != 0); + return (ARCHIVE_OK); + } else if (strcmp(key, "read_concatenated_archives") == 0) { + tar->read_concatenated_archives = (val != NULL && val[0] != 0); + return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options @@ -397,7 +411,7 @@ archive_read_format_tar_options(struct archive_read *a, * how much unconsumed data we have floating around, and to consume * anything outstanding since we're going to do read_aheads */ -static void +static void tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed) { if (*unconsumed) { @@ -442,6 +456,7 @@ archive_read_format_tar_read_header(struct archive_read *a, static int default_dev; struct tar *tar; const char *p; + const wchar_t *wp; int r; size_t l, unconsumed = 0; @@ -492,27 +507,22 @@ archive_read_format_tar_read_header(struct archive_read *a, } } - if (r == ARCHIVE_OK) { + if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) { /* * "Regular" entry with trailing '/' is really * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ - const wchar_t *wp; - wp = archive_entry_pathname_w(entry); - if (wp != NULL) { + if ((wp = archive_entry_pathname_w(entry)) != NULL) { l = wcslen(wp); - if (archive_entry_filetype(entry) == AE_IFREG - && wp[l-1] == L'/') + if (l > 0 && wp[l - 1] == L'/') { archive_entry_set_filetype(entry, AE_IFDIR); - } else { - p = archive_entry_pathname(entry); - if (p == NULL) - return (ARCHIVE_FAILED); + } + } else if ((p = archive_entry_pathname(entry)) != NULL) { l = strlen(p); - if (archive_entry_filetype(entry) == AE_IFREG - && p[l-1] == '/') + if (l > 0 && p[l - 1] == '/') { archive_entry_set_filetype(entry, AE_IFDIR); + } } } return (r); @@ -594,8 +604,12 @@ archive_read_format_tar_skip(struct archive_read *a) /* Do not consume the hole of a sparse file. */ request = 0; for (p = tar->sparse_list; p != NULL; p = p->next) { - if (!p->hole) + if (!p->hole) { + if (p->remaining >= INT64_MAX - request) { + return ARCHIVE_FATAL; + } request += p->remaining; + } } if (request > tar->entry_bytes_remaining) request = tar->entry_bytes_remaining; @@ -629,36 +643,50 @@ tar_read_header(struct archive_read *a, struct tar *tar, const struct archive_entry_header_ustar *header; const struct archive_entry_header_gnutar *gnuheader; - tar_flush_unconsumed(a, unconsumed); + /* Loop until we find a workable header record. */ + for (;;) { + tar_flush_unconsumed(a, unconsumed); - /* Read 512-byte header record */ - h = __archive_read_ahead(a, 512, &bytes); - if (bytes < 0) - return ((int)bytes); - if (bytes == 0) { /* EOF at a block boundary. */ - /* Some writers do omit the block of nulls. <sigh> */ - return (ARCHIVE_EOF); - } - if (bytes < 512) { /* Short block at EOF; this is bad. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated tar archive"); - return (ARCHIVE_FATAL); - } - *unconsumed = 512; + /* Read 512-byte header record */ + h = __archive_read_ahead(a, 512, &bytes); + if (bytes < 0) + return ((int)bytes); + if (bytes == 0) { /* EOF at a block boundary. */ + /* Some writers do omit the block of nulls. <sigh> */ + return (ARCHIVE_EOF); + } + if (bytes < 512) { /* Short block at EOF; this is bad. */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated tar archive"); + return (ARCHIVE_FATAL); + } + *unconsumed = 512; - /* Check for end-of-archive mark. */ - if (h[0] == 0 && archive_block_is_null(h)) { - /* Try to consume a second all-null record, as well. */ - tar_flush_unconsumed(a, unconsumed); - h = __archive_read_ahead(a, 512, NULL); - if (h != NULL) - __archive_read_consume(a, 512); - archive_clear_error(&a->archive); + /* Header is workable if it's not an end-of-archive mark. */ + if (h[0] != 0 || !archive_block_is_null(h)) + break; + + /* Ensure format is set for archives with only null blocks. */ if (a->archive.archive_format_name == NULL) { a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar"; } - return (ARCHIVE_EOF); + + if (!tar->read_concatenated_archives) { + /* Try to consume a second all-null record, as well. */ + tar_flush_unconsumed(a, unconsumed); + h = __archive_read_ahead(a, 512, NULL); + if (h != NULL && h[0] == 0 && archive_block_is_null(h)) + __archive_read_consume(a, 512); + archive_clear_error(&a->archive); + return (ARCHIVE_EOF); + } + + /* + * We're reading concatenated archives, ignore this block and + * loop to get the next. + */ } /* @@ -693,6 +721,8 @@ tar_read_header(struct archive_read *a, struct tar *tar, a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE; a->archive.archive_format_name = "POSIX pax interchange format"; err = header_pax_global(a, tar, entry, h, unconsumed); + if (err == ARCHIVE_EOF) + return (err); break; case 'K': /* Long link name (GNU tar, others) */ err = header_longlink(a, tar, entry, h, unconsumed); @@ -745,9 +775,9 @@ tar_read_header(struct archive_read *a, struct tar *tar, * extensions for both the AppleDouble extension entry and the * regular entry. */ - /* TODO: Should this be disabled on non-Mac platforms? */ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) && - tar->header_recursion_depth == 0) { + tar->header_recursion_depth == 0 && + tar->process_mac_extensions) { int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed); if (err2 < err) err = err2; @@ -790,12 +820,20 @@ checksum(struct archive_read *a, const void *h) { const unsigned char *bytes; const struct archive_entry_header_ustar *header; - int check, i, sum; + int check, sum; + size_t i; (void)a; /* UNUSED */ bytes = (const unsigned char *)h; header = (const struct archive_entry_header_ustar *)h; + /* Checksum field must hold an octal number */ + for (i = 0; i < sizeof(header->checksum); ++i) { + char c = header->checksum[i]; + if (c != ' ' && c != '\0' && (c < '0' || c > '7')) + return 0; + } + /* * Test the checksum. Note that POSIX specifies _unsigned_ * bytes for this calculation. @@ -1287,7 +1325,7 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, if (wp[0] == '/' && wp[1] != L'\0') wname = wp + 1; } - /* + /* * If last path element starts with "._", then * this is a Mac extension. */ @@ -1302,7 +1340,7 @@ read_mac_metadata_blob(struct archive_read *a, struct tar *tar, if (p[0] == '/' && p[1] != '\0') name = p + 1; } - /* + /* * If last path element starts with "._", then * this is a Mac extension. */ @@ -1626,7 +1664,7 @@ pax_header(struct archive_read *a, struct tar *tar, static int pax_attribute_xattr(struct archive_entry *entry, - char *name, char *value) + const char *name, const char *value) { char *name_decoded; void *value_decoded; @@ -1672,7 +1710,7 @@ pax_attribute_xattr(struct archive_entry *entry, */ static int pax_attribute(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, char *key, char *value) + struct archive_entry *entry, const char *key, const char *value) { int64_t s; long n; @@ -2089,6 +2127,10 @@ gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, else tar->sparse_list = p; tar->sparse_last = p; + if (remaining < 0 || offset < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data"); + return (ARCHIVE_FATAL); + } p->offset = offset; p->remaining = remaining; return (ARCHIVE_OK); @@ -2422,9 +2464,10 @@ tar_atol(const char *p, size_t char_cnt) static int64_t tar_atol_base_n(const char *p, size_t char_cnt, int base) { - int64_t l, limit, last_digit_limit; + int64_t l, maxval, limit, last_digit_limit; int digit, sign; + maxval = INT64_MAX; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; @@ -2441,6 +2484,10 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base) sign = -1; p++; char_cnt--; + + maxval = INT64_MIN; + limit = -(INT64_MIN / base); + last_digit_limit = INT64_MIN % base; } l = 0; @@ -2448,8 +2495,7 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base) digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt != 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { - l = INT64_MAX; /* Truncate on overflow. */ - break; + return maxval; /* Truncate on overflow. */ } l = (l * base) + digit; digit = *++p - '0'; @@ -2472,36 +2518,56 @@ tar_atol10(const char *p, size_t char_cnt) } /* - * Parse a base-256 integer. This is just a straight signed binary - * value in big-endian order, except that the high-order bit is - * ignored. + * Parse a base-256 integer. This is just a variable-length + * twos-complement signed binary value in big-endian order, except + * that the high-order bit is ignored. The values here can be up to + * 12 bytes, so we need to be careful about overflowing 64-bit + * (8-byte) integers. + * + * This code unashamedly assumes that the local machine uses 8-bit + * bytes and twos-complement arithmetic. */ static int64_t tar_atol256(const char *_p, size_t char_cnt) { - int64_t l, upper_limit, lower_limit; + uint64_t l; const unsigned char *p = (const unsigned char *)_p; + unsigned char c, neg; + + /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */ + c = *p; + if (c & 0x40) { + neg = 0xff; + c |= 0x80; + l = ~ARCHIVE_LITERAL_ULL(0); + } else { + neg = 0; + c &= 0x7f; + l = 0; + } - upper_limit = INT64_MAX / 256; - lower_limit = INT64_MIN / 256; + /* If more than 8 bytes, check that we can ignore + * high-order bits without overflow. */ + while (char_cnt > sizeof(int64_t)) { + --char_cnt; + if (c != neg) + return neg ? INT64_MIN : INT64_MAX; + c = *++p; + } - /* Pad with 1 or 0 bits, depending on sign. */ - if ((0x40 & *p) == 0x40) - l = (int64_t)-1; - else - l = 0; - l = (l << 6) | (0x3f & *p++); + /* c is first byte that fits; if sign mismatch, return overflow */ + if ((c ^ neg) & 0x80) { + return neg ? INT64_MIN : INT64_MAX; + } + + /* Accumulate remaining bytes. */ while (--char_cnt > 0) { - if (l > upper_limit) { - l = INT64_MAX; /* Truncate on overflow */ - break; - } else if (l < lower_limit) { - l = INT64_MIN; - break; - } - l = (l << 8) | (0xff & (int64_t)*p++); + l = (l << 8) | c; + c = *++p; } - return (l); + l = (l << 8) | c; + /* Return signed twos-complement value. */ + return (int64_t)(l); } /* diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c new file mode 100644 index 000000000000..46a59ea14ba8 --- /dev/null +++ b/libarchive/archive_read_support_format_warc.c @@ -0,0 +1,795 @@ +/*- + * Copyright (c) 2014 Sebastian Freundt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD$"); + +/** + * WARC is standardised by ISO TC46/SC4/WG12 and currently available as + * ISO 28500:2009. + * For the purposes of this file we used the final draft from: + * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf + * + * Todo: + * [ ] real-world warcs can contain resources at endpoints ending in / + * e.g. http://bibnum.bnf.fr/warc/ + * if you're lucky their response contains a Content-Location: header + * pointing to a unix-compliant filename, in the example above it's + * Content-Location: http://bibnum.bnf.fr/warc/index.html + * however, that's not mandated and github for example doesn't follow + * this convention. + * We need a set of archive options to control what to do with + * entries like these, at the moment care is taken to skip them. + * + **/ + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif +#ifdef HAVE_TIME_H +#include <time.h> +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_private.h" + +typedef enum { + WT_NONE, + /* warcinfo */ + WT_INFO, + /* metadata */ + WT_META, + /* resource */ + WT_RSRC, + /* request, unsupported */ + WT_REQ, + /* response, unsupported */ + WT_RSP, + /* revisit, unsupported */ + WT_RVIS, + /* conversion, unsupported */ + WT_CONV, + /* continutation, unsupported at the moment */ + WT_CONT, + /* invalid type */ + LAST_WT +} warc_type_t; + +typedef struct { + size_t len; + const char *str; +} warc_string_t; + +typedef struct { + size_t len; + char *str; +} warc_strbuf_t; + +struct warc_s { + /* content length ahead */ + size_t cntlen; + /* and how much we've processed so far */ + size_t cntoff; + /* and how much we need to consume between calls */ + size_t unconsumed; + + /* string pool */ + warc_strbuf_t pool; + /* previous version */ + unsigned int pver; + /* stringified format name */ + struct archive_string sver; +}; + +static int _warc_bid(struct archive_read *a, int); +static int _warc_cleanup(struct archive_read *a); +static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*); +static int _warc_skip(struct archive_read *a); +static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e); + +/* private routines */ +static unsigned int _warc_rdver(const char buf[10], size_t bsz); +static unsigned int _warc_rdtyp(const char *buf, size_t bsz); +static warc_string_t _warc_rduri(const char *buf, size_t bsz); +static ssize_t _warc_rdlen(const char *buf, size_t bsz); +static time_t _warc_rdrtm(const char *buf, size_t bsz); +static time_t _warc_rdmtm(const char *buf, size_t bsz); +static const char *_warc_find_eoh(const char *buf, size_t bsz); + + +int +archive_read_support_format_warc(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct warc_s *w; + int r; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); + + if ((w = malloc(sizeof(*w))) == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate warc data"); + return (ARCHIVE_FATAL); + } + memset(w, 0, sizeof(*w)); + + r = __archive_read_register_format( + a, w, "warc", + _warc_bid, NULL, _warc_rdhdr, _warc_read, + _warc_skip, NULL, _warc_cleanup, NULL, NULL); + + if (r != ARCHIVE_OK) { + free(w); + return (r); + } + return (ARCHIVE_OK); +} + +static int +_warc_cleanup(struct archive_read *a) +{ + struct warc_s *w = a->format->data; + + if (w->pool.len > 0U) { + free(w->pool.str); + } + archive_string_free(&w->sver); + free(w); + a->format->data = NULL; + return (ARCHIVE_OK); +} + +static int +_warc_bid(struct archive_read *a, int best_bid) +{ + const char *hdr; + ssize_t nrd; + unsigned int ver; + + (void)best_bid; /* UNUSED */ + + /* check first line of file, it should be a record already */ + if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) { + /* no idea what to do */ + return -1; + } else if (nrd < 12) { + /* nah, not for us, our magic cookie is at least 12 bytes */ + return -1; + } + + /* otherwise snarf the record's version number */ + ver = _warc_rdver(hdr, nrd); + if (ver == 0U || ver > 10000U) { + /* oh oh oh, best not to wager ... */ + return -1; + } + + /* otherwise be confident */ + return (64); +} + +static int +_warc_rdhdr(struct archive_read *a, struct archive_entry *entry) +{ +#define HDR_PROBE_LEN (12U) + struct warc_s *w = a->format->data; + unsigned int ver; + const char *buf; + ssize_t nrd; + const char *eoh; + /* for the file name, saves some strndup()'ing */ + warc_string_t fnam; + /* warc record type, not that we really use it a lot */ + warc_type_t ftyp; + /* content-length+error monad */ + ssize_t cntlen; + /* record time is the WARC-Date time we reinterpret it as ctime */ + time_t rtime; + /* mtime is the Last-Modified time which will be the entry's mtime */ + time_t mtime; + +start_over: + /* just use read_ahead() they keep track of unconsumed + * bits and bobs for us; no need to put an extra shift in + * and reproduce that functionality here */ + buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd); + + if (nrd < 0) { + /* no good */ + archive_set_error( + &a->archive, ARCHIVE_ERRNO_MISC, + "Bad record header"); + return (ARCHIVE_FATAL); + } else if (buf == NULL) { + /* there should be room for at least WARC/bla\r\n + * must be EOF therefore */ + return (ARCHIVE_EOF); + } + /* looks good so far, try and find the end of the header now */ + eoh = _warc_find_eoh(buf, nrd); + if (eoh == NULL) { + /* still no good, the header end might be beyond the + * probe we've requested, but then again who'd cram + * so much stuff into the header *and* be 28500-compliant */ + archive_set_error( + &a->archive, ARCHIVE_ERRNO_MISC, + "Bad record header"); + return (ARCHIVE_FATAL); + } else if ((ver = _warc_rdver(buf, eoh - buf)) > 10000U) { + /* nawww, I wish they promised backward compatibility + * anyhoo, in their infinite wisdom the 28500 guys might + * come up with something we can't possibly handle so + * best end things here */ + archive_set_error( + &a->archive, ARCHIVE_ERRNO_MISC, + "Unsupported record version"); + return (ARCHIVE_FATAL); + } else if ((cntlen = _warc_rdlen(buf, eoh - buf)) < 0) { + /* nightmare! the specs say content-length is mandatory + * so I don't feel overly bad stopping the reader here */ + archive_set_error( + &a->archive, EINVAL, + "Bad content length"); + return (ARCHIVE_FATAL); + } else if ((rtime = _warc_rdrtm(buf, eoh - buf)) == (time_t)-1) { + /* record time is mandatory as per WARC/1.0, + * so just barf here, fast and loud */ + archive_set_error( + &a->archive, EINVAL, + "Bad record time"); + return (ARCHIVE_FATAL); + } + + /* let the world know we're a WARC archive */ + a->archive.archive_format = ARCHIVE_FORMAT_WARC; + if (ver != w->pver) { + /* stringify this entry's version */ + archive_string_sprintf(&w->sver, + "WARC/%u.%u", ver / 10000, ver % 10000); + /* remember the version */ + w->pver = ver; + } + /* start off with the type */ + ftyp = _warc_rdtyp(buf, eoh - buf); + /* and let future calls know about the content */ + w->cntlen = cntlen; + w->cntoff = 0U; + mtime = 0;/* Avoid compiling error on some platform. */ + + switch (ftyp) { + case WT_RSRC: + case WT_RSP: + /* only try and read the filename in the cases that are + * guaranteed to have one */ + fnam = _warc_rduri(buf, eoh - buf); + /* check the last character in the URI to avoid creating + * directory endpoints as files, see Todo above */ + if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') { + /* break here for now */ + fnam.len = 0U; + fnam.str = NULL; + break; + } + /* bang to our string pool, so we save a + * malloc()+free() roundtrip */ + if (fnam.len + 1U > w->pool.len) { + w->pool.len = ((fnam.len + 64U) / 64U) * 64U; + w->pool.str = realloc(w->pool.str, w->pool.len); + } + memcpy(w->pool.str, fnam.str, fnam.len); + w->pool.str[fnam.len] = '\0'; + /* let noone else know about the pool, it's a secret, shhh */ + fnam.str = w->pool.str; + + /* snarf mtime or deduce from rtime + * this is a custom header added by our writer, it's quite + * hard to believe anyone else would go through with it + * (apart from being part of some http responses of course) */ + if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) { + mtime = rtime; + } + break; + default: + fnam.len = 0U; + fnam.str = NULL; + break; + } + + /* now eat some of those delicious buffer bits */ + __archive_read_consume(a, eoh - buf); + + switch (ftyp) { + case WT_RSRC: + case WT_RSP: + if (fnam.len > 0U) { + /* populate entry object */ + archive_entry_set_filetype(entry, AE_IFREG); + archive_entry_copy_pathname(entry, fnam.str); + archive_entry_set_size(entry, cntlen); + archive_entry_set_perm(entry, 0644); + /* rtime is the new ctime, mtime stays mtime */ + archive_entry_set_ctime(entry, rtime, 0L); + archive_entry_set_mtime(entry, mtime, 0L); + break; + } + /* FALLTHROUGH */ + default: + /* consume the content and start over */ + _warc_skip(a); + goto start_over; + } + return (ARCHIVE_OK); +} + +static int +_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off) +{ + struct warc_s *w = a->format->data; + const char *rab; + ssize_t nrd; + + if (w->cntoff >= w->cntlen) { + eof: + /* it's our lucky day, no work, we can leave early */ + *buf = NULL; + *bsz = 0U; + *off = w->cntoff + 4U/*for \r\n\r\n separator*/; + w->unconsumed = 0U; + return (ARCHIVE_EOF); + } + + rab = __archive_read_ahead(a, 1U, &nrd); + if (nrd < 0) { + *bsz = 0U; + /* big catastrophe */ + return (int)nrd; + } else if (nrd == 0) { + goto eof; + } else if ((size_t)nrd > w->cntlen - w->cntoff) { + /* clamp to content-length */ + nrd = w->cntlen - w->cntoff; + } + *off = w->cntoff; + *bsz = nrd; + *buf = rab; + + w->cntoff += nrd; + w->unconsumed = (size_t)nrd; + return (ARCHIVE_OK); +} + +static int +_warc_skip(struct archive_read *a) +{ + struct warc_s *w = a->format->data; + + __archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/); + w->cntlen = 0U; + w->cntoff = 0U; + return (ARCHIVE_OK); +} + + +/* private routines */ +static void* +deconst(const void *c) +{ + return (char *)0x1 + (((const char *)c) - (const char *)0x1); +} + +static char* +xmemmem(const char *hay, const size_t haysize, + const char *needle, const size_t needlesize) +{ + const char *const eoh = hay + haysize; + const char *const eon = needle + needlesize; + const char *hp; + const char *np; + const char *cand; + unsigned int hsum; + unsigned int nsum; + unsigned int eqp; + + /* trivial checks first + * a 0-sized needle is defined to be found anywhere in haystack + * then run strchr() to find a candidate in HAYSTACK (i.e. a portion + * that happens to begin with *NEEDLE) */ + if (needlesize == 0UL) { + return deconst(hay); + } else if ((hay = memchr(hay, *needle, haysize)) == NULL) { + /* trivial */ + return NULL; + } + + /* First characters of haystack and needle are the same now. Both are + * guaranteed to be at least one character long. Now computes the sum + * of characters values of needle together with the sum of the first + * needle_len characters of haystack. */ + for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U; + hp < eoh && np < eon; + hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++); + + /* HP now references the (NEEDLESIZE + 1)-th character. */ + if (np < eon) { + /* haystack is smaller than needle, :O */ + return NULL; + } else if (eqp) { + /* found a match */ + return deconst(hay); + } + + /* now loop through the rest of haystack, + * updating the sum iteratively */ + for (cand = hay; hp < eoh; hp++) { + hsum ^= *cand++; + hsum ^= *hp; + + /* Since the sum of the characters is already known to be + * equal at that point, it is enough to check just NEEDLESIZE - 1 + * characters for equality, + * also CAND is by design < HP, so no need for range checks */ + if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) { + return deconst(cand); + } + } + return NULL; +} + +static int +strtoi_lim(const char *str, const char **ep, int llim, int ulim) +{ + int res = 0; + const char *sp; + /* we keep track of the number of digits via rulim */ + int rulim; + + for (sp = str, rulim = ulim > 10 ? ulim : 10; + res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9'; + sp++, rulim /= 10) { + res *= 10; + res += *sp - '0'; + } + if (sp == str) { + res = -1; + } else if (res < llim || res > ulim) { + res = -2; + } + *ep = (const char*)sp; + return res; +} + +static time_t +time_from_tm(struct tm *t) +{ +#if HAVE_TIMEGM + /* Use platform timegm() if available. */ + return (timegm(t)); +#elif HAVE__MKGMTIME64 + return (_mkgmtime64(t)); +#else + /* Else use direct calculation using POSIX assumptions. */ + /* First, fix up tm_yday based on the year/month/day. */ + if (mktime(t) == (time_t)-1) + return ((time_t)-1); + /* 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 +} + +static time_t +xstrpisotime(const char *s, char **endptr) +{ +/** like strptime() but strictly for ISO 8601 Zulu strings */ + struct tm tm; + time_t res = (time_t)-1; + + /* make sure tm is clean */ + memset(&tm, 0, sizeof(tm)); + + /* as a courtesy to our callers, and since this is a non-standard + * routine, we skip leading whitespace */ + for (; isspace(*s); s++); + + /* read year */ + if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') { + goto out; + } + /* read month */ + if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') { + goto out; + } + /* read day-of-month */ + if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') { + goto out; + } + /* read hour */ + if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') { + goto out; + } + /* read minute */ + if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') { + goto out; + } + /* read second */ + if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') { + goto out; + } + + /* massage TM to fulfill some of POSIX' contraints */ + tm.tm_year -= 1900; + tm.tm_mon--; + + /* now convert our custom tm struct to a unix stamp using UTC */ + res = time_from_tm(&tm); + +out: + if (endptr != NULL) { + *endptr = deconst(s); + } + return res; +} + +static unsigned int +_warc_rdver(const char buf[10], size_t bsz) +{ + static const char magic[] = "WARC/"; + unsigned int ver; + + (void)bsz; /* UNUSED */ + + if (memcmp(buf, magic, sizeof(magic) - 1U) != 0) { + /* nope */ + return 99999U; + } + /* looks good so far, read the version number for a laugh */ + buf += sizeof(magic) - 1U; + /* most common case gets a quick-check here */ + if (memcmp(buf, "1.0\r\n", 5U) == 0) { + ver = 10000U; + } else { + switch (*buf) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + if (buf[1U] == '.') { + char *on; + + /* set up major version */ + ver = (buf[0U] - '0') * 10000U; + /* minor version, anyone? */ + ver += (strtol(buf + 2U, &on, 10)) * 100U; + /* don't parse anything else */ + if (on > buf + 2U) { + break; + } + } + /* FALLTHROUGH */ + case '9': + default: + /* just make the version ridiculously high */ + ver = 999999U; + break; + } + } + return ver; +} + +static unsigned int +_warc_rdtyp(const char *buf, size_t bsz) +{ + static const char _key[] = "\r\nWARC-Type:"; + const char *const eob = buf + bsz; + const char *val; + + if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { + /* no bother */ + return WT_NONE; + } + /* overread whitespace */ + for (val += sizeof(_key) - 1U; val < eob && isspace(*val); val++); + + if (val + 8U > eob) { + ; + } else if (memcmp(val, "resource", 8U) == 0) { + return WT_RSRC; + } else if (memcmp(val, "warcinfo", 8U) == 0) { + return WT_INFO; + } else if (memcmp(val, "metadata", 8U) == 0) { + return WT_META; + } else if (memcmp(val, "request", 7U) == 0) { + return WT_REQ; + } else if (memcmp(val, "response", 8U) == 0) { + return WT_RSP; + } else if (memcmp(val, "conversi", 8U) == 0) { + return WT_CONV; + } else if (memcmp(val, "continua", 8U) == 0) { + return WT_CONT; + } + return WT_NONE; +} + +static warc_string_t +_warc_rduri(const char *buf, size_t bsz) +{ + static const char _key[] = "\r\nWARC-Target-URI:"; + const char *const eob = buf + bsz; + const char *val; + const char *uri; + const char *eol; + warc_string_t res = {0U, NULL}; + + if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { + /* no bother */ + return res; + } + /* overread whitespace */ + for (val += sizeof(_key) - 1U; val < eob && isspace(*val); val++); + + /* overread URL designators */ + if ((uri = xmemmem(val, eob - val, "://", 3U)) == NULL) { + /* not touching that! */ + return res; + } else if ((eol = memchr(uri, '\n', eob - uri)) == NULL) { + /* no end of line? :O */ + return res; + } + + /* massage uri to point to after :// */ + uri += 3U; + /* also massage eol to point to the first whitespace + * after the last non-whitespace character before + * the end of the line */ + for (; eol > uri && isspace(eol[-1]); eol--); + + /* now then, inspect the URI */ + if (memcmp(val, "file", 4U) == 0) { + /* perfect, nothing left to do here */ + + } else if (memcmp(val, "http", 4U) == 0 || + memcmp(val, "ftp", 3U) == 0) { + /* overread domain, and the first / */ + while (uri < eol && *uri++ != '/'); + } else { + /* not sure what to do? best to bugger off */ + return res; + } + res.str = uri; + res.len = eol - uri; + return res; +} + +static ssize_t +_warc_rdlen(const char *buf, size_t bsz) +{ + static const char _key[] = "\r\nContent-Length:"; + const char *val; + char *on = NULL; + long int len; + + if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { + /* no bother */ + return -1; + } + + /* strtol kindly overreads whitespace for us, so use that */ + val += sizeof(_key) - 1U; + len = strtol(val, &on, 10); + if (on == NULL || !isspace(*on)) { + /* hm, can we trust that number? Best not. */ + return -1; + } + return (size_t)len; +} + +static time_t +_warc_rdrtm(const char *buf, size_t bsz) +{ + static const char _key[] = "\r\nWARC-Date:"; + const char *val; + char *on = NULL; + time_t res; + + if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { + /* no bother */ + return (time_t)-1; + } + + /* xstrpisotime() kindly overreads whitespace for us, so use that */ + val += sizeof(_key) - 1U; + res = xstrpisotime(val, &on); + if (on == NULL || !isspace(*on)) { + /* hm, can we trust that number? Best not. */ + return (time_t)-1; + } + return res; +} + +static time_t +_warc_rdmtm(const char *buf, size_t bsz) +{ + static const char _key[] = "\r\nLast-Modified:"; + const char *val; + char *on = NULL; + time_t res; + + if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { + /* no bother */ + return (time_t)-1; + } + + /* xstrpisotime() kindly overreads whitespace for us, so use that */ + val += sizeof(_key) - 1U; + res = xstrpisotime(val, &on); + if (on == NULL || !isspace(*on)) { + /* hm, can we trust that number? Best not. */ + return (time_t)-1; + } + return res; +} + +static const char* +_warc_find_eoh(const char *buf, size_t bsz) +{ + static const char _marker[] = "\r\n\r\n"; + const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U); + + if (hit != NULL) { + hit += sizeof(_marker) - 1U; + } + return hit; +} + +/* archive_read_support_format_warc.c ends here */ diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c index 780e749d7096..ab887505ce17 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -51,7 +51,7 @@ __FBSDID("$FreeBSD$"); #endif #include "archive.h" -#include "archive_crypto_private.h" +#include "archive_digest_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" @@ -468,7 +468,9 @@ archive_read_support_format_xar(struct archive *_a) xar_read_data, xar_read_data_skip, NULL, - xar_cleanup); + xar_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(xar); return (r); @@ -967,10 +969,14 @@ move_reading_point(struct archive_read *a, uint64_t offset) return ((int)step); xar->offset += step; } else { - archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, - "Cannot seek."); - return (ARCHIVE_FAILED); + int64_t pos = __archive_read_seek(a, offset, SEEK_SET); + if (pos == ARCHIVE_FAILED) { + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_MISC, + "Cannot seek."); + return (ARCHIVE_FAILED); + } + xar->offset = pos; } } return (ARCHIVE_OK); @@ -1101,20 +1107,23 @@ static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM - /* Use platform timegm() if available. */ - return (timegm(t)); + /* Use platform timegm() if available. */ + return (timegm(t)); #elif HAVE__MKGMTIME64 - return (_mkgmtime64(t)); + return (_mkgmtime64(t)); #else - /* 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); + /* 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 } @@ -1930,9 +1939,6 @@ unknowntag_start(struct archive_read *a, struct xar *xar, const char *name) { struct unknown_tag *tag; -#if DEBUG - fprintf(stderr, "unknowntag_start:%s\n", name); -#endif tag = malloc(sizeof(*tag)); if (tag == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); @@ -1942,6 +1948,9 @@ unknowntag_start(struct archive_read *a, struct xar *xar, const char *name) archive_string_init(&(tag->name)); archive_strcpy(&(tag->name), name); if (xar->unknowntags == NULL) { +#if DEBUG + fprintf(stderr, "UNKNOWNTAG_START:%s\n", name); +#endif xar->xmlsts_unknown = xar->xmlsts; xar->xmlsts = UNKNOWN; } @@ -1954,9 +1963,6 @@ unknowntag_end(struct xar *xar, const char *name) { struct unknown_tag *tag; -#if DEBUG - fprintf(stderr, "unknowntag_end:%s\n", name); -#endif tag = xar->unknowntags; if (tag == NULL || name == NULL) return; @@ -1964,8 +1970,12 @@ unknowntag_end(struct xar *xar, const char *name) xar->unknowntags = tag->next; archive_string_free(&(tag->name)); free(tag); - if (xar->unknowntags == NULL) + if (xar->unknowntags == NULL) { +#if DEBUG + fprintf(stderr, "UNKNOWNTAG_END:%s\n", name); +#endif xar->xmlsts = xar->xmlsts_unknown; + } } } @@ -2159,7 +2169,7 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) case FILE_ACL: if (strcmp(name, "appleextended") == 0) xar->xmlsts = FILE_ACL_APPLEEXTENDED; - if (strcmp(name, "default") == 0) + else if (strcmp(name, "default") == 0) xar->xmlsts = FILE_ACL_DEFAULT; else if (strcmp(name, "access") == 0) xar->xmlsts = FILE_ACL_ACCESS; @@ -2681,9 +2691,9 @@ xml_data(void *userData, const char *s, int len) #if DEBUG { char buff[1024]; - if (len > sizeof(buff)-1) - len = sizeof(buff)-1; - memcpy(buff, s, len); + if (len > (int)(sizeof(buff)-1)) + len = (int)(sizeof(buff)-1); + strncpy(buff, s, len); buff[len] = 0; fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff); } @@ -3183,9 +3193,8 @@ xml2_read_toc(struct archive_read *a) case XML_READER_TYPE_ELEMENT: empty = xmlTextReaderIsEmptyElement(reader); r = xml2_xmlattr_setup(a, &list, reader); - if (r != ARCHIVE_OK) - return (r); - r = xml_start(a, name, &list); + if (r == ARCHIVE_OK) + r = xml_start(a, name, &list); xmlattr_cleanup(&list); if (r != ARCHIVE_OK) return (r); diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 450a6f7da519..0a0be96b5990 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -1,6 +1,7 @@ /*- - * Copyright (c) 2004 Tim Kientzle - * Copyright (c) 2011-2012 Michihiro NAKAJIMA + * Copyright (c) 2004-2013 Tim Kientzle + * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA + * Copyright (c) 2013 Konrad Kleine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +28,21 @@ #include "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $"); +/* + * The definitive documentation of the Zip file format is: + * http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * + * The Info-Zip project has pioneered various extensions to better + * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855 + * "Ux", and 0x7875 "ux" extensions for time and ownership + * information. + * + * History of this code: The streaming Zip reader was first added to + * libarchive in January 2005. Support for seekable input sources was + * added in Nov 2011. Zip64 support (including a significant code + * refactoring) was added in 2014. + */ + #ifdef HAVE_ERRNO_H #include <errno.h> #endif @@ -38,9 +54,12 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #endif #include "archive.h" +#include "archive_digest_private.h" +#include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_hmac_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" @@ -51,42 +70,85 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 struct zip_entry { struct archive_rb_node node; + struct zip_entry *next; int64_t local_header_offset; int64_t compressed_size; int64_t uncompressed_size; int64_t gid; int64_t uid; - struct archive_entry *entry; struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; uint32_t crc32; uint16_t mode; - uint16_t flags; - char compression; - char system; + uint16_t zip_flags; /* From GP Flags Field */ + unsigned char compression; + unsigned char system; /* From "version written by" */ + unsigned char flags; /* Our extra markers. */ + unsigned char decdat;/* Used for Decryption check */ + + /* WinZip AES encryption extra field should be available + * when compression is 99. */ + struct { + /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */ + unsigned vendor; +#define AES_VENDOR_AE_1 0x0001 +#define AES_VENDOR_AE_2 0x0002 + /* AES encryption strength: + * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */ + unsigned strength; + /* Actual compression method. */ + unsigned char compression; + } aes_extra; +}; + +struct trad_enc_ctx { + uint32_t keys[3]; }; +/* Bits used in zip_flags. */ +#define ZIP_ENCRYPTED (1 << 0) +#define ZIP_LENGTH_AT_END (1 << 3) +#define ZIP_STRONG_ENCRYPTED (1 << 6) +#define ZIP_UTF8_NAME (1 << 11) +/* See "7.2 Single Password Symmetric Encryption Method" + in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ +#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13) + +/* Bits used in flags. */ +#define LA_USED_ZIP64 (1 << 0) +#define LA_FROM_CENTRAL_DIRECTORY (1 << 1) + +/* + * See "WinZip - AES Encryption Information" + * http://www.winzip.com/aes_info.htm + */ +/* Value used in compression method. */ +#define WINZIP_AES_ENCRYPTION 99 +/* Authentication code size. */ +#define AUTH_CODE_SIZE 10 +/**/ +#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) + struct zip { /* Structural information about the archive. */ - int64_t end_of_central_directory_offset; + struct archive_string format_name; int64_t central_directory_offset; - size_t central_directory_size; - size_t central_directory_entries; - char have_central_directory; - int64_t offset; + size_t central_directory_entries_total; + size_t central_directory_entries_on_this_disk; + int has_encrypted_entries; /* List of entries (seekable Zip only) */ - size_t entries_remaining; struct zip_entry *zip_entries; - struct zip_entry *entry; struct archive_rb_tree tree; struct archive_rb_tree tree_rsrc; + /* Bytes read but not yet consumed via __archive_read_consume() */ size_t unconsumed; - /* entry_bytes_remaining is the number of bytes we expect. */ + /* Information about entry we're currently reading. */ + struct zip_entry *entry; int64_t entry_bytes_remaining; /* These count the number of bytes actually read for the entry. */ @@ -95,852 +157,563 @@ struct zip { /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; + unsigned long (*crc32func)(unsigned long, const void *, + size_t); + char ignore_crc32; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; - ssize_t filename_length; - ssize_t extra_length; - +#ifdef HAVE_ZLIB_H unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; -#ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif - struct archive_string extra; struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; int init_default_conversion; - char format_name[64]; + int process_mac_extensions; + + char init_decryption; + + /* Decryption buffer. */ + unsigned char *decrypted_buffer; + unsigned char *decrypted_ptr; + size_t decrypted_buffer_size; + size_t decrypted_bytes_remaining; + size_t decrypted_unconsumed_bytes; + + /* Traditional PKWARE decryption. */ + struct trad_enc_ctx tctx; + char tctx_valid; + + /* WinZip AES decyption. */ + /* Contexts used for AES decryption. */ + archive_crypto_ctx cctx; + char cctx_valid; + archive_hmac_sha1_ctx hctx; + char hctx_valid; + + /* Strong encryption's decryption header information. */ + unsigned iv_size; + unsigned alg_id; + unsigned bit_len; + unsigned flags; + unsigned erd_size; + unsigned v_size; + unsigned v_crc32; + uint8_t *iv; + uint8_t *erd; + uint8_t *v_data; }; -#define ZIP_LENGTH_AT_END 8 -#define ZIP_ENCRYPTED (1<<0) -#define ZIP_STRONG_ENCRYPTED (1<<6) -#define ZIP_UTF8_NAME (1<<11) - -static int archive_read_format_zip_streamable_bid(struct archive_read *, - int); -static int archive_read_format_zip_seekable_bid(struct archive_read *, - int); -static int archive_read_format_zip_options(struct archive_read *, - const char *, const char *); -static int archive_read_format_zip_cleanup(struct archive_read *); -static int archive_read_format_zip_read_data(struct archive_read *, - const void **, size_t *, int64_t *); -static int archive_read_format_zip_read_data_skip(struct archive_read *a); -static int archive_read_format_zip_seekable_read_header( - struct archive_read *, struct archive_entry *); -static int archive_read_format_zip_streamable_read_header( - struct archive_read *, struct archive_entry *); -static ssize_t zip_get_local_file_header_size(struct archive_read *, size_t); -#ifdef HAVE_ZLIB_H -static int zip_deflate_init(struct archive_read *, struct zip *); -static int zip_read_data_deflate(struct archive_read *a, const void **buff, - size_t *size, int64_t *offset); -#endif -static int zip_read_data_none(struct archive_read *a, const void **buff, - size_t *size, int64_t *offset); -static int zip_read_local_file_header(struct archive_read *a, - struct archive_entry *entry, struct zip *); -static time_t zip_time(const char *); -static const char *compression_name(int compression); -static void process_extra(const char *, size_t, struct zip_entry *); - -int archive_read_support_format_zip_streamable(struct archive *); -int archive_read_support_format_zip_seekable(struct archive *); - -int -archive_read_support_format_zip_streamable(struct archive *_a) -{ - struct archive_read *a = (struct archive_read *)_a; - struct zip *zip; - int r; +/* Many systems define min or MIN, but not all. */ +#define zipmin(a,b) ((a) < (b) ? (a) : (b)) - archive_check_magic(_a, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); +/* ------------------------------------------------------------------------ */ - zip = (struct zip *)malloc(sizeof(*zip)); - if (zip == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate zip data"); - return (ARCHIVE_FATAL); - } - memset(zip, 0, sizeof(*zip)); +/* + Traditional PKWARE Decryption functions. + */ - r = __archive_read_register_format(a, - zip, - "zip", - archive_read_format_zip_streamable_bid, - archive_read_format_zip_options, - archive_read_format_zip_streamable_read_header, - archive_read_format_zip_read_data, - archive_read_format_zip_read_data_skip, - NULL, - archive_read_format_zip_cleanup); +static void +trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) +{ + uint8_t t; +#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) + + ctx->keys[0] = CRC32(ctx->keys[0], c); + ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; + t = (ctx->keys[1] >> 24) & 0xff; + ctx->keys[2] = CRC32(ctx->keys[2], t); +#undef CRC32 +} - if (r != ARCHIVE_OK) - free(zip); - return (ARCHIVE_OK); +static uint8_t +trad_enc_decypt_byte(struct trad_enc_ctx *ctx) +{ + unsigned temp = ctx->keys[2] | 2; + return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } -int -archive_read_support_format_zip_seekable(struct archive *_a) +static void +trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, + size_t in_len, uint8_t *out, size_t out_len) { - struct archive_read *a = (struct archive_read *)_a; - struct zip *zip; - int r; + unsigned i, max; - archive_check_magic(_a, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); + max = (unsigned)((in_len < out_len)? in_len: out_len); - zip = (struct zip *)malloc(sizeof(*zip)); - if (zip == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate zip data"); - return (ARCHIVE_FATAL); + for (i = 0; i < max; i++) { + uint8_t t = in[i] ^ trad_enc_decypt_byte(ctx); + out[i] = t; + trad_enc_update_keys(ctx, t); } - memset(zip, 0, sizeof(*zip)); - - r = __archive_read_register_format(a, - zip, - "zip", - archive_read_format_zip_seekable_bid, - archive_read_format_zip_options, - archive_read_format_zip_seekable_read_header, - archive_read_format_zip_read_data, - archive_read_format_zip_read_data_skip, - NULL, - archive_read_format_zip_cleanup); - - if (r != ARCHIVE_OK) - free(zip); - return (ARCHIVE_OK); } -int -archive_read_support_format_zip(struct archive *a) -{ - int r; - r = archive_read_support_format_zip_streamable(a); - if (r != ARCHIVE_OK) - return r; - return (archive_read_support_format_zip_seekable(a)); -} - -/* - * TODO: This is a performance sink because it forces the read core to - * drop buffered data from the start of file, which will then have to - * be re-read again if this bidder loses. - * - * We workaround this a little by passing in the best bid so far so - * that later bidders can do nothing if they know they'll never - * outbid. But we can certainly do better... - */ static int -archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) +trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len, + const uint8_t *key, size_t key_len, uint8_t *crcchk) { - struct zip *zip = (struct zip *)a->format->data; - int64_t filesize; - const char *p; - - /* If someone has already bid more than 32, then avoid - trashing the look-ahead buffers with a seek. */ - if (best_bid > 32) - return (-1); - - filesize = __archive_read_seek(a, -22, SEEK_END); - /* If we can't seek, then we can't bid. */ - if (filesize <= 0) - return 0; - - /* TODO: More robust search for end of central directory record. */ - if ((p = __archive_read_ahead(a, 22, NULL)) == NULL) - return 0; - /* First four bytes are signature for end of central directory - record. Four zero bytes ensure this isn't a multi-volume - Zip file (which we don't yet support). */ - if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0) { - int64_t i, tail; - int found; + uint8_t header[12]; - /* - * If there is a comment in end of central directory - * record, 22 bytes are too short. we have to read more - * to properly detect the record. Hopefully, a length - * of the comment is not longer than 16362 bytes(16K-22). - */ - if (filesize + 22 > 1024 * 16) { - tail = 1024 * 16; - filesize = __archive_read_seek(a, tail * -1, SEEK_END); - } else { - tail = filesize + 22; - filesize = __archive_read_seek(a, 0, SEEK_SET); - } - if (filesize < 0) - return 0; - if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) - return 0; - for (found = 0, i = 0;!found && i < tail - 22;) { - switch (p[i]) { - case 'P': - if (memcmp(p+i, - "PK\005\006\000\000\000\000", 8) == 0) { - p += i; - filesize += tail - - (22 + archive_le16dec(p+20)); - found = 1; - } else - i += 8; - break; - case 'K': i += 7; break; - case 005: i += 6; break; - case 006: i += 5; break; - default: i += 1; break; - } - } - if (!found) - return 0; + if (key_len < 12) { + *crcchk = 0xff; + return -1; } - /* Since we've already done the hard work of finding the - end of central directory record, let's save the important - information. */ - zip->central_directory_entries = archive_le16dec(p + 10); - zip->central_directory_size = archive_le32dec(p + 12); - zip->central_directory_offset = archive_le32dec(p + 16); - zip->end_of_central_directory_offset = filesize; + ctx->keys[0] = 305419896L; + ctx->keys[1] = 591751049L; + ctx->keys[2] = 878082192L; - /* Just one volume, so central dir must all be on this volume. */ - if (zip->central_directory_entries != archive_le16dec(p + 8)) - return 0; - /* Central directory can't extend beyond end of this file. */ - if (zip->central_directory_offset + - (int64_t)zip->central_directory_size > filesize) - return 0; + for (;pw_len; --pw_len) + trad_enc_update_keys(ctx, *pw++); - /* This is just a tiny bit higher than the maximum returned by - the streaming Zip bidder. This ensures that the more accurate - seeking Zip parser wins whenever seek is available. */ - return 32; + trad_enc_decrypt_update(ctx, key, 12, header, 12); + /* Return the last byte for CRC check. */ + *crcchk = header[11]; + return 0; } -static int -cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) +#if 0 +static void +crypt_derive_key_sha1(const void *p, int size, unsigned char *key, + int key_size) { - const struct zip_entry *e1 = (const struct zip_entry *)n1; - const struct zip_entry *e2 = (const struct zip_entry *)n2; - - return ((int)(e2->local_header_offset - e1->local_header_offset)); +#define MD_SIZE 20 + archive_sha1_ctx ctx; + unsigned char md1[MD_SIZE]; + unsigned char md2[MD_SIZE * 2]; + unsigned char mkb[64]; + int i; + + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, p, size); + archive_sha1_final(&ctx, md1); + + memset(mkb, 0x36, sizeof(mkb)); + for (i = 0; i < MD_SIZE; i++) + mkb[i] ^= md1[i]; + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, mkb, sizeof(mkb)); + archive_sha1_final(&ctx, md2); + + memset(mkb, 0x5C, sizeof(mkb)); + for (i = 0; i < MD_SIZE; i++) + mkb[i] ^= md1[i]; + archive_sha1_init(&ctx); + archive_sha1_update(&ctx, mkb, sizeof(mkb)); + archive_sha1_final(&ctx, md2 + MD_SIZE); + + if (key_size > 32) + key_size = 32; + memcpy(key, md2, key_size); +#undef MD_SIZE } +#endif -static int -cmp_key(const struct archive_rb_node *n, const void *key) -{ - /* This function won't be called */ - (void)n; /* UNUSED */ - (void)key; /* UNUSED */ - return 1; -} +/* + * Common code for streaming or seeking modes. + * + * Includes code to read local file headers, decompress data + * from entry bodies, and common API. + */ -static int -rsrc_cmp_node(const struct archive_rb_node *n1, - const struct archive_rb_node *n2) +static unsigned long +real_crc32(unsigned long crc, const void *buff, size_t len) { - const struct zip_entry *e1 = (const struct zip_entry *)n1; - const struct zip_entry *e2 = (const struct zip_entry *)n2; - - return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); + return crc32(crc, buff, (unsigned int)len); } -static int -rsrc_cmp_key(const struct archive_rb_node *n, const void *key) +/* Used by "ignorecrc32" option to speed up tests. */ +static unsigned long +fake_crc32(unsigned long crc, const void *buff, size_t len) { - const struct zip_entry *e = (const struct zip_entry *)n; - return (strcmp((const char *)key, e->rsrcname.s)); + (void)crc; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)len; /* UNUSED */ + return 0; } -static const char * -rsrc_basename(const char *name, size_t name_length) -{ - const char *s, *r; - - r = s = name; - for (;;) { - s = memchr(s, '/', name_length - (s - name)); - if (s == NULL) - break; - r = ++s; - } - return (r); -} +static struct { + int id; + const char * name; +} compression_methods[] = { + {0, "uncompressed"}, /* The file is stored (no compression) */ + {1, "shrinking"}, /* The file is Shrunk */ + {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */ + {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */ + {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */ + {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */ + {6, "imploded"}, /* The file is Imploded */ + {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ + {8, "deflation"}, /* The file is Deflated */ + {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */ + {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding + * (old IBM TERSE) */ + {11, "reserved"}, /* Reserved by PKWARE */ + {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ + {13, "reserved"}, /* Reserved by PKWARE */ + {14, "lzma"}, /* LZMA (EFS) */ + {15, "reserved"}, /* Reserved by PKWARE */ + {16, "reserved"}, /* Reserved by PKWARE */ + {17, "reserved"}, /* Reserved by PKWARE */ + {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ + {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ + {97, "wav-pack"}, /* WavPack compressed data */ + {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ + {99, "aes"} /* WinZip AES encryption */ +}; -static void -expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) +static const char * +compression_name(const int compression) { - struct archive_string str; - struct zip_entry *dir; - char *s; - - archive_string_init(&str); - archive_strncpy(&str, name, name_length); - for (;;) { - s = strrchr(str.s, '/'); - if (s == NULL) - break; - *s = '\0'; - /* Transfer the parent directory from zip->tree_rsrc RB - * tree to zip->tree RB tree to expose. */ - dir = (struct zip_entry *) - __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); - if (dir == NULL) - break; - __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); - archive_string_free(&dir->rsrcname); - __archive_rb_tree_insert_node(&zip->tree, &dir->node); + static const int num_compression_methods = + sizeof(compression_methods)/sizeof(compression_methods[0]); + int i=0; + + while(compression >= 0 && i < num_compression_methods) { + if (compression_methods[i].id == compression) + return compression_methods[i].name; + i++; } - archive_string_free(&str); + return "??"; } -static int -slurp_central_directory(struct archive_read *a, struct zip *zip) +/* Convert an MSDOS-style date/time into Unix-style time. */ +static time_t +zip_time(const char *p) { - unsigned i; - int64_t correction; - static const struct archive_rb_tree_ops rb_ops = { - &cmp_node, &cmp_key - }; - static const struct archive_rb_tree_ops rb_rsrc_ops = { - &rsrc_cmp_node, &rsrc_cmp_key - }; - - /* - * Consider the archive file we are reading may be SFX. - * So we have to calculate a SFX header size to revise - * ZIP header offsets. - */ - correction = zip->end_of_central_directory_offset - - (zip->central_directory_offset + zip->central_directory_size); - /* The central directory offset is relative value, and so - * we revise this offset for SFX. */ - zip->central_directory_offset += correction; - - __archive_read_seek(a, zip->central_directory_offset, SEEK_SET); - zip->offset = zip->central_directory_offset; - __archive_rb_tree_init(&zip->tree, &rb_ops); - __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); - - zip->zip_entries = calloc(zip->central_directory_entries, - sizeof(struct zip_entry)); - for (i = 0; i < zip->central_directory_entries; ++i) { - struct zip_entry *zip_entry = &zip->zip_entries[i]; - size_t filename_length, extra_length, comment_length; - uint32_t external_attributes; - const char *name, *p, *r; - - if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) - return ARCHIVE_FATAL; - if (memcmp(p, "PK\001\002", 4) != 0) { - archive_set_error(&a->archive, - -1, "Invalid central directory signature"); - return ARCHIVE_FATAL; - } - zip->have_central_directory = 1; - /* version = p[4]; */ - zip_entry->system = p[5]; - /* version_required = archive_le16dec(p + 6); */ - zip_entry->flags = archive_le16dec(p + 8); - zip_entry->compression = (char)archive_le16dec(p + 10); - zip_entry->mtime = zip_time(p + 12); - zip_entry->crc32 = archive_le32dec(p + 16); - zip_entry->compressed_size = archive_le32dec(p + 20); - zip_entry->uncompressed_size = archive_le32dec(p + 24); - filename_length = archive_le16dec(p + 28); - extra_length = archive_le16dec(p + 30); - comment_length = archive_le16dec(p + 32); - /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ - /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ - external_attributes = archive_le32dec(p + 38); - zip_entry->local_header_offset = - archive_le32dec(p + 42) + correction; - - /* If we can't guess the mode, leave it zero here; - when we read the local file header we might get - more information. */ - zip_entry->mode = 0; - if (zip_entry->system == 3) { - zip_entry->mode = external_attributes >> 16; - } - - /* - * Mac resource fork files are stored under the - * "__MACOSX/" directory, so we should check if - * it is. - */ - /* Make sure we have the file name. */ - if ((p = __archive_read_ahead(a, 46 + filename_length, NULL)) - == NULL) - return ARCHIVE_FATAL; - name = p + 46; - r = rsrc_basename(name, filename_length); - if (filename_length >= 9 && - strncmp("__MACOSX/", name, 9) == 0) { - /* If this file is not a resource fork nor - * a directory. We should treat it as a non - * resource fork file to expose it. */ - if (name[filename_length-1] != '/' && - (r - name < 3 || r[0] != '.' || r[1] != '_')) { - __archive_rb_tree_insert_node(&zip->tree, - &zip_entry->node); - /* Expose its parent directories. */ - expose_parent_dirs(zip, name, filename_length); - } else { - /* This file is a resource fork file or - * a directory. */ - archive_strncpy(&(zip_entry->rsrcname), name, - filename_length); - __archive_rb_tree_insert_node(&zip->tree_rsrc, - &zip_entry->node); - } - } else { - /* Generate resource fork name to find its resource - * file at zip->tree_rsrc. */ - archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); - archive_strncat(&(zip_entry->rsrcname), name, r - name); - archive_strcat(&(zip_entry->rsrcname), "._"); - archive_strncat(&(zip_entry->rsrcname), - name + (r - name), filename_length - (r - name)); - /* Register an entry to RB tree to sort it by - * file offset. */ - __archive_rb_tree_insert_node(&zip->tree, - &zip_entry->node); - } - - /* We don't read the filename until we get to the - local file header. Reading it here would speed up - table-of-contents operations (removing the need to - find and read local file header to get the - filename) at the cost of requiring a lot of extra - space. */ - /* We don't read the extra block here. We assume it - will be duplicated at the local file header. */ - __archive_read_consume(a, - 46 + filename_length + extra_length + comment_length); - } - - return ARCHIVE_OK; -} + int msTime, msDate; + struct tm ts; -static int64_t -zip_read_consume(struct archive_read *a, int64_t bytes) -{ - struct zip *zip = (struct zip *)a->format->data; - int64_t skip; + msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); + msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); - skip = __archive_read_consume(a, bytes); - if (skip > 0) - zip->offset += skip; - return (skip); + memset(&ts, 0, sizeof(ts)); + ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ + ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ + ts.tm_mday = msDate & 0x1f; /* Day of month. */ + ts.tm_hour = (msTime >> 11) & 0x1f; + ts.tm_min = (msTime >> 5) & 0x3f; + ts.tm_sec = (msTime << 1) & 0x3e; + ts.tm_isdst = -1; + return mktime(&ts); } -static int -zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, - struct zip_entry *rsrc) +/* + * The extra data is stored as a list of + * id1+size1+data1 + id2+size2+data2 ... + * triplets. id and size are 2 bytes each. + */ +static void +process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) { - struct zip *zip = (struct zip *)a->format->data; - unsigned char *metadata, *mp; - int64_t offset = zip->offset; - size_t remaining_bytes, metadata_bytes; - ssize_t hsize; - int ret = ARCHIVE_OK, eof; - - switch(rsrc->compression) { - case 0: /* No compression. */ -#ifdef HAVE_ZLIB_H - case 8: /* Deflate compression. */ -#endif - break; - default: /* Unsupported compression. */ - /* Return a warning. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported ZIP compression method (%s)", - compression_name(rsrc->compression)); - /* We can't decompress this entry, but we will - * be able to skip() it and try the next entry. */ - return (ARCHIVE_WARN); - } - - if (rsrc->uncompressed_size > (128 * 1024)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Mac metadata is too large: %jd > 128K bytes", - (intmax_t)rsrc->uncompressed_size); - return (ARCHIVE_WARN); - } - - metadata = malloc((size_t)rsrc->uncompressed_size); - if (metadata == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Mac metadata"); - return (ARCHIVE_FATAL); - } - - if (zip->offset < rsrc->local_header_offset) - zip_read_consume(a, rsrc->local_header_offset - zip->offset); - else if (zip->offset != rsrc->local_header_offset) { - __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); - zip->offset = zip->entry->local_header_offset; - } - - hsize = zip_get_local_file_header_size(a, 0); - zip_read_consume(a, hsize); + unsigned offset = 0; - remaining_bytes = (size_t)rsrc->compressed_size; - metadata_bytes = (size_t)rsrc->uncompressed_size; - mp = metadata; - eof = 0; - while (!eof && remaining_bytes) { - const unsigned char *p; - ssize_t bytes_avail; - size_t bytes_used; + while (offset < extra_length - 4) { + unsigned short headerid = archive_le16dec(p + offset); + unsigned short datasize = archive_le16dec(p + offset + 2); - p = __archive_read_ahead(a, 1, &bytes_avail); - if (p == NULL) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP file header"); - ret = ARCHIVE_WARN; - goto exit_mac_metadata; + offset += 4; + if (offset + datasize > extra_length) { + break; } - if ((size_t)bytes_avail > remaining_bytes) - bytes_avail = remaining_bytes; - switch(rsrc->compression) { - case 0: /* No compression. */ - memcpy(mp, p, bytes_avail); - bytes_used = (size_t)bytes_avail; - metadata_bytes -= bytes_used; - mp += bytes_used; - if (metadata_bytes == 0) - eof = 1; +#ifdef DEBUG + fprintf(stderr, "Header id 0x%04x, length %d\n", + headerid, datasize); +#endif + switch (headerid) { + case 0x0001: + /* Zip64 extended information extra field. */ + zip_entry->flags |= LA_USED_ZIP64; + if (zip_entry->uncompressed_size == 0xffffffff) { + if (datasize < 8) + break; + zip_entry->uncompressed_size = + archive_le64dec(p + offset); + offset += 8; + datasize -= 8; + } + if (zip_entry->compressed_size == 0xffffffff) { + if (datasize < 8) + break; + zip_entry->compressed_size = + archive_le64dec(p + offset); + offset += 8; + datasize -= 8; + } + if (zip_entry->local_header_offset == 0xffffffff) { + if (datasize < 8) + break; + zip_entry->local_header_offset = + archive_le64dec(p + offset); + offset += 8; + datasize -= 8; + } + /* archive_le32dec(p + offset) gives disk + * on which file starts, but we don't handle + * multi-volume Zip files. */ break; -#ifdef HAVE_ZLIB_H - case 8: /* Deflate compression. */ +#ifdef DEBUG + case 0x0017: { - int r; - - ret = zip_deflate_init(a, zip); - if (ret != ARCHIVE_OK) - goto exit_mac_metadata; - zip->stream.next_in = - (Bytef *)(uintptr_t)(const void *)p; - zip->stream.avail_in = (uInt)bytes_avail; - zip->stream.total_in = 0; - zip->stream.next_out = mp; - zip->stream.avail_out = (uInt)metadata_bytes; - zip->stream.total_out = 0; - - r = inflate(&zip->stream, 0); - switch (r) { - case Z_OK: - break; - case Z_STREAM_END: - eof = 1; - break; - case Z_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Out of memory for ZIP decompression"); - ret = ARCHIVE_FATAL; - goto exit_mac_metadata; - default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "ZIP decompression failed (%d)", r); - ret = ARCHIVE_FATAL; - goto exit_mac_metadata; + /* Strong encryption field. */ + if (archive_le16dec(p + offset) == 2) { + unsigned algId = + archive_le16dec(p + offset + 2); + unsigned bitLen = + archive_le16dec(p + offset + 4); + int flags = + archive_le16dec(p + offset + 6); + fprintf(stderr, "algId=0x%04x, bitLen=%u, " + "flgas=%d\n", algId, bitLen,flags); } - bytes_used = zip->stream.total_in; - metadata_bytes -= zip->stream.total_out; - mp += zip->stream.total_out; break; } #endif - default: - bytes_used = 0; + case 0x5455: + { + /* Extended time field "UT". */ + int flags = p[offset]; + offset++; + datasize--; + /* Flag bits indicate which dates are present. */ + if (flags & 0x01) + { +#ifdef DEBUG + fprintf(stderr, "mtime: %lld -> %d\n", + (long long)zip_entry->mtime, + archive_le32dec(p + offset)); +#endif + if (datasize < 4) + break; + zip_entry->mtime = archive_le32dec(p + offset); + offset += 4; + datasize -= 4; + } + if (flags & 0x02) + { + if (datasize < 4) + break; + zip_entry->atime = archive_le32dec(p + offset); + offset += 4; + datasize -= 4; + } + if (flags & 0x04) + { + if (datasize < 4) + break; + zip_entry->ctime = archive_le32dec(p + offset); + offset += 4; + datasize -= 4; + } break; } - zip_read_consume(a, bytes_used); - remaining_bytes -= bytes_used; - } - archive_entry_copy_mac_metadata(entry, metadata, - (size_t)rsrc->uncompressed_size - metadata_bytes); - - __archive_read_seek(a, offset, SEEK_SET); - zip->offset = offset; -exit_mac_metadata: - zip->decompress_init = 0; - free(metadata); - return (ret); -} - -static int -archive_read_format_zip_seekable_read_header(struct archive_read *a, - struct archive_entry *entry) -{ - struct zip *zip = (struct zip *)a->format->data; - struct zip_entry *rsrc; - int r, ret = ARCHIVE_OK; - - a->archive.archive_format = ARCHIVE_FORMAT_ZIP; - if (a->archive.archive_format_name == NULL) - a->archive.archive_format_name = "ZIP"; - - if (zip->zip_entries == NULL) { - r = slurp_central_directory(a, zip); - zip->entries_remaining = zip->central_directory_entries; - if (r != ARCHIVE_OK) - return r; - /* Get first entry whose local header offset is lower than - * other entries in the archive file. */ - zip->entry = - (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree); - } else if (zip->entry != NULL) { - /* Get next entry in local header offset order. */ - zip->entry = (struct zip_entry *)__archive_rb_tree_iterate( - &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT); - } - - if (zip->entries_remaining <= 0 || zip->entry == NULL) - return ARCHIVE_EOF; - --zip->entries_remaining; - - if (zip->entry->rsrcname.s) - rsrc = (struct zip_entry *)__archive_rb_tree_find_node( - &zip->tree_rsrc, zip->entry->rsrcname.s); - else - rsrc = NULL; - - /* File entries are sorted by the header offset, we should mostly - * use zip_read_consume to advance a read point to avoid redundant - * data reading. */ - if (zip->offset < zip->entry->local_header_offset) - zip_read_consume(a, - zip->entry->local_header_offset - zip->offset); - else if (zip->offset != zip->entry->local_header_offset) { - __archive_read_seek(a, zip->entry->local_header_offset, - SEEK_SET); - zip->offset = zip->entry->local_header_offset; - } - zip->unconsumed = 0; - r = zip_read_local_file_header(a, entry, zip); - if (r != ARCHIVE_OK) - return r; - if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { - const void *p; - struct archive_string_conv *sconv; - size_t linkname_length = (size_t)archive_entry_size(entry); - - archive_entry_set_size(entry, 0); - p = __archive_read_ahead(a, linkname_length, NULL); - if (p == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated Zip file"); - return ARCHIVE_FATAL; - } - - sconv = zip->sconv; - if (sconv == NULL && (zip->entry->flags & ZIP_UTF8_NAME)) - sconv = zip->sconv_utf8; - if (sconv == NULL) - sconv = zip->sconv_default; - if (archive_entry_copy_symlink_l(entry, p, linkname_length, - sconv) != 0) { - if (errno != ENOMEM && sconv == zip->sconv_utf8 && - (zip->entry->flags & ZIP_UTF8_NAME)) - archive_entry_copy_symlink_l(entry, p, - linkname_length, NULL); - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Symlink"); - return (ARCHIVE_FATAL); + case 0x5855: + { + /* Info-ZIP Unix Extra Field (old version) "UX". */ + if (datasize >= 8) { + zip_entry->atime = archive_le32dec(p + offset); + zip_entry->mtime = + archive_le32dec(p + offset + 4); } - /* - * Since there is no character-set regulation for - * symlink name, do not report the conversion error - * in an automatic conversion. - */ - if (sconv != zip->sconv_utf8 || - (zip->entry->flags & ZIP_UTF8_NAME) == 0) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Symlink cannot be converted " - "from %s to current locale.", - archive_string_conversion_charset_name( - sconv)); - ret = ARCHIVE_WARN; + if (datasize >= 12) { + zip_entry->uid = + archive_le16dec(p + offset + 8); + zip_entry->gid = + archive_le16dec(p + offset + 10); } + break; } - } - if (rsrc) { - int ret2 = zip_read_mac_metadata(a, entry, rsrc); - if (ret2 < ret) - ret = ret2; - } - return (ret); -} - -static int -archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) -{ - const char *p; - - (void)best_bid; /* UNUSED */ - - if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) - return (-1); - - /* - * Bid of 30 here is: 16 bits for "PK", - * next 16-bit field has four options (-2 bits). - * 16 + 16-2 = 30. - */ - if (p[0] == 'P' && p[1] == 'K') { - if ((p[2] == '\001' && p[3] == '\002') - || (p[2] == '\003' && p[3] == '\004') - || (p[2] == '\005' && p[3] == '\006') - || (p[2] == '\007' && p[3] == '\010') - || (p[2] == '0' && p[3] == '0')) - return (30); - } - - /* TODO: It's worth looking ahead a little bit for a valid - * PK signature. In particular, that would make it possible - * to read some UUEncoded SFX files or SFX files coming from - * a network socket. */ - - return (0); -} - -static int -archive_read_format_zip_options(struct archive_read *a, - const char *key, const char *val) -{ - struct zip *zip; - int ret = ARCHIVE_FAILED; - - zip = (struct zip *)(a->format->data); - if (strcmp(key, "compat-2x") == 0) { - /* Handle filnames as libarchive 2.x */ - zip->init_default_conversion = (val != NULL) ? 1 : 0; - return (ARCHIVE_OK); - } else if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "zip: hdrcharset option needs a character-set name" - ); - else { - zip->sconv = archive_string_conversion_from_charset( - &a->archive, val, 0); - if (zip->sconv != NULL) { - if (strcmp(val, "UTF-8") == 0) - zip->sconv_utf8 = zip->sconv; - ret = ARCHIVE_OK; - } else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); -} - -static int -archive_read_format_zip_streamable_read_header(struct archive_read *a, - struct archive_entry *entry) -{ - struct zip *zip; + case 0x6c78: + { + /* Experimental 'xl' field */ + /* + * Introduced Dec 2013 to provide a way to + * include external file attributes (and other + * fields that ordinarily appear only in + * central directory) in local file header. + * This provides file type and permission + * information necessary to support full + * streaming extraction. Currently being + * discussed with other Zip developers + * ... subject to change. + * + * Format: + * The field starts with a bitmap that specifies + * which additional fields are included. The + * bitmap is variable length and can be extended in + * the future. + * + * n bytes - feature bitmap: first byte has low-order + * 7 bits. If high-order bit is set, a subsequent + * byte holds the next 7 bits, etc. + * + * if bitmap & 1, 2 byte "version made by" + * if bitmap & 2, 2 byte "internal file attributes" + * if bitmap & 4, 4 byte "external file attributes" + * if bitmap & 8, 2 byte comment length + n byte comment + */ + int bitmap, bitmap_last; - a->archive.archive_format = ARCHIVE_FORMAT_ZIP; - if (a->archive.archive_format_name == NULL) - a->archive.archive_format_name = "ZIP"; + if (datasize < 1) + break; + bitmap_last = bitmap = 0xff & p[offset]; + offset += 1; + datasize -= 1; + + /* We only support first 7 bits of bitmap; skip rest. */ + while ((bitmap_last & 0x80) != 0 + && datasize >= 1) { + bitmap_last = p[offset]; + offset += 1; + datasize -= 1; + } - zip = (struct zip *)(a->format->data); + if (bitmap & 1) { + /* 2 byte "version made by" */ + if (datasize < 2) + break; + zip_entry->system + = archive_le16dec(p + offset) >> 8; + offset += 2; + datasize -= 2; + } + if (bitmap & 2) { + /* 2 byte "internal file attributes" */ + uint32_t internal_attributes; + if (datasize < 2) + break; + internal_attributes + = archive_le16dec(p + offset); + /* Not used by libarchive at present. */ + (void)internal_attributes; /* UNUSED */ + offset += 2; + datasize -= 2; + } + if (bitmap & 4) { + /* 4 byte "external file attributes" */ + uint32_t external_attributes; + if (datasize < 4) + break; + external_attributes + = archive_le32dec(p + offset); + if (zip_entry->system == 3) { + zip_entry->mode + = external_attributes >> 16; + } else if (zip_entry->system == 0) { + // Interpret MSDOS directory bit + if (0x10 == (external_attributes & 0x10)) { + zip_entry->mode = AE_IFDIR | 0775; + } else { + zip_entry->mode = AE_IFREG | 0664; + } + if (0x01 == (external_attributes & 0x01)) { + // Read-only bit; strip write permissions + zip_entry->mode &= 0555; + } + } else { + zip_entry->mode = 0; + } + offset += 4; + datasize -= 4; + } + if (bitmap & 8) { + /* 2 byte comment length + comment */ + uint32_t comment_length; + if (datasize < 2) + break; + comment_length + = archive_le16dec(p + offset); + offset += 2; + datasize -= 2; - /* Make sure we have a zip_entry structure to use. */ - if (zip->zip_entries == NULL) { - zip->zip_entries = malloc(sizeof(struct zip_entry)); - if (zip->zip_entries == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Out of memory"); - return ARCHIVE_FATAL; + if (datasize < comment_length) + break; + /* Comment is not supported by libarchive */ + offset += comment_length; + datasize -= comment_length; + } + break; } - } - zip->entry = zip->zip_entries; - memset(zip->entry, 0, sizeof(struct zip_entry)); - - /* Search ahead for the next local file header. */ - zip_read_consume(a, zip->unconsumed); - zip->unconsumed = 0; - for (;;) { - int64_t skipped = 0; - const char *p, *end; - ssize_t bytes; - - p = __archive_read_ahead(a, 4, &bytes); - if (p == NULL) - return (ARCHIVE_FATAL); - end = p + bytes; - - while (p + 4 <= end) { - if (p[0] == 'P' && p[1] == 'K') { - if (p[2] == '\001' && p[3] == '\002') - /* Beginning of central directory. */ - return (ARCHIVE_EOF); + case 0x7855: + /* Info-ZIP Unix Extra Field (type 2) "Ux". */ +#ifdef DEBUG + fprintf(stderr, "uid %d gid %d\n", + archive_le16dec(p + offset), + archive_le16dec(p + offset + 2)); +#endif + if (datasize >= 2) + zip_entry->uid = archive_le16dec(p + offset); + if (datasize >= 4) + zip_entry->gid = + archive_le16dec(p + offset + 2); + break; + case 0x7875: + { + /* Info-Zip Unix Extra Field (type 3) "ux". */ + int uidsize = 0, gidsize = 0; - if (p[2] == '\003' && p[3] == '\004') { - /* Regular file entry. */ - zip_read_consume(a, skipped); - return zip_read_local_file_header(a, - entry, zip); + /* TODO: support arbitrary uidsize/gidsize. */ + if (datasize >= 1 && p[offset] == 1) {/* version=1 */ + if (datasize >= 4) { + /* get a uid size. */ + uidsize = 0xff & (int)p[offset+1]; + if (uidsize == 2) + zip_entry->uid = + archive_le16dec( + p + offset + 2); + else if (uidsize == 4 && datasize >= 6) + zip_entry->uid = + archive_le32dec( + p + offset + 2); + } + if (datasize >= (2 + uidsize + 3)) { + /* get a gid size. */ + gidsize = 0xff & (int)p[offset+2+uidsize]; + if (gidsize == 2) + zip_entry->gid = + archive_le16dec( + p+offset+2+uidsize+1); + else if (gidsize == 4 && + datasize >= (2 + uidsize + 5)) + zip_entry->gid = + archive_le32dec( + p+offset+2+uidsize+1); } - - if (p[2] == '\005' && p[3] == '\006') - /* End of central directory. */ - return (ARCHIVE_EOF); } - ++p; - ++skipped; + break; } - zip_read_consume(a, skipped); - } -} - -static ssize_t -zip_get_local_file_header_size(struct archive_read *a, size_t extra) -{ - const char *p; - ssize_t filename_length, extra_length; - - if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP file header"); - return (ARCHIVE_WARN); + case 0x9901: + /* WinZIp AES extra data field. */ + if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { + /* Vendor version. */ + zip_entry->aes_extra.vendor = + archive_le16dec(p + offset); + /* AES encryption strength. */ + zip_entry->aes_extra.strength = p[offset + 4]; + /* Actual compression method. */ + zip_entry->aes_extra.compression = + p[offset + 5]; + } + break; + default: + break; + } + offset += datasize; } - p += extra; - - if (memcmp(p, "PK\003\004", 4) != 0) { - archive_set_error(&a->archive, -1, "Damaged Zip archive"); - return ARCHIVE_WARN; +#ifdef DEBUG + if (offset != extra_length) + { + fprintf(stderr, + "Extra data field contents do not match reported size!\n"); } - filename_length = archive_le16dec(p + 26); - extra_length = archive_le16dec(p + 28); - - return (30 + filename_length + extra_length); +#endif } /* @@ -957,16 +730,18 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, size_t len, filename_length, extra_length; struct archive_string_conv *sconv; struct zip_entry *zip_entry = zip->entry; - uint32_t local_crc32; - int64_t compressed_size, uncompressed_size; + struct zip_entry zip_entry_central_dir; int ret = ARCHIVE_OK; char version; + /* Save a copy of the original for consistency checks. */ + zip_entry_central_dir = *zip_entry; + zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; - zip->entry_crc32 = crc32(0, NULL, 0); + zip->entry_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { @@ -987,52 +762,31 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, } version = p[4]; zip_entry->system = p[5]; - zip_entry->flags = archive_le16dec(p + 6); + zip_entry->zip_flags = archive_le16dec(p + 6); + if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { + zip->has_encrypted_entries = 1; + archive_entry_set_is_data_encrypted(entry, 1); + if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED && + zip_entry->zip_flags & ZIP_ENCRYPTED && + zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) { + archive_entry_set_is_metadata_encrypted(entry, 1); + return ARCHIVE_FATAL; + } + } + zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); - local_crc32 = archive_le32dec(p + 14); - compressed_size = archive_le32dec(p + 18); - uncompressed_size = archive_le32dec(p + 22); + zip_entry->crc32 = archive_le32dec(p + 14); + if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + zip_entry->decdat = p[11]; + else + zip_entry->decdat = p[17]; + zip_entry->compressed_size = archive_le32dec(p + 18); + zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); - zip_read_consume(a, 30); - - if (zip->have_central_directory) { - /* If we read the central dir entry, we must have size - * information as well, so ignore the length-at-end flag. */ - zip_entry->flags &= ~ZIP_LENGTH_AT_END; - /* If we have values from both the local file header - and the central directory, warn about mismatches - which might indicate a damaged file. But some - writers always put zero in the local header; don't - bother warning about that. */ - if (local_crc32 != 0 && local_crc32 != zip_entry->crc32) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Inconsistent CRC32 values"); - ret = ARCHIVE_WARN; - } - if (compressed_size != 0 - && compressed_size != zip_entry->compressed_size) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Inconsistent compressed size"); - ret = ARCHIVE_WARN; - } - if (uncompressed_size != 0 - && uncompressed_size != zip_entry->uncompressed_size) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Inconsistent uncompressed size"); - ret = ARCHIVE_WARN; - } - } else { - /* If we don't have the CD info, use whatever we do have. */ - zip_entry->crc32 = local_crc32; - zip_entry->compressed_size = compressed_size; - zip_entry->uncompressed_size = uncompressed_size; - } + __archive_read_consume(a, 30); /* Read the filename. */ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) { @@ -1040,7 +794,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } - if (zip_entry->flags & ZIP_UTF8_NAME) { + if (zip_entry->zip_flags & ZIP_UTF8_NAME) { /* The filename is stored to be UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = @@ -1069,37 +823,119 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } - zip_read_consume(a, filename_length); + __archive_read_consume(a, filename_length); - if (zip_entry->mode == 0) { + /* Read the extra data. */ + if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_FATAL); + } + + process_extra(h, extra_length, zip_entry); + __archive_read_consume(a, extra_length); + + /* Work around a bug in Info-Zip: When reading from a pipe, it + * stats the pipe instead of synthesizing a file entry. */ + if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) { + zip_entry->mode &= ~ AE_IFMT; + zip_entry->mode |= AE_IFREG; + } + + if ((zip_entry->mode & AE_IFMT) == 0) { /* Especially in streaming mode, we can end up - here without having seen any mode information. + here without having seen proper mode information. Guess from the filename. */ wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); if (len > 0 && wp[len - 1] == L'/') - zip_entry->mode = AE_IFDIR | 0777; + zip_entry->mode |= AE_IFDIR; else - zip_entry->mode = AE_IFREG | 0666; + zip_entry->mode |= AE_IFREG; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; if (len > 0 && cp[len - 1] == '/') - zip_entry->mode = AE_IFDIR | 0777; + zip_entry->mode |= AE_IFDIR; else - zip_entry->mode = AE_IFREG | 0666; + zip_entry->mode |= AE_IFREG; + } + if (zip_entry->mode == AE_IFDIR) { + zip_entry->mode |= 0775; + } else if (zip_entry->mode == AE_IFREG) { + zip_entry->mode |= 0664; } } - /* Read the extra data. */ - if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP file header"); - return (ARCHIVE_FATAL); + /* Make sure directories end in '/' */ + if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) { + wp = archive_entry_pathname_w(entry); + if (wp != NULL) { + len = wcslen(wp); + if (len > 0 && wp[len - 1] != L'/') { + struct archive_wstring s; + archive_string_init(&s); + archive_wstrcat(&s, wp); + archive_wstrappend_wchar(&s, L'/'); + archive_entry_copy_pathname_w(entry, s.s); + } + } else { + cp = archive_entry_pathname(entry); + len = (cp != NULL)?strlen(cp):0; + if (len > 0 && cp[len - 1] != '/') { + struct archive_string s; + archive_string_init(&s); + archive_strcat(&s, cp); + archive_strappend_char(&s, '/'); + archive_entry_set_pathname(entry, s.s); + } + } + } + + if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) { + /* If this came from the central dir, it's size info + * is definitive, so ignore the length-at-end flag. */ + zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END; + /* If local header is missing a value, use the one from + the central directory. If both have it, warn about + mismatches. */ + if (zip_entry->crc32 == 0) { + zip_entry->crc32 = zip_entry_central_dir.crc32; + } else if (!zip->ignore_crc32 + && zip_entry->crc32 != zip_entry_central_dir.crc32) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Inconsistent CRC32 values"); + ret = ARCHIVE_WARN; + } + if (zip_entry->compressed_size == 0) { + zip_entry->compressed_size + = zip_entry_central_dir.compressed_size; + } else if (zip_entry->compressed_size + != zip_entry_central_dir.compressed_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Inconsistent compressed size: " + "%jd in central directory, %jd in local header", + (intmax_t)zip_entry_central_dir.compressed_size, + (intmax_t)zip_entry->compressed_size); + ret = ARCHIVE_WARN; + } + if (zip_entry->uncompressed_size == 0) { + zip_entry->uncompressed_size + = zip_entry_central_dir.uncompressed_size; + } else if (zip_entry->uncompressed_size + != zip_entry_central_dir.uncompressed_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Inconsistent uncompressed size: " + "%jd in central directory, %jd in local header", + (intmax_t)zip_entry_central_dir.uncompressed_size, + (intmax_t)zip_entry->uncompressed_size); + ret = ARCHIVE_WARN; + } } - process_extra(h, extra_length, zip_entry); - zip_read_consume(a, extra_length); /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); @@ -1108,155 +944,120 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_entry_set_mtime(entry, zip_entry->mtime, 0); archive_entry_set_ctime(entry, zip_entry->ctime, 0); archive_entry_set_atime(entry, zip_entry->atime, 0); - /* Set the size only if it's meaningful. */ - if (0 == (zip_entry->flags & ZIP_LENGTH_AT_END)) - archive_entry_set_size(entry, zip_entry->uncompressed_size); + if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { + size_t linkname_length; + + if (zip_entry->compressed_size > 64 * 1024) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Zip file with oversized link entry"); + return ARCHIVE_FATAL; + } + + linkname_length = (size_t)zip_entry->compressed_size; + + archive_entry_set_size(entry, 0); + p = __archive_read_ahead(a, linkname_length, NULL); + if (p == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Truncated Zip file"); + return ARCHIVE_FATAL; + } + + sconv = zip->sconv; + if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME)) + sconv = zip->sconv_utf8; + if (sconv == NULL) + sconv = zip->sconv_default; + if (archive_entry_copy_symlink_l(entry, p, linkname_length, + sconv) != 0) { + if (errno != ENOMEM && sconv == zip->sconv_utf8 && + (zip->entry->zip_flags & ZIP_UTF8_NAME)) + archive_entry_copy_symlink_l(entry, p, + linkname_length, NULL); + if (errno == ENOMEM) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Symlink"); + return (ARCHIVE_FATAL); + } + /* + * Since there is no character-set regulation for + * symlink name, do not report the conversion error + * in an automatic conversion. + */ + if (sconv != zip->sconv_utf8 || + (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Symlink cannot be converted " + "from %s to current locale.", + archive_string_conversion_charset_name( + sconv)); + ret = ARCHIVE_WARN; + } + } + zip_entry->uncompressed_size = zip_entry->compressed_size = 0; + + if (__archive_read_consume(a, linkname_length) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Read error skipping symlink target name"); + return ARCHIVE_FATAL; + } + } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + || zip_entry->uncompressed_size > 0) { + /* Set the size only if it's meaningful. */ + archive_entry_set_size(entry, zip_entry->uncompressed_size); + } zip->entry_bytes_remaining = zip_entry->compressed_size; /* If there's no body, force read_data() to return EOF immediately. */ - if (0 == (zip_entry->flags & ZIP_LENGTH_AT_END) + if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ - sprintf(zip->format_name, "ZIP %d.%d (%s)", + archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); - a->archive.archive_format_name = zip->format_name; + a->archive.archive_format_name = zip->format_name.s; return (ret); } -static const char * -compression_name(int compression) -{ - static const char *compression_names[] = { - "uncompressed", - "shrinking", - "reduced-1", - "reduced-2", - "reduced-3", - "reduced-4", - "imploded", - "reserved", - "deflation" - }; - - if (0 <= compression && compression < - (int)(sizeof(compression_names)/sizeof(compression_names[0]))) - return compression_names[compression]; - else - return "??"; -} - -/* Convert an MSDOS-style date/time into Unix-style time. */ -static time_t -zip_time(const char *p) -{ - int msTime, msDate; - struct tm ts; - - msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); - msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); - - memset(&ts, 0, sizeof(ts)); - ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ - ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ - ts.tm_mday = msDate & 0x1f; /* Day of month. */ - ts.tm_hour = (msTime >> 11) & 0x1f; - ts.tm_min = (msTime >> 5) & 0x3f; - ts.tm_sec = (msTime << 1) & 0x3e; - ts.tm_isdst = -1; - return mktime(&ts); -} - static int -archive_read_format_zip_read_data(struct archive_read *a, - const void **buff, size_t *size, int64_t *offset) +check_authentication_code(struct archive_read *a, const void *_p) { - int r; struct zip *zip = (struct zip *)(a->format->data); - *offset = zip->entry_uncompressed_bytes_read; - *size = 0; - *buff = NULL; - - /* If we hit end-of-entry last time, return ARCHIVE_EOF. */ - if (zip->end_of_entry) - return (ARCHIVE_EOF); - - /* Return EOF immediately if this is a non-regular file. */ - if (AE_IFREG != (zip->entry->mode & AE_IFMT)) - return (ARCHIVE_EOF); - - if (zip->entry->flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Encrypted file is unsupported"); - return (ARCHIVE_FAILED); - } - - zip_read_consume(a, zip->unconsumed); - zip->unconsumed = 0; - - switch(zip->entry->compression) { - case 0: /* No compression. */ - r = zip_read_data_none(a, buff, size, offset); - break; -#ifdef HAVE_ZLIB_H - case 8: /* Deflate compression. */ - r = zip_read_data_deflate(a, buff, size, offset); - break; -#endif - default: /* Unsupported compression. */ - /* Return a warning. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported ZIP compression method (%s)", - compression_name(zip->entry->compression)); - /* We can't decompress this entry, but we will - * be able to skip() it and try the next entry. */ - return (ARCHIVE_FAILED); - break; - } - if (r != ARCHIVE_OK) - return (r); - /* Update checksum */ - if (*size) - zip->entry_crc32 = crc32(zip->entry_crc32, *buff, - (unsigned)*size); - /* If we hit the end, swallow any end-of-data marker. */ - if (zip->end_of_entry) { - /* Check file size, CRC against these values. */ - if (zip->entry->compressed_size != - zip->entry_compressed_bytes_read) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP compressed data is wrong size " - "(read %jd, expected %jd)", - (intmax_t)zip->entry_compressed_bytes_read, - (intmax_t)zip->entry->compressed_size); - return (ARCHIVE_WARN); - } - /* Size field only stores the lower 32 bits of the actual - * size. */ - if ((zip->entry->uncompressed_size & UINT32_MAX) - != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP uncompressed data is wrong size " - "(read %jd, expected %jd)", - (intmax_t)zip->entry_uncompressed_bytes_read, - (intmax_t)zip->entry->uncompressed_size); - return (ARCHIVE_WARN); + /* Check authentication code. */ + if (zip->hctx_valid) { + const void *p; + uint8_t hmac[20]; + size_t hmac_len = 20; + int cmp; + + archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); + if (_p == NULL) { + /* Read authentication code. */ + p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + } else { + p = _p; } - /* Check computed CRC against header */ - if (zip->entry->crc32 != zip->entry_crc32) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "ZIP bad CRC: 0x%lx should be 0x%lx", - (unsigned long)zip->entry_crc32, - (unsigned long)zip->entry->crc32); + cmp = memcmp(hmac, p, AUTH_CODE_SIZE); + __archive_read_consume(a, AUTH_CODE_SIZE); + if (cmp != 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "ZIP bad Authentication code"); return (ARCHIVE_WARN); } } - return (ARCHIVE_OK); } @@ -1276,9 +1077,10 @@ archive_read_format_zip_read_data(struct archive_read *a, * TODO: Technically, the PK\007\010 signature is optional. * In the original spec, the data descriptor contained CRC * and size fields but had no leading signature. In practice, - * newer writers seem to provide the signature pretty consistently, - * but we might need to do something more complex here if - * we want to handle older archives that lack that signature. + * newer writers seem to provide the signature pretty consistently. + * + * For uncompressed data, the PK\007\010 marker seems essential + * to be sure we've actually seen the end of the entry. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * zip->end_of_entry if it consumes all of the data. @@ -1290,40 +1092,62 @@ zip_read_data_none(struct archive_read *a, const void **_buff, struct zip *zip; const char *buff; ssize_t bytes_avail; + int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); - if (zip->entry->flags & ZIP_LENGTH_AT_END) { + if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; + ssize_t grabbing_bytes = 24; - /* Grab at least 16 bytes. */ - buff = __archive_read_ahead(a, 16, &bytes_avail); - if (bytes_avail < 16) { + if (zip->hctx_valid) + grabbing_bytes += AUTH_CODE_SIZE; + /* Grab at least 24 bytes. */ + buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); + if (bytes_avail < grabbing_bytes) { /* Zip archives have end-of-archive markers that are longer than this, so a failure to get at - least 16 bytes really does indicate a truncated + least 24 bytes really does indicate a truncated file. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } - /* Check for a complete PK\007\010 signature. */ + /* Check for a complete PK\007\010 signature, followed + * by the correct 4-byte CRC. */ p = buff; - if (p[0] == 'P' && p[1] == 'K' + if (zip->hctx_valid) + p += AUTH_CODE_SIZE; + if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' - && archive_le32dec(p + 4) == zip->entry_crc32 - && archive_le32dec(p + 8) == - zip->entry_compressed_bytes_read - && archive_le32dec(p + 12) == - zip->entry_uncompressed_bytes_read) { - zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = archive_le32dec(p + 8); - zip->entry->uncompressed_size = archive_le32dec(p + 12); + && (archive_le32dec(p + 4) == zip->entry_crc32 + || zip->ignore_crc32 + || (zip->hctx_valid + && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { + if (zip->entry->flags & LA_USED_ZIP64) { + zip->entry->crc32 = archive_le32dec(p + 4); + zip->entry->compressed_size = + archive_le64dec(p + 8); + zip->entry->uncompressed_size = + archive_le64dec(p + 16); + zip->unconsumed = 24; + } else { + zip->entry->crc32 = archive_le32dec(p + 4); + zip->entry->compressed_size = + archive_le32dec(p + 8); + zip->entry->uncompressed_size = + archive_le32dec(p + 12); + zip->unconsumed = 16; + } + if (zip->hctx_valid) { + r = check_authentication_code(a, buff); + if (r != ARCHIVE_OK) + return (r); + } zip->end_of_entry = 1; - zip->unconsumed = 16; return (ARCHIVE_OK); } /* If not at EOF, ensure we consume at least one byte. */ @@ -1339,6 +1163,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { + if (zip->hctx_valid) + p -= AUTH_CODE_SIZE; break; } else { p += 4; } } @@ -1346,6 +1172,11 @@ zip_read_data_none(struct archive_read *a, const void **_buff, } else { if (zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; + if (zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } return (ARCHIVE_OK); } /* Grab a bunch of bytes. */ @@ -1359,6 +1190,26 @@ zip_read_data_none(struct archive_read *a, const void **_buff, if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = (ssize_t)zip->entry_bytes_remaining; } + if (zip->tctx_valid || zip->cctx_valid) { + size_t dec_size = bytes_avail; + + if (dec_size > zip->decrypted_buffer_size) + dec_size = zip->decrypted_buffer_size; + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + (const uint8_t *)buff, dec_size, + zip->decrypted_buffer, dec_size); + } else { + size_t dsize = dec_size; + archive_hmac_sha1_update(&zip->hctx, + (const uint8_t *)buff, dec_size); + archive_decrypto_aes_ctr_update(&zip->cctx, + (const uint8_t *)buff, dec_size, + zip->decrypted_buffer, &dsize); + } + bytes_avail = dec_size; + buff = (const char *)zip->decrypted_buffer; + } *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; @@ -1400,7 +1251,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, { struct zip *zip; ssize_t bytes_avail; - const void *compressed_buff; + const void *compressed_buff, *sp; int r; (void)offset; /* UNUSED */ @@ -1429,8 +1280,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ - compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); - if (0 == (zip->entry->flags & ZIP_LENGTH_AT_END) + compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; } @@ -1440,6 +1291,51 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } + if (zip->tctx_valid || zip->cctx_valid) { + if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { + size_t buff_remaining = zip->decrypted_buffer_size + - (zip->decrypted_ptr - zip->decrypted_buffer); + + if (buff_remaining > (size_t)bytes_avail) + buff_remaining = (size_t)bytes_avail; + + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && + zip->entry_bytes_remaining > 0) { + if ((int64_t)(zip->decrypted_bytes_remaining + + buff_remaining) + > zip->entry_bytes_remaining) { + if (zip->entry_bytes_remaining < + (int64_t)zip->decrypted_bytes_remaining) + buff_remaining = 0; + else + buff_remaining = + (size_t)zip->entry_bytes_remaining + - zip->decrypted_bytes_remaining; + } + } + if (buff_remaining > 0) { + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + compressed_buff, buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + buff_remaining); + } else { + size_t dsize = buff_remaining; + archive_decrypto_aes_ctr_update( + &zip->cctx, + compressed_buff, buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + &dsize); + } + zip->decrypted_bytes_remaining += buff_remaining; + } + } + bytes_avail = zip->decrypted_bytes_remaining; + compressed_buff = (const char *)zip->decrypted_ptr; + } + /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the @@ -1472,7 +1368,17 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; - zip_read_consume(a, bytes_avail); + if (zip->tctx_valid || zip->cctx_valid) { + zip->decrypted_bytes_remaining -= bytes_avail; + if (zip->decrypted_bytes_remaining == 0) + zip->decrypted_ptr = zip->decrypted_buffer; + else + zip->decrypted_ptr += bytes_avail; + } + /* Calculate compressed data as much as we used.*/ + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); + __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; @@ -1480,10 +1386,16 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip->entry_uncompressed_bytes_read += zip->stream.total_out; *buff = zip->uncompressed_buffer; - if (zip->end_of_entry && (zip->entry->flags & ZIP_LENGTH_AT_END)) { + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } + + if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { const char *p; - if (NULL == (p = __archive_read_ahead(a, 16, NULL))) { + if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP end-of-file record"); @@ -1492,10 +1404,19 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, /* Consume the optional PK\007\010 marker. */ if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010') { - zip->entry->crc32 = archive_le32dec(p + 4); - zip->entry->compressed_size = archive_le32dec(p + 8); - zip->entry->uncompressed_size = archive_le32dec(p + 12); - zip->unconsumed = 16; + p += 4; + zip->unconsumed = 4; + } + if (zip->entry->flags & LA_USED_ZIP64) { + zip->entry->crc32 = archive_le32dec(p); + zip->entry->compressed_size = archive_le64dec(p + 4); + zip->entry->uncompressed_size = archive_le64dec(p + 12); + zip->unconsumed += 20; + } else { + zip->entry->crc32 = archive_le32dec(p); + zip->entry->compressed_size = archive_le32dec(p + 4); + zip->entry->uncompressed_size = archive_le32dec(p + 8); + zip->unconsumed += 12; } } @@ -1504,27 +1425,780 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, #endif static int -archive_read_format_zip_read_data_skip(struct archive_read *a) +read_decryption_header(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const char *p; + unsigned int remaining_size; + unsigned int ts; + + /* + * Read an initialization vector data field. + */ + p = __archive_read_ahead(a, 2, NULL); + if (p == NULL) + goto truncated; + ts = zip->iv_size; + zip->iv_size = archive_le16dec(p); + __archive_read_consume(a, 2); + if (ts < zip->iv_size) { + free(zip->iv); + zip->iv = NULL; + } + p = __archive_read_ahead(a, zip->iv_size, NULL); + if (p == NULL) + goto truncated; + if (zip->iv == NULL) { + zip->iv = malloc(zip->iv_size); + if (zip->iv == NULL) + goto nomem; + } + memcpy(zip->iv, p, zip->iv_size); + __archive_read_consume(a, zip->iv_size); + + /* + * Read a size of remaining decryption header field. + */ + p = __archive_read_ahead(a, 14, NULL); + if (p == NULL) + goto truncated; + remaining_size = archive_le32dec(p); + if (remaining_size < 16 || remaining_size > (1 << 18)) + goto corrupted; + + /* Check if format version is supported. */ + if (archive_le16dec(p+4) != 3) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported encryption format version: %u", + archive_le16dec(p+4)); + return (ARCHIVE_FAILED); + } + + /* + * Read an encryption algorithm field. + */ + zip->alg_id = archive_le16dec(p+6); + switch (zip->alg_id) { + case 0x6601:/* DES */ + case 0x6602:/* RC2 */ + case 0x6603:/* 3DES 168 */ + case 0x6609:/* 3DES 112 */ + case 0x660E:/* AES 128 */ + case 0x660F:/* AES 192 */ + case 0x6610:/* AES 256 */ + case 0x6702:/* RC2 (version >= 5.2) */ + case 0x6720:/* Blowfish */ + case 0x6721:/* Twofish */ + case 0x6801:/* RC4 */ + /* Suuported encryption algorithm. */ + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption algorithm: %u", zip->alg_id); + return (ARCHIVE_FAILED); + } + + /* + * Read a bit length field. + */ + zip->bit_len = archive_le16dec(p+8); + + /* + * Read a flags field. + */ + zip->flags = archive_le16dec(p+10); + switch (zip->flags & 0xf000) { + case 0x0001: /* Password is required to decrypt. */ + case 0x0002: /* Certificates only. */ + case 0x0003: /* Password or certificate required to decrypt. */ + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption flag: %u", zip->flags); + return (ARCHIVE_FAILED); + } + if ((zip->flags & 0xf000) == 0 || + (zip->flags & 0xf000) == 0x4000) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unknown encryption flag: %u", zip->flags); + return (ARCHIVE_FAILED); + } + + /* + * Read an encrypted random data field. + */ + ts = zip->erd_size; + zip->erd_size = archive_le16dec(p+12); + __archive_read_consume(a, 14); + if ((zip->erd_size & 0xf) != 0 || + (zip->erd_size + 16) > remaining_size || + (zip->erd_size + 16) < zip->erd_size) + goto corrupted; + + if (ts < zip->erd_size) { + free(zip->erd); + zip->erd = NULL; + } + p = __archive_read_ahead(a, zip->erd_size, NULL); + if (p == NULL) + goto truncated; + if (zip->erd == NULL) { + zip->erd = malloc(zip->erd_size); + if (zip->erd == NULL) + goto nomem; + } + memcpy(zip->erd, p, zip->erd_size); + __archive_read_consume(a, zip->erd_size); + + /* + * Read a reserved data field. + */ + p = __archive_read_ahead(a, 4, NULL); + if (p == NULL) + goto truncated; + /* Reserved data size should be zero. */ + if (archive_le32dec(p) != 0) + goto corrupted; + __archive_read_consume(a, 4); + + /* + * Read a password validation data field. + */ + p = __archive_read_ahead(a, 2, NULL); + if (p == NULL) + goto truncated; + ts = zip->v_size; + zip->v_size = archive_le16dec(p); + __archive_read_consume(a, 2); + if ((zip->v_size & 0x0f) != 0 || + (zip->erd_size + zip->v_size + 16) > remaining_size || + (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size)) + goto corrupted; + if (ts < zip->v_size) { + free(zip->v_data); + zip->v_data = NULL; + } + p = __archive_read_ahead(a, zip->v_size, NULL); + if (p == NULL) + goto truncated; + if (zip->v_data == NULL) { + zip->v_data = malloc(zip->v_size); + if (zip->v_data == NULL) + goto nomem; + } + memcpy(zip->v_data, p, zip->v_size); + __archive_read_consume(a, zip->v_size); + + p = __archive_read_ahead(a, 4, NULL); + if (p == NULL) + goto truncated; + zip->v_crc32 = archive_le32dec(p); + __archive_read_consume(a, 4); + + /*return (ARCHIVE_OK); + * This is not fully implemnted yet.*/ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Encrypted file is unsupported"); + return (ARCHIVE_FAILED); +truncated: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); +corrupted: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Corrupted ZIP file data"); + return (ARCHIVE_FATAL); +nomem: + archive_set_error(&a->archive, ENOMEM, + "No memory for ZIP decryption"); + return (ARCHIVE_FATAL); +} + +static int +zip_alloc_decryption_buffer(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + size_t bs = 256 * 1024; + + if (zip->decrypted_buffer == NULL) { + zip->decrypted_buffer_size = bs; + zip->decrypted_buffer = malloc(bs); + if (zip->decrypted_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for ZIP decryption"); + return (ARCHIVE_FATAL); + } + } + zip->decrypted_ptr = zip->decrypted_buffer; + return (ARCHIVE_OK); +} + +static int +init_traditional_PKWARE_decryption(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const void *p; + int retry; + int r; + + if (zip->tctx_valid) + return (ARCHIVE_OK); + + /* + Read the 12 bytes encryption header stored at + the start of the data area. + */ +#define ENC_HEADER_SIZE 12 + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + && zip->entry_bytes_remaining < ENC_HEADER_SIZE) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated Zip encrypted body: only %jd bytes available", + (intmax_t)zip->entry_bytes_remaining); + return (ARCHIVE_FATAL); + } + + p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL); + if (p == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + + for (retry = 0;; retry++) { + const char *passphrase; + uint8_t crcchk; + + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + + /* + * Initialize ctx for Traditional PKWARE Decyption. + */ + r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), + p, ENC_HEADER_SIZE, &crcchk); + if (r == 0 && crcchk == zip->entry->decdat) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } + } + + __archive_read_consume(a, ENC_HEADER_SIZE); + zip->tctx_valid = 1; + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + zip->entry_bytes_remaining -= ENC_HEADER_SIZE; + } + /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/ + zip->entry_compressed_bytes_read += ENC_HEADER_SIZE; + zip->decrypted_bytes_remaining = 0; + + return (zip_alloc_decryption_buffer(a)); +#undef ENC_HEADER_SIZE +} + +static int +init_WinZip_AES_decryption(struct archive_read *a) +{ + struct zip *zip = (struct zip *)(a->format->data); + const void *p; + const uint8_t *pv; + size_t key_len, salt_len; + uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; + int retry; + int r; + + if (zip->cctx_valid || zip->hctx_valid) + return (ARCHIVE_OK); + + switch (zip->entry->aes_extra.strength) { + case 1: salt_len = 8; key_len = 16; break; + case 2: salt_len = 12; key_len = 24; break; + case 3: salt_len = 16; key_len = 32; break; + default: goto corrupted; + } + p = __archive_read_ahead(a, salt_len + 2, NULL); + if (p == NULL) + goto truncated; + + for (retry = 0;; retry++) { + const char *passphrase; + + passphrase = __archive_read_next_passphrase(a); + if (passphrase == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + (retry > 0)? + "Incorrect passphrase": + "Passphrase required for this entry"); + return (ARCHIVE_FAILED); + } + memset(derived_key, 0, sizeof(derived_key)); + r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), + p, salt_len, 1000, derived_key, key_len * 2 + 2); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Decryption is unsupported due to lack of " + "crypto library"); + return (ARCHIVE_FAILED); + } + + /* Check password verification value. */ + pv = ((const uint8_t *)p) + salt_len; + if (derived_key[key_len * 2] == pv[0] && + derived_key[key_len * 2 + 1] == pv[1]) + break;/* The passphrase is OK. */ + if (retry > 10000) { + /* Avoid infinity loop. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Too many incorrect passphrases"); + return (ARCHIVE_FAILED); + } + } + + r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); + if (r != 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Decryption is unsupported due to lack of crypto library"); + return (ARCHIVE_FAILED); + } + r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); + if (r != 0) { + archive_decrypto_aes_ctr_release(&zip->cctx); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to initialize HMAC-SHA1"); + return (ARCHIVE_FAILED); + } + zip->cctx_valid = zip->hctx_valid = 1; + __archive_read_consume(a, salt_len + 2); + zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE; + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + && zip->entry_bytes_remaining < 0) + goto corrupted; + zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE; + zip->decrypted_bytes_remaining = 0; + + zip->entry->compression = zip->entry->aes_extra.compression; + return (zip_alloc_decryption_buffer(a)); + +truncated: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); +corrupted: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Corrupted ZIP file data"); + return (ARCHIVE_FATAL); +} + +static int +archive_read_format_zip_read_data(struct archive_read *a, + const void **buff, size_t *size, int64_t *offset) +{ + int r; + struct zip *zip = (struct zip *)(a->format->data); + + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + zip->has_encrypted_entries = 0; + } + + *offset = zip->entry_uncompressed_bytes_read; + *size = 0; + *buff = NULL; + + /* If we hit end-of-entry last time, return ARCHIVE_EOF. */ + if (zip->end_of_entry) + return (ARCHIVE_EOF); + + /* Return EOF immediately if this is a non-regular file. */ + if (AE_IFREG != (zip->entry->mode & AE_IFMT)) + return (ARCHIVE_EOF); + + __archive_read_consume(a, zip->unconsumed); + zip->unconsumed = 0; + + if (zip->init_decryption) { + zip->has_encrypted_entries = 1; + if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) + r = read_decryption_header(a); + else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) + r = init_WinZip_AES_decryption(a); + else + r = init_traditional_PKWARE_decryption(a); + if (r != ARCHIVE_OK) + return (r); + zip->init_decryption = 0; + } + + switch(zip->entry->compression) { + case 0: /* No compression. */ + r = zip_read_data_none(a, buff, size, offset); + break; +#ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ + r = zip_read_data_deflate(a, buff, size, offset); + break; +#endif + default: /* Unsupported compression. */ + /* Return a warning. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported ZIP compression method (%s)", + compression_name(zip->entry->compression)); + /* We can't decompress this entry, but we will + * be able to skip() it and try the next entry. */ + return (ARCHIVE_FAILED); + break; + } + if (r != ARCHIVE_OK) + return (r); + /* Update checksum */ + if (*size) + zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff, + (unsigned)*size); + /* If we hit the end, swallow any end-of-data marker. */ + if (zip->end_of_entry) { + /* Check file size, CRC against these values. */ + if (zip->entry->compressed_size != + zip->entry_compressed_bytes_read) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP compressed data is wrong size " + "(read %jd, expected %jd)", + (intmax_t)zip->entry_compressed_bytes_read, + (intmax_t)zip->entry->compressed_size); + return (ARCHIVE_WARN); + } + /* Size field only stores the lower 32 bits of the actual + * size. */ + if ((zip->entry->uncompressed_size & UINT32_MAX) + != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP uncompressed data is wrong size " + "(read %jd, expected %jd)\n", + (intmax_t)zip->entry_uncompressed_bytes_read, + (intmax_t)zip->entry->uncompressed_size); + return (ARCHIVE_WARN); + } + /* Check computed CRC against header */ + if ((!zip->hctx_valid || + zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && + zip->entry->crc32 != zip->entry_crc32 + && !zip->ignore_crc32) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP bad CRC: 0x%lx should be 0x%lx", + (unsigned long)zip->entry_crc32, + (unsigned long)zip->entry->crc32); + return (ARCHIVE_WARN); + } + } + + return (ARCHIVE_OK); +} + +static int +archive_read_format_zip_cleanup(struct archive_read *a) +{ + struct zip *zip; + struct zip_entry *zip_entry, *next_zip_entry; + + zip = (struct zip *)(a->format->data); +#ifdef HAVE_ZLIB_H + if (zip->stream_valid) + inflateEnd(&zip->stream); + free(zip->uncompressed_buffer); +#endif + if (zip->zip_entries) { + zip_entry = zip->zip_entries; + while (zip_entry != NULL) { + next_zip_entry = zip_entry->next; + archive_string_free(&zip_entry->rsrcname); + free(zip_entry); + zip_entry = next_zip_entry; + } + } + free(zip->decrypted_buffer); + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + free(zip->iv); + free(zip->erd); + free(zip->v_data); + archive_string_free(&zip->format_name); + free(zip); + (a->format->data) = NULL; + return (ARCHIVE_OK); +} + +static int +archive_read_format_zip_has_encrypted_entries(struct archive_read *_a) +{ + if (_a && _a->format) { + struct zip * zip = (struct zip *)_a->format->data; + if (zip) { + return zip->has_encrypted_entries; + } + } + return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; +} + +static int +archive_read_format_zip_options(struct archive_read *a, + const char *key, const char *val) +{ + struct zip *zip; + int ret = ARCHIVE_FAILED; + + zip = (struct zip *)(a->format->data); + if (strcmp(key, "compat-2x") == 0) { + /* Handle filenames as libarchive 2.x */ + zip->init_default_conversion = (val != NULL) ? 1 : 0; + return (ARCHIVE_OK); + } else if (strcmp(key, "hdrcharset") == 0) { + if (val == NULL || val[0] == 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "zip: hdrcharset option needs a character-set name" + ); + else { + zip->sconv = archive_string_conversion_from_charset( + &a->archive, val, 0); + if (zip->sconv != NULL) { + if (strcmp(val, "UTF-8") == 0) + zip->sconv_utf8 = zip->sconv; + ret = ARCHIVE_OK; + } else + ret = ARCHIVE_FATAL; + } + return (ret); + } else if (strcmp(key, "ignorecrc32") == 0) { + /* Mostly useful for testing. */ + if (val == NULL || val[0] == 0) { + zip->crc32func = real_crc32; + zip->ignore_crc32 = 0; + } else { + zip->crc32func = fake_crc32; + zip->ignore_crc32 = 1; + } + return (ARCHIVE_OK); + } else if (strcmp(key, "mac-ext") == 0) { + zip->process_mac_extensions = (val != NULL && val[0] != 0); + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + +int +archive_read_support_format_zip(struct archive *a) +{ + int r; + r = archive_read_support_format_zip_streamable(a); + if (r != ARCHIVE_OK) + return r; + return (archive_read_support_format_zip_seekable(a)); +} + +/* ------------------------------------------------------------------------ */ + +/* + * Streaming-mode support + */ + + +static int +archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); +} + +static int +archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) +{ + const char *p; + + (void)best_bid; /* UNUSED */ + + if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) + return (-1); + + /* + * Bid of 29 here comes from: + * + 16 bits for "PK", + * + next 16-bit field has 6 options so contributes + * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits + * + * So we've effectively verified ~29 total bits of check data. + */ + if (p[0] == 'P' && p[1] == 'K') { + if ((p[2] == '\001' && p[3] == '\002') + || (p[2] == '\003' && p[3] == '\004') + || (p[2] == '\005' && p[3] == '\006') + || (p[2] == '\006' && p[3] == '\006') + || (p[2] == '\007' && p[3] == '\010') + || (p[2] == '0' && p[3] == '0')) + return (29); + } + + /* TODO: It's worth looking ahead a little bit for a valid + * PK signature. In particular, that would make it possible + * to read some UUEncoded SFX files or SFX files coming from + * a network socket. */ + + return (0); +} + +static int +archive_read_format_zip_streamable_read_header(struct archive_read *a, + struct archive_entry *entry) { struct zip *zip; + a->archive.archive_format = ARCHIVE_FORMAT_ZIP; + if (a->archive.archive_format_name == NULL) + a->archive.archive_format_name = "ZIP"; + zip = (struct zip *)(a->format->data); + /* + * It should be sufficient to call archive_read_next_header() for + * a reader to determine if an entry is encrypted or not. If the + * encryption of an entry is only detectable when calling + * archive_read_data(), so be it. We'll do the same check there + * as well. + */ + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) + zip->has_encrypted_entries = 0; + + /* Make sure we have a zip_entry structure to use. */ + if (zip->zip_entries == NULL) { + zip->zip_entries = malloc(sizeof(struct zip_entry)); + if (zip->zip_entries == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Out of memory"); + return ARCHIVE_FATAL; + } + } + zip->entry = zip->zip_entries; + memset(zip->entry, 0, sizeof(struct zip_entry)); + + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; + __archive_read_reset_passphrase(a); + + /* Search ahead for the next local file header. */ + __archive_read_consume(a, zip->unconsumed); + zip->unconsumed = 0; + for (;;) { + int64_t skipped = 0; + const char *p, *end; + ssize_t bytes; + + p = __archive_read_ahead(a, 4, &bytes); + if (p == NULL) + return (ARCHIVE_FATAL); + end = p + bytes; + + while (p + 4 <= end) { + if (p[0] == 'P' && p[1] == 'K') { + if (p[2] == '\003' && p[3] == '\004') { + /* Regular file entry. */ + __archive_read_consume(a, skipped); + return zip_read_local_file_header(a, + entry, zip); + } + + /* + * TODO: We cannot restore permissions + * based only on the local file headers. + * Consider scanning the central + * directory and returning additional + * entries for at least directories. + * This would allow us to properly set + * directory permissions. + * + * This won't help us fix symlinks + * and may not help with regular file + * permissions, either. <sigh> + */ + if (p[2] == '\001' && p[3] == '\002') { + return (ARCHIVE_EOF); + } + + /* End of central directory? Must be an + * empty archive. */ + if ((p[2] == '\005' && p[3] == '\006') + || (p[2] == '\006' && p[3] == '\006')) + return (ARCHIVE_EOF); + } + ++p; + ++skipped; + } + __archive_read_consume(a, skipped); + } +} + +static int +archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) +{ + struct zip *zip; + int64_t bytes_skipped; + + zip = (struct zip *)(a->format->data); + bytes_skipped = __archive_read_consume(a, zip->unconsumed); + zip->unconsumed = 0; + if (bytes_skipped < 0) + return (ARCHIVE_FATAL); + /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* So we know we're streaming... */ - if (0 == (zip->entry->flags & ZIP_LENGTH_AT_END)) { + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + || zip->entry->compressed_size > 0) { /* We know the compressed length, so we can just skip. */ - int64_t bytes_skipped = zip_read_consume(a, - zip->entry_bytes_remaining + zip->unconsumed); + bytes_skipped = __archive_read_consume(a, + zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); - zip->unconsumed = 0; return (ARCHIVE_OK); } + if (zip->init_decryption) { + int r; + + zip->has_encrypted_entries = 1; + if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) + r = read_decryption_header(a); + else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) + r = init_WinZip_AES_decryption(a); + else + r = init_traditional_PKWARE_decryption(a); + if (r != ARCHIVE_OK) + return (r); + zip->init_decryption = 0; + } + /* We're streaming and we don't know the length. */ /* If the body is compressed and we know the format, we can * find an exact end-of-entry by decompressing it. */ @@ -1544,8 +2218,6 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) #endif default: /* Uncompressed or unknown. */ /* Scan for a PK\007\010 signature. */ - zip_read_consume(a, zip->unconsumed); - zip->unconsumed = 0; for (;;) { const char *p, *buff; ssize_t bytes_avail; @@ -1563,180 +2235,819 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { - zip_read_consume(a, p - buff + 16); + if (zip->entry->flags & LA_USED_ZIP64) + __archive_read_consume(a, + p - buff + 24); + else + __archive_read_consume(a, + p - buff + 16); return ARCHIVE_OK; } else { p += 4; } } - zip_read_consume(a, p - buff); + __archive_read_consume(a, p - buff); } } } -static int -archive_read_format_zip_cleanup(struct archive_read *a) +int +archive_read_support_format_zip_streamable(struct archive *_a) { + struct archive_read *a = (struct archive_read *)_a; struct zip *zip; + int r; - zip = (struct zip *)(a->format->data); -#ifdef HAVE_ZLIB_H - if (zip->stream_valid) - inflateEnd(&zip->stream); -#endif - if (zip->zip_entries && zip->central_directory_entries) { - unsigned i; - for (i = 0; i < zip->central_directory_entries; i++) - archive_string_free(&(zip->zip_entries[i].rsrcname)); + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); + + zip = (struct zip *)calloc(1, sizeof(*zip)); + if (zip == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate zip data"); + return (ARCHIVE_FATAL); } - free(zip->zip_entries); - free(zip->uncompressed_buffer); - archive_string_free(&(zip->extra)); - free(zip); - (a->format->data) = NULL; + + /* Streamable reader doesn't support mac extensions. */ + zip->process_mac_extensions = 0; + + /* + * Until enough data has been read, we cannot tell about + * any encrypted entries yet. + */ + zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + zip->crc32func = real_crc32; + + r = __archive_read_register_format(a, + zip, + "zip", + archive_read_format_zip_streamable_bid, + archive_read_format_zip_options, + archive_read_format_zip_streamable_read_header, + archive_read_format_zip_read_data, + archive_read_format_zip_read_data_skip_streamable, + NULL, + archive_read_format_zip_cleanup, + archive_read_support_format_zip_capabilities_streamable, + archive_read_format_zip_has_encrypted_entries); + + if (r != ARCHIVE_OK) + free(zip); return (ARCHIVE_OK); } +/* ------------------------------------------------------------------------ */ + /* - * The extra data is stored as a list of - * id1+size1+data1 + id2+size2+data2 ... - * triplets. id and size are 2 bytes each. + * Seeking-mode support + */ + +static int +archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) +{ + (void)a; /* UNUSED */ + return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | + ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); +} + +/* + * TODO: This is a performance sink because it forces the read core to + * drop buffered data from the start of file, which will then have to + * be re-read again if this bidder loses. + * + * We workaround this a little by passing in the best bid so far so + * that later bidders can do nothing if they know they'll never + * outbid. But we can certainly do better... + */ +static int +read_eocd(struct zip *zip, const char *p, int64_t current_offset) +{ + /* Sanity-check the EOCD we've found. */ + + /* This must be the first volume. */ + if (archive_le16dec(p + 4) != 0) + return 0; + /* Central directory must be on this volume. */ + if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) + return 0; + /* All central directory entries must be on this volume. */ + if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) + return 0; + /* Central directory can't extend beyond start of EOCD record. */ + if (archive_le32dec(p + 16) + archive_le32dec(p + 12) + > current_offset) + return 0; + + /* Save the central directory location for later use. */ + zip->central_directory_offset = archive_le32dec(p + 16); + + /* This is just a tiny bit higher than the maximum + returned by the streaming Zip bidder. This ensures + that the more accurate seeking Zip parser wins + whenever seek is available. */ + return 32; +} + +/* + * Examine Zip64 EOCD locator: If it's valid, store the information + * from it. */ static void -process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) +read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) { - unsigned offset = 0; + int64_t eocd64_offset; + int64_t eocd64_size; + + /* Sanity-check the locator record. */ + + /* Central dir must be on first volume. */ + if (archive_le32dec(p + 4) != 0) + return; + /* Must be only a single volume. */ + if (archive_le32dec(p + 16) != 1) + return; + + /* Find the Zip64 EOCD record. */ + eocd64_offset = archive_le64dec(p + 8); + if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) + return; + if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) + return; + /* Make sure we can read all of it. */ + eocd64_size = archive_le64dec(p + 4) + 12; + if (eocd64_size < 56 || eocd64_size > 16384) + return; + if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) + return; + + /* Sanity-check the EOCD64 */ + if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ + return; + if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ + return; + /* CD can't be split. */ + if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) + return; + + /* Save the central directory offset for later use. */ + zip->central_directory_offset = archive_le64dec(p + 48); +} - while (offset < extra_length - 4) - { - unsigned short headerid = archive_le16dec(p + offset); - unsigned short datasize = archive_le16dec(p + offset + 2); - offset += 4; - if (offset + datasize > extra_length) +static int +archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) +{ + struct zip *zip = (struct zip *)a->format->data; + int64_t file_size, current_offset; + const char *p; + int i, tail; + + /* If someone has already bid more than 32, then avoid + trashing the look-ahead buffers with a seek. */ + if (best_bid > 32) + return (-1); + + file_size = __archive_read_seek(a, 0, SEEK_END); + if (file_size <= 0) + return 0; + + /* Search last 16k of file for end-of-central-directory + * record (which starts with PK\005\006) */ + tail = (int)zipmin(1024 * 16, file_size); + current_offset = __archive_read_seek(a, -tail, SEEK_END); + if (current_offset < 0) + return 0; + if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) + return 0; + /* Boyer-Moore search backwards from the end, since we want + * to match the last EOCD in the file (there can be more than + * one if there is an uncompressed Zip archive as a member + * within this Zip archive). */ + for (i = tail - 22; i > 0;) { + switch (p[i]) { + case 'P': + if (memcmp(p + i, "PK\005\006", 4) == 0) { + int ret = read_eocd(zip, p + i, + current_offset + i); + if (ret > 0) { + /* Zip64 EOCD locator precedes + * regular EOCD if present. */ + if (i >= 20 + && memcmp(p + i - 20, "PK\006\007", 4) == 0) { + read_zip64_eocd(a, zip, p + i - 20); + } + return (ret); + } + } + i -= 4; break; -#ifdef DEBUG - fprintf(stderr, "Header id 0x%x, length %d\n", - headerid, datasize); -#endif - switch (headerid) { - case 0x0001: - /* Zip64 extended information extra field. */ - if (datasize >= 8) - zip_entry->uncompressed_size = - archive_le64dec(p + offset); - if (datasize >= 16) - zip_entry->compressed_size = - archive_le64dec(p + offset + 8); + case 'K': i -= 1; break; + case 005: i -= 2; break; + case 006: i -= 3; break; + default: i -= 4; break; + } + } + return 0; +} + +/* The red-black trees are only used in seeking mode to manage + * the in-memory copy of the central directory. */ + +static int +cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) +{ + const struct zip_entry *e1 = (const struct zip_entry *)n1; + const struct zip_entry *e2 = (const struct zip_entry *)n2; + + if (e1->local_header_offset > e2->local_header_offset) + return -1; + if (e1->local_header_offset < e2->local_header_offset) + return 1; + return 0; +} + +static int +cmp_key(const struct archive_rb_node *n, const void *key) +{ + /* This function won't be called */ + (void)n; /* UNUSED */ + (void)key; /* UNUSED */ + return 1; +} + +static const struct archive_rb_tree_ops rb_ops = { + &cmp_node, &cmp_key +}; + +static int +rsrc_cmp_node(const struct archive_rb_node *n1, + const struct archive_rb_node *n2) +{ + const struct zip_entry *e1 = (const struct zip_entry *)n1; + const struct zip_entry *e2 = (const struct zip_entry *)n2; + + return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); +} + +static int +rsrc_cmp_key(const struct archive_rb_node *n, const void *key) +{ + const struct zip_entry *e = (const struct zip_entry *)n; + return (strcmp((const char *)key, e->rsrcname.s)); +} + +static const struct archive_rb_tree_ops rb_rsrc_ops = { + &rsrc_cmp_node, &rsrc_cmp_key +}; + +static const char * +rsrc_basename(const char *name, size_t name_length) +{ + const char *s, *r; + + r = s = name; + for (;;) { + s = memchr(s, '/', name_length - (s - name)); + if (s == NULL) break; - case 0x5455: - { - /* Extended time field "UT". */ - int flags = p[offset]; - offset++; - datasize--; - /* Flag bits indicate which dates are present. */ - if (flags & 0x01) - { -#ifdef DEBUG - fprintf(stderr, "mtime: %lld -> %d\n", - (long long)zip_entry->mtime, - archive_le32dec(p + offset)); -#endif - if (datasize < 4) - break; - zip_entry->mtime = archive_le32dec(p + offset); - offset += 4; - datasize -= 4; - } - if (flags & 0x02) - { - if (datasize < 4) - break; - zip_entry->atime = archive_le32dec(p + offset); - offset += 4; - datasize -= 4; - } - if (flags & 0x04) - { - if (datasize < 4) - break; - zip_entry->ctime = archive_le32dec(p + offset); - offset += 4; - datasize -= 4; + r = ++s; + } + return (r); +} + +static void +expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) +{ + struct archive_string str; + struct zip_entry *dir; + char *s; + + archive_string_init(&str); + archive_strncpy(&str, name, name_length); + for (;;) { + s = strrchr(str.s, '/'); + if (s == NULL) + break; + *s = '\0'; + /* Transfer the parent directory from zip->tree_rsrc RB + * tree to zip->tree RB tree to expose. */ + dir = (struct zip_entry *) + __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); + if (dir == NULL) + break; + __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); + archive_string_free(&dir->rsrcname); + __archive_rb_tree_insert_node(&zip->tree, &dir->node); + } + archive_string_free(&str); +} + +static int +slurp_central_directory(struct archive_read *a, struct zip *zip) +{ + ssize_t i; + unsigned found; + int64_t correction; + ssize_t bytes_avail; + const char *p; + + /* + * Find the start of the central directory. The end-of-CD + * record has our starting point, but there are lots of + * Zip archives which have had other data prepended to the + * file, which makes the recorded offsets all too small. + * So we search forward from the specified offset until we + * find the real start of the central directory. Then we + * know the correction we need to apply to account for leading + * padding. + */ + if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0) + return ARCHIVE_FATAL; + + found = 0; + while (!found) { + if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL) + return ARCHIVE_FATAL; + for (found = 0, i = 0; !found && i < bytes_avail - 4;) { + switch (p[i + 3]) { + case 'P': i += 3; break; + case 'K': i += 2; break; + case 001: i += 1; break; + case 002: + if (memcmp(p + i, "PK\001\002", 4) == 0) { + p += i; + found = 1; + } else + i += 4; + break; + case 005: i += 1; break; + case 006: + if (memcmp(p + i, "PK\005\006", 4) == 0) { + p += i; + found = 1; + } else if (memcmp(p + i, "PK\006\006", 4) == 0) { + p += i; + found = 1; + } else + i += 1; + break; + default: i += 4; break; } + } + __archive_read_consume(a, i); + } + correction = archive_filter_bytes(&a->archive, 0) + - zip->central_directory_offset; + + __archive_rb_tree_init(&zip->tree, &rb_ops); + __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); + + zip->central_directory_entries_total = 0; + while (1) { + struct zip_entry *zip_entry; + size_t filename_length, extra_length, comment_length; + uint32_t external_attributes; + const char *name, *r; + + if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) + return ARCHIVE_FATAL; + if (memcmp(p, "PK\006\006", 4) == 0 + || memcmp(p, "PK\005\006", 4) == 0) { break; + } else if (memcmp(p, "PK\001\002", 4) != 0) { + archive_set_error(&a->archive, + -1, "Invalid central directory signature"); + return ARCHIVE_FATAL; } - case 0x5855: - { - /* Info-ZIP Unix Extra Field (old version) "UX". */ - if (datasize >= 8) { - zip_entry->atime = archive_le32dec(p + offset); - zip_entry->mtime = - archive_le32dec(p + offset + 4); + if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) + return ARCHIVE_FATAL; + + zip_entry = calloc(1, sizeof(struct zip_entry)); + zip_entry->next = zip->zip_entries; + zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY; + zip->zip_entries = zip_entry; + zip->central_directory_entries_total++; + + /* version = p[4]; */ + zip_entry->system = p[5]; + /* version_required = archive_le16dec(p + 6); */ + zip_entry->zip_flags = archive_le16dec(p + 8); + if (zip_entry->zip_flags + & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ + zip->has_encrypted_entries = 1; + } + zip_entry->compression = (char)archive_le16dec(p + 10); + zip_entry->mtime = zip_time(p + 12); + zip_entry->crc32 = archive_le32dec(p + 16); + if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + zip_entry->decdat = p[13]; + else + zip_entry->decdat = p[19]; + zip_entry->compressed_size = archive_le32dec(p + 20); + zip_entry->uncompressed_size = archive_le32dec(p + 24); + filename_length = archive_le16dec(p + 28); + extra_length = archive_le16dec(p + 30); + comment_length = archive_le16dec(p + 32); + /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ + /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ + external_attributes = archive_le32dec(p + 38); + zip_entry->local_header_offset = + archive_le32dec(p + 42) + correction; + + /* If we can't guess the mode, leave it zero here; + when we read the local file header we might get + more information. */ + if (zip_entry->system == 3) { + zip_entry->mode = external_attributes >> 16; + } else if (zip_entry->system == 0) { + // Interpret MSDOS directory bit + if (0x10 == (external_attributes & 0x10)) { + zip_entry->mode = AE_IFDIR | 0775; + } else { + zip_entry->mode = AE_IFREG | 0664; } - if (datasize >= 12) { - zip_entry->uid = - archive_le16dec(p + offset + 8); - zip_entry->gid = - archive_le16dec(p + offset + 10); + if (0x01 == (external_attributes & 0x01)) { + // Read-only bit; strip write permissions + zip_entry->mode &= 0555; } - break; + } else { + zip_entry->mode = 0; } - case 0x7855: - /* Info-ZIP Unix Extra Field (type 2) "Ux". */ -#ifdef DEBUG - fprintf(stderr, "uid %d gid %d\n", - archive_le16dec(p + offset), - archive_le16dec(p + offset + 2)); + + /* We're done with the regular data; get the filename and + * extra data. */ + __archive_read_consume(a, 46); + p = __archive_read_ahead(a, filename_length + extra_length, + NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return ARCHIVE_FATAL; + } + process_extra(p + filename_length, extra_length, zip_entry); + + /* + * Mac resource fork files are stored under the + * "__MACOSX/" directory, so we should check if + * it is. + */ + if (!zip->process_mac_extensions) { + /* Treat every entry as a regular entry. */ + __archive_rb_tree_insert_node(&zip->tree, + &zip_entry->node); + } else { + name = p; + r = rsrc_basename(name, filename_length); + if (filename_length >= 9 && + strncmp("__MACOSX/", name, 9) == 0) { + /* If this file is not a resource fork nor + * a directory. We should treat it as a non + * resource fork file to expose it. */ + if (name[filename_length-1] != '/' && + (r - name < 3 || r[0] != '.' || r[1] != '_')) { + __archive_rb_tree_insert_node( + &zip->tree, &zip_entry->node); + /* Expose its parent directories. */ + expose_parent_dirs(zip, name, + filename_length); + } else { + /* This file is a resource fork file or + * a directory. */ + archive_strncpy(&(zip_entry->rsrcname), + name, filename_length); + __archive_rb_tree_insert_node( + &zip->tree_rsrc, &zip_entry->node); + } + } else { + /* Generate resource fork name to find its + * resource file at zip->tree_rsrc. */ + archive_strcpy(&(zip_entry->rsrcname), + "__MACOSX/"); + archive_strncat(&(zip_entry->rsrcname), + name, r - name); + archive_strcat(&(zip_entry->rsrcname), "._"); + archive_strncat(&(zip_entry->rsrcname), + name + (r - name), + filename_length - (r - name)); + /* Register an entry to RB tree to sort it by + * file offset. */ + __archive_rb_tree_insert_node(&zip->tree, + &zip_entry->node); + } + } + + /* Skip the comment too ... */ + __archive_read_consume(a, + filename_length + extra_length + comment_length); + } + + return ARCHIVE_OK; +} + +static ssize_t +zip_get_local_file_header_size(struct archive_read *a, size_t extra) +{ + const char *p; + ssize_t filename_length, extra_length; + + if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + return (ARCHIVE_WARN); + } + p += extra; + + if (memcmp(p, "PK\003\004", 4) != 0) { + archive_set_error(&a->archive, -1, "Damaged Zip archive"); + return ARCHIVE_WARN; + } + filename_length = archive_le16dec(p + 26); + extra_length = archive_le16dec(p + 28); + + return (30 + filename_length + extra_length); +} + +static int +zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, + struct zip_entry *rsrc) +{ + struct zip *zip = (struct zip *)a->format->data; + unsigned char *metadata, *mp; + int64_t offset = archive_filter_bytes(&a->archive, 0); + size_t remaining_bytes, metadata_bytes; + ssize_t hsize; + int ret = ARCHIVE_OK, eof; + + switch(rsrc->compression) { + case 0: /* No compression. */ + if (rsrc->uncompressed_size != rsrc->compressed_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Malformed OS X metadata entry: inconsistent size"); + return (ARCHIVE_FATAL); + } +#ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ #endif - if (datasize >= 2) - zip_entry->uid = archive_le16dec(p + offset); - if (datasize >= 4) - zip_entry->gid = - archive_le16dec(p + offset + 2); + break; + default: /* Unsupported compression. */ + /* Return a warning. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported ZIP compression method (%s)", + compression_name(rsrc->compression)); + /* We can't decompress this entry, but we will + * be able to skip() it and try the next entry. */ + return (ARCHIVE_WARN); + } + + if (rsrc->uncompressed_size > (4 * 1024 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Mac metadata is too large: %jd > 4M bytes", + (intmax_t)rsrc->uncompressed_size); + return (ARCHIVE_WARN); + } + if (rsrc->compressed_size > (4 * 1024 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Mac metadata is too large: %jd > 4M bytes", + (intmax_t)rsrc->compressed_size); + return (ARCHIVE_WARN); + } + + metadata = malloc((size_t)rsrc->uncompressed_size); + if (metadata == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Mac metadata"); + return (ARCHIVE_FATAL); + } + + if (offset < rsrc->local_header_offset) + __archive_read_consume(a, rsrc->local_header_offset - offset); + else if (offset != rsrc->local_header_offset) { + __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); + } + + hsize = zip_get_local_file_header_size(a, 0); + __archive_read_consume(a, hsize); + + remaining_bytes = (size_t)rsrc->compressed_size; + metadata_bytes = (size_t)rsrc->uncompressed_size; + mp = metadata; + eof = 0; + while (!eof && remaining_bytes) { + const unsigned char *p; + ssize_t bytes_avail; + size_t bytes_used; + + p = __archive_read_ahead(a, 1, &bytes_avail); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file header"); + ret = ARCHIVE_WARN; + goto exit_mac_metadata; + } + if ((size_t)bytes_avail > remaining_bytes) + bytes_avail = remaining_bytes; + switch(rsrc->compression) { + case 0: /* No compression. */ + if ((size_t)bytes_avail > metadata_bytes) + bytes_avail = metadata_bytes; + memcpy(mp, p, bytes_avail); + bytes_used = (size_t)bytes_avail; + metadata_bytes -= bytes_used; + mp += bytes_used; + if (metadata_bytes == 0) + eof = 1; break; - case 0x7875: +#ifdef HAVE_ZLIB_H + case 8: /* Deflate compression. */ { - /* Info-Zip Unix Extra Field (type 3) "ux". */ - int uidsize = 0, gidsize = 0; + int r; - if (datasize >= 1 && p[offset] == 1) {/* version=1 */ - if (datasize >= 4) { - /* get a uid size. */ - uidsize = p[offset+1]; - if (uidsize == 2) - zip_entry->uid = - archive_le16dec( - p + offset + 2); - else if (uidsize == 4 && datasize >= 6) - zip_entry->uid = - archive_le32dec( - p + offset + 2); - } - if (datasize >= (2 + uidsize + 3)) { - /* get a gid size. */ - gidsize = p[offset+2+uidsize]; - if (gidsize == 2) - zip_entry->gid = - archive_le16dec( - p+offset+2+uidsize+1); - else if (gidsize == 4 && - datasize >= (2 + uidsize + 5)) - zip_entry->gid = - archive_le32dec( - p+offset+2+uidsize+1); - } + ret = zip_deflate_init(a, zip); + if (ret != ARCHIVE_OK) + goto exit_mac_metadata; + zip->stream.next_in = + (Bytef *)(uintptr_t)(const void *)p; + zip->stream.avail_in = (uInt)bytes_avail; + zip->stream.total_in = 0; + zip->stream.next_out = mp; + zip->stream.avail_out = (uInt)metadata_bytes; + zip->stream.total_out = 0; + + r = inflate(&zip->stream, 0); + switch (r) { + case Z_OK: + break; + case Z_STREAM_END: + eof = 1; + break; + case Z_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Out of memory for ZIP decompression"); + ret = ARCHIVE_FATAL; + goto exit_mac_metadata; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "ZIP decompression failed (%d)", r); + ret = ARCHIVE_FATAL; + goto exit_mac_metadata; } + bytes_used = zip->stream.total_in; + metadata_bytes -= zip->stream.total_out; + mp += zip->stream.total_out; break; } +#endif default: + bytes_used = 0; break; } - offset += datasize; + __archive_read_consume(a, bytes_used); + remaining_bytes -= bytes_used; } -#ifdef DEBUG - if (offset != extra_length) - { - fprintf(stderr, - "Extra data field contents do not match reported size!\n"); + archive_entry_copy_mac_metadata(entry, metadata, + (size_t)rsrc->uncompressed_size - metadata_bytes); + +exit_mac_metadata: + __archive_read_seek(a, offset, SEEK_SET); + zip->decompress_init = 0; + free(metadata); + return (ret); +} + +static int +archive_read_format_zip_seekable_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + struct zip *zip = (struct zip *)a->format->data; + struct zip_entry *rsrc; + int64_t offset; + int r, ret = ARCHIVE_OK; + + /* + * It should be sufficient to call archive_read_next_header() for + * a reader to determine if an entry is encrypted or not. If the + * encryption of an entry is only detectable when calling + * archive_read_data(), so be it. We'll do the same check there + * as well. + */ + if (zip->has_encrypted_entries == + ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) + zip->has_encrypted_entries = 0; + + a->archive.archive_format = ARCHIVE_FORMAT_ZIP; + if (a->archive.archive_format_name == NULL) + a->archive.archive_format_name = "ZIP"; + + if (zip->zip_entries == NULL) { + r = slurp_central_directory(a, zip); + if (r != ARCHIVE_OK) + return r; + /* Get first entry whose local header offset is lower than + * other entries in the archive file. */ + zip->entry = + (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree); + } else if (zip->entry != NULL) { + /* Get next entry in local header offset order. */ + zip->entry = (struct zip_entry *)__archive_rb_tree_iterate( + &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT); + } + + if (zip->entry == NULL) + return ARCHIVE_EOF; + + if (zip->entry->rsrcname.s) + rsrc = (struct zip_entry *)__archive_rb_tree_find_node( + &zip->tree_rsrc, zip->entry->rsrcname.s); + else + rsrc = NULL; + + if (zip->cctx_valid) + archive_decrypto_aes_ctr_release(&zip->cctx); + if (zip->hctx_valid) + archive_hmac_sha1_cleanup(&zip->hctx); + zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; + __archive_read_reset_passphrase(a); + + /* File entries are sorted by the header offset, we should mostly + * use __archive_read_consume to advance a read point to avoid redundant + * data reading. */ + offset = archive_filter_bytes(&a->archive, 0); + if (offset < zip->entry->local_header_offset) + __archive_read_consume(a, + zip->entry->local_header_offset - offset); + else if (offset != zip->entry->local_header_offset) { + __archive_read_seek(a, zip->entry->local_header_offset, + SEEK_SET); + } + zip->unconsumed = 0; + r = zip_read_local_file_header(a, entry, zip); + if (r != ARCHIVE_OK) + return r; + if (rsrc) { + int ret2 = zip_read_mac_metadata(a, entry, rsrc); + if (ret2 < ret) + ret = ret2; + } + return (ret); +} + +/* + * We're going to seek for the next header anyway, so we don't + * need to bother doing anything here. + */ +static int +archive_read_format_zip_read_data_skip_seekable(struct archive_read *a) +{ + struct zip *zip; + zip = (struct zip *)(a->format->data); + + zip->unconsumed = 0; + return (ARCHIVE_OK); +} + +int +archive_read_support_format_zip_seekable(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct zip *zip; + int r; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); + + zip = (struct zip *)calloc(1, sizeof(*zip)); + if (zip == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate zip data"); + return (ARCHIVE_FATAL); } + +#ifdef HAVE_COPYFILE_H + /* Set this by default on Mac OS. */ + zip->process_mac_extensions = 1; #endif + + /* + * Until enough data has been read, we cannot tell about + * any encrypted entries yet. + */ + zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + zip->crc32func = real_crc32; + + r = __archive_read_register_format(a, + zip, + "zip", + archive_read_format_zip_seekable_bid, + archive_read_format_zip_options, + archive_read_format_zip_seekable_read_header, + archive_read_format_zip_read_data, + archive_read_format_zip_read_data_skip_seekable, + NULL, + archive_read_format_zip_cleanup, + archive_read_support_format_zip_capabilities_seekable, + archive_read_format_zip_has_encrypted_entries); + + if (r != ARCHIVE_OK) + free(zip); + return (ARCHIVE_OK); } diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 87f9288f1966..282c58e1eb9f 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -71,6 +71,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33 #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif +#if !defined(HAVE_WMEMMOVE) && !defined(wmemmove) +#define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) +#endif + struct archive_string_conv { struct archive_string_conv *next; char *from_charset; @@ -127,12 +131,7 @@ struct archive_string_conv { #define UNICODE_MAX 0x10FFFF #define UNICODE_R_CHAR 0xFFFD /* Replacement character. */ /* Set U+FFFD(Replacement character) in UTF-8. */ -#define UTF8_SET_R_CHAR(outp) do { \ - (outp)[0] = 0xef; \ - (outp)[1] = 0xbf; \ - (outp)[2] = 0xbd; \ -} while (0) -#define UTF8_R_CHAR_SIZE 3 +static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd}; static struct archive_string_conv *find_sconv_object(struct archive *, const char *, const char *); @@ -203,7 +202,7 @@ archive_string_append(struct archive_string *as, const char *p, size_t s) { if (archive_string_ensure(as, as->length + s + 1) == NULL) return (NULL); - memcpy(as->s + as->length, p, s); + memmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); @@ -214,7 +213,7 @@ archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) { if (archive_wstring_ensure(as, as->length + s + 1) == NULL) return (NULL); - wmemcpy(as->s + as->length, p, s); + wmemmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); @@ -738,7 +737,8 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as, } if (count == 0) ret = -1; - } while (0); + break; + } while (1); } as->length += count; as->s[as->length] = '\0'; @@ -2037,7 +2037,7 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) { size_t rbytes; if (sc->flag & SCONV_TO_UTF8) - rbytes = UTF8_R_CHAR_SIZE; + rbytes = sizeof(utf8_replacement_char); else rbytes = 2; @@ -2053,7 +2053,7 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, - as->length - to_size; } if (sc->flag & SCONV_TO_UTF8) - UTF8_SET_R_CHAR(outp); + memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char)); else if (sc->flag & SCONV_TO_UTF16BE) archive_be16enc(outp, UNICODE_R_CHAR); else @@ -2202,9 +2202,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { size_t remaining; - char *otp; const uint8_t *itp; - size_t avail; int return_value = 0; /* success */ /* @@ -2223,46 +2221,25 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD, * a Replacement Character in Unicode. */ - if (archive_string_ensure(as, as->length + length + 1) == NULL) - return (-1); remaining = length; itp = (const uint8_t *)_p; - otp = as->s + as->length; - avail = as->buffer_length - as->length -1; while (*itp && remaining > 0) { - if (*itp > 127 && (sc->flag & SCONV_TO_UTF8)) { - if (avail < UTF8_R_CHAR_SIZE) { - as->length = otp - as->s; - if (NULL == archive_string_ensure(as, - as->buffer_length + remaining + - UTF8_R_CHAR_SIZE)) - return (-1); - otp = as->s + as->length; - avail = as->buffer_length - as->length -1; + if (*itp > 127) { + // Non-ASCII: Substitute with suitable replacement + if (sc->flag & SCONV_TO_UTF8) { + if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) { + __archive_errx(1, "Out of memory"); + } + } else { + archive_strappend_char(as, '?'); } - /* - * When coping a string in UTF-8, unknown character - * should be U+FFFD (replacement character). - */ - UTF8_SET_R_CHAR(otp); - otp += UTF8_R_CHAR_SIZE; - avail -= UTF8_R_CHAR_SIZE; - itp++; - remaining--; - return_value = -1; - } else if (*itp > 127) { - *otp++ = '?'; - itp++; - remaining--; return_value = -1; } else { - *otp++ = (char)*itp++; - remaining--; + archive_strappend_char(as, *itp); } + ++itp; } - as->length = otp - as->s; - as->s[as->length] = '\0'; return (return_value); } @@ -2488,6 +2465,9 @@ unicode_to_utf8(char *p, size_t remaining, uint32_t uc) { char *_p = p; + /* Invalid Unicode char maps to Replacement character */ + if (uc > UNICODE_MAX) + uc = UNICODE_R_CHAR; /* Translate code point to UTF8 */ if (uc <= 0x7f) { if (remaining == 0) @@ -2504,22 +2484,13 @@ unicode_to_utf8(char *p, size_t remaining, uint32_t uc) *p++ = 0xe0 | ((uc >> 12) & 0x0f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); - } else if (uc <= UNICODE_MAX) { + } else { if (remaining < 4) return (0); *p++ = 0xf0 | ((uc >> 18) & 0x07); *p++ = 0x80 | ((uc >> 12) & 0x3f); *p++ = 0x80 | ((uc >> 6) & 0x3f); *p++ = 0x80 | (uc & 0x3f); - } else { - /* - * Undescribed code point should be U+FFFD - * (replacement character). - */ - if (remaining < UTF8_R_CHAR_SIZE) - return (0); - UTF8_SET_R_CHAR(p); - p += UTF8_R_CHAR_SIZE; } return (p - _p); } @@ -3887,7 +3858,7 @@ archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes, sc = archive_string_conversion_to_charset(a, "UTF-8", 1); if (sc == NULL) return (-1);/* Couldn't allocate memory for sc. */ - r = archive_strncpy_l(&(aes->aes_mbs), aes->aes_mbs.s, + r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s, aes->aes_mbs.length, sc); if (a == NULL) free_sconv_object(sc); @@ -4062,6 +4033,19 @@ archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs) } int +archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) +{ + if (utf8 == NULL) { + aes->aes_set = 0; + } + aes->aes_set = AES_SET_UTF8; + archive_string_empty(&(aes->aes_mbs)); + archive_string_empty(&(aes->aes_wcs)); + archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8)); + return (int)strlen(utf8); +} + +int archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, size_t len) { diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 34d8081cbce9..cc3d1c460728 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009-2012 Michihiro NAKAJIMA + * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * @@ -45,15 +45,30 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) #include <wincrypt.h> #endif +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#endif +#ifdef HAVE_LZMA_H +#include <lzma.h> +#endif +#ifdef HAVE_BZLIB_H +#include <bzlib.h> +#endif +#ifdef HAVE_LZ4_H +#include <lz4.h> +#endif #include "archive.h" #include "archive_private.h" +#include "archive_random_private.h" #include "archive_string.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif +static int archive_utility_string_sort_helper(char **, unsigned int); + /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) @@ -74,6 +89,88 @@ archive_version_string(void) return (ARCHIVE_VERSION_STRING); } +const char * +archive_version_details(void) +{ + static struct archive_string str; + static int init = 0; + const char *zlib = archive_zlib_version(); + const char *liblzma = archive_liblzma_version(); + const char *bzlib = archive_bzlib_version(); + const char *liblz4 = archive_liblz4_version(); + + if (!init) { + archive_string_init(&str); + + archive_strcat(&str, ARCHIVE_VERSION_STRING); + if (zlib != NULL) { + archive_strcat(&str, " zlib/"); + archive_strcat(&str, zlib); + } + if (liblzma) { + archive_strcat(&str, " liblzma/"); + archive_strcat(&str, liblzma); + } + if (bzlib) { + const char *p = bzlib; + const char *sep = strchr(p, ','); + if (sep == NULL) + sep = p + strlen(p); + archive_strcat(&str, " bz2lib/"); + archive_strncat(&str, p, sep - p); + } + if (liblz4) { + archive_strcat(&str, " liblz4/"); + archive_strcat(&str, liblz4); + } + } + return str.s; +} + +const char * +archive_zlib_version(void) +{ +#ifdef HAVE_ZLIB_H + return ZLIB_VERSION; +#else + return NULL; +#endif +} + +const char * +archive_liblzma_version(void) +{ +#ifdef HAVE_LZMA_H + return LZMA_VERSION_STRING; +#else + return NULL; +#endif +} + +const char * +archive_bzlib_version(void) +{ +#ifdef HAVE_BZLIB_H + return BZ2_bzlibVersion(); +#else + return NULL; +#endif +} + +const char * +archive_liblz4_version(void) +{ +#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4) +#define str(s) #s +#define NUMBER(x) str(x) + return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE); +#undef NUMBER +#undef str +#else + return NULL; +#endif +} + int archive_errno(struct archive *a) { @@ -206,6 +303,8 @@ __archive_errx(int retvalue, const char *msg) int __archive_mktemp(const char *tmpdir) { + static const wchar_t *prefix = L"libarchive_"; + static const wchar_t *suffix = L"XXXXXXXXXX"; static const wchar_t num[] = { L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F', @@ -280,10 +379,10 @@ __archive_mktemp(const char *tmpdir) /* * Create a temporary file. */ - archive_wstrcat(&temp_name, L"libarchive_"); - xp = temp_name.s + archive_strlen(&temp_name); - archive_wstrcat(&temp_name, L"XXXXXXXXXX"); + archive_wstrcat(&temp_name, prefix); + archive_wstrcat(&temp_name, suffix); ep = temp_name.s + archive_strlen(&temp_name); + xp = ep - wcslen(suffix); if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { @@ -420,7 +519,6 @@ __archive_mktemp(const char *tmpdir) struct stat st; int fd; char *tp, *ep; - unsigned seed; fd = -1; archive_string_init(&temp_name); @@ -444,21 +542,15 @@ __archive_mktemp(const char *tmpdir) archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); - fd = open("/dev/random", O_RDONLY | O_CLOEXEC); - __archive_ensure_cloexec_flag(fd); - if (fd < 0) - seed = time(NULL); - else { - if (read(fd, &seed, sizeof(seed)) < 0) - seed = time(NULL); - close(fd); - } do { char *p; p = tp; - while (p < ep) - *p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)]; + archive_random(p, ep - p); + while (p < ep) { + int d = *((unsigned char *)p) % sizeof(num); + *p++ = num[d]; + } fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); } while (fd < 0 && errno == EEXIST); @@ -499,3 +591,77 @@ __archive_ensure_cloexec_flag(int fd) } #endif } + +/* + * Utility function to sort a group of strings using quicksort. + */ +static int +archive_utility_string_sort_helper(char **strings, unsigned int n) +{ + unsigned int i, lesser_count, greater_count; + char **lesser, **greater, **tmp, *pivot; + int retval1, retval2; + + /* A list of 0 or 1 elements is already sorted */ + if (n <= 1) + return (ARCHIVE_OK); + + lesser_count = greater_count = 0; + lesser = greater = NULL; + pivot = strings[0]; + for (i = 1; i < n; i++) + { + if (strcmp(strings[i], pivot) < 0) + { + lesser_count++; + tmp = (char **)realloc(lesser, + lesser_count * sizeof(char *)); + if (!tmp) { + free(greater); + free(lesser); + return (ARCHIVE_FATAL); + } + lesser = tmp; + lesser[lesser_count - 1] = strings[i]; + } + else + { + greater_count++; + tmp = (char **)realloc(greater, + greater_count * sizeof(char *)); + if (!tmp) { + free(greater); + free(lesser); + return (ARCHIVE_FATAL); + } + greater = tmp; + greater[greater_count - 1] = strings[i]; + } + } + + /* quicksort(lesser) */ + retval1 = archive_utility_string_sort_helper(lesser, lesser_count); + for (i = 0; i < lesser_count; i++) + strings[i] = lesser[i]; + free(lesser); + + /* pivot */ + strings[lesser_count] = pivot; + + /* quicksort(greater) */ + retval2 = archive_utility_string_sort_helper(greater, greater_count); + for (i = 0; i < greater_count; i++) + strings[lesser_count + 1 + i] = greater[i]; + free(greater); + + return (retval1 < retval2) ? retval1 : retval2; +} + +int +archive_utility_string_sort(char **strings) +{ + unsigned int size = 0; + while (strings[size] != NULL) + size++; + return archive_utility_string_sort_helper(strings, size); +} diff --git a/libarchive/archive_virtual.c b/libarchive/archive_virtual.c index 0c4155f21e3b..de2595a9ead5 100644 --- a/libarchive/archive_virtual.c +++ b/libarchive/archive_virtual.c @@ -55,6 +55,14 @@ archive_filter_bytes(struct archive *a, int n) } int +archive_free(struct archive *a) +{ + if (a == NULL) + return (ARCHIVE_OK); + return ((a->vtable->archive_free)(a)); +} + +int archive_write_close(struct archive *a) { return ((a->vtable->archive_close)(a)); @@ -76,9 +84,7 @@ archive_write_fail(struct archive *a) int archive_write_free(struct archive *a) { - if (a == NULL) - return (ARCHIVE_OK); - return ((a->vtable->archive_free)(a)); + return archive_free(a); } #if ARCHIVE_VERSION_NUMBER < 4000000 @@ -93,9 +99,7 @@ archive_write_finish(struct archive *a) int archive_read_free(struct archive *a) { - if (a == NULL) - return (ARCHIVE_OK); - return ((a->vtable->archive_free)(a)); + return archive_free(a); } #if ARCHIVE_VERSION_NUMBER < 4000000 diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index d3bf758bb39e..d4e93fe78aaa 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -301,7 +301,7 @@ __la_open(const char *path, int flags, ...) ws = NULL; if ((flags & ~O_BINARY) == O_RDONLY) { /* - * When we open a directory, _open function returns + * When we open a directory, _open function returns * "Permission denied" error. */ attr = GetFileAttributesA(path); @@ -515,9 +515,9 @@ __hstat(HANDLE handle, struct ustat *st) else mode |= S_IFREG; st->st_mode = mode; - + fileTimeToUTC(&info.ftLastAccessTime, &t, &ns); - st->st_atime = t; + st->st_atime = t; st->st_atime_nsec = ns; fileTimeToUTC(&info.ftLastWriteTime, &t, &ns); st->st_mtime = t; @@ -525,7 +525,7 @@ __hstat(HANDLE handle, struct ustat *st) fileTimeToUTC(&info.ftCreationTime, &t, &ns); st->st_ctime = t; st->st_ctime_nsec = ns; - st->st_size = + st->st_size = ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1)) + (int64_t)(info.nFileSizeLow); #ifdef SIMULATE_WIN_STAT @@ -599,7 +599,7 @@ __la_stat(const char *path, struct stat *st) struct ustat u; int ret; - handle = la_CreateFile(path, 0, 0, NULL, OPEN_EXISTING, + handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index c6f5bc510513..8cf1c56d89d1 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -89,7 +89,7 @@ /* Alias the Windows _function to the POSIX equivalent. */ #define close _close -#define fcntl(fd, cmd, flg) /* No operation. */ +#define fcntl(fd, cmd, flg) /* No operation. */ #ifndef fileno #define fileno _fileno #endif @@ -109,13 +109,14 @@ #define lstat __la_stat #define open __la_open #define read __la_read -#if !defined(__BORLANDC__) +#if !defined(__BORLANDC__) && !defined(__WATCOMC__) #define setmode _setmode #endif #ifdef stat #undef stat #endif #define stat(path,stref) __la_stat(path,stref) +#if !defined(__WATCOMC__) #if !defined(__BORLANDC__) #define strdup _strdup #endif @@ -123,9 +124,12 @@ #if !defined(__BORLANDC__) #define umask _umask #endif +#endif #define waitpid __la_waitpid #define write __la_write +#if !defined(__WATCOMC__) + #ifndef O_RDONLY #define O_RDONLY _O_RDONLY #define O_WRONLY _O_WRONLY @@ -203,7 +207,7 @@ #define _S_IXGRP (_S_IXUSR >> 3) /* read permission, group */ #define _S_IWGRP (_S_IWUSR >> 3) /* write permission, group */ #define _S_IRGRP (_S_IRUSR >> 3) /* execute/search permission, group */ -#define _S_IRWXO (_S_IRWXG >> 3) +#define _S_IRWXO (_S_IRWXG >> 3) #define _S_IXOTH (_S_IXGRP >> 3) /* read permission, other */ #define _S_IWOTH (_S_IWGRP >> 3) /* write permission, other */ #define _S_IROTH (_S_IRGRP >> 3) /* execute/search permission, other */ @@ -217,12 +221,16 @@ #define S_IRWXG _S_IRWXG #define S_IXGRP _S_IXGRP #define S_IWGRP _S_IWGRP +#ifndef S_IRGRP #define S_IRGRP _S_IRGRP +#endif #define S_IRWXO _S_IRWXO #define S_IXOTH _S_IXOTH #define S_IWOTH _S_IWOTH #define S_IROTH _S_IROTH +#endif + #define F_DUPFD 0 /* Duplicate file descriptor. */ #define F_GETFD 1 /* Get file descriptor flags. */ #define F_SETFD 2 /* Set file descriptor flags. */ diff --git a/libarchive/archive_write.3 b/libarchive/archive_write.3 index 228bc2cb8821..376d71dee20e 100644 --- a/libarchive/archive_write.3 +++ b/libarchive/archive_write.3 @@ -155,7 +155,7 @@ myopen(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } -ssize_t +la_ssize_t mywrite(struct archive *a, void *client_data, const void *buff, size_t n) { struct mydata *mydata = client_data; @@ -186,8 +186,13 @@ write_archive(const char *outname, const char **filename) a = archive_write_new(); mydata->name = outname; - archive_write_add_filter_gzip(a); - archive_write_set_format_ustar(a); + /* Set archive format and filter according to output file extension. + * If it fails, set default format. Platform depended function. + * See supported formats in archive_write_set_format_filter_by_ext.c */ + if (archive_write_set_format_filter_by_ext(a, outname) != ARCHIVE_OK) { + archive_write_add_filter_gzip(a); + archive_write_set_format_ustar(a); + } archive_write_open(a, mydata, myopen, mywrite, myclose); while (*filename) { stat(*filename, &st); @@ -197,7 +202,7 @@ write_archive(const char *outname, const char **filename) archive_write_header(a, entry); if ((fd = open(*filename, O_RDONLY)) != -1) { len = read(fd, buff, sizeof(buff)); - while ( len > 0 ) { + while (len > 0) { archive_write_data(a, buff, len); len = read(fd, buff, sizeof(buff)); } @@ -213,7 +218,7 @@ int main(int argc, const char **argv) { const char *outname; argv++; - outname = argv++; + outname = *argv++; write_archive(outname, argv); return 0; } diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index a3d1a3380c05..e3fa3357ba7b 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -444,6 +444,12 @@ archive_write_client_close(struct archive_write_filter *f) /* Clear the close handler myself not to be called again. */ f->close = NULL; a->client_data = NULL; + /* Clear passphrase. */ + if (a->passphrase != NULL) { + memset(a->passphrase, 0, strlen(a->passphrase)); + free(a->passphrase); + a->passphrase = NULL; + } return (ret); } @@ -503,8 +509,9 @@ _archive_write_close(struct archive *_a) archive_clear_error(&a->archive); - /* Finish the last entry. */ - if (a->archive.state == ARCHIVE_STATE_DATA) + /* Finish the last entry if a finish callback is specified */ + if (a->archive.state == ARCHIVE_STATE_DATA + && a->format_finish_entry != NULL) r = ((a->format_finish_entry)(a)); /* Finish off the archive. */ @@ -591,6 +598,11 @@ _archive_write_free(struct archive *_a) /* Release various dynamic buffers. */ free((void *)(uintptr_t)(const void *)a->nulls); archive_string_free(&a->archive.error_string); + if (a->passphrase != NULL) { + /* A passphrase should be cleaned. */ + memset(a->passphrase, 0, strlen(a->passphrase)); + free(a->passphrase); + } a->archive.magic = 0; __archive_clean(&a->archive); free(a); @@ -638,6 +650,9 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); + if (r2 == ARCHIVE_FAILED) { + return (ARCHIVE_FAILED); + } if (r2 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); @@ -658,7 +673,8 @@ _archive_write_finish_entry(struct archive *_a) archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); - if (a->archive.state & ARCHIVE_STATE_DATA) + if (a->archive.state & ARCHIVE_STATE_DATA + && a->format_finish_entry != NULL) ret = (a->format_finish_entry)(a); a->archive.state = ARCHIVE_STATE_HEADER; return (ret); @@ -671,8 +687,13 @@ static ssize_t _archive_write_data(struct archive *_a, const void *buff, size_t s) { struct archive_write *a = (struct archive_write *)_a; + const size_t max_write = INT_MAX; + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); + /* In particular, this catches attempts to pass negative values. */ + if (s > max_write) + s = max_write; archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); } @@ -704,7 +725,7 @@ static const char * _archive_filter_name(struct archive *_a, int n) { struct archive_write_filter *f = filter_lookup(_a, n); - return f == NULL ? NULL : f->name; + return f != NULL ? f->name : NULL; } static int64_t diff --git a/libarchive/archive_write_add_filter.c b/libarchive/archive_write_add_filter.c index 81dd683aacc7..ad5dc832fdae 100644 --- a/libarchive/archive_write_add_filter.c +++ b/libarchive/archive_write_add_filter.c @@ -47,6 +47,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { ARCHIVE_FILTER_COMPRESS, archive_write_add_filter_compress }, { ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip }, { ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip }, + { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 }, { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma }, { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip }, diff --git a/libarchive/archive_write_add_filter_by_name.c b/libarchive/archive_write_add_filter_by_name.c index e4cba4afa01c..eac4011cb2ee 100644 --- a/libarchive/archive_write_add_filter_by_name.c +++ b/libarchive/archive_write_add_filter_by_name.c @@ -51,6 +51,7 @@ struct { const char *name; int (*setter)(struct archive *); } names[] = { "grzip", archive_write_add_filter_grzip }, { "gzip", archive_write_add_filter_gzip }, { "lrzip", archive_write_add_filter_lrzip }, + { "lz4", archive_write_add_filter_lz4 }, { "lzip", archive_write_add_filter_lzip }, { "lzma", archive_write_add_filter_lzma }, { "lzop", archive_write_add_filter_lzop }, diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 88da803a395a..68ed9579b027 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -105,7 +105,7 @@ archive_write_add_filter_bzip2(struct archive *_a) #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) return (ARCHIVE_OK); #else - data->pdata = __archive_write_program_allocate(); + data->pdata = __archive_write_program_allocate("bzip2"); if (data->pdata == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Out of memory"); diff --git a/libarchive/archive_write_add_filter_grzip.c b/libarchive/archive_write_add_filter_grzip.c index 8dc287eae033..371102d74c05 100644 --- a/libarchive/archive_write_add_filter_grzip.c +++ b/libarchive/archive_write_add_filter_grzip.c @@ -63,7 +63,7 @@ archive_write_add_filter_grzip(struct archive *_a) archive_set_error(_a, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } - data->pdata = __archive_write_program_allocate(); + data->pdata = __archive_write_program_allocate("grzip"); if (data->pdata == NULL) { free(data); archive_set_error(_a, ENOMEM, "Can't allocate memory"); diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c index da4607bb934c..04eb06c1c06f 100644 --- a/libarchive/archive_write_add_filter_gzip.c +++ b/libarchive/archive_write_add_filter_gzip.c @@ -119,7 +119,7 @@ archive_write_add_filter_gzip(struct archive *_a) data->compression_level = Z_DEFAULT_COMPRESSION; return (ARCHIVE_OK); #else - data->pdata = __archive_write_program_allocate(); + data->pdata = __archive_write_program_allocate("gzip"); if (data->pdata == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Out of memory"); diff --git a/libarchive/archive_write_add_filter_lrzip.c b/libarchive/archive_write_add_filter_lrzip.c index 85fdf6af57f5..e215f8903259 100644 --- a/libarchive/archive_write_add_filter_lrzip.c +++ b/libarchive/archive_write_add_filter_lrzip.c @@ -44,7 +44,7 @@ __FBSDID("$FreeBSD$"); struct write_lrzip { struct archive_write_program_data *pdata; int compression_level; - enum { lzma = 0, bzip2, gzip, lzo, zpaq } compression; + enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression; }; static int archive_write_lrzip_open(struct archive_write_filter *); @@ -69,7 +69,7 @@ archive_write_add_filter_lrzip(struct archive *_a) archive_set_error(_a, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } - data->pdata = __archive_write_program_allocate(); + data->pdata = __archive_write_program_allocate("lrzip"); if (data->pdata == NULL) { free(data); archive_set_error(_a, ENOMEM, "Can't allocate memory"); @@ -107,6 +107,8 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key, data->compression = gzip; else if (strcmp(value, "lzo") == 0) data->compression = lzo; + else if (strcmp(value, "none") == 0) + data->compression = none; else if (strcmp(value, "zpaq") == 0) data->compression = zpaq; else @@ -148,6 +150,9 @@ archive_write_lrzip_open(struct archive_write_filter *f) case lzo: archive_strcat(&as, " -l"); break; + case none: + archive_strcat(&as, " -n"); + break; case zpaq: archive_strcat(&as, " -z"); break; diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c new file mode 100644 index 000000000000..1d0ab8c56a16 --- /dev/null +++ b/libarchive/archive_write_add_filter_lz4.c @@ -0,0 +1,707 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LZ4_H +#include <lz4.h> +#endif +#ifdef HAVE_LZ4HC_H +#include <lz4hc.h> +#endif + +#include "archive.h" +#include "archive_endian.h" +#include "archive_private.h" +#include "archive_write_private.h" +#include "archive_xxhash.h" + +#define LZ4_MAGICNUMBER 0x184d2204 + +struct private_data { + int compression_level; + unsigned header_written:1; + unsigned version_number:1; + unsigned block_independence:1; + unsigned block_checksum:1; + unsigned stream_size:1; + unsigned stream_checksum:1; + unsigned preset_dictionary:1; + unsigned block_maximum_size:3; +#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 + int64_t total_in; + char *out; + char *out_buffer; + size_t out_buffer_size; + size_t out_block_size; + char *in; + char *in_buffer_allocated; + char *in_buffer; + size_t in_buffer_size; + size_t block_size; + + void *xxh32_state; + void *lz4_stream; +#else + struct archive_write_program_data *pdata; +#endif +}; + +static int archive_filter_lz4_close(struct archive_write_filter *); +static int archive_filter_lz4_free(struct archive_write_filter *); +static int archive_filter_lz4_open(struct archive_write_filter *); +static int archive_filter_lz4_options(struct archive_write_filter *, + const char *, const char *); +static int archive_filter_lz4_write(struct archive_write_filter *, + const void *, size_t); + +/* + * Add a lz4 compression filter to this write handle. + */ +int +archive_write_add_filter_lz4(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_data *data; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + + /* + * Setup default settings. + */ + data->compression_level = 1; + data->version_number = 0x01; + data->block_independence = 1; + data->block_checksum = 0; + data->stream_size = 0; + data->stream_checksum = 1; + data->preset_dictionary = 0; + data->block_maximum_size = 7; + + /* + * Setup a filter setting. + */ + f->data = data; + f->options = &archive_filter_lz4_options; + f->close = &archive_filter_lz4_close; + f->free = &archive_filter_lz4_free; + f->open = &archive_filter_lz4_open; + f->code = ARCHIVE_FILTER_LZ4; + f->name = "lz4"; +#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 + return (ARCHIVE_OK); +#else + /* + * We don't have lz4 library, and execute external lz4 program + * instead. + */ + data->pdata = __archive_write_program_allocate("lz4"); + if (data->pdata == NULL) { + free(data); + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + data->compression_level = 0; + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Using external lz4 program"); + return (ARCHIVE_WARN); +#endif +} + +/* + * Set write options. + */ +static int +archive_filter_lz4_options(struct archive_write_filter *f, + const char *key, const char *value) +{ + struct private_data *data = (struct private_data *)f->data; + + if (strcmp(key, "compression-level") == 0) { + int val; + if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) || + value[1] != '\0') + return (ARCHIVE_WARN); + +#ifndef HAVE_LZ4HC_H + if(val >= 3) + { + archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, + "High compression not included in this build"); + return (ARCHIVE_FATAL); + } +#endif + data->compression_level = val; + return (ARCHIVE_OK); + } + if (strcmp(key, "stream-checksum") == 0) { + data->stream_checksum = value != NULL; + return |