diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ab6291332..7cb9276467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,19 +246,15 @@ OPTION(ENABLE_CPIO "Enable cpio building" ON) OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE) OPTION(ENABLE_CAT "Enable cat building" ON) OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE) -IF(WIN32 AND NOT CYGWIN) - SET(ENABLE_UNZIP FALSE) - SET(ENABLE_UNZIP_SHARED FALSE) -ELSE() - OPTION(ENABLE_UNZIP "Enable unzip building" ON) - OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE) -ENDIF() +OPTION(ENABLE_UNZIP "Enable unzip building" ON) +OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE) OPTION(ENABLE_XATTR "Enable extended attribute support" ON) OPTION(ENABLE_ACL "Enable ACL support" ON) OPTION(ENABLE_ICONV "Enable iconv support" ON) OPTION(ENABLE_TEST "Enable unit and regression tests" ON) OPTION(ENABLE_COVERAGE "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)" FALSE) OPTION(ENABLE_INSTALL "Enable installing of libraries" ON) +OPTION(ENABLE_CLANG_TIDY "Enable custom build-time code health checks" OFF) IF(WIN32 AND MSVC) OPTION(MSVC_USE_STATIC_CRT "Use static CRT" OFF) @@ -341,27 +337,69 @@ INCLUDE(CheckTypeSize) # # Generate list.h # -MACRO (GENERATE_LIST_H _listfile _cmlist __list_sources) +MACRO (GENERATE_LIST_H _listfile _cmlist _target __list_sources) SET(_argv ${ARGV}) - # Remove _listfile and _cmlist from _argv - LIST(REMOVE_AT _argv 0 1) - IF (NOT EXISTS "${_listfile}" OR - ${_cmlist} IS_NEWER_THAN "${_listfile}") - - MESSAGE(STATUS "Generating ${_listfile}") - FILE(WRITE ${_listfile} "") - FOREACH (testfile ${_argv}) - IF (testfile MATCHES "^test_[^/]+[.]c$") - FILE(STRINGS ${testfile} testvar REGEX "^DEFINE_TEST") - FOREACH (deftest ${testvar}) - FILE(APPEND ${_listfile} "${deftest}\n") - ENDFOREACH (deftest) - ENDIF (testfile MATCHES "^test_[^/]+[.]c$") - ENDFOREACH (testfile) - - ENDIF (NOT EXISTS "${_listfile}" OR - ${_cmlist} IS_NEWER_THAN "${_listfile}") + # Remove _listfile, _cmlist, and _target from _argv + LIST(REMOVE_AT _argv 0 1 2) + # Convert source paths to absolute so discover_tests.cmake can find them + # regardless of the working directory when cmake -P runs. + SET(_gl_abs_sources "") + FOREACH(_gl_src ${_argv}) + IF(IS_ABSOLUTE "${_gl_src}") + LIST(APPEND _gl_abs_sources "${_gl_src}") + ELSE() + LIST(APPEND _gl_abs_sources "${CMAKE_CURRENT_SOURCE_DIR}/${_gl_src}") + ENDIF() + ENDFOREACH() + # Stamp out discover_tests.cmake with source list and output path baked in. + SET(SOURCES "${_gl_abs_sources}") + SET(OUTPUT "${_listfile}") + CONFIGURE_FILE( + ${CMAKE_SOURCE_DIR}/build/cmake/discover_tests.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/discover_tests.cmake + @ONLY) + UNSET(SOURCES) + UNSET(OUTPUT) + # list.h is regenerated at build time whenever any test .c file changes. + ADD_CUSTOM_COMMAND( + OUTPUT "${_listfile}" + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/discover_tests.cmake + DEPENDS ${_gl_abs_sources} + COMMENT "Generating ${_listfile}" + VERBATIM) + # Attaching list.h to the target tells CMake to build it before compiling. + TARGET_SOURCES(${_target} PRIVATE "${_listfile}") ENDMACRO (GENERATE_LIST_H) + +# +# Register individual tests with CTest by scanning DEFINE_TEST() declarations. +# +MACRO (DISCOVER_TESTS _component __list_sources) + IF(ENABLE_TEST) + SET(_sources ${ARGV}) + LIST(REMOVE_AT _sources 0) + FOREACH (_src ${_sources}) + IF (_src MATCHES "test_[^/]+\\.c$") + FILE(STRINGS "${_src}" _lines REGEX "^DEFINE_TEST\(.*\)") + FOREACH (_line ${_lines}) + STRING(REGEX MATCH "DEFINE_TEST\((.*)\)" _full_match ${_line}) + # CMake REGEX seems to handle ( and \( rather inconsistently... + # So we go through an extra dance to definitively strip the parens + STRING(REPLACE "(" "" _test_name_with_paren "${CMAKE_MATCH_1}") + STRING(REPLACE ")" "" _test_name "${_test_name_with_paren}") + SET(_full_test_name "${_component}_${_test_name}") + ADD_TEST(NAME ${_full_test_name} + COMMAND ${_component}_test -vv + -r ${CMAKE_CURRENT_SOURCE_DIR} + -s + ${_test_name}) + SET_TESTS_PROPERTIES(${_full_test_name} PROPERTIES SKIP_RETURN_CODE 2) + ENDFOREACH (_line) + ENDIF (_src MATCHES "test_[^/]+\\.c$") + ENDFOREACH (_src) + ENDIF(ENABLE_TEST) +ENDMACRO (DISCOVER_TESTS __list_sources) + # # Generate installation rules for man pages. # @@ -663,7 +701,7 @@ IF(ENABLE_ZSTD) ENDIF() FIND_PATH(ZSTD_INCLUDE_DIR zstd.h HINTS ${PC_ZSTD_INCLUDEDIR} ${PC_ZSTD_INCLUDE_DIRS}) - FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd HINTS ${PC_ZSTD_LIBDIR} ${PC_ZSTD_LIBRARY_DIRS}) + FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd zstd_static HINTS ${PC_ZSTD_LIBDIR} ${PC_ZSTD_LIBRARY_DIRS}) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) ELSE(ENABLE_ZSTD) @@ -732,6 +770,7 @@ CHECK_C_SOURCE_COMPILES("#include #include int main(void) { return FS_IOC_GETFLAGS; }" HAVE_WORKING_FS_IOC_GETFLAGS) +LA_CHECK_INCLUDE_FILE("intsafe.h" HAVE_INTSAFE_H) LA_CHECK_INCLUDE_FILE("linux/magic.h" HAVE_LINUX_MAGIC_H) LA_CHECK_INCLUDE_FILE("locale.h" HAVE_LOCALE_H) LA_CHECK_INCLUDE_FILE("membership.h" HAVE_MEMBERSHIP_H) @@ -746,7 +785,9 @@ LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H) LA_CHECK_INCLUDE_FILE("signal.h" HAVE_SIGNAL_H) LA_CHECK_INCLUDE_FILE("spawn.h" HAVE_SPAWN_H) LA_CHECK_INCLUDE_FILE("stdarg.h" HAVE_STDARG_H) +LA_CHECK_INCLUDE_FILE("stdckdint.h" HAVE_STDCKDINT_H) LA_CHECK_INCLUDE_FILE("stdint.h" HAVE_STDINT_H) +LA_CHECK_INCLUDE_FILE("stdio.h" HAVE_STDIO_H) LA_CHECK_INCLUDE_FILE("stdlib.h" HAVE_STDLIB_H) LA_CHECK_INCLUDE_FILE("string.h" HAVE_STRING_H) LA_CHECK_INCLUDE_FILE("strings.h" HAVE_STRINGS_H) @@ -780,12 +821,22 @@ IF(ENABLE_CNG) LA_CHECK_INCLUDE_FILE("bcrypt.h" HAVE_BCRYPT_H) IF(HAVE_BCRYPT_H) LIST(APPEND ADDITIONAL_LIBS "bcrypt") + # bcrypt supports these algorithms on all available versions + SET(ARCHIVE_CRYPTO_MD5 1) + SET(ARCHIVE_CRYPTO_MD5_WIN 1) + SET(ARCHIVE_CRYPTO_SHA1 1) + SET(ARCHIVE_CRYPTO_SHA1_WIN 1) + SET(ARCHIVE_CRYPTO_SHA256 1) + SET(ARCHIVE_CRYPTO_SHA256_WIN 1) + SET(ARCHIVE_CRYPTO_SHA384 1) + SET(ARCHIVE_CRYPTO_SHA384_WIN 1) + SET(ARCHIVE_CRYPTO_SHA512 1) + SET(ARCHIVE_CRYPTO_SHA512_WIN 1) ENDIF(HAVE_BCRYPT_H) ELSE(ENABLE_CNG) UNSET(HAVE_BCRYPT_H CACHE) ENDIF(ENABLE_CNG) # Following files need windows.h, so we should test it after windows.h test. -LA_CHECK_INCLUDE_FILE("wincrypt.h" HAVE_WINCRYPT_H) LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H) # @@ -813,11 +864,14 @@ IF(ENABLE_MBEDTLS) LIST(APPEND ADDITIONAL_LIBS ${MBEDCRYPTO_LIBRARY}) INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIRS}) + CMAKE_PUSH_CHECK_STATE() LIST(APPEND CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIRS}) LA_CHECK_INCLUDE_FILE("mbedtls/aes.h" HAVE_MBEDTLS_AES_H) LA_CHECK_INCLUDE_FILE("mbedtls/md.h" HAVE_MBEDTLS_MD_H) LA_CHECK_INCLUDE_FILE("mbedtls/pkcs5.h" HAVE_MBEDTLS_PKCS5_H) LA_CHECK_INCLUDE_FILE("mbedtls/version.h" HAVE_MBEDTLS_VERSION_H) + CMAKE_POP_CHECK_STATE() + ENDIF(MBEDTLS_FOUND) MARK_AS_ADVANCED(CLEAR MBEDTLS_INCLUDE_DIRS) MARK_AS_ADVANCED(CLEAR MBEDCRYPTO_LIBRARY) @@ -833,6 +887,7 @@ IF(ENABLE_NETTLE) LIST(APPEND ADDITIONAL_LIBS ${NETTLE_LIBRARIES}) INCLUDE_DIRECTORIES(${NETTLE_INCLUDE_DIR}) + CMAKE_PUSH_CHECK_STATE() LIST(APPEND CMAKE_REQUIRED_INCLUDES ${NETTLE_INCLUDE_DIR}) LA_CHECK_INCLUDE_FILE("nettle/aes.h" HAVE_NETTLE_AES_H) LA_CHECK_INCLUDE_FILE("nettle/hmac.h" HAVE_NETTLE_HMAC_H) @@ -841,6 +896,8 @@ IF(ENABLE_NETTLE) LA_CHECK_INCLUDE_FILE("nettle/ripemd160.h" HAVE_NETTLE_RIPEMD160_H) LA_CHECK_INCLUDE_FILE("nettle/sha.h" HAVE_NETTLE_SHA_H) LA_CHECK_INCLUDE_FILE("nettle/version.h" HAVE_NETTLE_VERSION_H) + CMAKE_POP_CHECK_STATE() + ENDIF(NETTLE_FOUND) MARK_AS_ADVANCED(CLEAR NETTLE_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR NETTLE_LIBRARIES) @@ -856,11 +913,15 @@ IF(ENABLE_OPENSSL AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") SET(HAVE_LIBCRYPTO 1) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_CRYPTO_LIBRARY}) + + CMAKE_PUSH_CHECK_STATE() SET(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) LA_CHECK_INCLUDE_FILE("openssl/evp.h" HAVE_OPENSSL_EVP_H) LA_CHECK_INCLUDE_FILE("openssl/opensslv.h" HAVE_OPENSSL_OPENSSLV_H) CHECK_FUNCTION_EXISTS(PKCS5_PBKDF2_HMAC_SHA1 HAVE_PKCS5_PBKDF2_HMAC_SHA1) + CMAKE_POP_CHECK_STATE() + ENDIF(OPENSSL_FOUND) ELSE() SET(OPENSSL_FOUND FALSE) # Override cached value @@ -881,9 +942,13 @@ ENDIF(NOT OPENSSL_FOUND) # libbsd for readpassphrase on Haiku IF("${CMAKE_SYSTEM_NAME}" MATCHES "Haiku") MESSAGE(STATUS "Adding libbsd for Haiku") + + CMAKE_PUSH_CHECK_STATE() SET(CMAKE_REQUIRED_LIBRARIES "bsd") FIND_LIBRARY(LIBBSD_LIBRARY NAMES bsd) LIST(APPEND ADDITIONAL_LIBS ${LIBBSD_LIBRARY}) + CMAKE_POP_CHECK_STATE() + ENDIF("${CMAKE_SYSTEM_NAME}" MATCHES "Haiku") # @@ -994,85 +1059,6 @@ main(int argc, char **argv) ENDFOREACH(ALGORITHM ${ALGORITHMS}) ENDMACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION) -# -# CRYPTO functions on Windows is defined at archive_windows.c, thus we do not -# need the test what the functions can be mapped to archive_{crypto name}_init, -# archive_{crypto name}_update and archive_{crypto name}_final. -# The functions on Windows use CALG_{crypto name} macro to create a crypt object -# and then we need to know what CALG_{crypto name} macros is available to show -# ARCHIVE_CRYPTO_{crypto name}_WIN macros because Windows 2000 and earlier version -# of Windows XP do not support SHA256, SHA384 and SHA512. -# -MACRO(CHECK_CRYPTO_WIN CRYPTO_LIST) - IF(WIN32 AND NOT CYGWIN) - FOREACH(CRYPTO ${CRYPTO_LIST}) - IF(NOT ARCHIVE_CRYPTO_${CRYPTO}) - IF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN) - STRING(TOUPPER "${CRYPTO}" crypto) - SET(ALGID "") - IF ("${CRYPTO}" MATCHES "^MD5$") - SET(ALGID "CALG_MD5") - ENDIF ("${CRYPTO}" MATCHES "^MD5$") - IF ("${CRYPTO}" MATCHES "^SHA1$") - SET(ALGID "CALG_SHA1") - ENDIF ("${CRYPTO}" MATCHES "^SHA1$") - IF ("${CRYPTO}" MATCHES "^SHA256$") - SET(ALGID "CALG_SHA_256") - ENDIF ("${CRYPTO}" MATCHES "^SHA256$") - IF ("${CRYPTO}" MATCHES "^SHA384$") - SET(ALGID "CALG_SHA_384") - ENDIF ("${CRYPTO}" MATCHES "^SHA384$") - IF ("${CRYPTO}" MATCHES "^SHA512$") - SET(ALGID "CALG_SHA_512") - ENDIF ("${CRYPTO}" MATCHES "^SHA512$") - - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h) - FILE(READ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/confdefs.h" - CONFDEFS_H) - - SET(SOURCE "${CONFDEFS_H} - -#define ${crypto}_COMPILE_TEST -#include -#include - -int -main(int argc, char **argv) -{ - return ${ALGID}; -} -") - SET(SOURCE_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check_crypto_win.c") - - FILE(WRITE "${SOURCE_FILE}" "${SOURCE}") - MESSAGE(STATUS "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN") - - TRY_COMPILE(ARCHIVE_CRYPTO_${CRYPTO}_WIN - ${CMAKE_BINARY_DIR} - ${SOURCE_FILE} - CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_BINARY_DIR};${CMAKE_CURRENT_SOURCE_DIR}/libarchive" - OUTPUT_VARIABLE OUTPUT) - - IF (ARCHIVE_CRYPTO_${CRYPTO}_WIN) - MESSAGE(STATUS - "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- found") - SET(ARCHIVE_CRYPTO_${CRYPTO} 1) - ELSE (ARCHIVE_CRYPTO_${CRYPTO}_WIN) - MESSAGE(STATUS - "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN -- not found") - FILE(APPEND - ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Checking support for ARCHIVE_CRYPTO_${CRYPTO}_WIN failed with the following output:\n" - "${OUTPUT}\n" - "Source file was:\n${SOURCE}\n") - ENDIF (ARCHIVE_CRYPTO_${CRYPTO}_WIN) - ENDIF(NOT DEFINED ARCHIVE_CRYPTO_${CRYPTO}_WIN) - ENDIF(NOT ARCHIVE_CRYPTO_${CRYPTO}) - ENDFOREACH(CRYPTO) - ENDIF(WIN32 AND NOT CYGWIN) -ENDMACRO(CHECK_CRYPTO_WIN CRYPTO_LIST) - # # Find iconv # POSIX defines the second arg as const char ** @@ -1510,6 +1496,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R) CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R) CHECK_FUNCTION_EXISTS_GLIBC(getresgid HAVE_GETRESGID) CHECK_FUNCTION_EXISTS_GLIBC(getresuid HAVE_GETRESUID) +CHECK_FUNCTION_EXISTS_GLIBC(gettimeofday HAVE_GETTIMEOFDAY) CHECK_FUNCTION_EXISTS_GLIBC(getvfsbyname HAVE_GETVFSBYNAME) CHECK_FUNCTION_EXISTS_GLIBC(gmtime_r HAVE_GMTIME_R) CHECK_FUNCTION_EXISTS_GLIBC(issetugid HAVE_ISSETUGID) @@ -2175,18 +2162,19 @@ ENDIF(ENABLE_ACL) # Check MD5/RMD160/SHA support # NOTE: Crypto checks must be run last before generating config.h # -CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" LIBC) -CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC2) -CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC3) -CHECK_CRYPTO("MD5;SHA1;SHA256;SHA384;SHA512" LIBSYSTEM) -CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" MBEDTLS) -CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" NETTLE) -CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" OPENSSL) +IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") + CHECK_CRYPTO("MD5;SHA1;SHA256;SHA384;SHA512" LIBSYSTEM) +ELSE() + CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" LIBC) + CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC2) + CHECK_CRYPTO("SHA256;SHA384;SHA512" LIBC3) + CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" MBEDTLS) + CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" NETTLE) + CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA384;SHA512" OPENSSL) -# Libmd has to be probed after OpenSSL. -CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA512" LIBMD) - -CHECK_CRYPTO_WIN("MD5;SHA1;SHA256;SHA384;SHA512") + # Libmd has to be probed after OpenSSL. + CHECK_CRYPTO("MD5;RMD160;SHA1;SHA256;SHA512" LIBMD) +ENDIF() # Check visibility annotations SET(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") @@ -2242,6 +2230,13 @@ IF(ENABLE_TEST) ADD_CUSTOM_TARGET(run_all_tests) ENDIF(ENABLE_TEST) +IF(ENABLE_CLANG_TIDY) + IF(NOT CLANG_TIDY_PATH) + SET(CLANG_TIDY_PATH clang-tidy) + ENDIF(NOT CLANG_TIDY_PATH) + SET(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_PATH}) +ENDIF(ENABLE_CLANG_TIDY) + add_subdirectory(libarchive) add_subdirectory(cat) add_subdirectory(tar) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index 196a49b1fa..76864a909e 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -666,6 +666,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the `getpwuid_r' function. */ #cmakedefine HAVE_GETPWUID_R 1 +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + /* Define to 1 if you have the `getvfsbyname' function. */ #cmakedefine HAVE_GETVFSBYNAME 1 @@ -684,6 +687,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ICONV_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTSAFE_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 @@ -1025,9 +1031,15 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDCKDINT_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDIO_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 diff --git a/build/cmake/discover_tests.cmake.in b/build/cmake/discover_tests.cmake.in new file mode 100644 index 0000000000..1a054bc63f --- /dev/null +++ b/build/cmake/discover_tests.cmake.in @@ -0,0 +1,13 @@ +# Generated by CMake at configure time -- do not edit. +# Scans test source files for DEFINE_TEST() declarations and writes list.h. +set(_sources @SOURCES@) +set(_output "@OUTPUT@") +file(WRITE "${_output}" "") +foreach(_src ${_sources}) + if(_src MATCHES "test_[^/]+\\.c$") + file(STRINGS "${_src}" _lines REGEX "^DEFINE_TEST") + foreach(_line ${_lines}) + file(APPEND "${_output}" "${_line}\n") + endforeach() + endif() +endforeach() diff --git a/build/version b/build/version index bd997daca6..c9c7ecb809 100644 --- a/build/version +++ b/build/version @@ -1 +1 @@ -3008006 +3008009 diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 4fb917132c..b29663742f 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -40,6 +40,7 @@ SET(libarchive_SOURCES archive_entry_xattr.c archive_hmac.c archive_hmac_private.h + archive_integer.h archive_match.c archive_openssl_evp_private.h archive_openssl_hmac_private.h diff --git a/libarchive/archive.h b/libarchive/archive.h index de472a18c3..a7de6f6dd8 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -34,7 +34,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3008006 +#define ARCHIVE_VERSION_NUMBER 3008009 #include #include /* for wchar_t */ @@ -177,7 +177,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.8.6" +#define ARCHIVE_VERSION_ONLY_STRING "3.8.9" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -210,7 +210,9 @@ __LA_DECL const char * archive_openssl_version(void); __LA_DECL const char * archive_libmd_version(void); __LA_DECL const char * archive_commoncrypto_version(void); __LA_DECL const char * archive_cng_version(void); +#if ARCHIVE_VERSION_NUMBER < 4000000 __LA_DECL const char * archive_wincrypt_version(void); +#endif __LA_DECL const char * archive_librichacl_version(void); __LA_DECL const char * archive_libacl_version(void); __LA_DECL const char * archive_libattr_version(void); @@ -653,7 +655,6 @@ __LA_DECL int archive_read_data_block(struct archive *a, /*- * Some convenience functions that are built on archive_read_data: * 'skip': skips entire entry - * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ __LA_DECL int archive_read_data_skip(struct archive *); @@ -1234,7 +1235,7 @@ __LA_DECL int archive_match_exclude_entry(struct archive *, int _flag, struct archive_entry *); /* - * Test if a file is excluded by its uid ,gid, uname or gname. + * Test if a file is excluded by its uid, gid, uname or gname. * The conditions are set by following functions. */ __LA_DECL int archive_match_owner_excluded(struct archive *, diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c index ab601833de..f1b1232f5e 100644 --- a/libarchive/archive_acl.c +++ b/libarchive/archive_acl.c @@ -56,9 +56,7 @@ static int archive_acl_add_entry_len_l(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name, size_t len, struct archive_string_conv *sc); static int archive_acl_text_want_type(struct archive_acl *acl, int flags); -static size_t archive_acl_text_len(struct archive_acl *acl, int want_type, - int flags, int wide, struct archive *a, - struct archive_string_conv *sc); +static size_t archive_acl_text_empty(struct archive_acl *acl, int want_type); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static int ismode_w(const wchar_t *start, const wchar_t *end, int *result); static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, @@ -67,9 +65,9 @@ static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); -static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, +static void append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id); -static void append_id_w(wchar_t **wp, int id); +static void append_id_w(struct archive_wstring *ws, int id); static int isint(const char *start, const char *end, int *result); static int ismode(const char *start, const char *end, int *result); static int is_nfs4_flags(const char *start, const char *end, @@ -78,9 +76,9 @@ static int is_nfs4_perms(const char *start, const char *end, int *result); static void next_field(const char **p, size_t *l, const char **start, const char **end, char *sep); -static void append_entry(char **p, const char *prefix, int type, +static void append_entry(struct archive_string *s, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id); -static void append_id(char **p, int id); +static void append_id(struct archive_string *s, int id); static const struct { const int perm; @@ -539,20 +537,12 @@ archive_acl_text_want_type(struct archive_acl *acl, int flags) } /* - * Calculate ACL text string length + * Check if ACL text would be empty */ static size_t -archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, - int wide, struct archive *a, struct archive_string_conv *sc) { +archive_acl_text_empty(struct archive_acl *acl, int want_type) { struct archive_acl_entry *ap; - const char *name; - const wchar_t *wname; - int count, idlen, tmp, r; - size_t length; - size_t len; - count = 0; - length = 0; for (ap = acl->acl_head; ap != NULL; ap = ap->next) { if ((ap->type & want_type) == 0) continue; @@ -565,107 +555,11 @@ archive_acl_text_len(struct archive_acl *acl, int want_type, int flags, || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER)) continue; - count++; - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0 - && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) - length += 8; /* "default:" */ - switch (ap->tag) { - case ARCHIVE_ENTRY_ACL_USER_OBJ: - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - length += 6; /* "owner@" */ - break; - } - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_USER: - case ARCHIVE_ENTRY_ACL_MASK: - length += 4; /* "user", "mask" */ - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - length += 6; /* "group@" */ - break; - } - /* FALLTHROUGH */ - case ARCHIVE_ENTRY_ACL_GROUP: - case ARCHIVE_ENTRY_ACL_OTHER: - length += 5; /* "group", "other" */ - break; - case ARCHIVE_ENTRY_ACL_EVERYONE: - length += 9; /* "everyone@" */ - break; - } - length += 1; /* colon after tag */ - if (ap->tag == ARCHIVE_ENTRY_ACL_USER || - ap->tag == ARCHIVE_ENTRY_ACL_GROUP) { - if (wide) { - r = archive_mstring_get_wcs(a, &ap->name, - &wname); - if (r == 0 && wname != NULL) - length += wcslen(wname); - else if (r < 0 && errno == ENOMEM) - return (0); - else - length += sizeof(uid_t) * 3 + 1; - } else { - r = archive_mstring_get_mbs_l(a, &ap->name, &name, - &len, sc); - if (r != 0) - return (0); - if (len > 0 && name != NULL) - length += len; - else - length += sizeof(uid_t) * 3 + 1; - } - length += 1; /* colon after user or group name */ - } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) - length += 1; /* 2nd colon empty user,group or other */ - if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) - && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) - && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER - || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) { - /* Solaris has no colon after other: and mask: */ - length = length - 1; - } - - if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - /* rwxpdDaARWcCos:fdinSFI:deny */ - length += 27; - if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0) - length += 1; /* allow, alarm, audit */ - } else - length += 3; /* rwx */ - - if ((ap->tag == ARCHIVE_ENTRY_ACL_USER || - ap->tag == ARCHIVE_ENTRY_ACL_GROUP) && - (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) { - length += 1; /* colon */ - /* ID digit count */ - idlen = 1; - tmp = ap->id; - while (tmp > 9) { - tmp = tmp / 10; - idlen++; - } - length += idlen; - } - length ++; /* entry separator */ + return (0); } - /* Add filemode-mapping access entries to the length */ - if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) { - /* "user::rwx\ngroup::rwx\nother:rwx\n" */ - length += 31; - } else { - /* "user::rwx\ngroup::rwx\nother::rwx\n" */ - length += 32; - } - } else if (count == 0) - return (0); - - /* The terminating character is included in count */ - return (length); + return (1); } /* @@ -676,15 +570,12 @@ wchar_t * archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive *a) { - int count; - size_t length; - size_t len; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct archive_acl_entry *ap; + struct archive_wstring ws; int id, r, want_type; - wchar_t *wp, *ws; want_type = archive_acl_text_want_type(acl, flags); @@ -695,9 +586,7 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; - length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL); - - if (length == 0) + if (archive_acl_text_empty(acl, want_type)) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) @@ -705,28 +594,20 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, else separator = L'\n'; - /* Now, allocate the string and actually populate it. */ - wp = ws = malloc(length * sizeof(*wp)); - if (wp == NULL) { - if (errno == ENOMEM) - __archive_errx(1, "No memory"); - return (NULL); - } - count = 0; + archive_string_init(&ws); if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *wp++ = separator; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_wstrappend_wchar(&ws, separator); + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *wp++ = separator; - append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_wstrappend_wchar(&ws, separator); + append_entry_w(&ws, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); - count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { @@ -748,108 +629,95 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags, prefix = NULL; r = archive_mstring_get_wcs(a, &ap->name, &wname); if (r == 0) { - if (count > 0) - *wp++ = separator; - if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) + if (ws.length > 0) + archive_wstrappend_wchar(&ws, separator); + if ((flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) || + wname == NULL) id = ap->id; else id = -1; - append_entry_w(&wp, prefix, ap->type, ap->tag, flags, + append_entry_w(&ws, prefix, ap->type, ap->tag, flags, wname, ap->permset, id); - count++; } else if (r < 0 && errno == ENOMEM) { - free(ws); + archive_wstring_free(&ws); return (NULL); } } - /* Add terminating character */ - *wp++ = L'\0'; - - len = wcslen(ws); - - if (len > length - 1) - __archive_errx(1, "Buffer overrun"); - if (text_len != NULL) - *text_len = len; + *text_len = ws.length; - return (ws); + return (ws.s); } static void -append_id_w(wchar_t **wp, int id) +append_id_w(struct archive_wstring *ws, int id) { if (id < 0) id = 0; if (id > 9) - append_id_w(wp, id / 10); - *(*wp)++ = L"0123456789"[id % 10]; + append_id_w(ws, id / 10); + archive_wstrappend_wchar(ws, L"0123456789"[id % 10]); } static void -append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, +append_entry_w(struct archive_wstring *ws, const wchar_t *prefix, int type, int tag, int flags, const wchar_t *wname, int perm, int id) { int i; - if (prefix != NULL) { - wcscpy(*wp, prefix); - *wp += wcslen(*wp); - } + if (prefix != NULL) + archive_wstrcat(ws, prefix); switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - wcscpy(*wp, L"owner@"); + archive_wstrcat(ws, L"owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: - wcscpy(*wp, L"user"); + archive_wstrcat(ws, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - wcscpy(*wp, L"group@"); + archive_wstrcat(ws, L"group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: - wcscpy(*wp, L"group"); + archive_wstrcat(ws, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: - wcscpy(*wp, L"mask"); + archive_wstrcat(ws, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: - wcscpy(*wp, L"other"); + archive_wstrcat(ws, L"other"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: - wcscpy(*wp, L"everyone@"); + archive_wstrcat(ws, L"everyone@"); wname = NULL; id = -1; break; default: - **wp = '\0'; break; } - *wp += wcslen(*wp); - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (wname != NULL) { - wcscpy(*wp, wname); - *wp += wcslen(*wp); + archive_wstrcat(ws, wname); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id_w(wp, id); + append_id_w(ws, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } @@ -857,51 +725,49 @@ append_entry_w(wchar_t **wp, const wchar_t *prefix, int type, if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ - *(*wp)++ = (perm & 0444) ? L'r' : L'-'; - *(*wp)++ = (perm & 0222) ? L'w' : L'-'; - *(*wp)++ = (perm & 0111) ? L'x' : L'-'; + archive_wstrappend_wchar(ws, (perm & 0444) ? L'r' : L'-'); + archive_wstrappend_wchar(ws, (perm & 0222) ? L'w' : L'-'); + archive_wstrappend_wchar(ws, (perm & 0111) ? L'x' : L'-'); } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) - *(*wp)++ = nfsv4_acl_perm_map[i].wc; + archive_wstrappend_wchar(ws, nfsv4_acl_perm_map[i].wc); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*wp)++ = L'-'; + archive_wstrappend_wchar(ws, L'-'); } - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) - *(*wp)++ = nfsv4_acl_flag_map[i].wc; + archive_wstrappend_wchar(ws, nfsv4_acl_flag_map[i].wc); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*wp)++ = L'-'; + archive_wstrappend_wchar(ws, L'-'); } - *(*wp)++ = L':'; + archive_wstrappend_wchar(ws, L':'); switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - wcscpy(*wp, L"allow"); + archive_wstrcat(ws, L"allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: - wcscpy(*wp, L"deny"); + archive_wstrcat(ws, L"deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - wcscpy(*wp, L"audit"); + archive_wstrcat(ws, L"audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - wcscpy(*wp, L"alarm"); + archive_wstrcat(ws, L"alarm"); break; default: - *(*wp) = L'\0'; break; } - *wp += wcslen(*wp); } if (id != -1) { - *(*wp)++ = L':'; - append_id_w(wp, id); + archive_wstrappend_wchar(ws, L':'); + append_id_w(ws, id); } } @@ -913,15 +779,13 @@ char * archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, struct archive_string_conv *sc) { - int count; - size_t length; size_t len; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; + struct archive_string s; int id, r, want_type; - char *p, *s; want_type = archive_acl_text_want_type(acl, flags); @@ -932,9 +796,7 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT; - length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc); - - if (length == 0) + if (archive_acl_text_empty(acl, want_type)) return (NULL); if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA) @@ -942,28 +804,20 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, else separator = '\n'; - /* Now, allocate the string and actually populate it. */ - p = s = malloc(length * sizeof(*p)); - if (p == NULL) { - if (errno == ENOMEM) - __archive_errx(1, "No memory"); - return (NULL); - } - count = 0; + archive_string_init(&s); if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL, acl->mode & 0700, -1); - *p++ = separator; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_strappend_char(&s, separator); + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); - *p++ = separator; - append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, + archive_strappend_char(&s, separator); + append_entry(&s, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_OTHER, flags, NULL, acl->mode & 0007, -1); - count += 3; } for (ap = acl->acl_head; ap != NULL; ap = ap->next) { @@ -986,109 +840,95 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags, r = archive_mstring_get_mbs_l( NULL, &ap->name, &name, &len, sc); if (r != 0) { - free(s); + archive_string_free(&s); return (NULL); } - if (count > 0) - *p++ = separator; + if (s.length > 0) + archive_strappend_char(&s, separator); if (name == NULL || (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) { id = ap->id; } else { id = -1; } - append_entry(&p, prefix, ap->type, ap->tag, flags, name, + append_entry(&s, prefix, ap->type, ap->tag, flags, name, ap->permset, id); - count++; } - /* Add terminating character */ - *p++ = '\0'; - - len = strlen(s); - - if (len > length - 1) - __archive_errx(1, "Buffer overrun"); - if (text_len != NULL) - *text_len = len; + *text_len = s.length; - return (s); + return (s.s); } static void -append_id(char **p, int id) +append_id(struct archive_string *s, int id) { if (id < 0) id = 0; if (id > 9) - append_id(p, id / 10); - *(*p)++ = "0123456789"[id % 10]; + append_id(s, id / 10); + archive_strappend_char(s, "0123456789"[id % 10]); } static void -append_entry(char **p, const char *prefix, int type, +append_entry(struct archive_string *s, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id) { int i; - if (prefix != NULL) { - strcpy(*p, prefix); - *p += strlen(*p); - } + if (prefix != NULL) + archive_strcat(s, prefix); switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - strcpy(*p, "owner@"); + archive_strcat(s, "owner@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: - strcpy(*p, "user"); + archive_strcat(s, "user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: name = NULL; id = -1; if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - strcpy(*p, "group@"); + archive_strcat(s, "group@"); break; } /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: - strcpy(*p, "group"); + archive_strcat(s, "group"); break; case ARCHIVE_ENTRY_ACL_MASK: - strcpy(*p, "mask"); + archive_strcat(s, "mask"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: - strcpy(*p, "other"); + archive_strcat(s, "other"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: - strcpy(*p, "everyone@"); + archive_strcat(s, "everyone@"); name = NULL; id = -1; break; default: - **p = '\0'; break; } - *p += strlen(*p); - *(*p)++ = ':'; + archive_strappend_char(s, ':'); if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) || tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { if (name != NULL) { - strcpy(*p, name); - *p += strlen(*p); + archive_strcat(s, name); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { - append_id(p, id); + append_id(s, id); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) id = -1; } @@ -1096,51 +936,49 @@ append_entry(char **p, const char *prefix, int type, if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0) || (tag != ARCHIVE_ENTRY_ACL_OTHER && tag != ARCHIVE_ENTRY_ACL_MASK)) - *(*p)++ = ':'; + archive_strappend_char(s, ':'); } if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { /* POSIX.1e ACL perms */ - *(*p)++ = (perm & 0444) ? 'r' : '-'; - *(*p)++ = (perm & 0222) ? 'w' : '-'; - *(*p)++ = (perm & 0111) ? 'x' : '-'; + archive_strappend_char(s, (perm & 0444) ? 'r' : '-'); + archive_strappend_char(s, (perm & 0222) ? 'w' : '-'); + archive_strappend_char(s, (perm & 0111) ? 'x' : '-'); } else { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) - *(*p)++ = nfsv4_acl_perm_map[i].c; + archive_strappend_char(s, nfsv4_acl_perm_map[i].c); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*p)++ = '-'; + archive_strappend_char(s, '-'); } - *(*p)++ = ':'; + archive_strappend_char(s, ':'); for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) - *(*p)++ = nfsv4_acl_flag_map[i].c; + archive_strappend_char(s, nfsv4_acl_flag_map[i].c); else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) - *(*p)++ = '-'; + archive_strappend_char(s, '-'); } - *(*p)++ = ':'; + archive_strappend_char(s, ':'); switch (type) { case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - strcpy(*p, "allow"); + archive_strcat(s, "allow"); break; case ARCHIVE_ENTRY_ACL_TYPE_DENY: - strcpy(*p, "deny"); + archive_strcat(s, "deny"); break; case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - strcpy(*p, "audit"); + archive_strcat(s, "audit"); break; case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - strcpy(*p, "alarm"); + archive_strcat(s, "alarm"); break; default: - *(*p) = '\0'; break; } - *p += strlen(*p); } if (id != -1) { - *(*p)++ = ':'; - append_id(p, id); + archive_strappend_char(s, ':'); + append_id(s, id); } } @@ -1248,11 +1086,18 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ - isint_w(field[n + 1].start, field[n + 1].end, &id); + if (isint_w(field[n + 1].start, field[n + 1].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } /* Field n+3 is optional. */ - if (id == -1 && fields > n+3) - isint_w(field[n + 3].start, field[n + 3].end, - &id); + if (id == -1 && fields > n+3 && + isint_w(field[n + 3].start, field[n + 3].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } tag = 0; s = field[n].start; @@ -1367,7 +1212,10 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; - isint_w(name.start, name.end, &id); + if (isint_w(name.start, name.end, &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } else n = 0; @@ -1402,7 +1250,11 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, ret = ARCHIVE_WARN; continue; } - isint_w(field[4 + n].start, field[4 + n].end, &id); + if (isint_w(field[4 + n].start, field[4 + n].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } /* Add entry to the internal list. */ @@ -1436,8 +1288,8 @@ isint_w(const wchar_t *start, const wchar_t *end, int *result) if (*start < L'0' || *start > L'9') return (0); if (n > (INT_MAX / 10) || - (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) { - n = INT_MAX; + (n == INT_MAX / 10 && (*start - L'0') >= INT_MAX % 10)) { + return (-1); } else { n *= 10; n += *start - L'0'; @@ -1664,7 +1516,7 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, const char *end; } field[6], name; - const char *s, *st; + const char *s, *st, *text_end; int numfields, fields, n, r, sol, ret; int type, types, tag, permset, id; size_t len; @@ -1687,6 +1539,7 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, ret = ARCHIVE_OK; types = 0; + text_end = (text == NULL) ? text : text + length; while (text != NULL && length > 0 && *text != '\0') { /* @@ -1713,6 +1566,18 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, return (ARCHIVE_FATAL); } + if (field[0].start == text_end) { + /* + * Empty entry: next_field() consumed the rest of the + * buffer as separators or whitespace, leaving + * field[0].start one past the end. 'text' is not + * guaranteed to be NUL terminated, so the byte after it + * must not be dereferenced. + */ + ret = ARCHIVE_WARN; + continue; + } + if (*(field[0].start) == '#') { /* Comment, skip entry */ continue; @@ -1747,15 +1612,21 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, type = want_type; /* Check for a numeric ID in field n+1 or n+3. */ - isint(field[n + 1].start, field[n + 1].end, &id); + if (isint(field[n + 1].start, field[n + 1].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } /* Field n+3 is optional. */ - if (id == -1 && fields > (n + 3)) - isint(field[n + 3].start, field[n + 3].end, - &id); + if (id == -1 && fields > (n + 3) && + isint(field[n + 3].start, field[n + 3].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } tag = 0; s = field[n].start; - st = field[n].start + 1; len = field[n].end - field[n].start; if (len == 0) { @@ -1763,6 +1634,8 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, continue; } + st = s + 1; + switch (*s) { case 'u': if (len == 1 || (len == 4 @@ -1868,7 +1741,10 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, tag == ARCHIVE_ENTRY_ACL_GROUP) { n = 1; name = field[1]; - isint(name.start, name.end, &id); + if (isint(name.start, name.end, &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } else n = 0; @@ -1903,8 +1779,11 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text, ret = ARCHIVE_WARN; continue; } - isint(field[4 + n].start, field[4 + n].end, - &id); + if (isint(field[4 + n].start, field[4 + n].end, + &id) < 0) { + ret = ARCHIVE_WARN; + continue; + } } /* Add entry to the internal list. */ @@ -1938,8 +1817,8 @@ isint(const char *start, const char *end, int *result) if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10) || - (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) { - n = INT_MAX; + (n == INT_MAX / 10 && (*start - '0') >= INT_MAX % 10)) { + return (-1); } else { n *= 10; n += *start - '0'; @@ -2121,7 +2000,10 @@ next_field(const char **p, size_t *l, const char **start, (*p)++; (*l)--; } - *sep = **p; + if (*l > 0) + *sep = **p; + else + *sep = '\0'; /* Handle in-field comments */ if (*sep == '#') { @@ -2129,7 +2011,10 @@ next_field(const char **p, size_t *l, const char **start, (*p)++; (*l)--; } - *sep = **p; + if (*l > 0) + *sep = **p; + else + *sep = '\0'; } /* Skip separator. */ diff --git a/libarchive/archive_acl_private.h b/libarchive/archive_acl_private.h index 54e4439692..4df8a0ac3a 100644 --- a/libarchive/archive_acl_private.h +++ b/libarchive/archive_acl_private.h @@ -27,8 +27,10 @@ #define ARCHIVE_ACL_PRIVATE_H_INCLUDED #ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST #error This header is only to be used internally to libarchive. #endif +#endif #include "archive_string.h" diff --git a/libarchive/archive_check_magic.c b/libarchive/archive_check_magic.c index 6b8e0c5595..788a76070b 100644 --- a/libarchive/archive_check_magic.c +++ b/libarchive/archive_check_magic.c @@ -85,6 +85,7 @@ state_name(unsigned s) case ARCHIVE_STATE_NEW: return ("new"); case ARCHIVE_STATE_HEADER: return ("header"); case ARCHIVE_STATE_DATA: return ("data"); + case ARCHIVE_STATE_DATA_RECOVERY: return ("data_recovery"); case ARCHIVE_STATE_EOF: return ("eof"); case ARCHIVE_STATE_CLOSED: return ("closed"); case ARCHIVE_STATE_FATAL: return ("fatal"); @@ -148,14 +149,14 @@ __archive_check_magic(struct archive *a, unsigned int magic, if (!handle_type) { errmsg("PROGRAMMER ERROR: Function "); errmsg(function); - errmsg(" invoked with invalid archive handle.\n"); + errmsg(" invoked with invalid archive handle\n"); diediedie(); } if (a->magic != magic) { archive_set_error(a, -1, "PROGRAMMER ERROR: Function '%s' invoked" - " on '%s' archive object, which is not supported.", + " on '%s' archive object, which is not supported", function, handle_type); a->state = ARCHIVE_STATE_FATAL; diff --git a/libarchive/archive_cmdline.c b/libarchive/archive_cmdline.c index 23bb05d642..ffc4f54749 100644 --- a/libarchive/archive_cmdline.c +++ b/libarchive/archive_cmdline.c @@ -36,7 +36,6 @@ #include "archive_cmdline_private.h" #include "archive_string.h" -static int cmdline_set_path(struct archive_cmdline *, const char *); static int cmdline_add_arg(struct archive_cmdline *, const char *); static ssize_t @@ -98,7 +97,7 @@ get_argument(struct archive_string *as, const char *p) /* * Set up command line arguments. - * Returns ARCHIVE_OK if everything okey. + * Returns ARCHIVE_OK if everything okay. * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an * empty command line. * Returns ARCHIVE_FATAL if no memory. @@ -123,9 +122,12 @@ __archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) r = ARCHIVE_FAILED;/* An empty command path. */ goto exit_function; } - r = cmdline_set_path(data, as.s); - if (r != ARCHIVE_OK) + free(data->path); + data->path = strdup(as.s); + if (data->path == NULL) { + r = ARCHIVE_FATAL; goto exit_function; + } p = strrchr(as.s, '/'); if (p == NULL) p = as.s; @@ -158,23 +160,7 @@ exit_function: } /* - * Set the program path. - */ -static int -cmdline_set_path(struct archive_cmdline *data, const char *path) -{ - char *newptr; - - newptr = realloc(data->path, strlen(path) + 1); - if (newptr == NULL) - return (ARCHIVE_FATAL); - data->path = newptr; - strcpy(data->path, path); - return (ARCHIVE_OK); -} - -/* - * Add a argument for the program. + * Add an argument for the program. */ static int cmdline_add_arg(struct archive_cmdline *data, const char *arg) diff --git a/libarchive/archive_crc32.h b/libarchive/archive_crc32.h index d86a507ce7..cbd25291a9 100644 --- a/libarchive/archive_crc32.h +++ b/libarchive/archive_crc32.h @@ -43,28 +43,65 @@ static unsigned long crc32(unsigned long crc, const void *_p, size_t len) { - unsigned long crc2, b, i; const unsigned char *p = _p; - static volatile int crc_tbl_inited = 0; - static unsigned long crc_tbl[256]; + static const unsigned long crc_tbl[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; if (_p == NULL) return (0); - if (!crc_tbl_inited) { - for (b = 0; b < 256; ++b) { - crc2 = b; - for (i = 8; i > 0; --i) { - if (crc2 & 1) - crc2 = (crc2 >> 1) ^ 0xedb88320UL; - else - crc2 = (crc2 >> 1); - } - crc_tbl[b] = crc2; - } - crc_tbl_inited = 1; - } - crc = crc ^ 0xffffffffUL; /* A use of this loop is about 20% - 30% faster than * no use version in any optimization option of gcc. */ diff --git a/libarchive/archive_cryptor.c b/libarchive/archive_cryptor.c index b6a02fd1e7..85b20ac9ac 100644 --- a/libarchive/archive_cryptor.c +++ b/libarchive/archive_cryptor.c @@ -57,7 +57,7 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifdef _MSC_VER #pragma comment(lib, "Bcrypt.lib") #endif @@ -197,7 +197,7 @@ aes_ctr_release(archive_crypto_ctx *ctx) return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#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) diff --git a/libarchive/archive_cryptor_private.h b/libarchive/archive_cryptor_private.h index 1f9298ffdc..069eccd994 100644 --- a/libarchive/archive_cryptor_private.h +++ b/libarchive/archive_cryptor_private.h @@ -62,7 +62,7 @@ typedef struct { size_t encr_pos; } archive_crypto_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include #define ARCHIVE_CRYPTOR_USE_CNG 1 @@ -148,16 +148,6 @@ typedef struct { #else -#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA512_WIN) -#if defined(_WIN32) && !defined(__CYGWIN__) && !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) -#define ARCHIVE_CRYPTOR_USE_WINCRYPT 1 -#endif -#endif - #define AES_BLOCK_SIZE 16 #define AES_MAX_KEY_SIZE 32 typedef int archive_crypto_ctx; diff --git a/libarchive/archive_digest.c b/libarchive/archive_digest.c index 45fc36af11..ab6db12beb 100644 --- a/libarchive/archive_digest.c +++ b/libarchive/archive_digest.c @@ -44,16 +44,11 @@ /* * Message digest functions for Windows platform. */ -#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ - defined(ARCHIVE_CRYPTO_SHA512_WIN) +#if defined(HAVE_BCRYPT_H) /* * Initialize a Message digest. */ -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA static int win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) { @@ -72,30 +67,6 @@ win_crypto_init(Digest_CTX *ctx, const WCHAR *algo) ctx->valid = 1; return (ARCHIVE_OK); } -#else -static int -win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId) -{ - - ctx->valid = 0; - if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, - prov, CRYPT_VERIFYCONTEXT)) { - if (GetLastError() != (DWORD)NTE_BAD_KEYSET) - return (ARCHIVE_FAILED); - if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL, - prov, CRYPT_NEWKEYSET)) - return (ARCHIVE_FAILED); - } - - if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) { - CryptReleaseContext(ctx->cryptProv, 0); - return (ARCHIVE_FAILED); - } - - ctx->valid = 1; - return (ARCHIVE_OK); -} -#endif /* * Update a Message digest. @@ -107,42 +78,26 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) if (!ctx->valid) return (ARCHIVE_FAILED); -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)buf, (ULONG)len, 0); -#else - CryptHashData(ctx->hash, - (unsigned char *)(uintptr_t)buf, - (DWORD)len, 0); -#endif return (ARCHIVE_OK); } static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { -#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA) - DWORD siglen = (DWORD)bufsize; -#endif - if (!ctx->valid) return (ARCHIVE_FAILED); -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0); BCryptDestroyHash(ctx->hHash); BCryptCloseAlgorithmProvider(ctx->hAlg, 0); -#else - CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0); - CryptDestroyHash(ctx->hash); - CryptReleaseContext(ctx->cryptProv, 0); -#endif ctx->valid = 0; return (ARCHIVE_OK); } -#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */ +#endif /* defined(HAVE_BCRYPT_H) */ /* MD5 implementations */ @@ -234,11 +189,7 @@ __archive_md5final(archive_md5_ctx *ctx, void *md) static int __archive_md5init(archive_md5_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5)); -#endif } static int @@ -645,11 +596,7 @@ __archive_sha1final(archive_sha1_ctx *ctx, void *md) static int __archive_sha1init(archive_sha1_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1)); -#endif } static int @@ -925,11 +872,7 @@ __archive_sha256final(archive_sha256_ctx *ctx, void *md) static int __archive_sha256init(archive_sha256_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256)); -#endif } static int @@ -1177,11 +1120,7 @@ __archive_sha384final(archive_sha384_ctx *ctx, void *md) static int __archive_sha384init(archive_sha384_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384)); -#endif } static int @@ -1453,11 +1392,7 @@ __archive_sha512final(archive_sha512_ctx *ctx, void *md) static int __archive_sha512init(archive_sha512_ctx *ctx) { -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM)); -#else - return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512)); -#endif } static int diff --git a/libarchive/archive_digest_private.h b/libarchive/archive_digest_private.h index deb134e4f8..7db999450e 100644 --- a/libarchive/archive_digest_private.h +++ b/libarchive/archive_digest_private.h @@ -165,8 +165,7 @@ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ +#if defined(HAVE_BCRYPT_H) #include #define ARCHIVE_CRYPTO_CNG 1 typedef struct { @@ -174,15 +173,6 @@ typedef struct { BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; } Digest_CTX; -#else -#include -#include -#define ARCHIVE_CRYPTO_WINCRYPT 1 -typedef struct { - int valid; - HCRYPTPROV cryptProv; - HCRYPTHASH hash; -} Digest_CTX; #endif #endif diff --git a/libarchive/archive_endian.h b/libarchive/archive_endian.h index 83b2efa531..939acc6670 100644 --- a/libarchive/archive_endian.h +++ b/libarchive/archive_endian.h @@ -70,6 +70,22 @@ archive_be16dec(const void *pp) return ((p0 << 8) | p1); } +static inline uint32_t +archive_be24dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + /* 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 p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p0 << 16) | (p1 << 8) | p2); +} + + static inline uint32_t archive_be32dec(const void *pp) { @@ -108,6 +124,21 @@ archive_le16dec(const void *pp) return ((p1 << 8) | p0); } +static inline uint32_t +archive_le24dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + /* 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 p2 = p[2]; + unsigned int p1 = p[1]; + unsigned int p0 = p[0]; + + return ((p2 << 16) | (p1 << 8) | p0); +} + static inline uint32_t archive_le32dec(const void *pp) { @@ -170,6 +201,16 @@ archive_le16enc(void *pp, uint16_t u) p[1] = (u >> 8) & 0xff; } +static inline void +archive_le24enc(void *pp, uint32_t u) +{ + unsigned char *p = (unsigned char *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; +} + static inline void archive_le32enc(void *pp, uint32_t u) { diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index dc74430f80..5b8351b4a1 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -1239,7 +1239,7 @@ _archive_entry_copy_link_l(struct archive_entry *entry, } void -archive_entry_set_mode(struct archive_entry *entry, mode_t m) +archive_entry_set_mode(struct archive_entry *entry, __LA_MODE_T m) { entry->stat_valid = 0; entry->acl.mode = m; @@ -1314,7 +1314,7 @@ _archive_entry_copy_pathname_l(struct archive_entry *entry, } void -archive_entry_set_perm(struct archive_entry *entry, mode_t p) +archive_entry_set_perm(struct archive_entry *entry, __LA_MODE_T p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; @@ -1562,17 +1562,21 @@ void archive_entry_copy_mac_metadata(struct archive_entry *entry, const void *p, size_t s) { - free(entry->mac_metadata); - if (p == NULL || s == 0) { - entry->mac_metadata = NULL; - entry->mac_metadata_size = 0; - } else { - entry->mac_metadata_size = s; - entry->mac_metadata = malloc(s); - if (entry->mac_metadata == NULL) - abort(); - memcpy(entry->mac_metadata, p, s); - } + void *metadata; + + if (p == NULL || s == 0) { + free(entry->mac_metadata); + entry->mac_metadata = NULL; + entry->mac_metadata_size = 0; + } else { + metadata = malloc(s); + if (metadata == NULL) + abort(); + memcpy(metadata, p, s); + free(entry->mac_metadata); + entry->mac_metadata = metadata; + entry->mac_metadata_size = s; + } } /* Digest handling */ diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index f7f1c522ee..9c859a44e3 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -28,7 +28,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3008006 +#define ARCHIVE_VERSION_NUMBER 3008009 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -666,7 +666,7 @@ __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *, la_int64_t /* offset */, la_int64_t /* length */); /* - * To retrieve the xattr list, first "reset", then repeatedly ask for the + * To retrieve the sparse list, first "reset", then repeatedly ask for the * "next" entry. */ diff --git a/libarchive/archive_entry_link_resolver.c b/libarchive/archive_entry_link_resolver.c index 77fcad61fd..947c65fca5 100644 --- a/libarchive/archive_entry_link_resolver.c +++ b/libarchive/archive_entry_link_resolver.c @@ -41,6 +41,7 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" /* * This is mostly a pretty straightforward hash table implementation. @@ -78,7 +79,7 @@ struct links_entry { struct archive_entry_linkresolver { struct links_entry **buckets; struct links_entry *spare; - unsigned long number_entries; + size_t number_entries; size_t number_buckets; int strategy; }; @@ -158,13 +159,12 @@ archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res, void archive_entry_linkresolver_free(struct archive_entry_linkresolver *res) { - struct links_entry *le; - if (res == NULL) return; - while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL) - archive_entry_free(le->entry); + while (next_entry(res, NEXT_ENTRY_ALL) != NULL) { + /* Actual freeing done by next_entry() */ + } free(res->buckets); free(res); } @@ -382,9 +382,13 @@ insert_entry(struct archive_entry_linkresolver *res, if (le == NULL) return (NULL); le->canonical = archive_entry_clone(entry); + if (le->canonical == NULL) { + free(le); + return (NULL); + } /* If the links cache is getting too full, enlarge the hash table. */ - if (res->number_entries > res->number_buckets * 2) + if (res->number_entries / 2 > res->number_buckets) grow_hash(res); hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry)); @@ -410,8 +414,7 @@ grow_hash(struct archive_entry_linkresolver *res) size_t i, bucket; /* Try to enlarge the bucket list. */ - new_size = res->number_buckets * 2; - if (new_size < res->number_buckets) + if (archive_ckd_mul_size(&new_size, res->number_buckets, 2)) return; new_buckets = calloc(new_size, sizeof(struct links_entry *)); diff --git a/libarchive/archive_entry_sparse.c b/libarchive/archive_entry_sparse.c index c430896119..1e84ff7f85 100644 --- a/libarchive/archive_entry_sparse.c +++ b/libarchive/archive_entry_sparse.c @@ -127,9 +127,10 @@ archive_entry_sparse_count(struct archive_entry *entry) int archive_entry_sparse_reset(struct archive_entry * entry) { + /* Counting can change sparse_head, so do it first */ + int count = archive_entry_sparse_count(entry); entry->sparse_p = entry->sparse_head; - - return archive_entry_sparse_count(entry); + return (count); } int diff --git a/libarchive/archive_entry_strmode.c b/libarchive/archive_entry_strmode.c index 5faa2faeef..22171992dd 100644 --- a/libarchive/archive_entry_strmode.c +++ b/libarchive/archive_entry_strmode.c @@ -28,9 +28,6 @@ #ifdef HAVE_SYS_STAT_H #include #endif -#ifdef HAVE_STRING_H -#include -#endif #include "archive_entry.h" #include "archive_entry_private.h" @@ -38,16 +35,10 @@ const char * archive_entry_strmode(struct archive_entry *entry) { - static const mode_t permbits[] = - { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 }; char *bp = entry->strmode; - mode_t mode; + mode_t mask, mode; int i; - /* Fill in a default string, then selectively override. */ - strcpy(bp, "?rwxrwxrwx "); - - mode = archive_entry_mode(entry); switch (archive_entry_filetype(entry)) { case AE_IFREG: bp[0] = '-'; break; case AE_IFBLK: bp[0] = 'b'; break; @@ -57,30 +48,22 @@ archive_entry_strmode(struct archive_entry *entry) case AE_IFSOCK: bp[0] = 's'; break; case AE_IFIFO: bp[0] = 'p'; break; default: - if (archive_entry_hardlink(entry) != NULL) { - bp[0] = 'h'; - break; - } + bp[0] = (archive_entry_hardlink(entry) != NULL) ? 'h' : '?'; + break; } - for (i = 0; i < 9; i++) - if (!(mode & permbits[i])) - bp[i+1] = '-'; + mode = archive_entry_mode(entry); + for (i = 0, mask = 0400; i < 9; i++, mask >>= 1) + bp[i + 1] = (mode & mask) ? "rwx"[i % 3] : '-'; - if (mode & S_ISUID) { - if (mode & 0100) bp[3] = 's'; - else bp[3] = 'S'; - } - if (mode & S_ISGID) { - if (mode & 0010) bp[6] = 's'; - else bp[6] = 'S'; - } - if (mode & S_ISVTX) { - if (mode & 0001) bp[9] = 't'; - else bp[9] = 'T'; - } - if (archive_entry_acl_types(entry) != 0) - bp[10] = '+'; + if (mode & S_ISUID) + bp[3] = (mode & 0100) ? 's' : 'S'; + if (mode & S_ISGID) + bp[6] = (mode & 0010) ? 's' : 'S'; + if (mode & S_ISVTX) + bp[9] = (mode & 0001) ? 't' : 'T'; + bp[10] = (archive_entry_acl_types(entry) != 0) ? '+' : ' '; + bp[11] = '\0'; return (bp); } diff --git a/libarchive/archive_entry_xattr.c b/libarchive/archive_entry_xattr.c index b92e1878bc..7a78f0c3e1 100644 --- a/libarchive/archive_entry_xattr.c +++ b/libarchive/archive_entry_xattr.c @@ -96,11 +96,11 @@ archive_entry_xattr_add_entry(struct archive_entry *entry, if ((xp->name = strdup(name)) == NULL) __archive_errx(1, "Out of memory"); - if ((xp->value = malloc(size)) != NULL) { - memcpy(xp->value, value, size); - xp->size = size; - } else - xp->size = 0; + if ((xp->value = malloc(size)) == NULL) + __archive_errx(1, "Out of memory"); + + memcpy(xp->value, value, size); + xp->size = size; xp->next = entry->xattr_head; entry->xattr_head = xp; diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c index 458092f41b..e1e8f05ff0 100644 --- a/libarchive/archive_hmac.c +++ b/libarchive/archive_hmac.c @@ -74,7 +74,7 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) memset(ctx, 0, sizeof(*ctx)); } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifndef BCRYPT_HASH_REUSABLE_FLAG # define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 diff --git a/libarchive/archive_hmac_private.h b/libarchive/archive_hmac_private.h index 36b8e3320e..af00beadb5 100644 --- a/libarchive/archive_hmac_private.h +++ b/libarchive/archive_hmac_private.h @@ -52,7 +52,7 @@ int __libarchive_hmac_build_hack(void); typedef CCHmacContext archive_hmac_sha1_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #include typedef struct { diff --git a/libarchive/archive_integer.h b/libarchive/archive_integer.h new file mode 100644 index 0000000000..1e71592790 --- /dev/null +++ b/libarchive/archive_integer.h @@ -0,0 +1,267 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * 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. + */ + +#ifndef ARCHIVE_INTEGER_H_INCLUDED +#define ARCHIVE_INTEGER_H_INCLUDED + +#include "archive_platform.h" + +/* Note: This is a purely internal header! */ +/* Do not use this outside of libarchive internal code! */ + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif + +#ifdef HAVE_INTSAFE_H +#define ENABLE_INTSAFE_SIGNED_FUNCTIONS +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDCKDINT_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifdef HAVE_STDCKDINT_H +#define USE_STDCKDINT 1 +#elif (__GNUC__ >= 5 && !defined(__INTEL_COMPILER)) +#define USE_BUILTIN 1 +#elif __has_builtin(__builtin_add_overflow) +#define USE_BUILTIN 1 +#elif defined HAVE_INTSAFE_H +#define USE_INTSAFE 1 +#endif + +/* + * Disabling inline keyword for compilers known to choke on it: + * - 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(__IBMC__) && __IBMC__ < 700 +#define inline +#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590 +#define inline +#elif defined(_MSC_VER) || defined(__osf__) +#define inline __inline +#endif + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongAdd(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((b > 0 && a > INT64_MAX - b) || + (b < 0 && a < INT64_MIN - b)) + return 1; + + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_size(size_t *result, size_t a, size_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + return SizeTAdd(a, b, result); +#else + if (a > SIZE_MAX - b) + return 1; + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_add_u64(uint64_t *result, uint64_t a, uint64_t b) +{ +#if USE_STDCKDINT + return ckd_add(result, a, b); +#elif USE_BUILTIN + return __builtin_add_overflow(a, b, result); +#elif USE_INTSAFE + ULONGLONG res; + int ret; + + ret = ULongLongAdd(a, b, &res); + *result = (uint64_t)res; + return ret; +#else + if (a > UINT64_MAX - b) + return 1; + *result = a + b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongMult(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((a > 0 && b > 0 && a > INT64_MAX / b) || + (a < 0 && b > 0 && a < INT64_MIN / b) || + (a > 0 && b < 0 && b < INT64_MIN / a) || + (a < 0 && b < 0 && a < INT64_MAX / b)) + return 1; + + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_size(size_t *result, size_t a, size_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + return SizeTMult(a, b, result); +#else + if (b != 0 && a > SIZE_MAX / b) + return 1; + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_mul_u64(uint64_t *result, uint64_t a, uint64_t b) +{ +#if USE_STDCKDINT + return ckd_mul(result, a, b); +#elif USE_BUILTIN + return __builtin_mul_overflow(a, b, result); +#elif USE_INTSAFE + ULONGLONG res; + int ret; + + ret = ULongLongMult(a, b, &res); + *result = (uint64_t)res; + return ret; +#else + if (b != 0 && a > UINT64_MAX / b) + return 1; + *result = a * b; + return 0; +#endif +} + +/* Returns 0 on success, a non-zero value otherwise. */ +static inline int +archive_ckd_sub_i64(int64_t *result, int64_t a, int64_t b) +{ +#if USE_STDCKDINT + return ckd_sub(result, a, b); +#elif USE_BUILTIN + return __builtin_sub_overflow(a, b, result); +#elif USE_INTSAFE + LONGLONG res; + int ret; + + ret = LongLongSub(a, b, &res); + *result = (int64_t)res; + return ret; +#else + if ((b > 0 && a < INT64_MIN + b) || + (b < 0 && a > INT64_MAX + b)) + return 1; + + *result = a - b; + return 0; +#endif +} + +#if !defined(TIME_MAX) +#define TIME_MAX (((time_t)0 < (time_t)-1) ? (time_t)~0 : \ + sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MAX : \ + sizeof(time_t) == sizeof(long) ? (time_t)LONG_MAX : \ + sizeof(time_t) == sizeof(int) ? (time_t)INT_MAX : \ + sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MAX : \ + 1 /* I give up */) +#endif +#if !defined(TIME_MIN) +#define TIME_MIN (((time_t)0 < (time_t)-1) ? (time_t)0 : \ + sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MIN : \ + sizeof(time_t) == sizeof(long) ? (time_t)LONG_MIN : \ + sizeof(time_t) == sizeof(int) ? (time_t)INT_MIN : \ + sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MIN : \ + -1 /* I give up */) +#endif + +#endif diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index 51a0e3fd57..ecd470bd15 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -40,6 +40,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_entry.h" #include "archive_pathmatch.h" @@ -216,6 +217,14 @@ error_nomem(struct archive_match *a) return (ARCHIVE_FATAL); } +static int +error_pattern(struct archive_match *a) +{ + archive_set_error(&(a->archive), EINVAL, "Failed to apply pattern"); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); +} + /* * Create an ARCHIVE_MATCH object. */ @@ -269,7 +278,7 @@ archive_match_free(struct archive *_a) * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_excluded(struct archive *_a, struct archive_entry *entry) @@ -293,6 +302,8 @@ archive_match_excluded(struct archive *_a, struct archive_entry *entry) #else r = path_excluded(a, 1, archive_entry_pathname(entry)); #endif + if (r < 0) + return (error_pattern(a)); if (r != 0) return (r); } @@ -449,13 +460,14 @@ archive_match_include_pattern_from_file_w(struct archive *_a, * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_path_excluded(struct archive *_a, struct archive_entry *entry) { struct archive_match *a; + int r; archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, ARCHIVE_STATE_NEW, "archive_match_path_excluded"); @@ -471,10 +483,13 @@ archive_match_path_excluded(struct archive *_a, if ((a->setflag & PATTERN_IS_SET) == 0) return (0); #if defined(_WIN32) && !defined(__CYGWIN__) - return (path_excluded(a, 0, archive_entry_pathname_w(entry))); + r = path_excluded(a, 0, archive_entry_pathname_w(entry)); #else - return (path_excluded(a, 1, archive_entry_pathname(entry))); + r = path_excluded(a, 1, archive_entry_pathname(entry)); #endif + if (r < 0) + return (error_pattern(a)); + return (r); } /* @@ -629,11 +644,12 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist, } r = archive_read_next_header(ar, &ae); if (r != ARCHIVE_OK) { - archive_read_free(ar); if (r == ARCHIVE_EOF) { + archive_read_free(ar); return (ARCHIVE_OK); } else { archive_copy_error(&(a->archive), ar); + archive_read_free(ar); return (r); } } @@ -1007,7 +1023,7 @@ archive_match_exclude_entry(struct archive *_a, int flag, * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_time_excluded(struct archive *_a, @@ -1653,7 +1669,7 @@ archive_match_include_gname_w(struct archive *_a, const wchar_t *gname) * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. - * Returns <0 if something error happened. + * Returns <0 if some error happened. */ int archive_match_owner_excluded(struct archive *_a, @@ -1684,15 +1700,22 @@ add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id) if (ids->count + 1 >= ids->size) { void *p; + size_t alloc_size, new_size; if (ids->size == 0) - ids->size = 8; - else - ids->size *= 2; - p = realloc(ids->ids, sizeof(*ids->ids) * ids->size); + new_size = 8; + else { + if (archive_ckd_mul_size(&new_size, ids->size, 2)) + return (error_nomem(a)); + } + if (archive_ckd_mul_size(&alloc_size, + new_size, sizeof(*ids->ids))) + return (error_nomem(a)); + p = realloc(ids->ids, alloc_size); if (p == NULL) return (error_nomem(a)); ids->ids = (int64_t *)p; + ids->size = new_size; } /* Find an insert point. */ diff --git a/libarchive/archive_options.c b/libarchive/archive_options.c index 6e2c0d2a59..24480b97e2 100644 --- a/libarchive/archive_options.c +++ b/libarchive/archive_options.c @@ -31,8 +31,8 @@ #include "archive_options_private.h" -static const char * -parse_option(const char **str, +static char * +parse_option(char **str, const char **mod, const char **opt, const char **val); int @@ -90,7 +90,9 @@ _archive_set_either_option(struct archive *a, const char *m, const char *o, cons if (r2 == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - if (r2 == ARCHIVE_WARN - 1) + if (r1 == ARCHIVE_WARN - 1) + return r2; + if (r2 == ARCHIVE_WARN -1) return r1; return r1 > r2 ? r1 : r2; } @@ -100,8 +102,8 @@ _archive_set_options(struct archive *a, const char *options, unsigned int magic, const char *fn, option_handler use_option) { int allok = 1, anyok = 0, ignore_mod_err = 0, r; - char *data; - const char *s, *mod, *opt, *val; + char *data, *s; + const char *mod, *opt, *val; archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn); @@ -113,7 +115,7 @@ _archive_set_options(struct archive *a, const char *options, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } - s = (const char *)data; + s = data; do { mod = opt = val = NULL; @@ -165,11 +167,11 @@ _archive_set_options(struct archive *a, const char *options, return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED; } -static const char * -parse_option(const char **s, const char **m, const char **o, const char **v) +static char * +parse_option(char **s, const char **m, const char **o, const char **v) { - const char *end, *mod, *opt, *val; - char *p; + const char *mod, *val; + char *end, *opt, *p; end = NULL; mod = NULL; @@ -180,7 +182,7 @@ parse_option(const char **s, const char **m, const char **o, const char **v) if (p != NULL) { *p = '\0'; - end = ((const char *)p) + 1; + end = p + 1; } if (0 == strlen(opt)) { diff --git a/libarchive/archive_pack_dev.c b/libarchive/archive_pack_dev.c index 3c6209b984..e293512928 100644 --- a/libarchive/archive_pack_dev.c +++ b/libarchive/archive_pack_dev.c @@ -319,7 +319,7 @@ compare_format(const void *key, const void *element) pack_t * pack_find(const char *name) { - struct format *format; + const struct format *format; format = bsearch(name, formats, sizeof(formats)/sizeof(formats[0]), diff --git a/libarchive/archive_parse_date.c b/libarchive/archive_parse_date.c index d9e968387d..c1b0f18311 100644 --- a/libarchive/archive_parse_date.c +++ b/libarchive/archive_parse_date.c @@ -36,6 +36,7 @@ #include #include "archive.h" +#include "archive_integer.h" /* Basic time units. */ #define EPOCH 1970 @@ -49,7 +50,7 @@ enum DSTMODE { DSTon, DSToff, DSTmaybe }; enum { tAM, tPM }; /* Token types returned by nexttoken() */ enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, - tUNUMBER, tZONE, tDST }; + tUNUMBER, tZONE, tDST, tERROR }; struct token { int token; time_t value; }; /* @@ -818,20 +819,33 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) } /* - * Parses and consumes an unsigned number. - * Returns 1 if any number is parsed. Otherwise, *value is unchanged. + * Parses and consumes an unsigned 64-bit number. + * Returns UINT64_MAX if the number overflows. */ -static char -consume_unsigned_number(const char **in, time_t *value) -{ - char c; - if (isdigit((unsigned char)(c = **in))) { - for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) - *value = 10 * *value + c - '0'; - (*in)--; - return 1; +static uint64_t +consume_unsigned_number(const char **in) { + uint64_t value = 0; + unsigned char c; + + /* Get the first character, abort if it's not a digit */ + c = (unsigned char)(**in); + if (c < '0' || c > '9') { + return UINT64_MAX; } - return 0; + + /* Fold digits into the value, abort on overflow */ + while (c >= '0' && c <= '9') { + unsigned char digit = c - '0'; + + /* Return error if the result would overflow UINT64_MAX */ + if (archive_ckd_mul_u64(&value, value, 10) || + archive_ckd_add_u64(&value, value, digit)) { + return UINT64_MAX; + } + (*in)++; + c = (unsigned char)(**in); + } + return value; } /* @@ -906,12 +920,19 @@ nexttoken(const char **in, time_t *value) } /* - * Not in the word table, maybe it's a number. Note: - * Because '-' and '+' have other special meanings, I - * don't deal with signed numbers here. + * Not in the word table. If it starts with a digit, + * it must be a number. Note: Because '-' and '+' have + * other special meanings, I don't deal with signed + * numbers here. */ - if (consume_unsigned_number(in, value)) { - return (tUNUMBER); + if (isdigit((unsigned char)(**in))) { + uint64_t val = consume_unsigned_number(in); + if (val > 9999) { + return (tERROR); + } else { + *value = val; + return (tUNUMBER); + } } return *(*in)++; @@ -949,6 +970,7 @@ difftm (struct tm *a, struct tm *b) static time_t parse_unix_epoch(const char *p) { + uint64_t val; time_t epoch; /* may begin with + */ @@ -957,12 +979,18 @@ parse_unix_epoch(const char *p) } /* followed by some number */ - if (!consume_unsigned_number(&p, &epoch)) + val = consume_unsigned_number(&p); + /* Truncate to time_t */ + epoch = (time_t)val; + /* If truncated value is different, then + * the value is too large for `time_t`. */ + if (epoch < 0 || (uint64_t)epoch != val) { return (time_t)-1; - - /* ...and nothing else */ - if (*p != '\0') + } + /* If there's any more characters, fail. */ + if (*p != '\0') { return (time_t)-1; + } return epoch; } diff --git a/libarchive/archive_pathmatch.c b/libarchive/archive_pathmatch.c index 19e0889ffe..46bdb51056 100644 --- a/libarchive/archive_pathmatch.c +++ b/libarchive/archive_pathmatch.c @@ -35,6 +35,8 @@ #include "archive_pathmatch.h" +#define MAX_RECURSION 100 + /* * Check whether a character 'c' is matched by a list specification [...]: * * Leading '!' or '^' negates the class. @@ -167,9 +169,13 @@ pm_slashskip_w(const wchar_t *s) { } static int -pm(const char *p, const char *s, int flags) +pm(const char *p, const char *s, int flags, int depth) { const char *end; + int r; + + if (depth > MAX_RECURSION) + return (-1); /* * Ignore leading './', './/', '././', etc. @@ -202,8 +208,9 @@ pm(const char *p, const char *s, int flags) if (*p == '\0') return (1); while (*s) { - if (archive_pathmatch(p, s, flags)) - return (1); + r = pm(p, s, flags, depth + 1); + if (r) + return (r); ++s; } return (0); @@ -220,7 +227,7 @@ pm(const char *p, const char *s, int flags) } if (*end == ']') { /* We found [...], try to match it. */ - if (!pm_list(p + 1, end, *s, flags)) + if (*s == '\0' || !pm_list(p + 1, end, *s, flags)) return (0); p = end; /* Jump to trailing ']' char. */ break; @@ -272,9 +279,13 @@ pm(const char *p, const char *s, int flags) } static int -pm_w(const wchar_t *p, const wchar_t *s, int flags) +pm_w(const wchar_t *p, const wchar_t *s, int flags, int depth) { const wchar_t *end; + int r; + + if (depth > MAX_RECURSION) + return (-1); /* * Ignore leading './', './/', '././', etc. @@ -307,8 +318,9 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags) if (*p == L'\0') return (1); while (*s) { - if (archive_pathmatch_w(p, s, flags)) - return (1); + r = pm_w(p, s, flags, depth + 1); + if (r) + return (r); ++s; } return (0); @@ -325,7 +337,7 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags) } if (*end == L']') { /* We found [...], try to match it. */ - if (!pm_list_w(p + 1, end, *s, flags)) + if (*s == L'\0' || !pm_list_w(p + 1, end, *s, flags)) return (0); p = end; /* Jump to trailing ']' char. */ break; @@ -387,7 +399,7 @@ __archive_pathmatch(const char *p, const char *s, int flags) return (0); /* Leading '^' anchors the start of the pattern. */ - if (*p == '^') { + if ((flags & PATHMATCH_NO_ANCHOR_START) && *p == '^') { ++p; flags &= ~PATHMATCH_NO_ANCHOR_START; } @@ -401,22 +413,25 @@ __archive_pathmatch(const char *p, const char *s, int flags) ++p; while (*s == '/') ++s; - return (pm(p, s, flags)); + return (pm(p, s, flags, 0)); } /* If start is unanchored, try to match start of each path element. */ if (flags & PATHMATCH_NO_ANCHOR_START) { for ( ; s != NULL; s = strchr(s, '/')) { + int r; + if (*s == '/') s++; - if (pm(p, s, flags)) - return (1); + r = pm(p, s, flags, 0); + if (r) + return (r); } return (0); } /* Default: Match from beginning. */ - return (pm(p, s, flags)); + return (pm(p, s, flags, 0)); } int @@ -429,7 +444,7 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags) return (0); /* Leading '^' anchors the start of the pattern. */ - if (*p == L'^') { + if ((flags & PATHMATCH_NO_ANCHOR_START) && *p == L'^') { ++p; flags &= ~PATHMATCH_NO_ANCHOR_START; } @@ -443,20 +458,23 @@ __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags) ++p; while (*s == L'/') ++s; - return (pm_w(p, s, flags)); + return (pm_w(p, s, flags, 0)); } /* If start is unanchored, try to match start of each path element. */ if (flags & PATHMATCH_NO_ANCHOR_START) { for ( ; s != NULL; s = wcschr(s, L'/')) { + int r; + if (*s == L'/') s++; - if (pm_w(p, s, flags)) - return (1); + r = pm_w(p, s, flags, 0); + if (r) + return (r); } return (0); } /* Default: Match from beginning. */ - return (pm_w(p, s, flags)); + return (pm_w(p, s, flags, 0)); } diff --git a/libarchive/archive_ppmd8.c b/libarchive/archive_ppmd8.c index 30196d64a9..04b1c0c3e1 100644 --- a/libarchive/archive_ppmd8.c +++ b/libarchive/archive_ppmd8.c @@ -61,7 +61,7 @@ typedef struct CPpmd8_Node_ #define EMPTY_NODE 0xFFFFFFFF -void Ppmd8_Construct(CPpmd8 *p) +static void Ppmd8_Construct(CPpmd8 *p) { unsigned i, k, m; @@ -89,14 +89,14 @@ void Ppmd8_Construct(CPpmd8 *p) } } -void Ppmd8_Free(CPpmd8 *p) +static void Ppmd8_Free(CPpmd8 *p) { free(p->Base); p->Size = 0; p->Base = 0; } -Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size) +static Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size) { if (p->Base == 0 || p->Size != size) { @@ -407,7 +407,7 @@ static void RestartModel(CPpmd8 *p) } } -void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) +static void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) { p->MaxOrder = maxOrder; p->RestoreMethod = restoreMethod; @@ -1042,7 +1042,7 @@ static void Rescale(CPpmd8 *p) p->FoundState = STATS(p->MinContext); } -CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) +static CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) { CPpmd_See *see; if (p->MinContext->NumStats != 0xFF) @@ -1078,7 +1078,7 @@ static void NextContext(CPpmd8 *p) } } -void Ppmd8_Update1(CPpmd8 *p) +static void Ppmd8_Update1(CPpmd8 *p) { CPpmd_State *s = p->FoundState; s->Freq += 4; @@ -1093,7 +1093,7 @@ void Ppmd8_Update1(CPpmd8 *p) NextContext(p); } -void Ppmd8_Update1_0(CPpmd8 *p) +static void Ppmd8_Update1_0(CPpmd8 *p) { p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq); p->RunLength += p->PrevSuccess; @@ -1103,7 +1103,7 @@ void Ppmd8_Update1_0(CPpmd8 *p) NextContext(p); } -void Ppmd8_UpdateBin(CPpmd8 *p) +static void Ppmd8_UpdateBin(CPpmd8 *p) { p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196)); p->PrevSuccess = 1; @@ -1111,7 +1111,7 @@ void Ppmd8_UpdateBin(CPpmd8 *p) NextContext(p); } -void Ppmd8_Update2(CPpmd8 *p) +static void Ppmd8_Update2(CPpmd8 *p) { p->MinContext->SummFreq += 4; if ((p->FoundState->Freq += 4) > MAX_FREQ) @@ -1127,7 +1127,7 @@ This code is based on: PPMd var.I (2002): Dmitry Shkarin : Public domain Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ -Bool Ppmd8_RangeDec_Init(CPpmd8 *p) +static Bool Ppmd8_RangeDec_Init(CPpmd8 *p) { unsigned i; p->Low = 0; @@ -1161,7 +1161,7 @@ static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size) #define MASK(sym) ((signed char *)charMask)[sym] -int Ppmd8_DecodeSymbol(CPpmd8 *p) +static int Ppmd8_DecodeSymbol(CPpmd8 *p) { size_t charMask[256 / sizeof(size_t)]; if (p->MinContext->NumStats != 0) diff --git a/libarchive/archive_ppmd8_private.h b/libarchive/archive_ppmd8_private.h index 454b75f41f..f0493de046 100644 --- a/libarchive/archive_ppmd8_private.h +++ b/libarchive/archive_ppmd8_private.h @@ -83,12 +83,6 @@ typedef struct UInt16 BinSumm[25][64]; } CPpmd8; -void Ppmd8_Construct(CPpmd8 *p); -Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size); -void Ppmd8_Free(CPpmd8 *p); -void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod); -#define Ppmd8_WasAllocated(p) ((p)->Base != NULL) - /* ---------- Internal Functions ---------- */ @@ -104,30 +98,11 @@ extern const Byte PPMD8_kExpEscape[16]; #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats))) #endif -void Ppmd8_Update1(CPpmd8 *p); -void Ppmd8_Update1_0(CPpmd8 *p); -void Ppmd8_Update2(CPpmd8 *p); -void Ppmd8_UpdateBin(CPpmd8 *p); - #define Ppmd8_GetBinSumm(p) \ &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \ p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \ p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)] -CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale); - - -/* ---------- Decode ---------- */ - -Bool Ppmd8_RangeDec_Init(CPpmd8 *p); -#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0) -int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */ - -/* ---------- Encode ---------- */ - -#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; } -void Ppmd8_RangeEnc_FlushData(CPpmd8 *p); -void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */ typedef struct { diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index 3a926c6886..0f068639b9 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -61,12 +61,38 @@ #define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U) #define ARCHIVE_MATCH_MAGIC (0xcad11c9U) +/* + * Having the state be a bitmask makes it easy to check + * for combinations of allowed states. These should + * generally only be used in public API entry points, + * primarily in archive_read.c, archive_write.c, etc. + * Internal callbacks can rely on the core machinery + * to only call them when appropriate. + * + * Generally checked via `__archive_check_magic()`. + */ + +/* Newly created archive object, not yet opened */ #define ARCHIVE_STATE_NEW 1U +/* Archive is ready to read a header. */ #define ARCHIVE_STATE_HEADER 2U +/* A header has been read: client can ask for data + * or they can ask for the next header and + * we'll automatically skip the remaining data. */ #define ARCHIVE_STATE_DATA 4U +/* Similar to STATE_DATA but after a FAILED header: + * the client may not read data, but they may ask + * for the next header and we'll recover. */ +#define ARCHIVE_STATE_DATA_RECOVERY 8U +/* End-of-archive has been reached. Client can only + * close or free the archive. */ #define ARCHIVE_STATE_EOF 0x10U +/* Archive is closed; client is only allowed to free the archive. */ #define ARCHIVE_STATE_CLOSED 0x20U +/* Archive is in a FATAL error state: a close request + * is permitted but ignored. */ #define ARCHIVE_STATE_FATAL 0x8000U +/* Any valid (non-fatal) state. */ #define ARCHIVE_STATE_ANY (0xFFFFU & ~ARCHIVE_STATE_FATAL) struct archive_vtable { diff --git a/libarchive/archive_random.c b/libarchive/archive_random.c index 8c48d2d3b5..28cba00bd3 100644 --- a/libarchive/archive_random.c +++ b/libarchive/archive_random.c @@ -58,18 +58,12 @@ static void la_arc4random_buf(void *, size_t); #include "archive_random_private.h" #if defined(_WIN32) && !defined(__CYGWIN__) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ #include /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) #endif - -#elif defined(HAVE_WINCRYPT_H) -#include -#endif #endif #ifndef O_CLOEXEC @@ -85,7 +79,6 @@ int archive_random(void *buf, size_t nbytes) { #if defined(_WIN32) && !defined(__CYGWIN__) -# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA NTSTATUS status; BCRYPT_ALG_HANDLE hAlg; @@ -98,25 +91,6 @@ archive_random(void *buf, size_t nbytes) return ARCHIVE_FAILED; return ARCHIVE_OK; -# else - HCRYPTPROV hProv; - BOOL success; - - success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT); - if (!success && GetLastError() == (DWORD)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; -# endif #elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) la_arc4random_buf(buf, nbytes); return ARCHIVE_OK; diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index c9b9d59815..e844e42d1a 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -36,6 +36,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #include #ifdef HAVE_STDLIB_H #include @@ -49,6 +52,7 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -99,7 +103,12 @@ archive_read_new(void) a->archive.state = ARCHIVE_STATE_NEW; a->entry = archive_entry_new2(&a->archive); + if (a->entry == NULL) { + free(a); + return (NULL); + } a->archive.vtable = &archive_read_vtable; + a->entry_bytes_declared = -1; a->passphrases.last = &a->passphrases.first; @@ -131,12 +140,16 @@ archive_read_open(struct archive *a, void *client_data, archive_open_callback *client_opener, archive_read_callback *client_reader, archive_close_callback *client_closer) { + int r; + /* Old archive_read_open() is just a thin shell around * archive_read_open1. */ archive_read_set_open_callback(a, client_opener); archive_read_set_read_callback(a, client_reader); archive_read_set_close_callback(a, client_closer); - archive_read_set_callback_data(a, client_data); + r = archive_read_set_callback_data(a, client_data); + if (r < 0) + return (r); return archive_read_open1(a); } @@ -148,9 +161,13 @@ archive_read_open2(struct archive *a, void *client_data, archive_skip_callback *client_skipper, archive_close_callback *client_closer) { + int r; + /* Old archive_read_open2() is just a thin shell around * archive_read_open1. */ - archive_read_set_callback_data(a, client_data); + r = archive_read_set_callback_data(a, client_data); + if (r < 0) + return (r); archive_read_set_open_callback(a, client_opener); archive_read_set_read_callback(a, client_reader); archive_read_set_skip_callback(a, client_skipper); @@ -159,28 +176,28 @@ archive_read_open2(struct archive *a, void *client_data, } static ssize_t -client_read_proxy(struct archive_read_filter *self, const void **buff) +client_read_proxy(struct archive_read_filter *f, const void **buff) { ssize_t r; - r = (self->archive->client.reader)(&self->archive->archive, - self->data, buff); + r = (f->archive->client.reader)(&f->archive->archive, + f->data, buff); return (r); } static int64_t -client_skip_proxy(struct archive_read_filter *self, int64_t request) +client_skip_proxy(struct archive_read_filter *f, int64_t request) { if (request < 0) - __archive_errx(1, "Negative skip requested."); + __archive_errx(1, "Negative skip requested"); if (request == 0) return 0; - if (self->archive->client.skipper != NULL) { + if (f->archive->client.skipper != NULL) { int64_t total = 0; for (;;) { int64_t get, ask = request; - get = (self->archive->client.skipper) - (&self->archive->archive, self->data, ask); + get = (f->archive->client.skipper) + (&f->archive->archive, f->data, ask); total += get; if (get == 0 || get == request) return (total); @@ -188,7 +205,7 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) return ARCHIVE_FATAL; request -= get; } - } else if (self->archive->client.seeker != NULL + } else if (f->archive->client.seeker != NULL && request > 64 * 1024) { /* If the client provided a seeker but not a skipper, * we can use the seeker to skip forward. @@ -201,9 +218,9 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) * to just reading and discarding. That's why we * only do this for skips of over 64k. */ - int64_t before = self->position; - int64_t after = (self->archive->client.seeker) - (&self->archive->archive, self->data, request, SEEK_CUR); + int64_t before = f->position; + int64_t after = (f->archive->client.seeker) + (&f->archive->archive, f->data, request, SEEK_CUR); if (after != before + request) return ARCHIVE_FATAL; return after - before; @@ -212,20 +229,20 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) } static int64_t -client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence) +client_seek_proxy(struct archive_read_filter *f, int64_t offset, int whence) { /* DO NOT use the skipper here! If we transparently handled * forward seek here by using the skipper, that will break * other libarchive code that assumes a successful forward * seek means it can also seek backwards. */ - if (self->archive->client.seeker == NULL) { - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + if (f->archive->client.seeker == NULL) { + archive_set_error(&f->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); + return (f->archive->client.seeker)(&f->archive->archive, + f->data, offset, whence); } static int @@ -247,47 +264,44 @@ read_client_close_proxy(struct archive_read *a) } static int -client_close_proxy(struct archive_read_filter *self) +client_close_proxy(struct archive_read_filter *f) { - return read_client_close_proxy(self->archive); + return read_client_close_proxy(f->archive); } static int -client_open_proxy(struct archive_read_filter *self) +client_switch_proxy(struct archive_read_filter *f, unsigned int iindex) { - int r = ARCHIVE_OK; - if (self->archive->client.opener != NULL) - r = (self->archive->client.opener)( - (struct archive *)self->archive, self->data); - return (r); -} + struct archive_read *a; + int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; + void *data2; -static int -client_switch_proxy(struct archive_read_filter *self, unsigned int iindex) -{ - int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; - void *data2 = NULL; + while (f->upstream != NULL) + f = f->upstream; + a = f->archive; /* Don't do anything if already in the specified data node */ - if (self->archive->client.cursor == iindex) + if (a->client.cursor == iindex) return (ARCHIVE_OK); - self->archive->client.cursor = iindex; - data2 = self->archive->client.dataset[self->archive->client.cursor].data; - if (self->archive->client.switcher != NULL) + a->client.cursor = iindex; + data2 = a->client.dataset[a->client.cursor].data; + if (a->client.switcher != NULL) { - r1 = r2 = (self->archive->client.switcher) - ((struct archive *)self->archive, self->data, data2); - self->data = data2; + r1 = r2 = (a->client.switcher) + ((struct archive *)a, f->data, data2); + f->data = data2; } else { /* Attempt to call close and open instead */ - if (self->archive->client.closer != NULL) - r1 = (self->archive->client.closer) - ((struct archive *)self->archive, self->data); - self->data = data2; - r2 = client_open_proxy(self); + if (a->client.closer != NULL) + r1 = (a->client.closer) + ((struct archive *)a, f->data); + f->data = data2; + if (a->client.opener != NULL) + r2 = (a->client.opener) + ((struct archive *)a, f->data); } return (r1 < r2) ? r1 : r2; } @@ -379,7 +393,7 @@ archive_read_set_callback_data2(struct archive *_a, void *client_data, if (a->client.dataset == NULL) { archive_set_error(&a->archive, ENOMEM, - "No memory."); + "No memory"); return ARCHIVE_FATAL; } a->client.nodes = 1; @@ -388,7 +402,7 @@ archive_read_set_callback_data2(struct archive *_a, void *client_data, if (iindex > a->client.nodes - 1) { archive_set_error(&a->archive, EINVAL, - "Invalid index specified."); + "Invalid index specified"); return ARCHIVE_FATAL; } a->client.dataset[iindex].data = client_data; @@ -403,23 +417,37 @@ archive_read_add_callback_data(struct archive *_a, void *client_data, { struct archive_read *a = (struct archive_read *)_a; void *p; + size_t alloc_size; unsigned int i; + unsigned int nodes; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_add_callback_data"); if (iindex > a->client.nodes) { archive_set_error(&a->archive, EINVAL, - "Invalid index specified."); + "Invalid index specified"); return ARCHIVE_FATAL; } - p = realloc(a->client.dataset, sizeof(*a->client.dataset) - * (++(a->client.nodes))); + + if (a->client.nodes == UINT_MAX || + archive_ckd_mul_size(&alloc_size, + (size_t)a->client.nodes + 1, sizeof(*a->client.dataset))) { + archive_set_error(&a->archive, ENOMEM, + "No memory"); + return ARCHIVE_FATAL; + } + + nodes = a->client.nodes + 1; + p = realloc(a->client.dataset, alloc_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, - "No memory."); + "No memory"); return ARCHIVE_FATAL; } + a->client.dataset = (struct archive_read_data_node *)p; + a->client.nodes = nodes; + for (i = a->client.nodes - 1; i > iindex; i--) { a->client.dataset[i].data = a->client.dataset[i-1].data; a->client.dataset[i].begin_position = -1; @@ -454,7 +482,7 @@ int archive_read_open1(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - struct archive_read_filter *filter, *tmp; + struct archive_read_filter *f, *tmp; int slot, e = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, @@ -478,23 +506,23 @@ archive_read_open1(struct archive *_a) } } - filter = calloc(1, sizeof(*filter)); - if (filter == NULL) + f = calloc(1, sizeof(*f)); + if (f == NULL) return (ARCHIVE_FATAL); - filter->bidder = NULL; - filter->upstream = NULL; - filter->archive = a; - filter->data = a->client.dataset[0].data; - filter->vtable = &none_reader_vtable; - filter->name = "none"; - filter->code = ARCHIVE_FILTER_NONE; - filter->can_skip = 1; - filter->can_seek = 1; + f->bidder = NULL; + f->upstream = NULL; + f->archive = a; + f->data = a->client.dataset[0].data; + f->vtable = &none_reader_vtable; + f->name = "none"; + f->code = ARCHIVE_FILTER_NONE; + f->can_skip = 1; + f->can_seek = 1; a->client.dataset[0].begin_position = 0; if (!a->filter || !a->bypass_filter_bidding) { - a->filter = filter; + a->filter = f; /* Build out the input pipeline. */ e = choose_filters(a); if (e < ARCHIVE_WARN) { @@ -508,7 +536,7 @@ archive_read_open1(struct archive *_a) tmp = a->filter; while (tmp->upstream) tmp = tmp->upstream; - tmp->upstream = filter; + tmp->upstream = f; } if (!a->format) @@ -543,7 +571,7 @@ choose_filters(struct archive_read *a) { int number_bidders, i, bid, best_bid, number_filters; struct archive_read_filter_bidder *bidder, *best_bidder; - struct archive_read_filter *filter; + struct archive_read_filter *f; ssize_t avail; int r; @@ -575,13 +603,13 @@ choose_filters(struct archive_read *a) return (ARCHIVE_OK); } - filter = calloc(1, sizeof(*filter)); - if (filter == NULL) + f = calloc(1, sizeof(*f)); + if (f == NULL) return (ARCHIVE_FATAL); - filter->bidder = best_bidder; - filter->archive = a; - filter->upstream = a->filter; - a->filter = filter; + f->bidder = best_bidder; + f->archive = a; + f->upstream = a->filter; + a->filter = f; r = (best_bidder->vtable->init)(a->filter); if (r != ARCHIVE_OK) { __archive_read_free_filters(a); @@ -611,7 +639,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) int r1 = ARCHIVE_OK, r2; archive_check_magic(_a, ARCHIVE_READ_MAGIC, - ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA | + ARCHIVE_STATE_DATA_RECOVERY, "archive_read_next_header"); archive_entry_clear(entry); @@ -620,12 +649,16 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) /* * If client didn't consume entire data, skip any remainder * (This is especially important for GNU incremental directories.) + * A header that failed to parse (DATA_RECOVERY) still needs the + * same treatment: whatever of its body the format reader left + * unconsumed must be skipped before we can read the next header. */ - if (a->archive.state == ARCHIVE_STATE_DATA) { + if (a->archive.state == ARCHIVE_STATE_DATA || + a->archive.state == ARCHIVE_STATE_DATA_RECOVERY) { r1 = archive_read_data_skip(&a->archive); if (r1 == ARCHIVE_EOF) archive_set_error(&a->archive, EIO, - "Premature end-of-file."); + "Premature end-of-file"); if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) { a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); @@ -659,8 +692,26 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; + case ARCHIVE_FAILED: + /* + * This entry's header could not be parsed, so its metadata + * cannot be trusted. ARCHIVE_STATE_DATA_RECOVERY still + * permits skipping past it (the format reader is + * responsible for ensuring that's actually possible), but + * blocks archive_read_data() and friends, which all check + * for ARCHIVE_STATE_DATA specifically and would otherwise + * return content for an entry we don't actually understand. + */ + a->archive.state = ARCHIVE_STATE_DATA_RECOVERY; + break; } + if (r2 == ARCHIVE_OK || r2 == ARCHIVE_WARN) + a->entry_bytes_declared = archive_entry_size_is_set(entry) + ? archive_entry_size(entry) : -1; + else + a->entry_bytes_declared = -1; + __archive_reset_read_data(&a->archive); a->data_start_node = a->client.cursor; @@ -749,7 +800,7 @@ 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 + * If the archive format does 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 @@ -919,7 +970,8 @@ archive_read_data_skip(struct archive *_a) size_t size; int64_t offset; - archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_DATA | ARCHIVE_STATE_DATA_RECOVERY, "archive_read_data_skip"); if (a->format->read_data_skip != NULL) @@ -934,7 +986,10 @@ archive_read_data_skip(struct archive *_a) if (r == ARCHIVE_EOF) r = ARCHIVE_OK; - a->archive.state = ARCHIVE_STATE_HEADER; + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + else + a->archive.state = ARCHIVE_STATE_HEADER; return (r); } @@ -942,17 +997,21 @@ la_int64_t archive_seek_data(struct archive *_a, int64_t offset, int whence) { struct archive_read *a = (struct archive_read *)_a; + la_int64_t r; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_seek_data_block"); if (a->format->seek_data == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Internal error: " - "No format_seek_data_block function registered"); - return (ARCHIVE_FATAL); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Cannot seek data with this format"); + return (ARCHIVE_FAILED); } - return (a->format->seek_data)(a, offset, whence); + r = (a->format->seek_data)(a, offset, whence); + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + return (r); } /* @@ -968,6 +1027,8 @@ _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read *a = (struct archive_read *)_a; + int r; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); @@ -975,10 +1036,14 @@ _archive_read_data_block(struct archive *_a, archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: " "No format->read_data function registered"); + a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - return (a->format->read_data)(a, buff, size, offset); + r = (a->format->read_data)(a, buff, size, offset); + if (r == ARCHIVE_FATAL) + a->archive.state = ARCHIVE_STATE_FATAL; + return (r); } static int @@ -1317,13 +1382,13 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) } const void * -__archive_read_filter_ahead(struct archive_read_filter *filter, +__archive_read_filter_ahead(struct archive_read_filter *f, size_t min, ssize_t *avail) { ssize_t bytes_read; size_t tocopy; - if (filter->fatal) { + if (f->fatal) { if (avail) *avail = ARCHIVE_FATAL; return (NULL); @@ -1340,79 +1405,79 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, * note that min == 0 is a perfectly well-defined * request. */ - if (filter->avail >= min && filter->avail > 0) { + if (f->avail >= min && f->avail > 0) { if (avail != NULL) - *avail = filter->avail; - return (filter->next); + *avail = f->avail; + return (f->next); } /* * We can satisfy directly from client buffer if everything * currently in the copy buffer is still in the client buffer. */ - if (filter->client_total >= filter->client_avail + filter->avail - && filter->client_avail + filter->avail >= min) { + if (f->client_total >= f->client_avail + f->avail + && f->client_avail + f->avail >= min) { /* "Roll back" to client buffer. */ - filter->client_avail += filter->avail; - filter->client_next -= filter->avail; + f->client_avail += f->avail; + f->client_next -= f->avail; /* Copy buffer is now empty. */ - filter->avail = 0; - filter->next = filter->buffer; + f->avail = 0; + f->next = f->buffer; /* Return data from client buffer. */ if (avail != NULL) - *avail = filter->client_avail; - return (filter->client_next); + *avail = f->client_avail; + return (f->client_next); } /* Move data forward in copy buffer if necessary. */ - if (filter->next > filter->buffer && - filter->next + min > filter->buffer + filter->buffer_size) { - if (filter->avail > 0) - memmove(filter->buffer, filter->next, - filter->avail); - filter->next = filter->buffer; + if (f->next > f->buffer && + min > f->buffer_size - (f->next - f->buffer)) { + if (f->avail > 0) + memmove(f->buffer, f->next, + f->avail); + f->next = f->buffer; } /* If we've used up the client data, get more. */ - if (filter->client_avail <= 0) { - if (filter->end_of_file) { + if (f->client_avail <= 0) { + if (f->end_of_file) { if (avail != NULL) - *avail = filter->avail; + *avail = f->avail; return (NULL); } - bytes_read = (filter->vtable->read)(filter, - &filter->client_buff); + bytes_read = (f->vtable->read)(f, + &f->client_buff); if (bytes_read < 0) { /* Read error. */ - filter->client_total = filter->client_avail = 0; - filter->client_next = - filter->client_buff = NULL; - filter->fatal = 1; + f->client_total = f->client_avail = 0; + f->client_next = + f->client_buff = NULL; + f->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_read == 0) { /* Check for another client object first */ - if (filter->archive->client.cursor != - filter->archive->client.nodes - 1) { - if (client_switch_proxy(filter, - filter->archive->client.cursor + 1) + if (f->archive->client.cursor != + f->archive->client.nodes - 1) { + if (client_switch_proxy(f, + f->archive->client.cursor + 1) == ARCHIVE_OK) continue; } /* Premature end-of-file. */ - filter->client_total = filter->client_avail = 0; - filter->client_next = - filter->client_buff = NULL; - filter->end_of_file = 1; + f->client_total = f->client_avail = 0; + f->client_next = + f->client_buff = NULL; + f->end_of_file = 1; /* Return whatever we do have. */ if (avail != NULL) - *avail = filter->avail; + *avail = f->avail; return (NULL); } - filter->client_total = bytes_read; - filter->client_avail = filter->client_total; - filter->client_next = filter->client_buff; + f->client_total = bytes_read; + f->client_avail = f->client_total; + f->client_next = f->client_buff; } else { /* * We can't satisfy the request from the copy @@ -1422,67 +1487,66 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, */ /* Ensure the buffer is big enough. */ - if (min > filter->buffer_size) { - size_t s, t; + if (min > f->buffer_size) { + size_t s; char *p; /* Double the buffer; watch for overflow. */ - s = t = filter->buffer_size; + s = f->buffer_size; if (s == 0) s = min; while (s < min) { - t *= 2; - if (t <= s) { /* Integer overflow! */ + if (archive_ckd_mul_size(&s, s, 2)) { + /* Integer overflow! */ archive_set_error( - &filter->archive->archive, + &f->archive->archive, ENOMEM, "Unable to allocate copy" " buffer"); - filter->fatal = 1; + f->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } - s = t; } /* Now s >= min, so allocate a new buffer. */ p = malloc(s); if (p == NULL) { archive_set_error( - &filter->archive->archive, + &f->archive->archive, ENOMEM, "Unable to allocate copy buffer"); - filter->fatal = 1; + f->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } /* Move data into newly-enlarged buffer. */ - if (filter->avail > 0) - memmove(p, filter->next, filter->avail); - free(filter->buffer); - filter->next = filter->buffer = p; - filter->buffer_size = s; + if (f->avail > 0) + memmove(p, f->next, f->avail); + free(f->buffer); + f->next = f->buffer = p; + f->buffer_size = s; } /* We can add client data to copy buffer. */ /* First estimate: copy to fill rest of buffer. */ - tocopy = (filter->buffer + filter->buffer_size) - - (filter->next + filter->avail); + tocopy = (f->buffer + f->buffer_size) + - (f->next + f->avail); /* Don't waste time buffering more than we need to. */ - if (tocopy + filter->avail > min) - tocopy = min - filter->avail; + if (tocopy + f->avail > min) + tocopy = min - f->avail; /* Don't copy more than is available. */ - if (tocopy > filter->client_avail) - tocopy = filter->client_avail; + if (tocopy > f->client_avail) + tocopy = f->client_avail; - memcpy(filter->next + filter->avail, - filter->client_next, tocopy); + memcpy(f->next + f->avail, + f->client_next, tocopy); /* Remove this data from client buffer. */ - filter->client_next += tocopy; - filter->client_avail -= tocopy; + f->client_next += tocopy; + f->client_avail -= tocopy; /* add it to copy buffer. */ - filter->avail += tocopy; + f->avail += tocopy; } } } @@ -1497,7 +1561,7 @@ __archive_read_consume(struct archive_read *a, int64_t request) } int64_t -__archive_read_filter_consume(struct archive_read_filter * filter, +__archive_read_filter_consume(struct archive_read_filter *f, int64_t request) { int64_t skipped; @@ -1507,13 +1571,13 @@ __archive_read_filter_consume(struct archive_read_filter * filter, if (request == 0) return 0; - skipped = advance_file_pointer(filter, request); + skipped = advance_file_pointer(f, request); if (skipped == request) return (skipped); /* We hit EOF before we satisfied the skip request. */ if (skipped < 0) /* Map error code to 0 for error message below. */ skipped = 0; - archive_set_error(&filter->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated input file (needed %jd bytes, only %jd available)", (intmax_t)request, (intmax_t)skipped); @@ -1527,45 +1591,45 @@ __archive_read_filter_consume(struct archive_read_filter * filter, * Returns a negative value if there's an I/O error. */ static int64_t -advance_file_pointer(struct archive_read_filter *filter, int64_t request) +advance_file_pointer(struct archive_read_filter *f, int64_t request) { int64_t bytes_skipped, total_bytes_skipped = 0; ssize_t bytes_read; size_t min; - if (filter->fatal) + if (f->fatal) return (-1); /* Use up the copy buffer first. */ - if (filter->avail > 0) { - min = (size_t)minimum(request, (int64_t)filter->avail); - filter->next += min; - filter->avail -= min; + if (f->avail > 0) { + min = (size_t)minimum(request, (int64_t)f->avail); + f->next += min; + f->avail -= min; request -= min; - filter->position += min; + f->position += min; total_bytes_skipped += min; } /* Then use up the client buffer. */ - if (filter->client_avail > 0) { - min = (size_t)minimum(request, (int64_t)filter->client_avail); - filter->client_next += min; - filter->client_avail -= min; + if (f->client_avail > 0) { + min = (size_t)minimum(request, (int64_t)f->client_avail); + f->client_next += min; + f->client_avail -= min; request -= min; - filter->position += min; + f->position += min; total_bytes_skipped += min; } if (request == 0) return (total_bytes_skipped); /* If there's an optimized skip function, use it. */ - if (filter->can_skip != 0) { - bytes_skipped = client_skip_proxy(filter, request); + if (f->can_skip != 0) { + bytes_skipped = client_skip_proxy(f, request); if (bytes_skipped < 0) { /* error */ - filter->fatal = 1; + f->fatal = 1; return (bytes_skipped); } - filter->position += bytes_skipped; + f->position += bytes_skipped; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; if (request == 0) @@ -1574,37 +1638,37 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request) /* Use ordinary reads as necessary to complete the request. */ for (;;) { - bytes_read = (filter->vtable->read)(filter, &filter->client_buff); + bytes_read = (f->vtable->read)(f, &f->client_buff); if (bytes_read < 0) { - filter->client_buff = NULL; - filter->fatal = 1; + f->client_buff = NULL; + f->fatal = 1; return (bytes_read); } if (bytes_read == 0) { - if (filter->archive->client.cursor != - filter->archive->client.nodes - 1) { - if (client_switch_proxy(filter, - filter->archive->client.cursor + 1) + if (f->archive->client.cursor != + f->archive->client.nodes - 1) { + if (client_switch_proxy(f, + f->archive->client.cursor + 1) == ARCHIVE_OK) continue; } - filter->client_buff = NULL; - filter->end_of_file = 1; + f->client_buff = NULL; + f->end_of_file = 1; return (total_bytes_skipped); } if (bytes_read >= request) { - filter->client_next = - ((const char *)filter->client_buff) + request; - filter->client_avail = (size_t)(bytes_read - request); - filter->client_total = bytes_read; + f->client_next = + ((const char *)f->client_buff) + request; + f->client_avail = (size_t)(bytes_read - request); + f->client_total = bytes_read; total_bytes_skipped += request; - filter->position += request; + f->position += request; return (total_bytes_skipped); } - filter->position += bytes_read; + f->position += bytes_read; total_bytes_skipped += bytes_read; request -= bytes_read; } @@ -1620,23 +1684,23 @@ __archive_read_seek(struct archive_read *a, int64_t offset, int whence) } int64_t -__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, +__archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, int whence) { struct archive_read_client *client; int64_t r; unsigned int cursor; - if (filter->closed || filter->fatal) + if (f->closed || f->fatal) return (ARCHIVE_FATAL); - if (filter->can_seek == 0) + if (f->can_seek == 0) return (ARCHIVE_FAILED); - client = &(filter->archive->client); + client = &(f->archive->client); switch (whence) { case SEEK_CUR: /* Adjust the offset and use SEEK_SET instead */ - offset += filter->position; + offset += f->position; __LA_FALLTHROUGH; case SEEK_SET: cursor = 0; @@ -1653,10 +1717,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, client->dataset[++cursor].begin_position = r; } while (1) { - r = client_switch_proxy(filter, cursor); + r = client_switch_proxy(f, cursor); if (r != ARCHIVE_OK) return r; - if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) + if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0) return r; client->dataset[cursor].total_size = r; if (client->dataset[cursor].begin_position + @@ -1671,7 +1735,7 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, if (offset < 0 || offset > client->dataset[cursor].total_size) return ARCHIVE_FATAL; - if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0) + if ((r = client_seek_proxy(f, offset, SEEK_SET)) < 0) return r; break; @@ -1687,10 +1751,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, client->dataset[++cursor].begin_position = r; } while (1) { - r = client_switch_proxy(filter, cursor); + r = client_switch_proxy(f, cursor); if (r != ARCHIVE_OK) return r; - if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) + if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0) return r; client->dataset[cursor].total_size = r; r = client->dataset[cursor].begin_position + @@ -1711,9 +1775,9 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, client->dataset[cursor].total_size; } offset = (r + offset) - client->dataset[cursor].begin_position; - if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK) + if ((r = client_switch_proxy(f, cursor)) != ARCHIVE_OK) return r; - r = client_seek_proxy(filter, offset, SEEK_SET); + r = client_seek_proxy(f, offset, SEEK_SET); if (r < ARCHIVE_OK) return r; break; @@ -1741,10 +1805,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, * size is (r - offset). Can we use that to simplify * the TODO items above? */ - filter->avail = filter->client_avail = 0; - filter->next = filter->buffer; - filter->position = r; - filter->end_of_file = 0; + f->avail = f->client_avail = 0; + f->next = f->buffer; + f->position = r; + f->end_of_file = 0; } return r; } diff --git a/libarchive/archive_read_append_filter.c b/libarchive/archive_read_append_filter.c index cd88df1199..8de1b2ea8f 100644 --- a/libarchive/archive_read_append_filter.c +++ b/libarchive/archive_read_append_filter.c @@ -37,9 +37,9 @@ int archive_read_append_filter(struct archive *_a, int code) { int r1, r2, number_bidders, i; - char str[20]; - struct archive_read_filter_bidder *bidder; - struct archive_read_filter *filter; + const char *str; + struct archive_read_filter_bidder *b; + struct archive_read_filter *f; struct archive_read *a = (struct archive_read *)_a; r2 = (ARCHIVE_OK); @@ -53,15 +53,15 @@ archive_read_append_filter(struct archive *_a, int code) r1 = (ARCHIVE_OK); break; case ARCHIVE_FILTER_GZIP: - strcpy(str, "gzip"); + str = "gzip"; r1 = archive_read_support_filter_gzip(_a); break; case ARCHIVE_FILTER_BZIP2: - strcpy(str, "bzip2"); + str = "bzip2"; r1 = archive_read_support_filter_bzip2(_a); break; case ARCHIVE_FILTER_COMPRESS: - strcpy(str, "compress (.Z)"); + str = "compress (.Z)"; r1 = archive_read_support_filter_compress(_a); break; case ARCHIVE_FILTER_PROGRAM: @@ -69,41 +69,45 @@ archive_read_append_filter(struct archive *_a, int code) "Cannot append program filter using archive_read_append_filter"); return (ARCHIVE_FATAL); case ARCHIVE_FILTER_LZMA: - strcpy(str, "lzma"); + str = "lzma"; r1 = archive_read_support_filter_lzma(_a); break; case ARCHIVE_FILTER_XZ: - strcpy(str, "xz"); + str = "xz"; r1 = archive_read_support_filter_xz(_a); break; case ARCHIVE_FILTER_UU: - strcpy(str, "uu"); + str = "uu"; r1 = archive_read_support_filter_uu(_a); break; case ARCHIVE_FILTER_RPM: - strcpy(str, "rpm"); + str = "rpm"; r1 = archive_read_support_filter_rpm(_a); break; case ARCHIVE_FILTER_LZ4: - strcpy(str, "lz4"); + str = "lz4"; r1 = archive_read_support_filter_lz4(_a); break; case ARCHIVE_FILTER_ZSTD: - strcpy(str, "zstd"); + str = "zstd"; r1 = archive_read_support_filter_zstd(_a); break; case ARCHIVE_FILTER_LZIP: - strcpy(str, "lzip"); + str = "lzip"; r1 = archive_read_support_filter_lzip(_a); break; case ARCHIVE_FILTER_LZOP: - strcpy(str, "lzop"); + str = "lzop"; r1 = archive_read_support_filter_lzop(_a); break; case ARCHIVE_FILTER_LRZIP: - strcpy(str, "lrzip"); + str = "lrzip"; r1 = archive_read_support_filter_lrzip(_a); break; + case ARCHIVE_FILTER_GRZIP: + str = "grzip"; + r1 = archive_read_support_filter_grzip(_a); + break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Invalid filter code specified"); @@ -114,30 +118,30 @@ archive_read_append_filter(struct archive *_a, int code) { number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); - bidder = a->bidders; - for (i = 1; i < number_bidders; i++, bidder++) + b = a->bidders; + for (i = 1; i < number_bidders; i++, b++) { - if (!bidder->name || !strcmp(bidder->name, str)) + if (!b->name || !strcmp(b->name, str)) break; } - if (!bidder->name || strcmp(bidder->name, str)) + if (!b->name || strcmp(b->name, str)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: Unable to append filter"); return (ARCHIVE_FATAL); } - filter = calloc(1, sizeof(*filter)); - if (filter == NULL) + f = calloc(1, sizeof(*f)); + if (f == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - filter->bidder = bidder; - filter->archive = a; - filter->upstream = a->filter; - a->filter = filter; - r2 = (bidder->vtable->init)(a->filter); + f->bidder = b; + f->archive = a; + f->upstream = a->filter; + a->filter = f; + r2 = (b->vtable->init)(a->filter); if (r2 != ARCHIVE_OK) { __archive_read_free_filters(a); return (ARCHIVE_FATAL); @@ -159,8 +163,8 @@ archive_read_append_filter_program_signature(struct archive *_a, const char *cmd, const void *signature, size_t signature_len) { int r, number_bidders, i; - struct archive_read_filter_bidder *bidder; - struct archive_read_filter *filter; + struct archive_read_filter_bidder *b; + struct archive_read_filter *f; struct archive_read *a = (struct archive_read *)_a; if (archive_read_support_filter_program_signature(_a, cmd, signature, @@ -169,36 +173,36 @@ archive_read_append_filter_program_signature(struct archive *_a, number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); - bidder = a->bidders; - for (i = 0; i < number_bidders; i++, bidder++) + b = a->bidders; + for (i = 0; i < number_bidders; i++, b++) { /* Program bidder name set to filter name after initialization */ - if (bidder->data && !bidder->name) + if (b->data && !b->name) break; } - if (!bidder->data) + if (!b->data) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal error: Unable to append program filter"); return (ARCHIVE_FATAL); } - filter = calloc(1, sizeof(*filter)); - if (filter == NULL) + f = calloc(1, sizeof(*f)); + if (f == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - filter->bidder = bidder; - filter->archive = a; - filter->upstream = a->filter; - a->filter = filter; - r = (bidder->vtable->init)(a->filter); + f->bidder = b; + f->archive = a; + f->upstream = a->filter; + a->filter = f; + r = (b->vtable->init)(a->filter); if (r != ARCHIVE_OK) { __archive_read_free_filters(a); return (ARCHIVE_FATAL); } - bidder->name = a->filter->name; + b->name = a->filter->name; a->bypass_filter_bidding = 1; return r; diff --git a/libarchive/archive_read_data_into_fd.c b/libarchive/archive_read_data_into_fd.c index 8fd5e12442..d92ab9a512 100644 --- a/libarchive/archive_read_data_into_fd.c +++ b/libarchive/archive_read_data_into_fd.c @@ -37,6 +37,7 @@ #include "archive.h" #include "archive_private.h" +#include "archive_read_private.h" /* Maximum amount of data to write at one time. */ #define MAX_WRITE (1024 * 1024) @@ -46,9 +47,9 @@ */ static int pad_to(struct archive *a, int fd, int can_lseek, - size_t nulls_size, const char *nulls, - int64_t target_offset, int64_t actual_offset) + char **nulls, int64_t target_offset, int64_t actual_offset) { + const size_t nulls_size = 16384; size_t to_write; ssize_t bytes_written; @@ -56,16 +57,26 @@ pad_to(struct archive *a, int fd, int can_lseek, actual_offset = lseek(fd, target_offset - actual_offset, SEEK_CUR); if (actual_offset != target_offset) { - archive_set_error(a, errno, "Seek error"); + archive_set_error(a, + actual_offset == -1 ? errno : ARCHIVE_ERRNO_MISC, + "Seek error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } + if (*nulls == NULL) { + *nulls = calloc(1, nulls_size); + if (*nulls == NULL) { + archive_set_error(a, errno, "Out of memory"); + return (ARCHIVE_FATAL); + } + } + while (target_offset > actual_offset) { to_write = nulls_size; if (target_offset < actual_offset + (int64_t)nulls_size) to_write = (size_t)(target_offset - actual_offset); - bytes_written = write(fd, nulls, to_write); + bytes_written = write(fd, *nulls, to_write); if (bytes_written < 0) { archive_set_error(a, errno, "Write error"); return (ARCHIVE_FATAL); @@ -84,29 +95,48 @@ archive_read_data_into_fd(struct archive *a, int fd) const void *buff; size_t size, bytes_to_write; ssize_t bytes_written; + int64_t fd_offset; int64_t target_offset; int64_t actual_offset = 0; + int64_t declared_size; int can_lseek; char *nulls = NULL; - size_t nulls_size = 16384; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd"); + declared_size = ((struct archive_read *)a)->entry_bytes_declared; + can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode); - if (!can_lseek) { - nulls = calloc(1, nulls_size); - if (!nulls) { - r = ARCHIVE_FATAL; - goto cleanup; - } + if (can_lseek) { + fd_offset = lseek(fd, 0, SEEK_CUR); + if (fd_offset == -1) + can_lseek = 0; } while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) == ARCHIVE_OK) { const char *p = buff; + int overran = 0; + + if (declared_size >= 0) { + /* We know the expected size, let's enforce that we don't overrun. */ + if (target_offset > declared_size) { + /* We're already beyond the end? Don't write any more. */ + target_offset = declared_size; + size = 0; + overran = 1; + } else if (size > (uint64_t)(declared_size - target_offset)) { + /* Above is safe because target_offset <= declared_size */ + /* This block is bigger than the expected remainder */ + size = (size_t)(declared_size - target_offset); + overran = 1; + } + /* Else size <= expected remainder of data and we're OK. */ + } + if (target_offset > actual_offset) { - r = pad_to(a, fd, can_lseek, nulls_size, nulls, + r = pad_to(a, fd, can_lseek, &nulls, target_offset, actual_offset); if (r != ARCHIVE_OK) break; @@ -126,18 +156,52 @@ archive_read_data_into_fd(struct archive *a, int fd) p += bytes_written; size -= bytes_written; } + if (overran) { + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Actual entry size exceeds the declared size " + "(%jd); truncated output at the declared size", + (intmax_t)declared_size); + r = ARCHIVE_FAILED; + goto cleanup; + } } if (r == ARCHIVE_EOF && target_offset > actual_offset) { - r2 = pad_to(a, fd, can_lseek, nulls_size, nulls, - target_offset, actual_offset); - if (r2 != ARCHIVE_OK) - r = r2; + int trailing_hole_overran = + declared_size >= 0 && target_offset > declared_size; + if (trailing_hole_overran) + target_offset = declared_size; + if (target_offset > actual_offset) { + r2 = pad_to(a, fd, can_lseek, &nulls, + target_offset, actual_offset); + if (r2 != ARCHIVE_OK) + r = r2; + else + actual_offset = target_offset; + } + if (trailing_hole_overran) { + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Actual entry size exceeds the declared size " + "(%jd); truncated output at the declared size", + (intmax_t)declared_size); + r = ARCHIVE_FAILED; + goto cleanup; + } } cleanup: free(nulls); if (r != ARCHIVE_EOF) return (r); - return (ARCHIVE_OK); + r = ARCHIVE_OK; + if (can_lseek) { + int64_t offset = lseek(fd, 0, SEEK_CUR); + if (offset - fd_offset != actual_offset) { + archive_set_error(a, + offset == -1 ? errno : ARCHIVE_ERRNO_MISC, + "Seek error"); + r = ARCHIVE_FATAL; + } + } + return (r); } diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 6e6bae6a4e..c4ad595fa4 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -256,6 +256,11 @@ archive_read_disk_entry_from_file(struct archive *_a, char *linkbuffer; ssize_t lnklen; + if (st->st_size >= SSIZE_MAX) { + archive_set_error(&a->archive, ENOMEM, + "Couldn't read link data"); + return (ARCHIVE_FAILED); + } linkbuffer = malloc(linkbuffer_len + 1); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -894,7 +899,7 @@ setup_sparse_fiemap(struct archive_read_disk *a, r = ioctl(*fd, FS_IOC_FIEMAP, fm); if (r < 0) { - /* When something error happens, it is better we + /* When some error happens, it is better we * should return ARCHIVE_OK because an earlier * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ goto exit_setup_sparse_fiemap; diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 94fa8fef96..a3c9e5833a 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -453,6 +453,10 @@ archive_read_disk_new(void) a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = &archive_read_disk_vtable; a->entry = archive_entry_new2(&a->archive); + if (a->entry == NULL) { + free(a); + return (NULL); + } a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->flags = ARCHIVE_READDISK_MAC_COPYFILE; @@ -920,8 +924,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1034,8 +1038,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1060,8 +1064,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1413,18 +1417,18 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) /* * This is the new filesystem which we have to generate a new ID for. */ - fid = t->max_filesystem_id++; - if (fid > MAX_FILESYSTEM_ID) { + fid = t->max_filesystem_id; + if (fid >= MAX_FILESYSTEM_ID) { archive_set_error(&a->archive, ENOMEM, "Too many filesystems"); return (ARCHIVE_FATAL); } - if (t->max_filesystem_id > t->allocated_filesystem) { + if (fid + 1 > t->allocated_filesystem) { int s; void *p; - s = t->max_filesystem_id * 2; + s = (fid + 1) * 2; p = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); + s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); @@ -1433,6 +1437,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } + t->max_filesystem_id = fid + 1; t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; diff --git a/libarchive/archive_read_disk_set_standard_lookup.c b/libarchive/archive_read_disk_set_standard_lookup.c index 3512d343f6..01285d91bb 100644 --- a/libarchive/archive_read_disk_set_standard_lookup.c +++ b/libarchive/archive_read_disk_set_standard_lookup.c @@ -45,6 +45,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #if defined(_WIN32) && !defined(__CYGWIN__) int @@ -209,7 +210,8 @@ lookup_uname_helper(struct name_cache *cache, id_t id) * we just double it and try again. Because the buffer * is kept around in the cache object, we shouldn't * have to do this very often. */ - nbuff_size = cache->buff_size * 2; + if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2)) + break; nbuff = realloc(cache->buff, nbuff_size); if (nbuff == NULL) break; @@ -276,7 +278,8 @@ lookup_gname_helper(struct name_cache *cache, id_t id) /* ERANGE means our buffer was too small, but POSIX * doesn't tell us how big the buffer should be, so * we just double it and try again. */ - nbuff_size = cache->buff_size * 2; + if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2)) + break; nbuff = realloc(cache->buff, nbuff_size); if (nbuff == NULL) break; diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 117ca505fa..eb4699a3f8 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -37,10 +37,11 @@ #include #include "archive.h" -#include "archive_string.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_disk_private.h" +#include "archive_string.h" #include "archive_time_private.h" #ifndef O_BINARY @@ -151,6 +152,8 @@ struct tree { size_t dirname_length; int depth; + /* Whether this tree began as a wildcard search */ + int rootwild; BY_HANDLE_FILE_INFORMATION lst; BY_HANDLE_FILE_INFORMATION st; @@ -358,6 +361,8 @@ la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype) } indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + if (indata == NULL) + return (-1); ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, 1024, &inbytes, NULL); if (ret == 0) { @@ -553,6 +558,10 @@ archive_read_disk_new(void) a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = &archive_read_disk_vtable; a->entry = archive_entry_new2(&a->archive); + if (a->entry == NULL) { + free(a); + return (NULL); + } a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->flags = ARCHIVE_READDISK_MAC_COPYFILE; @@ -924,7 +933,27 @@ next_entry(struct archive_read_disk *a, struct tree *t, case 0: return (ARCHIVE_EOF); case TREE_POSTDESCENT: + if (t->depth == 1 && t->rootwild && t->symlink_mode == 'H') { + /* + * We have descended from a directory at depth 0 specified by a + * wildcard search in 'H'ybrid link mode. All items under this + * directory should be 'P'hysical. + */ + t->symlink_mode = 'P'; + a->follow_symlinks = 0; + } + break; case TREE_POSTASCENT: + if (t->depth == 0 && t->rootwild && t->initial_symlink_mode == 'H') { + /* + * We have returned to the root of a wildcard search, in 'H'ybrid + * mode, so restore the symlink mode we overwrote in + * TREE_POSTDESCENT. + */ + t->symlink_mode = t->initial_symlink_mode; + /* Mimics setup done in setup_symlink_mode */ + a->follow_symlinks = 1; + } break; case TREE_REGULAR: lst = tree_current_lstat(t); @@ -946,8 +975,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -964,7 +993,14 @@ next_entry(struct archive_read_disk *a, struct tree *t, switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ - t->symlink_mode = 'P'; + if (!t->rootwild) { + /* + * 'H': When we started with a wildcard, entries at the starting + * depth should be kept in `H` mode; switching into 'P' mode + * is handled in TREE_POSTDESCENT when we enter depth = 1. + */ + t->symlink_mode = 'P'; + } /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': @@ -1018,8 +1054,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1044,8 +1080,8 @@ next_entry(struct archive_read_disk *a, struct tree *t, if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { - archive_set_error(&(a->archive), errno, - "Failed : %s", archive_error_string(a->matching)); + archive_set_error(&(a->archive), archive_errno(a->matching), + "%s", archive_error_string(a->matching)); return (r); } if (r) { @@ -1450,18 +1486,18 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) /* * There is a new filesystem, we generate a new ID for. */ - fid = t->max_filesystem_id++; - if (fid > MAX_FILESYSTEM_ID) { + fid = t->max_filesystem_id; + if (fid >= MAX_FILESYSTEM_ID) { archive_set_error(&a->archive, ENOMEM, "Too many filesystems"); return (ARCHIVE_FATAL); } - if (t->max_filesystem_id > t->allocated_filesystem) { + if (fid + 1 > t->allocated_filesystem) { int s; void *p; - s = t->max_filesystem_id * 2; + s = (fid + 1) * 2; p = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); + s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); @@ -1470,6 +1506,7 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } + t->max_filesystem_id = fid + 1; t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; @@ -1632,6 +1669,8 @@ tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path, struct tree_entry *te; te = calloc(1, sizeof(*te)); + if (te == NULL) + return; te->next = t->stack; te->parent = t->current; if (te->parent) @@ -1704,6 +1743,8 @@ tree_open(const wchar_t *path, int symlink_mode, int restore_time) struct tree *t; t = calloc(1, sizeof(*t)); + if (t == NULL) + return (NULL); archive_string_init(&(t->full_path)); archive_string_init(&t->path); if (archive_wstring_ensure(&t->path, 15) == NULL) { @@ -1726,6 +1767,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time) t->full_path_dir_length = 0; t->dirname_length = 0; t->depth = 0; + t->rootwild = 0; t->descend = 0; t->current = NULL; t->d = INVALID_HANDLE_VALUE; @@ -1779,6 +1821,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time) t->full_path.length = wcslen(t->full_path.s); t->full_path_dir_length = archive_strlen(&t->full_path); } + t->rootwild = 1; } tree_push(t, base, t->full_path.s, 0, 0, 0, NULL); archive_wstring_free(&ws); @@ -2498,7 +2541,12 @@ setup_sparse_from_disk(struct archive_read_disk *a, (DWORD)outranges_size, &retbytes, NULL); if (ret == 0 && GetLastError() == ERROR_MORE_DATA) { free(outranges); - outranges_size *= 2; + if (archive_ckd_mul_size(&outranges_size, outranges_size, 2)) { + archive_set_error(&a->archive, ENOMEM, + "Couldn't allocate memory"); + exit_sts = ARCHIVE_FATAL; + goto exit_setup_sparse; + } outranges = (FILE_ALLOCATED_RANGE_BUFFER *) malloc(outranges_size); if (outranges == NULL) { diff --git a/libarchive/archive_read_filter.3 b/libarchive/archive_read_filter.3 index 72ff240fd3..05513ececd 100644 --- a/libarchive/archive_read_filter.3 +++ b/libarchive/archive_read_filter.3 @@ -142,7 +142,9 @@ These functions return .Cm ARCHIVE_OK if the compression is fully supported, .Cm ARCHIVE_WARN -if the compression is supported only through an external program. +if the compression is supported only through an external program, or +.Cm ARCHIVE_FATAL +on error. .Pp .Fn archive_read_support_filter_none always succeeds. @@ -158,5 +160,4 @@ functions. .Xr archive_read 3 , .Xr archive_read_data 3 , .Xr archive_read_format 3 , -.Xr archive_read_format 3 , .Xr libarchive 3 diff --git a/libarchive/archive_read_open.3 b/libarchive/archive_read_open.3 index 081b7114be..0e85d16f0d 100644 --- a/libarchive/archive_read_open.3 +++ b/libarchive/archive_read_open.3 @@ -31,6 +31,7 @@ .Nm archive_read_open_fd , .Nm archive_read_open_FILE , .Nm archive_read_open_filename , +.Nm archive_read_open_filenames , .Nm archive_read_open_memory .Nd functions for reading streaming archives .Sh LIBRARY @@ -65,6 +66,12 @@ Streaming Archive Library (libarchive, -larchive) .Fa "size_t block_size" .Fc .Ft int +.Fo archive_read_open_filenames +.Fa "struct archive *" +.Fa "const char **filenames" +.Fa "size_t block_size" +.Fc +.Ft int .Fn archive_read_open_memory "struct archive *" "const void *buff" "size_t size" .Sh DESCRIPTION .Bl -tag -compact -width indent @@ -111,6 +118,15 @@ Like except that it accepts a simple filename and a block size. A NULL filename represents standard input. This function is safe for use with tape drives or other blocked devices. +.It Fn archive_read_open_filenames +Like +.Fn archive_read_open , +except that it accepts a NULL-terminated array of filenames to open in +sequence (as would be created by the +.Xr split 1 +command) and a block size. +A NULL filename at the beginning of the array represents standard input. +This function is safe for use with tape drives or other blocked devices. .It Fn archive_read_open_memory Like .Fn archive_read_open , diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c index c85a62a3e2..358e516faf 100644 --- a/libarchive/archive_read_open_fd.c +++ b/libarchive/archive_read_open_fd.c @@ -69,6 +69,7 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size) la_seek_stat_t st; struct read_fd_data *mine; void *b; + int r; archive_clear_error(a); if (la_seek_fstat(fd, &st) != 0) { @@ -107,7 +108,9 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size) 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); + r = archive_read_set_callback_data(a, mine); + if (r < 0) + return (r); return (archive_read_open1(a)); } diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index ba63969909..a16aac9777 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -70,6 +70,7 @@ archive_read_open_FILE(struct archive *a, FILE *f) struct read_FILE_data *mine; size_t block_size = 128 * 1024; void *b; + int r; archive_clear_error(a); mine = calloc(1, sizeof(*mine)); @@ -104,7 +105,9 @@ archive_read_open_FILE(struct archive *a, FILE *f) 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); + r = archive_read_set_callback_data(a, mine); + if (r < 0) + return (r); return (archive_read_open1(a)); } diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index 9ec1e6c0e8..91f64b694a 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -40,6 +40,9 @@ #ifdef HAVE_IO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -181,7 +184,7 @@ archive_read_open_filenames_w(struct archive *a, const wchar_t **wfilenames, if (wfilename == NULL) wfilename = L""; mine = calloc(1, - sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); + sizeof(*mine) + wcslen(wfilename) * MB_LEN_MAX); if (mine == NULL) goto no_memory; mine->block_size = block_size; @@ -315,7 +318,7 @@ file_open(struct archive *a, void *client_data) } #else archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unexpedted operation in archive_read_open_filename"); + "Unexpected operation in archive_read_open_filename"); goto fail; #endif } diff --git a/libarchive/archive_read_open_memory.c b/libarchive/archive_read_open_memory.c index 460bb5ae6a..441662d464 100644 --- a/libarchive/archive_read_open_memory.c +++ b/libarchive/archive_read_open_memory.c @@ -68,6 +68,7 @@ archive_read_open_memory2(struct archive *a, const void *buff, size_t size, size_t read_size) { struct read_memory_data *mine; + int r; mine = calloc(1, sizeof(*mine)); if (mine == NULL) { @@ -82,7 +83,9 @@ archive_read_open_memory2(struct archive *a, const void *buff, archive_read_set_seek_callback(a, memory_read_seek); archive_read_set_skip_callback(a, memory_read_skip); archive_read_set_close_callback(a, memory_read_close); - archive_read_set_callback_data(a, mine); + r = archive_read_set_callback_data(a, mine); + if (r < 0) + return (r); return (archive_read_open1(a)); } diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 0c374f487e..3bd8de2de6 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -76,10 +76,10 @@ struct archive_read_filter_bidder { struct archive_read_filter_vtable { /* Return next block. */ ssize_t (*read)(struct archive_read_filter *, const void **); - /* Close (just this filter) and free(self). */ - int (*close)(struct archive_read_filter *self); + /* Close (just this filter) and free(filter). */ + int (*close)(struct archive_read_filter *); /* Read any header metadata if available. */ - int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry); + int (*read_header)(struct archive_read_filter *, struct archive_entry *); }; /* @@ -180,6 +180,15 @@ struct archive_read { /* File offset of beginning of most recently-read header. */ int64_t header_position; + /* + * Declared uncompressed size of the entry whose header was most + * recently read, or -1 if no size was declared. Lets format- + * agnostic consumers like archive_read_data_into_fd() cap their + * output at the size an entry actually promised, regardless of + * whether the format reader enforces that itself. + */ + int64_t entry_bytes_declared; + /* Nodes and offsets of compressed data block */ unsigned int data_start_node; unsigned int data_end_node; diff --git a/libarchive/archive_read_support_filter_all.c b/libarchive/archive_read_support_filter_all.c index cb46d120d1..f08677301f 100644 --- a/libarchive/archive_read_support_filter_all.c +++ b/libarchive/archive_read_support_filter_all.c @@ -43,34 +43,34 @@ archive_read_support_filter_all(struct archive *a) archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_filter_all"); - /* Bzip falls back to "bunzip2" command-line */ + /* Bzip falls back to "bzip2 -d" command-line */ archive_read_support_filter_bzip2(a); /* The decompress code doesn't use an outside library. */ archive_read_support_filter_compress(a); /* Gzip decompress falls back to "gzip -d" command-line. */ archive_read_support_filter_gzip(a); - /* Lzip falls back to "unlzip" command-line program. */ + /* Lzip falls back to "lzip -d -q" command-line. */ archive_read_support_filter_lzip(a); /* The LZMA file format has a very weak signature, so it * may not be feasible to keep this here, but we'll try. * This will come back out if there are problems. */ - /* Lzma falls back to "unlzma" command-line program. */ + /* Lzma falls back to "lzma -d -qq" command-line. */ archive_read_support_filter_lzma(a); - /* Xz falls back to "unxz" command-line program. */ + /* Xz falls back to "xz -d -qq" command-line. */ archive_read_support_filter_xz(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_uu(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_rpm(a); - /* The decode code always uses "lrzip -q -d" command-line. */ + /* The decode code always uses "lrzip -d -q" command-line. */ archive_read_support_filter_lrzip(a); /* Lzop decompress falls back to "lzop -d" command-line. */ 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. */ + /* Lz4 falls back to "lz4 -d -q" command-line. */ archive_read_support_filter_lz4(a); - /* Zstd falls back to "zstd -d" command-line program. */ + /* Zstd falls back to "zstd -d -qq" command-line. */ archive_read_support_filter_zstd(a); /* Note: We always return ARCHIVE_OK here, even if some of the diff --git a/libarchive/archive_read_support_filter_bzip2.c b/libarchive/archive_read_support_filter_bzip2.c index 479237c277..588953f2aa 100644 --- a/libarchive/archive_read_support_filter_bzip2.c +++ b/libarchive/archive_read_support_filter_bzip2.c @@ -28,6 +28,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #include #ifdef HAVE_STDLIB_H #include @@ -47,7 +50,7 @@ #include "archive_read_private.h" #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) -struct private_data { +struct bzip2 { bz_stream stream; char *out_block; size_t out_block_size; @@ -63,8 +66,7 @@ static int bzip2_filter_close(struct archive_read_filter *); /* * Note that we can detect bzip2 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 bzlib is unavailable. + * error messages.) */ static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int bzip2_reader_init(struct archive_read_filter *); @@ -110,16 +112,15 @@ archive_read_support_filter_bzip2(struct archive *_a) * from verifying as much as we would like. */ static int -bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter) +bzip2_reader_bid(struct archive_read_filter_bidder *b, struct archive_read_filter *f) { const unsigned char *buffer; - ssize_t avail; int bits_checked; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ /* Minimal bzip2 archive is 14 bytes. */ - buffer = __archive_read_filter_ahead(filter, 14, &avail); + buffer = __archive_read_filter_ahead(f, 14, NULL); if (buffer == NULL) return (0); @@ -150,21 +151,21 @@ bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_fi #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) /* - * 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. + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "bzip2 -d" + * in case that's available. */ static int -bzip2_reader_init(struct archive_read_filter *self) +bzip2_reader_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "bzip2 -d"); + r = __archive_read_program(f, "bzip2 -d"); /* 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_BZIP2; - self->name = "bzip2"; + f->code = ARCHIVE_FILTER_BZIP2; + f->name = "bzip2"; return (r); } @@ -181,29 +182,29 @@ bzip2_reader_vtable = { * Setup the callbacks. */ static int -bzip2_reader_init(struct archive_read_filter *self) +bzip2_reader_init(struct archive_read_filter *f) { static const size_t out_block_size = 64 * 1024; void *out_block; - struct private_data *state; + struct bzip2 *bzip2; - self->code = ARCHIVE_FILTER_BZIP2; - self->name = "bzip2"; + f->code = ARCHIVE_FILTER_BZIP2; + f->name = "bzip2"; - state = calloc(1, sizeof(*state)); + bzip2 = calloc(1, sizeof(*bzip2)); out_block = malloc(out_block_size); - if (state == NULL || out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + if (bzip2 == NULL || out_block == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for bzip2 decompression"); free(out_block); - free(state); + free(bzip2); return (ARCHIVE_FATAL); } - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->vtable = &bzip2_reader_vtable; + f->data = bzip2; + bzip2->out_block_size = out_block_size; + bzip2->out_block = out_block; + f->vtable = &bzip2_reader_vtable; return (ARCHIVE_OK); } @@ -212,42 +213,42 @@ bzip2_reader_init(struct archive_read_filter *self) * Return the next block of decompressed data. */ static ssize_t -bzip2_filter_read(struct archive_read_filter *self, const void **p) +bzip2_filter_read(struct archive_read_filter *f, const void **p) { - struct private_data *state; + struct bzip2 *bzip2 = f->data; size_t decompressed; const char *read_buf; ssize_t ret; - state = (struct private_data *)self->data; - - if (state->eof) { + if (bzip2->eof) { *p = NULL; return (0); } /* Empty our output buffer. */ - state->stream.next_out = state->out_block; - state->stream.avail_out = (uint32_t)state->out_block_size; + bzip2->stream.next_out = bzip2->out_block; + bzip2->stream.avail_out = (uint32_t)bzip2->out_block_size; /* Try to fill the output buffer. */ for (;;) { - if (!state->valid) { - if (bzip2_reader_bid(self->bidder, self->upstream) == 0) { - state->eof = 1; - *p = state->out_block; - decompressed = state->stream.next_out - - state->out_block; + ssize_t max_in; + + if (!bzip2->valid) { + if (bzip2_reader_bid(f->bidder, f->upstream) == 0) { + bzip2->eof = 1; + *p = bzip2->out_block; + decompressed = bzip2->stream.next_out + - bzip2->out_block; return (decompressed); } /* Initialize compression library. */ - ret = BZ2_bzDecompressInit(&(state->stream), + ret = BZ2_bzDecompressInit(&(bzip2->stream), 0 /* library verbosity */, 0 /* don't use low-mem algorithm */); /* If init fails, try low-memory algorithm instead. */ if (ret == BZ_MEM_ERROR) - ret = BZ2_bzDecompressInit(&(state->stream), + ret = BZ2_bzDecompressInit(&(bzip2->stream), 0 /* library verbosity */, 1 /* do use low-mem algo */); @@ -266,65 +267,63 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) detail = "mis-compiled library"; break; } - archive_set_error(&self->archive->archive, err, + archive_set_error(&f->archive->archive, err, "Internal error initializing decompressor%s%s", detail == NULL ? "" : ": ", detail); return (ARCHIVE_FATAL); } - state->valid = 1; + bzip2->valid = 1; } /* stream.next_in is really const, but bzlib * doesn't declare it so. */ read_buf = - __archive_read_filter_ahead(self->upstream, 1, &ret); + __archive_read_filter_ahead(f->upstream, 1, &ret); if (read_buf == NULL) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated bzip2 input"); return (ARCHIVE_FATAL); } - state->stream.next_in = (char *)(uintptr_t)read_buf; - state->stream.avail_in = (uint32_t)ret; - /* There is no more data, return whatever we have. */ - if (ret == 0) { - state->eof = 1; - *p = state->out_block; - decompressed = state->stream.next_out - - state->out_block; - return (decompressed); - } + bzip2->stream.next_in = (char *)(uintptr_t)read_buf; + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (ret > max_in) + ret = max_in; + bzip2->stream.avail_in = (uint32_t)ret; /* Decompress as much as we can in one pass. */ - ret = BZ2_bzDecompress(&(state->stream)); - __archive_read_filter_consume(self->upstream, - state->stream.next_in - read_buf); + ret = BZ2_bzDecompress(&(bzip2->stream)); + __archive_read_filter_consume(f->upstream, + bzip2->stream.next_in - read_buf); switch (ret) { case BZ_STREAM_END: /* Found end of stream. */ - switch (BZ2_bzDecompressEnd(&(state->stream))) { + switch (BZ2_bzDecompressEnd(&(bzip2->stream))) { case BZ_OK: break; default: - archive_set_error(&(self->archive->archive), + archive_set_error(&(f->archive->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FATAL); } - state->valid = 0; + bzip2->valid = 0; /* FALLTHROUGH */ case BZ_OK: /* Decompressor made some progress. */ /* If we filled our buffer, update stats and return. */ - if (state->stream.avail_out == 0) { - *p = state->out_block; - decompressed = state->stream.next_out - - state->out_block; + if (bzip2->stream.avail_out == 0) { + *p = bzip2->out_block; + decompressed = bzip2->stream.next_out + - bzip2->out_block; return (decompressed); } break; default: /* Return an error. */ - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FATAL); } @@ -335,28 +334,26 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -bzip2_filter_close(struct archive_read_filter *self) +bzip2_filter_close(struct archive_read_filter *f) { - struct private_data *state; + struct bzip2 *bzip2 = f->data; int ret = ARCHIVE_OK; - state = (struct private_data *)self->data; - - if (state->valid) { - switch (BZ2_bzDecompressEnd(&state->stream)) { + if (bzip2->valid) { + switch (BZ2_bzDecompressEnd(&bzip2->stream)) { case BZ_OK: break; default: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); ret = ARCHIVE_FATAL; } - state->valid = 0; + bzip2->valid = 0; } - free(state->out_block); - free(state); + free(bzip2->out_block); + free(bzip2); return (ret); } diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c index b89eaabe52..4e1f1dc52b 100644 --- a/libarchive/archive_read_support_filter_compress.c +++ b/libarchive/archive_read_support_filter_compress.c @@ -90,7 +90,7 @@ * names and structure here don't exactly match those used by compress. */ -struct private_data { +struct compress { /* Input variables. */ const unsigned char *next_in; size_t avail_in; @@ -104,6 +104,7 @@ struct private_data { void *out_block; /* Decompression status variables. */ + int initialized; int use_reset_code; int end_of_stream; /* EOF status. */ int maxcode; /* Largest code. */ @@ -168,17 +169,16 @@ archive_read_support_filter_compress(struct archive *_a) * This logic returns zero if any part of the signature fails. */ static int -compress_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +compress_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *buffer; - ssize_t avail; int bits_checked; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ /* Shortest valid compress file is 3 bytes. */ - buffer = __archive_read_filter_ahead(filter, 3, &avail); + buffer = __archive_read_filter_ahead(f, 3, NULL); if (buffer == NULL) return (0); @@ -207,61 +207,69 @@ compress_reader_vtable = { * Setup the callbacks. */ static int -compress_bidder_init(struct archive_read_filter *self) +compress_bidder_init(struct archive_read_filter *f) { - struct private_data *state; + struct compress *compress; static const size_t out_block_size = 64 * 1024; void *out_block; - int code; - self->code = ARCHIVE_FILTER_COMPRESS; - self->name = "compress (.Z)"; + f->code = ARCHIVE_FILTER_COMPRESS; + f->name = "compress (.Z)"; - state = calloc(1, sizeof(*state)); + compress = calloc(1, sizeof(*compress)); out_block = malloc(out_block_size); - if (state == NULL || out_block == NULL) { + if (compress == NULL || out_block == NULL) { free(out_block); - free(state); - archive_set_error(&self->archive->archive, ENOMEM, + free(compress); + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for %s decompression", - self->name); + f->name); return (ARCHIVE_FATAL); } - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->vtable = &compress_reader_vtable; + f->data = compress; + compress->out_block_size = out_block_size; + compress->out_block = out_block; + f->vtable = &compress_reader_vtable; - /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */ + return (ARCHIVE_OK); +} - (void)getbits(self, 8); /* Skip first signature byte. */ - (void)getbits(self, 8); /* Skip second signature byte. */ +static int +compress_filter_init(struct archive_read_filter *f) +{ + struct compress *compress = f->data; + int code; + + compress->initialized = 1; + + (void)getbits(f, 8); /* Skip first signature byte. */ + (void)getbits(f, 8); /* Skip second signature byte. */ /* Get compression parameters. */ - code = getbits(self, 8); - if ((code & 0x1f) > 16) { - archive_set_error(&self->archive->archive, -1, + code = getbits(f, 8); + if (code < 0 || (code & 0x1f) > 16) { + archive_set_error(&f->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; + compress->maxcode_bits = code & 0x1f; + compress->maxcode = (1 << compress->maxcode_bits); + compress->use_reset_code = code & 0x80; /* Initialize decompressor. */ - state->free_ent = 256; - state->stackp = state->stack; - if (state->use_reset_code) - state->free_ent++; - state->bits = 9; - state->section_end_code = (1<bits) - 1; - state->oldcode = -1; + compress->free_ent = 256; + compress->stackp = compress->stack; + if (compress->use_reset_code) + compress->free_ent++; + compress->bits = 9; + compress->section_end_code = (1<bits) - 1; + compress->oldcode = -1; for (code = 255; code >= 0; code--) { - state->prefix[code] = 0; - state->suffix[code] = code; + compress->prefix[code] = 0; + compress->suffix[code] = code; } - next_code(self); + next_code(f); return (ARCHIVE_OK); } @@ -271,27 +279,31 @@ compress_bidder_init(struct archive_read_filter *self) * as necessary. */ static ssize_t -compress_filter_read(struct archive_read_filter *self, const void **pblock) +compress_filter_read(struct archive_read_filter *f, const void **pblock) { - struct private_data *state; + struct compress *compress = f->data; unsigned char *p, *start, *end; int ret; - state = (struct private_data *)self->data; - if (state->end_of_stream) { + if (!compress->initialized) { + ret = compress_filter_init(f); + if (ret != ARCHIVE_OK) + return (ret); + } + if (compress->end_of_stream) { *pblock = NULL; return (0); } - p = start = (unsigned char *)state->out_block; - end = start + state->out_block_size; + p = start = (unsigned char *)compress->out_block; + end = start + compress->out_block_size; - while (p < end && !state->end_of_stream) { - if (state->stackp > state->stack) { - *p++ = *--state->stackp; + while (p < end && !compress->end_of_stream) { + if (compress->stackp > compress->stack) { + *p++ = *--compress->stackp; } else { - ret = next_code(self); + ret = next_code(f); if (ret == -1) - state->end_of_stream = ret; + compress->end_of_stream = ret; else if (ret != ARCHIVE_OK) return (ret); } @@ -305,12 +317,12 @@ compress_filter_read(struct archive_read_filter *self, const void **pblock) * Close and release the filter. */ static int -compress_filter_close(struct archive_read_filter *self) +compress_filter_close(struct archive_read_filter *f) { - struct private_data *state = (struct private_data *)self->data; + struct compress *compress = f->data; - free(state->out_block); - free(state); + free(compress->out_block); + free(compress); return (ARCHIVE_OK); } @@ -320,25 +332,18 @@ compress_filter_close(struct archive_read_filter *self) * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise. */ static int -next_code(struct archive_read_filter *self) +next_code(struct archive_read_filter *f) { - struct private_data *state = (struct private_data *)self->data; + struct compress *compress = f->data; int code, newcode; - static int debug_buff[1024]; - static unsigned debug_index; - again: - code = newcode = getbits(self, state->bits); + code = newcode = getbits(f, compress->bits); if (code < 0) return (code); - debug_buff[debug_index++] = code; - if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0])) - debug_index = 0; - /* If it's a reset code, reset the dictionary. */ - if ((code == 256) && state->use_reset_code) { + if ((code == 256) && compress->use_reset_code) { /* * The original 'compress' implementation blocked its * I/O in a manner that resulted in junk bytes being @@ -346,63 +351,63 @@ again: * this junk. (Yes, the number of *bytes* to skip is * a function of the current *bit* length.) */ - int skip_bytes = state->bits - - (state->bytes_in_section % state->bits); - skip_bytes %= state->bits; - state->bits_avail = 0; /* Discard rest of this byte. */ + int skip_bytes = compress->bits - + (compress->bytes_in_section % compress->bits); + skip_bytes %= compress->bits; + compress->bits_avail = 0; /* Discard rest of this byte. */ while (skip_bytes-- > 0) { - code = getbits(self, 8); + code = getbits(f, 8); if (code < 0) return (code); } /* Now, actually do the reset. */ - state->bytes_in_section = 0; - state->bits = 9; - state->section_end_code = (1 << state->bits) - 1; - state->free_ent = 257; - state->oldcode = -1; + compress->bytes_in_section = 0; + compress->bits = 9; + compress->section_end_code = (1 << compress->bits) - 1; + compress->free_ent = 257; + compress->oldcode = -1; goto again; } - if (code > state->free_ent - || (code == state->free_ent && state->oldcode < 0)) { + if (code > compress->free_ent + || (code == compress->free_ent && compress->oldcode < 0)) { /* An invalid code is a fatal error. */ - archive_set_error(&(self->archive->archive), -1, + archive_set_error(&(f->archive->archive), -1, "Invalid compressed data"); return (ARCHIVE_FATAL); } /* Special case for KwKwK string. */ - if (code >= state->free_ent) { - *state->stackp++ = state->finbyte; - code = state->oldcode; + if (code >= compress->free_ent) { + *compress->stackp++ = compress->finbyte; + code = compress->oldcode; } /* Generate output characters in reverse order. */ while (code >= 256) { - *state->stackp++ = state->suffix[code]; - code = state->prefix[code]; + *compress->stackp++ = compress->suffix[code]; + code = compress->prefix[code]; } - *state->stackp++ = state->finbyte = code; + *compress->stackp++ = compress->finbyte = code; /* Generate the new entry. */ - code = state->free_ent; - if (code < state->maxcode && state->oldcode >= 0) { - state->prefix[code] = state->oldcode; - state->suffix[code] = state->finbyte; - ++state->free_ent; + code = compress->free_ent; + if (code < compress->maxcode && compress->oldcode >= 0) { + compress->prefix[code] = compress->oldcode; + compress->suffix[code] = compress->finbyte; + ++compress->free_ent; } - if (state->free_ent > state->section_end_code) { - state->bits++; - state->bytes_in_section = 0; - if (state->bits == state->maxcode_bits) - state->section_end_code = state->maxcode; + if (compress->free_ent > compress->section_end_code) { + compress->bits++; + compress->bytes_in_section = 0; + if (compress->bits == compress->maxcode_bits) + compress->section_end_code = compress->maxcode; else - state->section_end_code = (1 << state->bits) - 1; + compress->section_end_code = (1 << compress->bits) - 1; } /* Remember previous code. */ - state->oldcode = newcode; + compress->oldcode = newcode; return (ARCHIVE_OK); } @@ -412,9 +417,9 @@ again: * -1 indicates end of available data. */ static int -getbits(struct archive_read_filter *self, int n) +getbits(struct archive_read_filter *f, int n) { - struct private_data *state = (struct private_data *)self->data; + struct compress *compress = f->data; int code; ssize_t ret; static const int mask[] = { @@ -422,31 +427,31 @@ getbits(struct archive_read_filter *self, int n) 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; - while (state->bits_avail < n) { - if (state->avail_in <= 0) { - if (state->consume_unnotified) { - __archive_read_filter_consume(self->upstream, - state->consume_unnotified); - state->consume_unnotified = 0; + while (compress->bits_avail < n) { + if (compress->avail_in <= 0) { + if (compress->consume_unnotified) { + __archive_read_filter_consume(f->upstream, + compress->consume_unnotified); + compress->consume_unnotified = 0; } - state->next_in - = __archive_read_filter_ahead(self->upstream, + compress->next_in + = __archive_read_filter_ahead(f->upstream, 1, &ret); if (ret == 0) return (-1); - if (ret < 0 || state->next_in == NULL) + if (compress->next_in == NULL) return (ARCHIVE_FATAL); - state->consume_unnotified = state->avail_in = ret; + compress->consume_unnotified = compress->avail_in = ret; } - state->bit_buffer |= *state->next_in++ << state->bits_avail; - state->avail_in--; - state->bits_avail += 8; - state->bytes_in_section++; + compress->bit_buffer |= *compress->next_in++ << compress->bits_avail; + compress->avail_in--; + compress->bits_avail += 8; + compress->bytes_in_section++; } - code = state->bit_buffer; - state->bit_buffer >>= n; - state->bits_avail -= n; + code = compress->bit_buffer; + compress->bit_buffer >>= n; + compress->bits_avail -= n; return (code & mask[n]); } diff --git a/libarchive/archive_read_support_filter_grzip.c b/libarchive/archive_read_support_filter_grzip.c index 15b6757cb9..9d9ad46eae 100644 --- a/libarchive/archive_read_support_filter_grzip.c +++ b/libarchive/archive_read_support_filter_grzip.c @@ -62,7 +62,7 @@ archive_read_support_filter_grzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, NULL, + if (__archive_read_register_bidder(a, NULL, "grzip", &grzip_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -76,16 +76,15 @@ archive_read_support_filter_grzip(struct archive *_a) * Bidder just verifies the header and returns the number of verified bits. */ static int -grzip_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +grzip_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *p; - ssize_t avail; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(f, sizeof(grzip_magic), NULL); + if (p == NULL) return (0); if (memcmp(p, grzip_magic, sizeof(grzip_magic))) @@ -95,15 +94,15 @@ grzip_bidder_bid(struct archive_read_filter_bidder *self, } static int -grzip_bidder_init(struct archive_read_filter *self) +grzip_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "grzip -d"); + r = __archive_read_program(f, "grzip -d"); /* 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_GRZIP; - self->name = "grzip"; + f->code = ARCHIVE_FILTER_GRZIP; + f->name = "grzip"; return (r); } diff --git a/libarchive/archive_read_support_filter_gzip.c b/libarchive/archive_read_support_filter_gzip.c index 726385e328..89ae0f3714 100644 --- a/libarchive/archive_read_support_filter_gzip.c +++ b/libarchive/archive_read_support_filter_gzip.c @@ -51,12 +51,11 @@ #include "archive_read_private.h" #ifdef HAVE_ZLIB_H -struct private_data { +struct gzip { z_stream stream; char in_stream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; unsigned long crc; uint32_t mtime; char *name; @@ -71,12 +70,7 @@ static int gzip_filter_close(struct archive_read_filter *); /* * Note that we can detect gzip 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 zlib is unavailable. - * - * TODO: If zlib is unavailable, gzip_bidder_init() should - * use the compress_program framework to try to fire up an external - * gzip program. + * error messages.) */ static int gzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -126,9 +120,9 @@ archive_read_support_filter_gzip(struct archive *_a) #define MAX_FILENAME_LENGTH (1024 * 1024L) #define MAX_COMMENT_LENGTH (1024 * 1024L) static ssize_t -peek_at_header(struct archive_read_filter *filter, int *pbits, +peek_at_header(struct archive_read_filter *f, int *pbits, #ifdef HAVE_ZLIB_H - struct private_data *state + struct gzip *gzip #else void *state #endif @@ -145,8 +139,8 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, /* Start by looking at the first ten bytes of the header, which * is all fixed layout. */ len = 10; - p = __archive_read_filter_ahead(filter, len, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(f, len, &avail); + if (p == NULL) return (0); /* We only support deflation- third byte must be 0x08. */ if (memcmp(p, "\x1F\x8B\x08", 3) != 0) @@ -158,8 +152,8 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, header_flags = p[3]; /* Bytes 4-7 are mod time in little endian. */ #ifdef HAVE_ZLIB_H - if (state) - state->mtime = archive_le32dec(p + 4); + if (gzip) + gzip->mtime = archive_le32dec(p + 4); #endif /* Byte 8 is deflate flags. */ /* XXXX TODO: return deflate flags back to consume_header for use @@ -168,10 +162,10 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, /* Optional extra data: 2 byte length plus variable body. */ if (header_flags & 4) { - p = __archive_read_filter_ahead(filter, len + 2, &avail); + p = __archive_read_filter_ahead(f, len + 2, &avail); if (p == NULL) return (0); - len += ((int)p[len + 1] << 8) | (int)p[len]; + len += archive_le16dec(p + len); len += 2; } @@ -186,7 +180,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, if (avail > MAX_FILENAME_LENGTH) { return (0); } - p = __archive_read_filter_ahead(filter, + p = __archive_read_filter_ahead(f, len, &avail); } if (p == NULL) @@ -194,10 +188,10 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, } while (p[len - 1] != 0); #ifdef HAVE_ZLIB_H - if (state) { + if (gzip) { /* Reset the name in case of repeat header reads. */ - free(state->name); - state->name = strdup((const char *)&p[file_start]); + free(gzip->name); + gzip->name = strdup((const char *)&p[file_start]); } #endif } @@ -210,7 +204,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, if (avail > MAX_COMMENT_LENGTH) { return (0); } - p = __archive_read_filter_ahead(filter, + p = __archive_read_filter_ahead(f, len, &avail); } if (p == NULL) @@ -220,7 +214,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, /* Optional header CRC */ if ((header_flags & 2)) { - p = __archive_read_filter_ahead(filter, len + 2, &avail); + p = __archive_read_filter_ahead(f, len + 2, &avail); if (p == NULL) return (0); #if 0 @@ -242,14 +236,14 @@ peek_at_header(struct archive_read_filter *filter, int *pbits, * Bidder just verifies the header and returns the number of verified bits. */ static int -gzip_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +gzip_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { int bits_checked; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - if (peek_at_header(filter, &bits_checked, NULL)) + if (peek_at_header(f, &bits_checked, NULL)) return (bits_checked); return (0); } @@ -262,35 +256,33 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self, * in case that's available. */ static int -gzip_bidder_init(struct archive_read_filter *self) +gzip_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "gzip -d"); + r = __archive_read_program(f, "gzip -d"); /* 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_GZIP; - self->name = "gzip"; + f->code = ARCHIVE_FILTER_GZIP; + f->name = "gzip"; return (r); } #else static int -gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry) +gzip_read_header(struct archive_read_filter *f, struct archive_entry *entry) { - struct private_data *state; + struct gzip *gzip = f->data; - state = (struct private_data *)self->data; - - /* A mtime of 0 is considered invalid/missing. */ - if (state->mtime != 0) - archive_entry_set_mtime(entry, state->mtime, 0); + /* An mtime of 0 is considered invalid/missing. */ + if (gzip->mtime != 0) + archive_entry_set_mtime(entry, gzip->mtime, 0); /* If the name is available, extract it. */ - if (state->name) - archive_entry_set_pathname(entry, state->name); + if (gzip->name) + archive_entry_set_pathname(entry, gzip->name); return (ARCHIVE_OK); } @@ -308,85 +300,95 @@ gzip_reader_vtable = { * Initialize the filter object. */ static int -gzip_bidder_init(struct archive_read_filter *self) +gzip_bidder_init(struct archive_read_filter *f) { - struct private_data *state; + struct gzip *gzip; static const size_t out_block_size = 64 * 1024; void *out_block; - self->code = ARCHIVE_FILTER_GZIP; - self->name = "gzip"; + f->code = ARCHIVE_FILTER_GZIP; + f->name = "gzip"; - state = calloc(1, sizeof(*state)); + gzip = calloc(1, sizeof(*gzip)); out_block = malloc(out_block_size); - if (state == NULL || out_block == NULL) { + if (gzip == NULL || out_block == NULL) { free(out_block); - free(state); - archive_set_error(&self->archive->archive, ENOMEM, + free(gzip); + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for gzip decompression"); return (ARCHIVE_FATAL); } - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->vtable = &gzip_reader_vtable; + f->data = gzip; + gzip->out_block_size = out_block_size; + gzip->out_block = out_block; + f->vtable = &gzip_reader_vtable; - state->in_stream = 0; /* We're not actually within a stream yet. */ + gzip->in_stream = 0; /* We're not actually within a stream yet. */ return (ARCHIVE_OK); } static int -consume_header(struct archive_read_filter *self) +consume_header(struct archive_read_filter *f) { - struct private_data *state; - ssize_t avail; + struct gzip *gzip = f->data; + ssize_t avail, max_in; size_t len; int ret; - state = (struct private_data *)self->data; - /* If this is a real header, consume it. */ - len = peek_at_header(self->upstream, NULL, state); + len = peek_at_header(f->upstream, NULL, gzip); if (len == 0) return (ARCHIVE_EOF); - __archive_read_filter_consume(self->upstream, len); + __archive_read_filter_consume(f->upstream, len); /* Initialize CRC accumulator. */ - state->crc = crc32(0L, NULL, 0); + gzip->crc = crc32(0L, NULL, 0); /* Initialize compression library. */ - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 1, &avail); - state->stream.avail_in = (uInt)avail; - ret = inflateInit2(&(state->stream), + gzip->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(f->upstream, 1, &avail); + if (avail < 0) { + archive_set_error(&f->archive->archive, + ARCHIVE_ERRNO_MISC, + "Failed to read gzip input"); + return (ARCHIVE_FATAL); + } + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (avail > max_in) + avail = max_in; + gzip->stream.avail_in = (uInt)avail; + ret = inflateInit2(&(gzip->stream), -15 /* Don't check for zlib header */); /* Decipher the error code. */ switch (ret) { case Z_OK: - state->in_stream = 1; + gzip->in_stream = 1; return (ARCHIVE_OK); case Z_STREAM_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid setup parameter"); break; case Z_MEM_ERROR: - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Internal error initializing compression library: " "out of memory"); break; case Z_VERSION_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " "invalid library version"); break; default: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library: " " Zlib error %d", ret); @@ -396,60 +398,55 @@ consume_header(struct archive_read_filter *self) } static int -consume_trailer(struct archive_read_filter *self) +consume_trailer(struct archive_read_filter *f) { - struct private_data *state; + struct gzip *gzip = f->data; const unsigned char *p; - ssize_t avail; - state = (struct private_data *)self->data; - - state->in_stream = 0; - switch (inflateEnd(&(state->stream))) { + gzip->in_stream = 0; + switch (inflateEnd(&(gzip->stream))) { case Z_OK: break; default: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up gzip decompressor"); return (ARCHIVE_FATAL); } /* GZip trailer is a fixed 8 byte structure. */ - p = __archive_read_filter_ahead(self->upstream, 8, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(f->upstream, 8, NULL); + if (p == NULL) return (ARCHIVE_FATAL); /* XXX TODO: Verify the length and CRC. */ /* We've verified the trailer, so consume it now. */ - __archive_read_filter_consume(self->upstream, 8); + __archive_read_filter_consume(f->upstream, 8); return (ARCHIVE_OK); } static ssize_t -gzip_filter_read(struct archive_read_filter *self, const void **p) +gzip_filter_read(struct archive_read_filter *f, const void **p) { - struct private_data *state; + struct gzip *gzip = f->data; size_t decompressed; ssize_t avail_in, max_in; int ret; - state = (struct private_data *)self->data; - /* Empty our output buffer. */ - state->stream.next_out = state->out_block; - state->stream.avail_out = (uInt)state->out_block_size; + gzip->stream.next_out = gzip->out_block; + gzip->stream.avail_out = (uInt)gzip->out_block_size; /* Try to fill the output buffer. */ - while (state->stream.avail_out > 0 && !state->eof) { + while (gzip->stream.avail_out > 0 && !gzip->eof) { /* If we're not in a stream, read a header * and initialize the decompression library. */ - if (!state->in_stream) { - ret = consume_header(self); + if (!gzip->in_stream) { + ret = consume_header(f); if (ret == ARCHIVE_EOF) { - state->eof = 1; + gzip->eof = 1; break; } if (ret < ARCHIVE_OK) @@ -459,10 +456,10 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) /* Peek at the next available data. */ /* ZLib treats stream.next_in as const but doesn't declare * it so, hence this ugly cast. */ - state->stream.next_in = (unsigned char *)(uintptr_t) - __archive_read_filter_ahead(self->upstream, 1, &avail_in); - if (state->stream.next_in == NULL) { - archive_set_error(&self->archive->archive, + gzip->stream.next_in = (unsigned char *)(uintptr_t) + __archive_read_filter_ahead(f->upstream, 1, &avail_in); + if (gzip->stream.next_in == NULL) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated gzip input"); return (ARCHIVE_FATAL); @@ -473,27 +470,27 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) max_in = UINT_MAX; if (avail_in > max_in) avail_in = max_in; - state->stream.avail_in = (uInt)avail_in; + gzip->stream.avail_in = (uInt)avail_in; /* Decompress and consume some of that data. */ - ret = inflate(&(state->stream), 0); + ret = inflate(&(gzip->stream), 0); switch (ret) { case Z_OK: /* Decompressor made some progress. */ - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); + __archive_read_filter_consume(f->upstream, + avail_in - gzip->stream.avail_in); break; case Z_STREAM_END: /* Found end of stream. */ - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); + __archive_read_filter_consume(f->upstream, + avail_in - gzip->stream.avail_in); /* Consume the stream trailer; release the * decompression library. */ - ret = consume_trailer(self); + ret = consume_trailer(f); if (ret < ARCHIVE_OK) return (ret); break; default: /* Return an error. */ - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "gzip decompression failed"); return (ARCHIVE_FATAL); @@ -501,12 +498,11 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) } /* We've read as much as we can. */ - decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; + decompressed = gzip->stream.next_out - gzip->out_block; if (decompressed == 0) *p = NULL; else - *p = state->out_block; + *p = gzip->out_block; return (decompressed); } @@ -514,29 +510,28 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -gzip_filter_close(struct archive_read_filter *self) +gzip_filter_close(struct archive_read_filter *f) { - struct private_data *state; + struct gzip *gzip = f->data; int ret; - state = (struct private_data *)self->data; ret = ARCHIVE_OK; - if (state->in_stream) { - switch (inflateEnd(&(state->stream))) { + if (gzip->in_stream) { + switch (inflateEnd(&(gzip->stream))) { case Z_OK: break; default: - archive_set_error(&(self->archive->archive), + archive_set_error(&(f->archive->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up gzip compressor"); ret = ARCHIVE_FATAL; } } - free(state->name); - free(state->out_block); - free(state); + free(gzip->name); + free(gzip->out_block); + free(gzip); return (ret); } diff --git a/libarchive/archive_read_support_filter_lrzip.c b/libarchive/archive_read_support_filter_lrzip.c index a562d538ed..33910fe10a 100644 --- a/libarchive/archive_read_support_filter_lrzip.c +++ b/libarchive/archive_read_support_filter_lrzip.c @@ -75,19 +75,19 @@ archive_read_support_filter_lrzip(struct archive *_a) * Bidder just verifies the header and returns the number of verified bits. */ static int -lrzip_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +lrzip_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *p; - ssize_t avail, len; + ssize_t len; int i; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ /* Start by looking at the first six bytes of the header, which * is all fixed layout. */ len = 6; - p = __archive_read_filter_ahead(filter, len, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(f, len, NULL); + if (p == NULL) return (0); if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN)) @@ -105,15 +105,15 @@ lrzip_bidder_bid(struct archive_read_filter_bidder *self, } static int -lrzip_bidder_init(struct archive_read_filter *self) +lrzip_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "lrzip -d -q"); + r = __archive_read_program(f, "lrzip -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_LRZIP; - self->name = "lrzip"; + f->code = ARCHIVE_FILTER_LRZIP; + f->name = "lrzip"; return (r); } diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c index 144572ef23..19086a1fef 100644 --- a/libarchive/archive_read_support_filter_lz4.c +++ b/libarchive/archive_read_support_filter_lz4.c @@ -44,6 +44,7 @@ #include "archive.h" #include "archive_endian.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_xxhash.h" @@ -56,7 +57,7 @@ #define LZ4_SKIPPABLE_MASK 0xFFFFFFF0 #if defined(HAVE_LIBLZ4) -struct private_data { +struct lz4 { enum { SELECT_STREAM, READ_DEFAULT_STREAM, READ_DEFAULT_BLOCK, @@ -95,8 +96,7 @@ static int lz4_filter_close(struct archive_read_filter *); /* * 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. + * error messages.) */ static int lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); static int lz4_reader_init(struct archive_read_filter *); @@ -139,58 +139,81 @@ archive_read_support_filter_lz4(struct archive *_a) * from verifying as much as we would like. */ static int -lz4_reader_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +lz4_reader_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *buffer; ssize_t avail; int bits_checked = 0; - ssize_t min_lz4_archive_size = 11; + const size_t min_lz4_archive_size = 11; - // LZ4 skippable frames contain a 4 byte magic number followed by - // a 4 byte frame data size, then that number of bytes of data. Regular - // frames contain a 4 byte magic number followed by a 2-14 byte frame - // header, some data, and a 3 byte end marker. - ssize_t min_lz4_frame_size = 8; + /* + * LZ4 skippable frames contain a 4 byte magic number followed by + * a 4 byte frame data size, then that number of bytes of data. + * Regular frames contain a 4 byte magic number followed by a 2-14 + * byte frame header, some data, and a 3 byte end marker. + */ + const size_t min_lz4_frame_size = 8; - ssize_t offset_in_buffer = 0; - ssize_t max_lookahead = 64 * 1024; + size_t offset_in_buffer = 0; + const size_t max_lookahead = 64 * 1024; + uint32_t magic_number; - (void)self; // UNUSED + (void)b; /* UNUSED */ - // Zstd and LZ4 skippable frame magic numbers are identical. To - // differentiate these two, we need to look for a non-skippable - // frame. + /* + * Zstd and LZ4 skippable frame magic numbers are identical. To + * differentiate these two, we need to look for a non-skippable + * frame. + */ - // Minimal lz4 archive is 11 bytes. - buffer = __archive_read_filter_ahead(filter, min_lz4_archive_size, &avail); + /* Minimal lz4 archive is 11 bytes. */ + buffer = __archive_read_filter_ahead(f, min_lz4_archive_size, + &avail); if (buffer == NULL) return (0); - uint32_t magic_number = archive_le32dec(buffer); + magic_number = archive_le32dec(buffer); while ((magic_number & LZ4_SKIPPABLE_MASK) == LZ4_SKIPPABLE_START) { + size_t min; + uint32_t frame_data_size; - offset_in_buffer += 4; // Skip over the magic number + /* Skip over the magic number */ + offset_in_buffer += 4; - // Ensure that we can read another 4 bytes. - if (offset_in_buffer + 4 > avail) { - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + 4, &avail); + /* Ensure that we can read another 4 bytes. */ + if (offset_in_buffer + 4 > (size_t)avail) { + buffer = __archive_read_filter_ahead(f, + offset_in_buffer + 4, &avail); if (buffer == NULL) return (0); } - uint32_t frame_data_size = archive_le32dec(buffer + offset_in_buffer); + frame_data_size = archive_le32dec(buffer + offset_in_buffer); - // Skip over the 4 frame data size bytes, plus the value stored there. - offset_in_buffer += 4 + frame_data_size; + /* Skip over the 4 frame data size bytes */ + offset_in_buffer += 4; - // There should be at least one more frame if this is LZ4 data. - if (offset_in_buffer + min_lz4_frame_size > avail) { // TODO: should this be >= ? - if (offset_in_buffer + min_lz4_frame_size > max_lookahead) + /* Skip over the value stored there. */ + if (archive_ckd_add_size(&offset_in_buffer, + offset_in_buffer, frame_data_size)) + return (0); + + /* + * There should be at least one more frame + * if this is LZ4 data. + */ + if (archive_ckd_add_size(&min, + offset_in_buffer, min_lz4_frame_size)) + return (0); + /* TODO: should this be >= ? */ + if (min > (size_t)avail) { + if (min > max_lookahead) return (0); - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + min_lz4_frame_size, &avail); + buffer = __archive_read_filter_ahead(f, + min, &avail); if (buffer == NULL) return (0); } @@ -198,8 +221,10 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, magic_number = archive_le32dec(buffer + offset_in_buffer); } - // We have skipped over any skippable frames. Either a regular LZ4 frame - // follows, or this isn't LZ4 data. + /* + * We have skipped over any skippable frames. Either a regular LZ4 frame + * follows, or this isn't LZ4 data. + */ bits_checked = offset_in_buffer; buffer = buffer + offset_in_buffer; @@ -241,21 +266,21 @@ lz4_reader_bid(struct archive_read_filter_bidder *self, #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. + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "lz4 -d -q" + * in case that's available. */ static int -lz4_reader_init(struct archive_read_filter *self) +lz4_reader_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "lz4 -d -q"); + r = __archive_read_program(f, "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"; + f->code = ARCHIVE_FILTER_LZ4; + f->name = "lz4"; return (r); } @@ -272,69 +297,73 @@ lz4_reader_vtable = { * Setup the callbacks. */ static int -lz4_reader_init(struct archive_read_filter *self) +lz4_reader_init(struct archive_read_filter *f) { - struct private_data *state; + struct lz4 *lz4; - self->code = ARCHIVE_FILTER_LZ4; - self->name = "lz4"; + f->code = ARCHIVE_FILTER_LZ4; + f->name = "lz4"; - state = calloc(1, sizeof(*state)); - if (state == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + lz4 = calloc(1, sizeof(*lz4)); + if (lz4 == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); return (ARCHIVE_FATAL); } - self->data = state; - state->stage = SELECT_STREAM; - self->vtable = &lz4_reader_vtable; + f->data = lz4; + lz4->stage = SELECT_STREAM; + f->vtable = &lz4_reader_vtable; return (ARCHIVE_OK); } static int -lz4_allocate_out_block(struct archive_read_filter *self) +lz4_allocate_out_block(struct archive_read_filter *f) { - struct private_data *state = (struct private_data *)self->data; - size_t out_block_size = state->flags.block_maximum_size; + struct lz4 *lz4 = f->data; + size_t out_block_size = lz4->flags.block_maximum_size; void *out_block; - if (!state->flags.block_independence) + if (!lz4->flags.block_independence) out_block_size += 64 * 1024; - if (state->out_block_size < out_block_size) { - free(state->out_block); + if (lz4->out_block_size < out_block_size) { + free(lz4->out_block); + lz4->out_block = NULL; out_block = malloc(out_block_size); - state->out_block_size = out_block_size; if (out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + lz4->out_block_size = 0; + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); return (ARCHIVE_FATAL); } - state->out_block = out_block; + lz4->out_block_size = out_block_size; + lz4->out_block = out_block; } - if (!state->flags.block_independence) - memset(state->out_block, 0, 64 * 1024); + if (!lz4->flags.block_independence) + memset(lz4->out_block, 0, 64 * 1024); return (ARCHIVE_OK); } static int -lz4_allocate_out_block_for_legacy(struct archive_read_filter *self) +lz4_allocate_out_block_for_legacy(struct archive_read_filter *f) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; size_t out_block_size = LEGACY_BLOCK_SIZE; void *out_block; - if (state->out_block_size < out_block_size) { - free(state->out_block); + if (lz4->out_block_size < out_block_size) { + free(lz4->out_block); + lz4->out_block = NULL; out_block = malloc(out_block_size); - state->out_block_size = out_block_size; if (out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + lz4->out_block_size = 0; + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for lz4 decompression"); return (ARCHIVE_FATAL); } - state->out_block = out_block; + lz4->out_block_size = out_block_size; + lz4->out_block = out_block; } return (ARCHIVE_OK); } @@ -343,90 +372,96 @@ lz4_allocate_out_block_for_legacy(struct archive_read_filter *self) * Return the next block of decompressed data. */ static ssize_t -lz4_filter_read(struct archive_read_filter *self, const void **p) +lz4_filter_read(struct archive_read_filter *f, const void **p) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; ssize_t ret; - if (state->eof) { + if (lz4->eof) { *p = NULL; return (0); } - __archive_read_filter_consume(self->upstream, state->unconsumed); - state->unconsumed = 0; + __archive_read_filter_consume(f->upstream, lz4->unconsumed); + lz4->unconsumed = 0; - switch (state->stage) { + switch (lz4->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."); + /* Reading an lz4 stream already failed. */ + archive_set_error(&f->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) + ret = lz4_filter_read_default_stream(f, p); + if (ret != 0 || lz4->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) + ret = lz4_filter_read_legacy_stream(f, p); + if (ret != 0 || lz4->stage != SELECT_STREAM) return ret; break; default: - archive_set_error(&self->archive->archive, - ARCHIVE_ERRNO_MISC, "Program error."); + archive_set_error(&f->archive->archive, + ARCHIVE_ERRNO_MISC, "Program error"); return (ARCHIVE_FATAL); } - while (state->stage == SELECT_STREAM) { + while (lz4->stage == SELECT_STREAM) { const char *read_buf; /* Read a magic number. */ - read_buf = __archive_read_filter_ahead(self->upstream, 4, + read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL); if (read_buf == NULL) { - state->eof = 1; + lz4->eof = 1; *p = NULL; return (0); } uint32_t number = archive_le32dec(read_buf); - __archive_read_filter_consume(self->upstream, 4); + __archive_read_filter_consume(f->upstream, 4); if (number == LZ4_MAGICNUMBER) - return lz4_filter_read_default_stream(self, p); + return lz4_filter_read_default_stream(f, p); else if (number == LZ4_LEGACY) - return lz4_filter_read_legacy_stream(self, p); + return lz4_filter_read_legacy_stream(f, p); else if ((number & LZ4_SKIPPABLE_MASK) == LZ4_SKIPPABLE_START) { read_buf = __archive_read_filter_ahead( - self->upstream, 4, NULL); + f->upstream, 4, NULL); if (read_buf == NULL) { archive_set_error( - &self->archive->archive, + &f->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); + int64_t skip_bytes = archive_le32dec(read_buf); + if (__archive_read_filter_consume(f->upstream, + 4 + skip_bytes) < 0) { + archive_set_error( + &f->archive->archive, + ARCHIVE_ERRNO_MISC, + "Malformed lz4 data"); + return (ARCHIVE_FATAL); + } } else { /* Ignore following unrecognized data. */ - state->eof = 1; + lz4->eof = 1; *p = NULL; return (0); } } - state->eof = 1; + lz4->eof = 1; *p = NULL; return (0); } static int -lz4_filter_read_descriptor(struct archive_read_filter *self) +lz4_filter_read_descriptor(struct archive_read_filter *f) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; const char *read_buf; ssize_t bytes_remaining; ssize_t descriptor_bytes; @@ -434,10 +469,10 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) unsigned int chsum, chsum_verifier; /* Make sure we have 2 bytes for flags. */ - read_buf = __archive_read_filter_ahead(self->upstream, 2, + read_buf = __archive_read_filter_ahead(f->upstream, 2, &bytes_remaining); if (read_buf == NULL) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated lz4 input"); return (ARCHIVE_FATAL); @@ -453,11 +488,11 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) /* 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; + lz4->flags.block_independence = (flag & 0x20) != 0; + lz4->flags.block_checksum = (flag & 0x10)?4:0; + lz4->flags.stream_size = (flag & 0x08) != 0; + lz4->flags.stream_checksum = (flag & 0x04) != 0; + lz4->flags.preset_dictionary = (flag & 0x01) != 0; /* BD */ bd = (unsigned char)read_buf[1]; @@ -467,16 +502,16 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) /* Get a maximum block size. */ switch (read_buf[1] >> 4) { case 4: /* 64 KB */ - state->flags.block_maximum_size = 64 * 1024; + lz4->flags.block_maximum_size = 64 * 1024; break; case 5: /* 256 KB */ - state->flags.block_maximum_size = 256 * 1024; + lz4->flags.block_maximum_size = 256 * 1024; break; case 6: /* 1 MB */ - state->flags.block_maximum_size = 1024 * 1024; + lz4->flags.block_maximum_size = 1024 * 1024; break; case 7: /* 4 MB */ - state->flags.block_maximum_size = 4 * 1024 * 1024; + lz4->flags.block_maximum_size = 4 * 1024 * 1024; break; default: goto malformed_error; @@ -484,15 +519,15 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) /* Read the whole descriptor in a stream block. */ descriptor_bytes = 3; - if (state->flags.stream_size) + if (lz4->flags.stream_size) descriptor_bytes += 8; - if (state->flags.preset_dictionary) + if (lz4->flags.preset_dictionary) descriptor_bytes += 4; if (bytes_remaining < descriptor_bytes) { - read_buf = __archive_read_filter_ahead(self->upstream, + read_buf = __archive_read_filter_ahead(f->upstream, descriptor_bytes, &bytes_remaining); if (read_buf == NULL) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated lz4 input"); return (ARCHIVE_FATAL); @@ -507,30 +542,32 @@ lz4_filter_read_descriptor(struct archive_read_filter *self) goto malformed_error; #endif - __archive_read_filter_consume(self->upstream, descriptor_bytes); + __archive_read_filter_consume(f->upstream, descriptor_bytes); /* Make sure we have a large enough buffer for uncompressed data. */ - if (lz4_allocate_out_block(self) != ARCHIVE_OK) + if (lz4_allocate_out_block(f) != ARCHIVE_OK) return (ARCHIVE_FATAL); - if (state->flags.stream_checksum) - state->xxh32_state = __archive_xxhash.XXH32_init(0); + if (lz4->flags.stream_checksum) { + lz4->xxh32_state = __archive_xxhash.XXH32_init(0); + if (lz4->xxh32_state == NULL) + return (ARCHIVE_FATAL); + } - state->decoded_size = 0; + lz4->decoded_size = 0; /* Success */ return (ARCHIVE_OK); malformed_error: - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->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) +lz4_filter_read_data_block(struct archive_read_filter *f, const void **p) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; ssize_t compressed_size; const char *read_buf; - ssize_t bytes_remaining; int checksum_size; ssize_t uncompressed_size; size_t prefix64k; @@ -538,20 +575,19 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) *p = NULL; /* Make sure we have 4 bytes for a block size. */ - read_buf = __archive_read_filter_ahead(self->upstream, 4, - &bytes_remaining); + read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL); if (read_buf == NULL) goto truncated_error; compressed_size = archive_le32dec(read_buf); - if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size) + if ((compressed_size & 0x7fffffff) > lz4->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); + __archive_read_filter_consume(f->upstream, 4); return 0; } - checksum_size = state->flags.block_checksum; + checksum_size = lz4->flags.block_checksum; /* Check if the block is uncompressed. */ if (compressed_size & 0x80000000U) { compressed_size &= 0x7fffffff; @@ -564,8 +600,8 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) 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); + read_buf = __archive_read_filter_ahead(f->upstream, + 4 + compressed_size + checksum_size, NULL); if (read_buf == NULL) goto truncated_error; @@ -585,24 +621,24 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) /* 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) { + if (!lz4->flags.block_independence) { prefix64k = 64 * 1024; if (uncompressed_size < (ssize_t)prefix64k) { - memcpy(state->out_block + memcpy(lz4->out_block + prefix64k - uncompressed_size, read_buf + 4, uncompressed_size); - memset(state->out_block, 0, + memset(lz4->out_block, 0, prefix64k - uncompressed_size); } else { - memcpy(state->out_block, + memcpy(lz4->out_block, read_buf + 4 + uncompressed_size - prefix64k, prefix64k); } - state->decoded_size = 0; + lz4->decoded_size = 0; } - state->unconsumed = 4 + uncompressed_size + checksum_size; + lz4->unconsumed = 4 + uncompressed_size + checksum_size; *p = read_buf + 4; return uncompressed_size; } @@ -610,169 +646,168 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p) /* Decompress a block data. */ - if (state->flags.block_independence) { + if (lz4->flags.block_independence) { prefix64k = 0; uncompressed_size = LZ4_decompress_safe(read_buf + 4, - state->out_block, (int)compressed_size, - state->flags.block_maximum_size); + lz4->out_block, (int)compressed_size, + lz4->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); + if (lz4->decoded_size) { + if (lz4->decoded_size < prefix64k) { + memmove(lz4->out_block + + prefix64k - lz4->decoded_size, + lz4->out_block + prefix64k, + lz4->decoded_size); + memset(lz4->out_block, 0, + prefix64k - lz4->decoded_size); } else { - memmove(state->out_block, - state->out_block + state->decoded_size, + memmove(lz4->out_block, + lz4->out_block + lz4->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, + lz4->out_block + prefix64k, (int)compressed_size, + lz4->flags.block_maximum_size, + lz4->out_block, (int)prefix64k); #else uncompressed_size = LZ4_decompress_safe_withPrefix64k( read_buf + 4, - state->out_block + prefix64k, (int)compressed_size, - state->flags.block_maximum_size); + lz4->out_block + prefix64k, (int)compressed_size, + lz4->flags.block_maximum_size); #endif } /* Check if an error occurred in the decompression process. */ if (uncompressed_size < 0) { - archive_set_error(&(self->archive->archive), + archive_set_error(&(f->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; + lz4->unconsumed = 4 + compressed_size + checksum_size; + *p = lz4->out_block + prefix64k; + lz4->decoded_size = uncompressed_size; return uncompressed_size; malformed_error: - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "malformed lz4 data"); return (ARCHIVE_FATAL); truncated_error: - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->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) +lz4_filter_read_default_stream(struct archive_read_filter *f, const void **p) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; const char *read_buf; - ssize_t bytes_remaining; ssize_t ret; - if (state->stage == SELECT_STREAM) { - state->stage = READ_DEFAULT_STREAM; + if (lz4->stage == SELECT_STREAM) { + lz4->stage = READ_DEFAULT_STREAM; /* First, read a descriptor. */ - if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK) + if((ret = lz4_filter_read_descriptor(f)) != ARCHIVE_OK) return (ret); - state->stage = READ_DEFAULT_BLOCK; + lz4->stage = READ_DEFAULT_BLOCK; } /* Decompress a block. */ - ret = lz4_filter_read_data_block(self, p); + ret = lz4_filter_read_data_block(f, 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; + lz4->stage = SELECT_STREAM; /* Optional processing, checking a stream sum. */ - if (state->flags.stream_checksum) { - if (state->stage == SELECT_STREAM) { + if (lz4->flags.stream_checksum) { + if (lz4->stage == SELECT_STREAM) { unsigned int checksum; unsigned int checksum_stream; - read_buf = __archive_read_filter_ahead(self->upstream, - 4, &bytes_remaining); + read_buf = __archive_read_filter_ahead(f->upstream, + 4, NULL); if (read_buf == NULL) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated lz4 input"); return (ARCHIVE_FATAL); } checksum = archive_le32dec(read_buf); - __archive_read_filter_consume(self->upstream, 4); + __archive_read_filter_consume(f->upstream, 4); checksum_stream = __archive_xxhash.XXH32_digest( - state->xxh32_state); - state->xxh32_state = NULL; + lz4->xxh32_state); + lz4->xxh32_state = NULL; if (checksum != checksum_stream) { #ifndef DONT_FAIL_ON_CRC_ERROR - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "lz4 stream checksum error"); return (ARCHIVE_FATAL); #endif } } else if (ret > 0) - __archive_xxhash.XXH32_update(state->xxh32_state, + __archive_xxhash.XXH32_update(lz4->xxh32_state, *p, (int)ret); } return (ret); } static ssize_t -lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p) +lz4_filter_read_legacy_stream(struct archive_read_filter *f, const void **p) { - struct private_data *state = (struct private_data *)self->data; + struct lz4 *lz4 = f->data; uint32_t compressed; const char *read_buf; ssize_t ret; *p = NULL; - ret = lz4_allocate_out_block_for_legacy(self); + ret = lz4_allocate_out_block_for_legacy(f); 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); + read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL); if (read_buf == NULL) { - if (state->stage == SELECT_STREAM) { - state->stage = READ_LEGACY_STREAM; - archive_set_error(&self->archive->archive, + if (lz4->stage == SELECT_STREAM) { + lz4->stage = READ_LEGACY_STREAM; + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated lz4 input"); return (ARCHIVE_FATAL); } - state->stage = SELECT_STREAM; + lz4->stage = SELECT_STREAM; return 0; } - state->stage = READ_LEGACY_BLOCK; + lz4->stage = READ_LEGACY_BLOCK; compressed = archive_le32dec(read_buf); if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) { - state->stage = SELECT_STREAM; + lz4->stage = SELECT_STREAM; return 0; } /* Make sure we have a whole block. */ - read_buf = __archive_read_filter_ahead(self->upstream, + read_buf = __archive_read_filter_ahead(f->upstream, 4 + compressed, NULL); if (read_buf == NULL) { - archive_set_error(&(self->archive->archive), + archive_set_error(&(f->archive->archive), ARCHIVE_ERRNO_MISC, "truncated lz4 input"); return (ARCHIVE_FATAL); } - ret = LZ4_decompress_safe(read_buf + 4, state->out_block, - compressed, (int)state->out_block_size); + ret = LZ4_decompress_safe(read_buf + 4, lz4->out_block, + compressed, (int)lz4->out_block_size); if (ret < 0) { - archive_set_error(&(self->archive->archive), + archive_set_error(&(f->archive->archive), ARCHIVE_ERRNO_MISC, "lz4 decompression failed"); return (ARCHIVE_FATAL); } - *p = state->out_block; - state->unconsumed = 4 + compressed; + *p = lz4->out_block; + lz4->unconsumed = 4 + compressed; return ret; } @@ -780,15 +815,14 @@ lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -lz4_filter_close(struct archive_read_filter *self) +lz4_filter_close(struct archive_read_filter *f) { - struct private_data *state; + struct lz4 *lz4 = f->data; int ret = ARCHIVE_OK; - state = (struct private_data *)self->data; - free(state->xxh32_state); - free(state->out_block); - free(state); + free(lz4->xxh32_state); + free(lz4->out_block); + free(lz4); return (ret); } diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c index b0c4bb13e8..ebfaf01e85 100644 --- a/libarchive/archive_read_support_filter_lzop.c +++ b/libarchive/archive_read_support_filter_lzop.c @@ -68,10 +68,9 @@ #define LZOP_HEADER_MAGIC_LEN 9 #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) -struct read_lzop { +struct lzop { unsigned char *out_block; size_t out_block_size; - int64_t total_out; int flags; uint32_t compressed_cksum; uint32_t uncompressed_cksum; @@ -110,7 +109,7 @@ archive_read_support_filter_lzop(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, NULL, + if (__archive_read_register_bidder(a, NULL, "lzop", &lzop_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -118,7 +117,6 @@ archive_read_support_filter_lzop(struct archive *_a) #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) return (ARCHIVE_OK); #else - /* Return ARCHIVE_WARN since this always uses an external program. */ archive_set_error(_a, ARCHIVE_ERRNO_MISC, "Using external lzop program for lzop decompression"); return (ARCHIVE_WARN); @@ -129,16 +127,15 @@ archive_read_support_filter_lzop(struct archive *_a) * Bidder just verifies the header and returns the number of verified bits. */ static int -lzop_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +lzop_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *p; - ssize_t avail; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail); - if (p == NULL || avail == 0) + p = __archive_read_filter_ahead(f, LZOP_HEADER_MAGIC_LEN, NULL); + if (p == NULL) return (0); if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN)) @@ -154,16 +151,16 @@ lzop_bidder_bid(struct archive_read_filter_bidder *self, * in case that's available. */ static int -lzop_bidder_init(struct archive_read_filter *self) +lzop_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "lzop -d"); + r = __archive_read_program(f, "lzop -d"); /* 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_LZOP; - self->name = "lzop"; + f->code = ARCHIVE_FILTER_LZOP; + f->name = "lzop"; return (r); } #else @@ -178,47 +175,47 @@ lzop_reader_vtable = { * Initialize the filter object. */ static int -lzop_bidder_init(struct archive_read_filter *self) +lzop_bidder_init(struct archive_read_filter *f) { - struct read_lzop *state; + struct lzop *lzop; - self->code = ARCHIVE_FILTER_LZOP; - self->name = "lzop"; + f->code = ARCHIVE_FILTER_LZOP; + f->name = "lzop"; - state = calloc(1, sizeof(*state)); - if (state == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + lzop = calloc(1, sizeof(*lzop)); + if (lzop == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for lzop decompression"); return (ARCHIVE_FATAL); } - self->data = state; - self->vtable = &lzop_reader_vtable; + f->data = lzop; + f->vtable = &lzop_reader_vtable; return (ARCHIVE_OK); } static int -consume_header(struct archive_read_filter *self) +consume_header(struct archive_read_filter *f) { - struct read_lzop *state = (struct read_lzop *)self->data; + struct lzop *lzop = f->data; const unsigned char *p, *_p; unsigned checksum, flags, len, method, version; /* * Check LZOP magic code. */ - p = __archive_read_filter_ahead(self->upstream, + p = __archive_read_filter_ahead(f->upstream, LZOP_HEADER_MAGIC_LEN, NULL); if (p == NULL) return (ARCHIVE_EOF); if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN)) return (ARCHIVE_EOF); - __archive_read_filter_consume(self->upstream, + __archive_read_filter_consume(f->upstream, LZOP_HEADER_MAGIC_LEN); - p = __archive_read_filter_ahead(self->upstream, 29, NULL); + p = __archive_read_filter_ahead(f->upstream, 29, NULL); if (p == NULL) goto truncated; _p = p; @@ -228,7 +225,7 @@ consume_header(struct archive_read_filter *self) if (version >= 0x940) { unsigned reqversion = archive_be16dec(p); p += 2; if (reqversion < 0x900) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Invalid required version"); return (ARCHIVE_FAILED); } @@ -236,7 +233,7 @@ consume_header(struct archive_read_filter *self) method = *p++; if (method < 1 || method > 3) { - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Unsupported method"); return (ARCHIVE_FAILED); } @@ -255,7 +252,7 @@ consume_header(struct archive_read_filter *self) ;/* NOP */ #endif else if (level > 9) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Invalid level"); return (ARCHIVE_FAILED); } @@ -273,7 +270,7 @@ consume_header(struct archive_read_filter *self) len = *p++; /* Read filename length */ len += p - _p; /* Make sure we have all bytes we need to calculate checksum. */ - p = __archive_read_filter_ahead(self->upstream, len + 4, NULL); + p = __archive_read_filter_ahead(f->upstream, len + 4, NULL); if (p == NULL) goto truncated; if (flags & CRC32_HEADER) @@ -284,201 +281,199 @@ consume_header(struct archive_read_filter *self) if (archive_be32dec(p + len) != checksum) goto corrupted; #endif - __archive_read_filter_consume(self->upstream, len + 4); + __archive_read_filter_consume(f->upstream, len + 4); if (flags & EXTRA_FIELD) { /* Skip extra field */ - p = __archive_read_filter_ahead(self->upstream, 4, NULL); + p = __archive_read_filter_ahead(f->upstream, 4, NULL); if (p == NULL) goto truncated; len = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, + __archive_read_filter_consume(f->upstream, (int64_t)len + 4 + 4); } - state->flags = flags; - state->in_stream = 1; + lzop->flags = flags; + lzop->in_stream = 1; return (ARCHIVE_OK); truncated: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); return (ARCHIVE_FAILED); corrupted: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header"); return (ARCHIVE_FAILED); } static int -consume_block_info(struct archive_read_filter *self) +consume_block_info(struct archive_read_filter *f) { - struct read_lzop *state = (struct read_lzop *)self->data; + struct lzop *lzop = f->data; const unsigned char *p; - unsigned flags = state->flags; + unsigned flags = lzop->flags; - p = __archive_read_filter_ahead(self->upstream, 4, NULL); + p = __archive_read_filter_ahead(f->upstream, 4, NULL); if (p == NULL) goto truncated; - state->uncompressed_size = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, 4); - if (state->uncompressed_size == 0) + lzop->uncompressed_size = archive_be32dec(p); + __archive_read_filter_consume(f->upstream, 4); + if (lzop->uncompressed_size == 0) return (ARCHIVE_EOF); - if (state->uncompressed_size > MAX_BLOCK_SIZE) + if (lzop->uncompressed_size > MAX_BLOCK_SIZE) goto corrupted; - p = __archive_read_filter_ahead(self->upstream, 4, NULL); + p = __archive_read_filter_ahead(f->upstream, 4, NULL); if (p == NULL) goto truncated; - state->compressed_size = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, 4); - if (state->compressed_size > state->uncompressed_size) + lzop->compressed_size = archive_be32dec(p); + __archive_read_filter_consume(f->upstream, 4); + if (lzop->compressed_size > lzop->uncompressed_size) goto corrupted; if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) { - p = __archive_read_filter_ahead(self->upstream, 4, NULL); + p = __archive_read_filter_ahead(f->upstream, 4, NULL); if (p == NULL) goto truncated; - state->compressed_cksum = state->uncompressed_cksum = + lzop->compressed_cksum = lzop->uncompressed_cksum = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, 4); + __archive_read_filter_consume(f->upstream, 4); } if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) && - state->compressed_size < state->uncompressed_size) { - p = __archive_read_filter_ahead(self->upstream, 4, NULL); + lzop->compressed_size < lzop->uncompressed_size) { + p = __archive_read_filter_ahead(f->upstream, 4, NULL); if (p == NULL) goto truncated; - state->compressed_cksum = archive_be32dec(p); - __archive_read_filter_consume(self->upstream, 4); + lzop->compressed_cksum = archive_be32dec(p); + __archive_read_filter_consume(f->upstream, 4); } return (ARCHIVE_OK); truncated: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); return (ARCHIVE_FAILED); corrupted: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header"); return (ARCHIVE_FAILED); } static ssize_t -lzop_filter_read(struct archive_read_filter *self, const void **p) +lzop_filter_read(struct archive_read_filter *f, const void **p) { - struct read_lzop *state = (struct read_lzop *)self->data; + struct lzop *lzop = f->data; const void *b; lzo_uint out_size; uint32_t cksum; int ret, r; - if (state->unconsumed_bytes) { - __archive_read_filter_consume(self->upstream, - state->unconsumed_bytes); - state->unconsumed_bytes = 0; + if (lzop->unconsumed_bytes) { + __archive_read_filter_consume(f->upstream, + lzop->unconsumed_bytes); + lzop->unconsumed_bytes = 0; } - if (state->eof) + if (lzop->eof) return (0); for (;;) { - if (!state->in_stream) { - ret = consume_header(self); + if (!lzop->in_stream) { + ret = consume_header(f); if (ret < ARCHIVE_OK) return (ret); if (ret == ARCHIVE_EOF) { - state->eof = 1; + lzop->eof = 1; return (0); } } - ret = consume_block_info(self); + ret = consume_block_info(f); if (ret < ARCHIVE_OK) return (ret); if (ret == ARCHIVE_EOF) - state->in_stream = 0; + lzop->in_stream = 0; else break; } - if (state->out_block == NULL || - state->out_block_size < state->uncompressed_size) { + if (lzop->out_block == NULL || + lzop->out_block_size < lzop->uncompressed_size) { void *new_block; - new_block = realloc(state->out_block, state->uncompressed_size); + new_block = realloc(lzop->out_block, lzop->uncompressed_size); if (new_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for lzop decompression"); return (ARCHIVE_FATAL); } - state->out_block = new_block; - state->out_block_size = state->uncompressed_size; + lzop->out_block = new_block; + lzop->out_block_size = lzop->uncompressed_size; } - b = __archive_read_filter_ahead(self->upstream, - state->compressed_size, NULL); + b = __archive_read_filter_ahead(f->upstream, + lzop->compressed_size, NULL); if (b == NULL) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); return (ARCHIVE_FATAL); } - if (state->flags & CRC32_COMPRESSED) - cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size); - else if (state->flags & ADLER32_COMPRESSED) - cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size); + if (lzop->flags & CRC32_COMPRESSED) + cksum = crc32(crc32(0, NULL, 0), b, lzop->compressed_size); + else if (lzop->flags & ADLER32_COMPRESSED) + cksum = adler32(adler32(0, NULL, 0), b, lzop->compressed_size); else - cksum = state->compressed_cksum; - if (cksum != state->compressed_cksum) { - archive_set_error(&self->archive->archive, + cksum = lzop->compressed_cksum; + if (cksum != lzop->compressed_cksum) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Corrupted data"); return (ARCHIVE_FATAL); } /* - * If the both uncompressed size and compressed size are the same, + * If both uncompressed size and compressed size are the same, * we do not decompress this block. */ - if (state->uncompressed_size == state->compressed_size) { + if (lzop->uncompressed_size == lzop->compressed_size) { *p = b; - state->total_out += state->compressed_size; - state->unconsumed_bytes = state->compressed_size; - return ((ssize_t)state->uncompressed_size); + lzop->unconsumed_bytes = lzop->compressed_size; + return ((ssize_t)lzop->uncompressed_size); } /* * Drive lzo uncompression. */ - out_size = (lzo_uint)state->uncompressed_size; - r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size, - state->out_block, &out_size, NULL); + out_size = (lzo_uint)lzop->uncompressed_size; + r = lzo1x_decompress_safe(b, (lzo_uint)lzop->compressed_size, + lzop->out_block, &out_size, NULL); switch (r) { case LZO_E_OK: - if (out_size == state->uncompressed_size) + if (out_size == lzop->uncompressed_size) break; - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Corrupted data"); return (ARCHIVE_FATAL); case LZO_E_OUT_OF_MEMORY: - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "lzop decompression failed: out of memory"); return (ARCHIVE_FATAL); default: - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "lzop decompression failed: %d", r); return (ARCHIVE_FATAL); } - if (state->flags & CRC32_UNCOMPRESSED) - cksum = crc32(crc32(0, NULL, 0), state->out_block, - state->uncompressed_size); - else if (state->flags & ADLER32_UNCOMPRESSED) - cksum = adler32(adler32(0, NULL, 0), state->out_block, - state->uncompressed_size); + if (lzop->flags & CRC32_UNCOMPRESSED) + cksum = crc32(crc32(0, NULL, 0), lzop->out_block, + lzop->uncompressed_size); + else if (lzop->flags & ADLER32_UNCOMPRESSED) + cksum = adler32(adler32(0, NULL, 0), lzop->out_block, + lzop->uncompressed_size); else - cksum = state->uncompressed_cksum; - if (cksum != state->uncompressed_cksum) { - archive_set_error(&self->archive->archive, + cksum = lzop->uncompressed_cksum; + if (cksum != lzop->uncompressed_cksum) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Corrupted data"); return (ARCHIVE_FATAL); } - __archive_read_filter_consume(self->upstream, state->compressed_size); - *p = state->out_block; - state->total_out += out_size; + __archive_read_filter_consume(f->upstream, lzop->compressed_size); + *p = lzop->out_block; return ((ssize_t)out_size); } @@ -486,12 +481,12 @@ lzop_filter_read(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -lzop_filter_close(struct archive_read_filter *self) +lzop_filter_close(struct archive_read_filter *f) { - struct read_lzop *state = (struct read_lzop *)self->data; + struct lzop *lzop = f->data; - free(state->out_block); - free(state); + free(lzop->out_block); + free(lzop); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c index 9f187f852d..cf7c36137e 100644 --- a/libarchive/archive_read_support_filter_program.c +++ b/libarchive/archive_read_support_filter_program.c @@ -87,7 +87,7 @@ archive_read_support_filter_program(struct archive *a, const char *cmd) * bid twice in the same pipeline. */ struct program_bidder { - char *description; + struct archive_string description; char *cmd; void *signature; size_t signature_len; @@ -102,7 +102,7 @@ static void program_bidder_free(struct archive_read_filter_bidder *); /* * The actual filter needs to track input and output data. */ -struct program_filter { +struct program { struct archive_string description; #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE child; @@ -145,6 +145,8 @@ archive_read_support_filter_program_signature(struct archive *_a, state->cmd = strdup(cmd); if (state->cmd == NULL) goto memerr; + archive_strcpy(&state->description, "Program: "); + archive_strcat(&state->description, cmd); if (signature != NULL && signature_len > 0) { state->signature_len = signature_len; @@ -168,9 +170,9 @@ memerr: } static void -program_bidder_free(struct archive_read_filter_bidder *self) +program_bidder_free(struct archive_read_filter_bidder *b) { - struct program_bidder *state = (struct program_bidder *)self->data; + struct program_bidder *state = (struct program_bidder *)b->data; free_state(state); } @@ -180,6 +182,7 @@ free_state(struct program_bidder *state) { if (state) { + archive_string_free(&state->description); free(state->cmd); free(state->signature); free(state); @@ -193,15 +196,15 @@ free_state(struct program_bidder *state) * we're called, then never bid again. */ static int -program_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *upstream) +program_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { - struct program_bidder *state = self->data; + struct program_bidder *state = b->data; const char *p; /* If we have a signature, use that to match. */ if (state->signature_len > 0) { - p = __archive_read_filter_ahead(upstream, + p = __archive_read_filter_ahead(f, state->signature_len, NULL); if (p == NULL) return (0); @@ -226,36 +229,36 @@ program_bidder_bid(struct archive_read_filter_bidder *self, * (including error message if the child came to a bad end). */ static int -child_stop(struct archive_read_filter *self, struct program_filter *state) +child_stop(struct archive_read_filter *f, struct program *program) { /* Close our side of the I/O with the child. */ - if (state->child_stdin != -1) { - close(state->child_stdin); - state->child_stdin = -1; + if (program->child_stdin != -1) { + close(program->child_stdin); + program->child_stdin = -1; } - if (state->child_stdout != -1) { - close(state->child_stdout); - state->child_stdout = -1; + if (program->child_stdout != -1) { + close(program->child_stdout); + program->child_stdout = -1; } - if (state->child != 0) { + if (program->child != 0) { /* Reap the child. */ do { - state->waitpid_return - = waitpid(state->child, &state->exit_status, 0); - } while (state->waitpid_return == -1 && errno == EINTR); - state->child = 0; + program->waitpid_return + = waitpid(program->child, &program->exit_status, 0); + } while (program->waitpid_return == -1 && errno == EINTR); + program->child = 0; } - if (state->waitpid_return < 0) { + if (program->waitpid_return < 0) { /* waitpid() failed? This is ugly. */ - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Error closing child process"); return (ARCHIVE_WARN); } #if !defined(_WIN32) || defined(__CYGWIN__) - if (WIFSIGNALED(state->exit_status)) { + if (WIFSIGNALED(program->exit_status)) { #ifdef SIGPIPE /* If the child died because we stopped reading before * it was done, that's okay. Some archive formats @@ -263,24 +266,24 @@ child_stop(struct archive_read_filter *self, struct program_filter *state) /* The alternative to this would be to add a step * before close(child_stdout) above to read from the * child until the child has no more to write. */ - if (WTERMSIG(state->exit_status) == SIGPIPE) + if (WTERMSIG(program->exit_status) == SIGPIPE) return (ARCHIVE_OK); #endif - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Child process exited with signal %d", - WTERMSIG(state->exit_status)); + WTERMSIG(program->exit_status)); return (ARCHIVE_WARN); } #endif /* !_WIN32 || __CYGWIN__ */ - if (WIFEXITED(state->exit_status)) { - if (WEXITSTATUS(state->exit_status) == 0) + if (WIFEXITED(program->exit_status)) { + if (WEXITSTATUS(program->exit_status) == 0) return (ARCHIVE_OK); - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Child process exited with status %d", - WEXITSTATUS(state->exit_status)); + WEXITSTATUS(program->exit_status)); return (ARCHIVE_WARN); } @@ -291,13 +294,13 @@ child_stop(struct archive_read_filter *self, struct program_filter *state) * Use select() to decide whether the child is ready for read or write. */ static ssize_t -child_read(struct archive_read_filter *self, char *buf, size_t buf_len) +child_read(struct archive_read_filter *f, char *buf, size_t buf_len) { - struct program_filter *state = self->data; + struct program *program = f->data; ssize_t ret, requested, avail; const char *p; #if defined(_WIN32) && !defined(__CYGWIN__) - HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout); + HANDLE handle = (HANDLE)_get_osfhandle(program->child_stdout); #endif requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; @@ -325,7 +328,7 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len) break; } #endif - ret = read(state->child_stdout, buf, requested); + ret = read(program->child_stdout, buf, requested); } while (ret == -1 && errno == EINTR); if (ret > 0) @@ -333,44 +336,44 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len) if (ret == 0 || (ret == -1 && errno == EPIPE)) /* Child has closed its output; reap the child * and return the status. */ - return (child_stop(self, state)); + return (child_stop(f, program)); if (ret == -1 && errno != EAGAIN) return (-1); - if (state->child_stdin == -1) { + if (program->child_stdin == -1) { /* Block until child has some I/O ready. */ - __archive_check_child(state->child_stdin, - state->child_stdout); + __archive_check_child(program->child_stdin, + program->child_stdout); continue; } /* Get some more data from upstream. */ - p = __archive_read_filter_ahead(self->upstream, 1, &avail); + p = __archive_read_filter_ahead(f->upstream, 1, &avail); if (p == NULL) { - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); + close(program->child_stdin); + program->child_stdin = -1; + fcntl(program->child_stdout, F_SETFL, 0); if (avail < 0) return (avail); continue; } do { - ret = write(state->child_stdin, p, avail); + ret = write(program->child_stdin, p, avail); } while (ret == -1 && errno == EINTR); if (ret > 0) { /* Consume whatever we managed to write. */ - __archive_read_filter_consume(self->upstream, ret); + __archive_read_filter_consume(f->upstream, ret); } else if (ret == -1 && errno == EAGAIN) { /* Block until child has some I/O ready. */ - __archive_check_child(state->child_stdin, - state->child_stdout); + __archive_check_child(program->child_stdin, + program->child_stdout); } else { /* Write failed. */ - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); + close(program->child_stdin); + program->child_stdin = -1; + fcntl(program->child_stdout, F_SETFL, 0); /* If it was a bad error, we're done; otherwise * it was EPIPE or EOF, and we can still read * from the child. */ @@ -387,9 +390,9 @@ program_reader_vtable = { }; int -__archive_read_program(struct archive_read_filter *self, const char *cmd) +__archive_read_program(struct archive_read_filter *f, const char *cmd) { - struct program_filter *state; + struct program *program; static const size_t out_buf_len = 65536; char *out_buf; const char *prefix = "Program: "; @@ -397,70 +400,71 @@ __archive_read_program(struct archive_read_filter *self, const char *cmd) size_t l; l = strlen(prefix) + strlen(cmd) + 1; - state = calloc(1, sizeof(*state)); + program = calloc(1, sizeof(*program)); out_buf = malloc(out_buf_len); - if (state == NULL || out_buf == NULL || - archive_string_ensure(&state->description, l) == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + if (program == NULL || out_buf == NULL || + archive_string_ensure(&program->description, l) == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate input data"); - if (state != NULL) { - archive_string_free(&state->description); - free(state); + if (program != NULL) { + archive_string_free(&program->description); + free(program); } free(out_buf); return (ARCHIVE_FATAL); } - archive_strcpy(&state->description, prefix); - archive_strcat(&state->description, cmd); + archive_strcpy(&program->description, prefix); + archive_strcat(&program->description, cmd); - self->code = ARCHIVE_FILTER_PROGRAM; - self->name = state->description.s; + f->code = ARCHIVE_FILTER_PROGRAM; + f->name = program->description.s; - state->out_buf = out_buf; - state->out_buf_len = out_buf_len; + program->out_buf = out_buf; + program->out_buf_len = out_buf_len; - ret = __archive_create_child(cmd, &state->child_stdin, - &state->child_stdout, &state->child); + ret = __archive_create_child(cmd, &program->child_stdin, + &program->child_stdout, &program->child); if (ret != ARCHIVE_OK) { - free(state->out_buf); - archive_string_free(&state->description); - free(state); - archive_set_error(&self->archive->archive, EINVAL, + free(program->out_buf); + archive_string_free(&program->description); + free(program); + archive_set_error(&f->archive->archive, EINVAL, "Can't initialize filter; unable to run program \"%s\"", cmd); return (ARCHIVE_FATAL); } - self->data = state; - self->vtable = &program_reader_vtable; + f->data = program; + f->vtable = &program_reader_vtable; /* XXX Check that we can read at least one byte? */ return (ARCHIVE_OK); } static int -program_bidder_init(struct archive_read_filter *self) +program_bidder_init(struct archive_read_filter *f) { struct program_bidder *bidder_state; + int r; - bidder_state = (struct program_bidder *)self->bidder->data; - return (__archive_read_program(self, bidder_state->cmd)); + bidder_state = (struct program_bidder *)f->bidder->data; + r = __archive_read_program(f, bidder_state->cmd); + f->name = bidder_state->description.s; + return (r); } static ssize_t -program_filter_read(struct archive_read_filter *self, const void **buff) +program_filter_read(struct archive_read_filter *f, const void **buff) { - struct program_filter *state; + struct program *program = f->data; ssize_t bytes; size_t total; char *p; - state = (struct program_filter *)self->data; - total = 0; - p = state->out_buf; - while (state->child_stdout != -1 && total < state->out_buf_len) { - bytes = child_read(self, p, state->out_buf_len - total); + p = program->out_buf; + while (program->child_stdout != -1 && total < program->out_buf_len) { + bytes = child_read(f, p, program->out_buf_len - total); if (bytes < 0) /* No recovery is possible if we can no longer * read from the child. */ @@ -472,23 +476,22 @@ program_filter_read(struct archive_read_filter *self, const void **buff) p += bytes; } - *buff = state->out_buf; + *buff = program->out_buf; return (total); } static int -program_filter_close(struct archive_read_filter *self) +program_filter_close(struct archive_read_filter *f) { - struct program_filter *state; + struct program *program = f->data; int e; - state = (struct program_filter *)self->data; - e = child_stop(self, state); + e = child_stop(f, program); /* Release our private data. */ - free(state->out_buf); - archive_string_free(&state->description); - free(state); + free(program->out_buf); + archive_string_free(&program->description); + free(program); return (e); } diff --git a/libarchive/archive_read_support_filter_rpm.c b/libarchive/archive_read_support_filter_rpm.c index 25ace4a25b..01bd1bf7af 100644 --- a/libarchive/archive_read_support_filter_rpm.c +++ b/libarchive/archive_read_support_filter_rpm.c @@ -38,20 +38,7 @@ #include "archive_read_private.h" struct rpm { - int64_t total_in; - uint64_t hpos; - uint64_t hlen; - unsigned char header[16]; - enum { - ST_LEAD, /* Skipping 'Lead' section. */ - ST_HEADER, /* Reading 'Header' section; - * first 16 bytes. */ - ST_HEADER_DATA, /* Skipping 'Header' section. */ - ST_PADDING, /* Skipping padding data after the - * 'Header' section. */ - ST_ARCHIVE /* Reading 'Archive' section. */ - } state; - int first_header; + int data_reached; }; #define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */ #define RPM_MIN_HEAD_SIZE 16 /* Minimum size of 'Head'. */ @@ -64,8 +51,6 @@ static ssize_t rpm_filter_read(struct archive_read_filter *, const void **); static int rpm_filter_close(struct archive_read_filter *); -static inline size_t rpm_limit_bytes(uint64_t, size_t); - #if ARCHIVE_VERSION_NUMBER < 4000000 /* Deprecated; remove in libarchive 4.0 */ int @@ -91,39 +76,38 @@ archive_read_support_filter_rpm(struct archive *_a) } static int -rpm_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +rpm_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { - const unsigned char *b; - ssize_t avail; + const unsigned char *p; int bits_checked; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - b = __archive_read_filter_ahead(filter, 8, &avail); - if (b == NULL) + p = __archive_read_filter_ahead(f, 8, NULL); + if (p == NULL) return (0); bits_checked = 0; /* * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB */ - if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0) + if (memcmp(p, "\xED\xAB\xEE\xDB", 4) != 0) return (0); bits_checked += 32; /* * Check major version. */ - if (b[4] != 3 && b[4] != 4) + if (p[4] != 3 && p[4] != 4) return (0); bits_checked += 8; /* * Check package type; binary or source. */ - if (b[6] != 0) + if (p[6] != 0) return (0); bits_checked += 8; - if (b[7] != 0 && b[7] != 1) + if (p[7] != 0 && p[7] != 1) return (0); bits_checked += 8; @@ -137,156 +121,127 @@ rpm_reader_vtable = { }; static int -rpm_bidder_init(struct archive_read_filter *self) +rpm_bidder_init(struct archive_read_filter *f) { struct rpm *rpm; - self->code = ARCHIVE_FILTER_RPM; - self->name = "rpm"; + f->code = ARCHIVE_FILTER_RPM; + f->name = "rpm"; rpm = calloc(1, sizeof(*rpm)); if (rpm == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for rpm"); return (ARCHIVE_FATAL); } - self->data = rpm; - rpm->state = ST_LEAD; - self->vtable = &rpm_reader_vtable; + f->data = rpm; + rpm->data_reached = 0; + f->vtable = &rpm_reader_vtable; return (ARCHIVE_OK); } -static inline size_t -rpm_limit_bytes(uint64_t bytes, size_t max) +static ssize_t +skip_padding(struct archive_read_filter *f) { - return (bytes > max ? max : (size_t)bytes); + const unsigned char *h; + ssize_t avail, count, r; + + do { + h = __archive_read_filter_ahead(f->upstream, 1, &avail); + if (h == NULL) + return (ARCHIVE_FATAL); + for (count = 0; count < avail && *h++ == '\0'; count++) + ; + r = __archive_read_filter_consume(f->upstream, count); + if (r < 0) + return (r); + } while (count == avail); + + return (ARCHIVE_OK); } static ssize_t -rpm_filter_read(struct archive_read_filter *self, const void **buff) +skip_prologue(struct archive_read_filter *f) { - struct rpm *rpm; - const unsigned char *b; - ssize_t avail_in, total, used; - size_t n; - uint64_t section; - uint64_t bytes; + const unsigned char *h; + ssize_t r; + int header, seen_header = 0; + + /* Skip lead size. */ + r = __archive_read_filter_consume(f->upstream, RPM_LEAD_SIZE); + if (r < 0) + return (r); - rpm = (struct rpm *)self->data; - *buff = NULL; - total = avail_in = 0; - b = NULL; - used = 0; do { - if (b == NULL) { - b = __archive_read_filter_ahead(self->upstream, 1, - &avail_in); - if (b == NULL) { - if (avail_in < 0) - return (ARCHIVE_FATAL); - else - break; - } - } + /* Read header intro. */ + h = __archive_read_filter_ahead(f->upstream, + RPM_MIN_HEAD_SIZE, NULL); + if (h == NULL) + return (ARCHIVE_FATAL); - switch (rpm->state) { - case ST_LEAD: - if (rpm->total_in + avail_in < RPM_LEAD_SIZE) - used += avail_in; - else { - n = (size_t)(RPM_LEAD_SIZE - rpm->total_in); - used += n; - b += n; - rpm->state = ST_HEADER; - rpm->hpos = 0; - rpm->hlen = 0; - rpm->first_header = 1; - } - break; - case ST_HEADER: - n = rpm_limit_bytes(RPM_MIN_HEAD_SIZE - rpm->hpos, - avail_in - used); - memcpy(rpm->header+rpm->hpos, b, n); - b += n; - used += n; - rpm->hpos += n; + header = (memcmp(h, "\x8E\xAD\xE8\x01", 4) == 0); + if (header) { + int64_t bytes, length, section; - if (rpm->hpos == RPM_MIN_HEAD_SIZE) { - if (rpm->header[0] != 0x8e || - rpm->header[1] != 0xad || - rpm->header[2] != 0xe8 || - rpm->header[3] != 0x01) { - if (rpm->first_header) { - archive_set_error( - &self->archive->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Unrecognized rpm header"); - return (ARCHIVE_FATAL); - } - rpm->state = ST_ARCHIVE; - *buff = rpm->header; - total = RPM_MIN_HEAD_SIZE; - break; - } - /* Calculate 'Header' length. */ - section = archive_be32dec(rpm->header+8); - bytes = archive_be32dec(rpm->header+12); - rpm->hlen = rpm->hpos + section * 16 + bytes; - rpm->state = ST_HEADER_DATA; - rpm->first_header = 0; - } - break; - case ST_HEADER_DATA: - n = rpm_limit_bytes(rpm->hlen - rpm->hpos, - avail_in - used); - b += n; - used += n; - rpm->hpos += n; - if (rpm->hpos == rpm->hlen) - rpm->state = ST_PADDING; - break; - case ST_PADDING: - while (used < avail_in) { - if (*b != 0) { - /* Read next header. */ - rpm->state = ST_HEADER; - rpm->hpos = 0; - rpm->hlen = 0; - break; - } - b++; - used++; - } - break; - case ST_ARCHIVE: - *buff = b; - total = avail_in; - used = avail_in; - break; - } - if (used == avail_in) { - rpm->total_in += used; - __archive_read_filter_consume(self->upstream, used); - b = NULL; - used = 0; - } - } while (total == 0 && avail_in > 0); + seen_header = 1; - if (used > 0 && b != NULL) { - rpm->total_in += used; - __archive_read_filter_consume(self->upstream, used); + /* Calculate header length. */ + section = archive_be32dec(h + 8); + bytes = archive_be32dec(h + 12); + length = RPM_MIN_HEAD_SIZE + section * 16 + bytes; + + /* Skip header. */ + r = __archive_read_filter_consume(f->upstream, + length); + if (r < 0) + return (r); + + /* Skip padding. */ + r = skip_padding(f); + if (r != ARCHIVE_OK) + return (r); + } + } while (header); + + /* At least one header must have been encountered. */ + if (!seen_header) { + archive_set_error( + &f->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unrecognized rpm header"); + return (ARCHIVE_FATAL); } - return (total); + + return (ARCHIVE_OK); +} + +static ssize_t +rpm_filter_read(struct archive_read_filter *f, const void **buff) +{ + struct rpm *rpm = f->data; + ssize_t r; + + if (!rpm->data_reached) { + r = skip_prologue(f); + if (r != ARCHIVE_OK) + return (r); + rpm->data_reached = 1; + } + + *buff = __archive_read_filter_ahead(f->upstream, 1, &r); + if (r > 0) + __archive_read_filter_consume(f->upstream, r); + + return r; } static int -rpm_filter_close(struct archive_read_filter *self) +rpm_filter_close(struct archive_read_filter *f) { - struct rpm *rpm; + struct rpm *rpm = f->data; - rpm = (struct rpm *)self->data; free(rpm); return (ARCHIVE_OK); diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c index acb8feb79b..c0f6f48ed4 100644 --- a/libarchive/archive_read_support_filter_uu.c +++ b/libarchive/archive_read_support_filter_uu.c @@ -43,9 +43,7 @@ /* Maximum lookahead during bid phase */ #define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */ -#define UUENCODE_MAX_LINE_LENGTH 34*1024 /* in bytes */ - -struct uudecode { +struct uu { int64_t total; unsigned char *in_buff; #define IN_BUFF_SIZE (1024) @@ -65,7 +63,7 @@ struct uudecode { }; static int uudecode_bidder_bid(struct archive_read_filter_bidder *, - struct archive_read_filter *filter); + struct archive_read_filter *f); static int uudecode_bidder_init(struct archive_read_filter *); static int uudecode_read_header(struct archive_read_filter *, @@ -209,7 +207,7 @@ get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize) } static ssize_t -bid_get_line(struct archive_read_filter *filter, +bid_get_line(struct archive_read_filter *f, const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl, size_t* nbytes_read) { @@ -224,7 +222,7 @@ bid_get_line(struct archive_read_filter *filter, len = get_line(*b, *avail, nl); /* - * Read bytes more while it does not reach the end of line. + * Read more bytes while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit && *nbytes_read < UUENCODE_BID_MAX_READ) { @@ -237,12 +235,12 @@ bid_get_line(struct archive_read_filter *filter, if (nbytes_req < (size_t)*ravail + 160) nbytes_req <<= 1; - *b = __archive_read_filter_ahead(filter, nbytes_req, avail); + *b = __archive_read_filter_ahead(f, nbytes_req, avail); if (*b == NULL) { if (*ravail >= *avail) return (0); /* Reading bytes reaches the end of a stream. */ - *b = __archive_read_filter_ahead(filter, *avail, avail); + *b = __archive_read_filter_ahead(f, *avail, avail); quit = 1; } *nbytes_read = *avail; @@ -260,42 +258,42 @@ bid_get_line(struct archive_read_filter *filter, #define UUDECODE(c) (((c) - 0x20) & 0x3f) static int -uudecode_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +uudecode_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { - const unsigned char *b; + const unsigned char *p; ssize_t avail, ravail; ssize_t len, nl; int l; int firstline; size_t nbytes_read; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - b = __archive_read_filter_ahead(filter, 1, &avail); - if (b == NULL) + p = __archive_read_filter_ahead(f, 1, &avail); + if (p == NULL) return (0); firstline = 20; ravail = avail; nbytes_read = avail; for (;;) { - len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read); + len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read); if (len < 0 || nl == 0) return (0); /* No match found. */ - if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0) + if (len - nl >= 11 && memcmp(p, "begin ", 6) == 0) l = 6; - else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0) + else if (len -nl >= 18 && memcmp(p, "begin-base64 ", 13) == 0) l = 13; else l = 0; - if (l > 0 && (b[l] < '0' || b[l] > '7' || - b[l+1] < '0' || b[l+1] > '7' || - b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' ')) + if (l > 0 && (p[l] < '0' || p[l] > '7' || + p[l+1] < '0' || p[l+1] > '7' || + p[l+2] < '0' || p[l+2] > '7' || p[l+3] != ' ')) l = 0; - b += len; + p += len; avail -= len; if (l) break; @@ -307,51 +305,51 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self, } if (!avail) return (0); - len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read); + len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read); if (len < 0 || nl == 0) return (0);/* There are non-ascii characters. */ avail -= len; if (l == 6) { /* "begin " */ - if (!uuchar[*b]) + if (!uuchar[*p]) return (0); /* Get a length of decoded bytes. */ - l = UUDECODE(*b++); len--; + l = UUDECODE(*p++); len--; if (l > 45) /* Normally, maximum length is 45(character 'M'). */ return (0); if (l > len - nl) return (0); /* Line too short. */ while (l) { - if (!uuchar[*b++]) + if (!uuchar[*p++]) return (0); --len; --l; } if (len-nl == 1 && - (uuchar[*b] || /* Check sum. */ - (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */ - ++b; + (uuchar[*p] || /* Check sum. */ + (*p >= 'a' && *p <= 'z'))) {/* Padding data(MINIX). */ + ++p; --len; } - b += nl; - if (avail && uuchar[*b]) + p += nl; + if (avail && uuchar[*p]) return (firstline+30); } else if (l == 13) { /* "begin-base64 " */ while (len-nl > 0) { - if (!base64[*b++]) + if (!base64[*p++]) return (0); --len; } - b += nl; + p += nl; - if (avail >= 5 && memcmp(b, "====\n", 5) == 0) + if (avail >= 5 && memcmp(p, "====\n", 5) == 0) return (firstline+40); - if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0) + if (avail >= 6 && memcmp(p, "====\r\n", 6) == 0) return (firstline+40); - if (avail > 0 && base64[*b]) + if (avail > 0 && base64[*p]) return (firstline+30); } @@ -366,46 +364,46 @@ uudecode_reader_vtable = { }; static int -uudecode_bidder_init(struct archive_read_filter *self) +uudecode_bidder_init(struct archive_read_filter *f) { - struct uudecode *uudecode; + struct uu *uu; void *out_buff; void *in_buff; - self->code = ARCHIVE_FILTER_UU; - self->name = "uu"; + f->code = ARCHIVE_FILTER_UU; + f->name = "uu"; - uudecode = calloc(1, sizeof(*uudecode)); + uu = calloc(1, sizeof(*uu)); out_buff = malloc(OUT_BUFF_SIZE); in_buff = malloc(IN_BUFF_SIZE); - if (uudecode == NULL || out_buff == NULL || in_buff == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + if (uu == NULL || out_buff == NULL || in_buff == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for uudecode"); - free(uudecode); + free(uu); free(out_buff); free(in_buff); return (ARCHIVE_FATAL); } - self->data = uudecode; - uudecode->in_buff = in_buff; - uudecode->in_cnt = 0; - uudecode->in_allocated = IN_BUFF_SIZE; - uudecode->out_buff = out_buff; - uudecode->state = ST_FIND_HEAD; - uudecode->mode_set = 0; - uudecode->name = NULL; - self->vtable = &uudecode_reader_vtable; + f->data = uu; + uu->in_buff = in_buff; + uu->in_cnt = 0; + uu->in_allocated = IN_BUFF_SIZE; + uu->out_buff = out_buff; + uu->state = ST_FIND_HEAD; + uu->mode_set = 0; + uu->name = NULL; + f->vtable = &uudecode_reader_vtable; return (ARCHIVE_OK); } static int -ensure_in_buff_size(struct archive_read_filter *self, - struct uudecode *uudecode, size_t size) +ensure_in_buff_size(struct archive_read_filter *f, + struct uu *uu, size_t size) { - if (size > uudecode->in_allocated) { + if (size > uu->in_allocated) { unsigned char *ptr; size_t newsize; @@ -413,7 +411,7 @@ ensure_in_buff_size(struct archive_read_filter *self, * Calculate a new buffer size for in_buff. * Increase its value until it is enough for our needs. */ - newsize = uudecode->in_allocated; + newsize = uu->in_allocated; do { if (newsize < IN_BUFF_SIZE*32) newsize <<= 1; @@ -423,43 +421,40 @@ ensure_in_buff_size(struct archive_read_filter *self, /* Allocate the new buffer. */ ptr = malloc(newsize); if (ptr == NULL) { - free(ptr); - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for uudecode"); return (ARCHIVE_FATAL); } /* Move the remaining data in in_buff into the new buffer. */ - if (uudecode->in_cnt) - memmove(ptr, uudecode->in_buff, uudecode->in_cnt); + if (uu->in_cnt) + memmove(ptr, uu->in_buff, uu->in_cnt); /* Replace in_buff with the new buffer. */ - free(uudecode->in_buff); - uudecode->in_buff = ptr; - uudecode->in_allocated = newsize; + free(uu->in_buff); + uu->in_buff = ptr; + uu->in_allocated = newsize; } return (ARCHIVE_OK); } static int -uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry) +uudecode_read_header(struct archive_read_filter *f, struct archive_entry *entry) { + struct uu *uu = f->data; - struct uudecode *uudecode; - uudecode = (struct uudecode *)self->data; + if (uu->mode_set != 0) + archive_entry_set_mode(entry, S_IFREG | uu->mode); - if (uudecode->mode_set != 0) - archive_entry_set_mode(entry, S_IFREG | uudecode->mode); - - if (uudecode->name != NULL) - archive_entry_set_pathname(entry, uudecode->name); + if (uu->name != NULL) + archive_entry_set_pathname(entry, uu->name); return (ARCHIVE_OK); } static ssize_t -uudecode_filter_read(struct archive_read_filter *self, const void **buff) +uudecode_filter_read(struct archive_read_filter *f, const void **buff) { - struct uudecode *uudecode; + struct uu *uu = f->data; const unsigned char *b, *d; unsigned char *out; ssize_t avail_in, ravail; @@ -467,10 +462,8 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff) ssize_t total; ssize_t len, llen, nl, namelen; - uudecode = (struct uudecode *)self->data; - read_more: - d = __archive_read_filter_ahead(self->upstream, 1, &avail_in); + d = __archive_read_filter_ahead(f->upstream, 1, &avail_in); if (d == NULL && avail_in < 0) return (ARCHIVE_FATAL); /* Quiet a code analyzer; make sure avail_in must be zero @@ -479,15 +472,17 @@ read_more: avail_in = 0; used = 0; total = 0; - out = uudecode->out_buff; + out = uu->out_buff; + if (avail_in > 2 * UUENCODE_BID_MAX_READ) + avail_in = 2 * UUENCODE_BID_MAX_READ; ravail = avail_in; - if (uudecode->state == ST_IGNORE) { + if (uu->state == ST_IGNORE) { used = avail_in; goto finish; } - if (uudecode->in_cnt) { - if (uudecode->in_cnt > UUENCODE_MAX_LINE_LENGTH) { - archive_set_error(&self->archive->archive, + if (uu->in_cnt) { + if (uu->in_cnt > UUENCODE_BID_MAX_READ) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid format data"); return (ARCHIVE_FATAL); @@ -496,14 +491,15 @@ read_more: * If there is remaining data which is saved by * a previous call, use it first. */ - if (ensure_in_buff_size(self, uudecode, - avail_in + uudecode->in_cnt) != ARCHIVE_OK) + if (ensure_in_buff_size(f, uu, + avail_in + uu->in_cnt) != ARCHIVE_OK) return (ARCHIVE_FATAL); - memcpy(uudecode->in_buff + uudecode->in_cnt, - d, avail_in); - d = uudecode->in_buff; - avail_in += uudecode->in_cnt; - uudecode->in_cnt = 0; + if (avail_in > 0) + memcpy(uu->in_buff + uu->in_cnt, + d, avail_in); + d = uu->in_buff; + avail_in += uu->in_cnt; + uu->in_cnt = 0; } for (;used < avail_in; d += llen, used += llen) { ssize_t l, body; @@ -512,22 +508,28 @@ read_more: len = get_line(b, avail_in - used, &nl); if (len < 0) { /* Non-ascii character is found. */ - if (uudecode->state == ST_FIND_HEAD && - (uudecode->total > 0 || total > 0)) { - uudecode->state = ST_IGNORE; + if (uu->state == ST_FIND_HEAD && + (uu->total > 0 || total > 0)) { + uu->state = ST_IGNORE; used = avail_in; goto finish; } - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); } + if (len > UUENCODE_BID_MAX_READ) { + archive_set_error(&f->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid format data"); + return (ARCHIVE_FATAL); + } llen = len; - if ((nl == 0) && (uudecode->state != ST_UUEND)) { + if ((nl == 0) && (uu->state != ST_UUEND)) { if (total == 0 && ravail <= 0) { /* There is nothing more to read, fail */ - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Missing format data"); return (ARCHIVE_FATAL); @@ -536,28 +538,28 @@ read_more: * Save remaining data which does not contain * NL('\n','\r'). */ - if (ensure_in_buff_size(self, uudecode, len) + if (ensure_in_buff_size(f, uu, len) != ARCHIVE_OK) return (ARCHIVE_FATAL); - if (uudecode->in_buff != b) - memmove(uudecode->in_buff, b, len); - uudecode->in_cnt = len; + if (uu->in_buff != b) + memmove(uu->in_buff, b, len); + uu->in_cnt = len; if (total == 0) { /* Do not return 0; it means end-of-file. - * We should try to read bytes more. */ + * We should try to read more bytes. */ __archive_read_filter_consume( - self->upstream, ravail); + f->upstream, ravail); goto read_more; } used += len; break; } - switch (uudecode->state) { + switch (uu->state) { default: case ST_FIND_HEAD: /* Do not read more than UUENCODE_BID_MAX_READ bytes */ if (total + len >= UUENCODE_BID_MAX_READ) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid format data"); return (ARCHIVE_FATAL); @@ -573,30 +575,30 @@ read_more: b[l+1] >= '0' && b[l+1] <= '7' && b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') { if (l == 6) - uudecode->state = ST_READ_UU; + uu->state = ST_READ_UU; else - uudecode->state = ST_READ_BASE64; - uudecode->mode = (mode_t)( + uu->state = ST_READ_BASE64; + uu->mode = (mode_t)( ((int)(b[l] - '0') * 64) + ((int)(b[l+1] - '0') * 8) + (int)(b[l+2] - '0')); - uudecode->mode_set = 1; + uu->mode_set = 1; namelen = len - nl - 4 - l; if (namelen > 1) { - if (uudecode->name != NULL) - free(uudecode->name); - uudecode->name = malloc(namelen + 1); - if (uudecode->name == NULL) { - archive_set_error( - &self->archive->archive, - ENOMEM, - "Can't allocate data for uudecode"); + if (uu->name != NULL) + free(uu->name); + uu->name = malloc(namelen + 1); + if (uu->name == NULL) { + archive_set_error( + &f->archive->archive, + ENOMEM, + "Can't allocate data for uudecode"); return (ARCHIVE_FATAL); } - strncpy(uudecode->name, + strncpy(uu->name, (const char *)(b + l + 4), namelen); - uudecode->name[namelen] = '\0'; + uu->name[namelen] = '\0'; } } break; @@ -605,7 +607,7 @@ read_more: goto finish; body = len - nl; if (!uuchar[*b] || body <= 0) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); @@ -614,13 +616,13 @@ read_more: l = UUDECODE(*b++); body--; if (l > body) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); } if (l == 0) { - uudecode->state = ST_UUEND; + uu->state = ST_UUEND; break; } while (l > 0) { @@ -649,7 +651,7 @@ read_more: } } if (l) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); @@ -657,9 +659,9 @@ read_more: break; case ST_UUEND: if (len - nl == 3 && memcmp(b, "end ", 3) == 0) - uudecode->state = ST_FIND_HEAD; + uu->state = ST_FIND_HEAD; else { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); @@ -671,7 +673,7 @@ read_more: l = len - nl; if (l >= 3 && b[0] == '=' && b[1] == '=' && b[2] == '=') { - uudecode->state = ST_FIND_HEAD; + uu->state = ST_FIND_HEAD; break; } while (l > 0) { @@ -704,7 +706,7 @@ read_more: } } if (l && *b != '=') { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); return (ARCHIVE_FATAL); @@ -715,23 +717,22 @@ read_more: finish: if (ravail < avail_in) used -= avail_in - ravail; - __archive_read_filter_consume(self->upstream, used); + __archive_read_filter_consume(f->upstream, used); - *buff = uudecode->out_buff; - uudecode->total += total; + *buff = uu->out_buff; + uu->total += total; return (total); } static int -uudecode_filter_close(struct archive_read_filter *self) +uudecode_filter_close(struct archive_read_filter *f) { - struct uudecode *uudecode; + struct uu *uu = f->data; - uudecode = (struct uudecode *)self->data; - free(uudecode->in_buff); - free(uudecode->out_buff); - free(uudecode->name); - free(uudecode); + free(uu->in_buff); + free(uu->out_buff); + free(uu->name); + free(uu); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c index dcbf734eca..7c8e0cb52e 100644 --- a/libarchive/archive_read_support_filter_xz.c +++ b/libarchive/archive_read_support_filter_xz.c @@ -50,11 +50,10 @@ #if HAVE_LZMA_H && HAVE_LIBLZMA -struct private_data { +struct xz { lzma_stream stream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; char eof; /* True = found end of compressed data. */ char in_stream; @@ -83,8 +82,7 @@ static int xz_lzma_bidder_init(struct archive_read_filter *); /* * Note that we can detect xz and lzma compressed files 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 no lzma library is available. + * can give better error messages.) */ static int xz_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -199,15 +197,14 @@ archive_read_support_filter_lzip(struct archive *_a) * Test whether we can handle this data. */ static int -xz_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +xz_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *buffer; - ssize_t avail; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - buffer = __archive_read_filter_ahead(filter, 6, &avail); + buffer = __archive_read_filter_ahead(f, 6, NULL); if (buffer == NULL) return (0); @@ -233,18 +230,17 @@ xz_bidder_bid(struct archive_read_filter_bidder *self, * they have other evidence (file name, command-line option) to go on. */ static int -lzma_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +lzma_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *buffer; - ssize_t avail; uint32_t dicsize; uint64_t uncompressed_size; int bits_checked; - (void)self; /* UNUSED */ + (void)b; /* UNUSED */ - buffer = __archive_read_filter_ahead(filter, 14, &avail); + buffer = __archive_read_filter_ahead(f, 14, NULL); if (buffer == NULL) return (0); @@ -340,14 +336,13 @@ lzma_bidder_bid(struct archive_read_filter_bidder *self, } static int -lzip_has_member(struct archive_read_filter *filter) +lzip_has_member(struct archive_read_filter *f) { const unsigned char *buffer; - ssize_t avail; int bits_checked; int log2dic; - buffer = __archive_read_filter_ahead(filter, 6, &avail); + buffer = __archive_read_filter_ahead(f, 6, NULL); if (buffer == NULL) return (0); @@ -374,12 +369,12 @@ lzip_has_member(struct archive_read_filter *filter) } static int -lzip_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +lzip_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { - (void)self; /* UNUSED */ - return (lzip_has_member(filter)); + (void)b; /* UNUSED */ + return (lzip_has_member(f)); } #if HAVE_LZMA_H && HAVE_LIBLZMA @@ -388,34 +383,34 @@ lzip_bidder_bid(struct archive_read_filter_bidder *self, * liblzma 4.999.7 and later support both lzma and xz streams. */ static int -xz_bidder_init(struct archive_read_filter *self) +xz_bidder_init(struct archive_read_filter *f) { - self->code = ARCHIVE_FILTER_XZ; - self->name = "xz"; - return (xz_lzma_bidder_init(self)); + f->code = ARCHIVE_FILTER_XZ; + f->name = "xz"; + return (xz_lzma_bidder_init(f)); } static int -lzma_bidder_init(struct archive_read_filter *self) +lzma_bidder_init(struct archive_read_filter *f) { - self->code = ARCHIVE_FILTER_LZMA; - self->name = "lzma"; - return (xz_lzma_bidder_init(self)); + f->code = ARCHIVE_FILTER_LZMA; + f->name = "lzma"; + return (xz_lzma_bidder_init(f)); } static int -lzip_bidder_init(struct archive_read_filter *self) +lzip_bidder_init(struct archive_read_filter *f) { - self->code = ARCHIVE_FILTER_LZIP; - self->name = "lzip"; - return (xz_lzma_bidder_init(self)); + f->code = ARCHIVE_FILTER_LZIP; + f->name = "lzip"; + return (xz_lzma_bidder_init(f)); } /* * Set an error code and choose an error message */ static void -set_error(struct archive_read_filter *self, int ret) +set_error(struct archive_read_filter *f, int ret) { switch (ret) { @@ -423,36 +418,36 @@ set_error(struct archive_read_filter *self, int ret) case LZMA_OK: /* Decompressor made some progress. */ break; case LZMA_MEM_ERROR: - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); break; case LZMA_MEMLIMIT_ERROR: - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Lzma library error: Out of memory"); break; case LZMA_FORMAT_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); break; case LZMA_OPTIONS_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); break; case LZMA_DATA_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); break; case LZMA_BUF_ERROR: - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); break; default: /* Return an error. */ - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); break; @@ -469,84 +464,83 @@ xz_lzma_reader_vtable = { * Setup the callbacks. */ static int -xz_lzma_bidder_init(struct archive_read_filter *self) +xz_lzma_bidder_init(struct archive_read_filter *f) { static const size_t out_block_size = 64 * 1024; void *out_block; - struct private_data *state; + struct xz *xz; int ret; - state = calloc(1, sizeof(*state)); + xz = calloc(1, sizeof(*xz)); out_block = malloc(out_block_size); - if (state == NULL || out_block == NULL) { - archive_set_error(&self->archive->archive, ENOMEM, + if (xz == NULL || out_block == NULL) { + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for xz decompression"); free(out_block); - free(state); + free(xz); return (ARCHIVE_FATAL); } - self->data = state; - state->out_block_size = out_block_size; - state->out_block = out_block; - self->vtable = &xz_lzma_reader_vtable; + f->data = xz; + xz->out_block_size = out_block_size; + xz->out_block = out_block; + f->vtable = &xz_lzma_reader_vtable; - state->stream.avail_in = 0; + xz->stream.avail_in = 0; - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; + xz->stream.next_out = xz->out_block; + xz->stream.avail_out = xz->out_block_size; - state->crc32 = 0; - if (self->code == ARCHIVE_FILTER_LZIP) { + xz->crc32 = 0; + if (f->code == ARCHIVE_FILTER_LZIP) { /* * We have to read a lzip header and use it to initialize * compression library, thus we cannot initialize the * library for lzip here. */ - state->in_stream = 0; + xz->in_stream = 0; return (ARCHIVE_OK); } else - state->in_stream = 1; + xz->in_stream = 1; /* Initialize compression library. */ - if (self->code == ARCHIVE_FILTER_XZ) - ret = lzma_stream_decoder(&(state->stream), + if (f->code == ARCHIVE_FILTER_XZ) + ret = lzma_stream_decoder(&(xz->stream), LZMA_MEMLIMIT,/* memlimit */ LZMA_CONCATENATED); else - ret = lzma_alone_decoder(&(state->stream), + ret = lzma_alone_decoder(&(xz->stream), LZMA_MEMLIMIT);/* memlimit */ if (ret == LZMA_OK) return (ARCHIVE_OK); /* Library setup failed: Choose an error message and clean up. */ - set_error(self, ret); + set_error(f, ret); - free(state->out_block); - free(state); - self->data = NULL; + free(xz->out_block); + free(xz); + f->data = NULL; + f->vtable = NULL; return (ARCHIVE_FATAL); } static int -lzip_init(struct archive_read_filter *self) +lzip_init(struct archive_read_filter *f) { - struct private_data *state; + struct xz *xz = f->data; const unsigned char *h; lzma_filter filters[2]; unsigned char props[5]; - ssize_t avail_in; uint32_t dicsize; int log2dic, ret; - state = (struct private_data *)self->data; - h = __archive_read_filter_ahead(self->upstream, 6, &avail_in); + h = __archive_read_filter_ahead(f->upstream, 6, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Get a version number. */ - state->lzip_ver = h[4]; + xz->lzip_ver = h[4]; /* * Setup lzma property. @@ -563,8 +557,8 @@ lzip_init(struct archive_read_filter *self) archive_le32enc(props+1, dicsize); /* Consume lzip header. */ - __archive_read_filter_consume(self->upstream, 6); - state->member_in = 6; + __archive_read_filter_consume(f->upstream, 6); + xz->member_in = 6; filters[0].id = LZMA_FILTER_LZMA1; filters[0].options = NULL; @@ -573,74 +567,73 @@ lzip_init(struct archive_read_filter *self) ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props)); if (ret != LZMA_OK) { - set_error(self, ret); + set_error(f, ret); return (ARCHIVE_FATAL); } - ret = lzma_raw_decoder(&(state->stream), filters); + ret = lzma_raw_decoder(&(xz->stream), filters); free(filters[0].options); if (ret != LZMA_OK) { - set_error(self, ret); + set_error(f, ret); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static int -lzip_tail(struct archive_read_filter *self) +lzip_tail(struct archive_read_filter *f) { - struct private_data *state; - const unsigned char *f; + struct xz *xz = f->data; + const unsigned char *p; ssize_t avail_in; int tail; - state = (struct private_data *)self->data; - if (state->lzip_ver == 0) + if (xz->lzip_ver == 0) tail = 12; else tail = 20; - f = __archive_read_filter_ahead(self->upstream, tail, &avail_in); - if (f == NULL && avail_in < 0) + p = __archive_read_filter_ahead(f->upstream, tail, &avail_in); + if (p == NULL && avail_in < 0) return (ARCHIVE_FATAL); - if (f == NULL || avail_in < tail) { - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + if (p == NULL || avail_in < tail) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Remaining data is less bytes"); return (ARCHIVE_FAILED); } /* Check the crc32 value of the uncompressed data of the current * member */ - if (state->crc32 != archive_le32dec(f)) { + if (xz->crc32 != archive_le32dec(p)) { #ifndef DONT_FAIL_ON_CRC_ERROR - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: CRC32 error"); return (ARCHIVE_FAILED); #endif } /* Check the uncompressed size of the current member */ - if ((uint64_t)state->member_out != archive_le64dec(f + 4)) { - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + if ((uint64_t)xz->member_out != archive_le64dec(p + 4)) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Uncompressed size error"); return (ARCHIVE_FAILED); } /* Check the total size of the current member */ - if (state->lzip_ver == 1 && - (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) { - archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + if (xz->lzip_ver == 1 && + (uint64_t)xz->member_in + tail != archive_le64dec(p + 12)) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Lzip: Member size error"); return (ARCHIVE_FAILED); } - __archive_read_filter_consume(self->upstream, tail); + __archive_read_filter_consume(f->upstream, tail); /* If current lzip data consists of multi member, try decompressing * a next member. */ - if (lzip_has_member(self->upstream) != 0) { - state->in_stream = 0; - state->crc32 = 0; - state->member_out = 0; - state->member_in = 0; - state->eof = 0; + if (lzip_has_member(f->upstream) != 0) { + xz->in_stream = 0; + xz->crc32 = 0; + xz->member_out = 0; + xz->member_in = 0; + xz->eof = 0; } return (ARCHIVE_OK); } @@ -649,83 +642,80 @@ lzip_tail(struct archive_read_filter *self) * Return the next block of decompressed data. */ static ssize_t -xz_filter_read(struct archive_read_filter *self, const void **p) +xz_filter_read(struct archive_read_filter *f, const void **p) { - struct private_data *state; + struct xz *xz = f->data; size_t decompressed; ssize_t avail_in; int64_t member_in; int ret; - state = (struct private_data *)self->data; - redo: /* Empty our output buffer. */ - state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; - member_in = state->member_in; + xz->stream.next_out = xz->out_block; + xz->stream.avail_out = xz->out_block_size; + member_in = xz->member_in; /* Try to fill the output buffer. */ - while (state->stream.avail_out > 0 && !state->eof) { - if (!state->in_stream) { + while (xz->stream.avail_out > 0 && !xz->eof) { + if (!xz->in_stream) { /* * Initialize liblzma for lzip */ - ret = lzip_init(self); + ret = lzip_init(f); if (ret != ARCHIVE_OK) return (ret); - state->in_stream = 1; + xz->in_stream = 1; } - state->stream.next_in = - __archive_read_filter_ahead(self->upstream, 1, &avail_in); - if (state->stream.next_in == NULL && avail_in < 0) { - archive_set_error(&self->archive->archive, + xz->stream.next_in = + __archive_read_filter_ahead(f->upstream, 1, &avail_in); + if (xz->stream.next_in == NULL && avail_in < 0) { + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "truncated input"); return (ARCHIVE_FATAL); } - state->stream.avail_in = avail_in; + xz->stream.avail_in = avail_in; /* Decompress as much as we can in one pass. */ - ret = lzma_code(&(state->stream), - (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN); + ret = lzma_code(&(xz->stream), + (xz->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN); switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ - state->eof = 1; + xz->eof = 1; /* FALL THROUGH */ case LZMA_OK: /* Decompressor made some progress. */ - __archive_read_filter_consume(self->upstream, - avail_in - state->stream.avail_in); - state->member_in += - avail_in - state->stream.avail_in; + __archive_read_filter_consume(f->upstream, + avail_in - xz->stream.avail_in); + xz->member_in += + avail_in - xz->stream.avail_in; break; default: - set_error(self, ret); + set_error(f, ret); return (ARCHIVE_FATAL); } } - decompressed = state->stream.next_out - state->out_block; - state->total_out += decompressed; - state->member_out += decompressed; + decompressed = xz->stream.next_out - xz->out_block; + xz->member_out += decompressed; if (decompressed == 0) { - if (member_in != state->member_in && - self->code == ARCHIVE_FILTER_LZIP && - state->eof) { - ret = lzip_tail(self); + if (member_in != xz->member_in && + f->code == ARCHIVE_FILTER_LZIP && + xz->eof) { + ret = lzip_tail(f); if (ret != ARCHIVE_OK) return (ret); - if (!state->eof) + if (!xz->eof) goto redo; } *p = NULL; } else { - *p = state->out_block; - if (self->code == ARCHIVE_FILTER_LZIP) { - state->crc32 = lzma_crc32(state->out_block, - decompressed, state->crc32); - if (state->eof) { - ret = lzip_tail(self); + *p = xz->out_block; + if (f->code == ARCHIVE_FILTER_LZIP) { + xz->crc32 = lzma_crc32(xz->out_block, + decompressed, xz->crc32); + if (xz->eof) { + ret = lzip_tail(f); if (ret != ARCHIVE_OK) return (ret); } @@ -738,14 +728,13 @@ xz_filter_read(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -xz_filter_close(struct archive_read_filter *self) +xz_filter_close(struct archive_read_filter *f) { - struct private_data *state; + struct xz *xz = f->data; - state = (struct private_data *)self->data; - lzma_end(&(state->stream)); - free(state->out_block); - free(state); + lzma_end(&(xz->stream)); + free(xz->out_block); + free(xz); return (ARCHIVE_OK); } @@ -759,44 +748,44 @@ xz_filter_close(struct archive_read_filter *self) * */ static int -lzma_bidder_init(struct archive_read_filter *self) +lzma_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "lzma -d -qq"); + r = __archive_read_program(f, "lzma -d -qq"); /* 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_LZMA; - self->name = "lzma"; + f->code = ARCHIVE_FILTER_LZMA; + f->name = "lzma"; return (r); } static int -xz_bidder_init(struct archive_read_filter *self) +xz_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "xz -d -qq"); + r = __archive_read_program(f, "xz -d -qq"); /* 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_XZ; - self->name = "xz"; + f->code = ARCHIVE_FILTER_XZ; + f->name = "xz"; return (r); } static int -lzip_bidder_init(struct archive_read_filter *self) +lzip_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "lzip -d -q"); + r = __archive_read_program(f, "lzip -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_LZIP; - self->name = "lzip"; + f->code = ARCHIVE_FILTER_LZIP; + f->name = "lzip"; return (r); } diff --git a/libarchive/archive_read_support_filter_zstd.c b/libarchive/archive_read_support_filter_zstd.c index da7c540dbc..12eac04ca3 100644 --- a/libarchive/archive_read_support_filter_zstd.c +++ b/libarchive/archive_read_support_filter_zstd.c @@ -48,16 +48,16 @@ #include "archive.h" #include "archive_endian.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" #if HAVE_ZSTD_H && HAVE_LIBZSTD -struct private_data { +struct zstd { ZSTD_DStream *dstream; unsigned char *out_block; size_t out_block_size; - int64_t total_out; char in_frame; /* True = in the middle of a zstd frame. */ char eof; /* True = found end of compressed data. */ }; @@ -70,8 +70,7 @@ static int zstd_filter_close(struct archive_read_filter *); /* * Note that we can detect zstd compressed files 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 no zstd library - * is available. + * messages.) */ static int zstd_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -105,60 +104,82 @@ archive_read_support_filter_zstd(struct archive *_a) * Test whether we can handle this data. */ static int -zstd_bidder_bid(struct archive_read_filter_bidder *self, - struct archive_read_filter *filter) +zstd_bidder_bid(struct archive_read_filter_bidder *b, + struct archive_read_filter *f) { const unsigned char *buffer; ssize_t avail; + /* + * Zstandard skippable frames contain a 4 byte magic number followed + * by a 4 byte frame data size, then that number of bytes of data. + * Regular frames contain a 4 byte magic number followed by a 2-14 + * byte frame header, some data, and a 3 byte end marker. + */ + const size_t min_zstd_frame_size = 8; - // Zstandard skippable frames contain a 4 byte magic number followed by - // a 4 byte frame data size, then that number of bytes of data. Regular - // frames contain a 4 byte magic number followed by a 2-14 byte frame - // header, some data, and a 3 byte end marker. - ssize_t min_zstd_frame_size = 8; + size_t offset_in_buffer = 0; + const size_t max_lookahead = 64 * 1024; + uint32_t magic_number; - ssize_t offset_in_buffer = 0; - ssize_t max_lookahead = 64 * 1024; + /* Zstd regular frame magic number. */ + const uint32_t zstd_magic = 0xFD2FB528U; - // Zstd regular frame magic number. - uint32_t zstd_magic = 0xFD2FB528U; + /* + * Note: Zstd and LZ4 skippable frame magic numbers are identical. + * To differentiate these two, we need to look for a non-skippable + * frame. + */ + const uint32_t zstd_magic_skippable_start = 0x184D2A50; + const uint32_t zstd_magic_skippable_mask = 0xFFFFFFF0; - // Note: Zstd and LZ4 skippable frame magic numbers are identical. - // To differentiate these two, we need to look for a non-skippable - // frame. - uint32_t zstd_magic_skippable_start = 0x184D2A50; - uint32_t zstd_magic_skippable_mask = 0xFFFFFFF0; + (void) b; /* UNUSED */ - (void) self; // UNUSED - - buffer = __archive_read_filter_ahead(filter, min_zstd_frame_size, &avail); + buffer = __archive_read_filter_ahead(f, min_zstd_frame_size, + &avail); if (buffer == NULL) return (0); - uint32_t magic_number = archive_le32dec(buffer); + magic_number = archive_le32dec(buffer); - while ((magic_number & zstd_magic_skippable_mask) == zstd_magic_skippable_start) { + while ((magic_number & zstd_magic_skippable_mask) == + zstd_magic_skippable_start) { + size_t min; + uint32_t frame_data_size; - offset_in_buffer += 4; // Skip over the magic number + /* Skip over the magic number */ + offset_in_buffer += 4; - // Ensure that we can read another 4 bytes. - if (offset_in_buffer + 4 > avail) { - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + 4, &avail); + /* Ensure that we can read another 4 bytes. */ + if (offset_in_buffer + 4 > (size_t)avail) { + buffer = __archive_read_filter_ahead(f, + offset_in_buffer + 4, &avail); if (buffer == NULL) return (0); } - uint32_t frame_data_size = archive_le32dec(buffer + offset_in_buffer); + frame_data_size = archive_le32dec(buffer + offset_in_buffer); - // Skip over the 4 frame data size bytes, plus the value stored there. - offset_in_buffer += 4 + frame_data_size; + /* Skip over the 4 frame data size bytes */ + offset_in_buffer += 4; - // There should be at least one more frame if this is zstd data. - if (offset_in_buffer + min_zstd_frame_size > avail) { - if (offset_in_buffer + min_zstd_frame_size > max_lookahead) + /* Skip over the value stored there. */ + if (archive_ckd_add_size(&offset_in_buffer, + offset_in_buffer, frame_data_size)) + return (0); + + /* + * There should be at least one more frame + * if this is zstd data. + */ + if (archive_ckd_add_size(&min, + offset_in_buffer, min_zstd_frame_size)) + return (0); + if (min > (size_t)avail) { + if (min > max_lookahead) return (0); - buffer = __archive_read_filter_ahead(filter, offset_in_buffer + min_zstd_frame_size, &avail); + buffer = __archive_read_filter_ahead(f, + min, &avail); if (buffer == NULL) return (0); } @@ -166,8 +187,10 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, magic_number = archive_le32dec(buffer + offset_in_buffer); } - // We have skipped over any skippable frames. Either a regular zstd frame - // follows, or this isn't zstd data. + /* + * We have skipped over any skippable frames. Either a regular zstd + * frame follows, or this isn't zstd data. + */ if (magic_number == zstd_magic) return (offset_in_buffer + 4); @@ -179,20 +202,20 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self, /* * If we don't have the library on this system, we can't do the - * decompression directly. We can, however, try to run "zstd -d" + * decompression directly. We can, however, try to run "zstd -d -qq" * in case that's available. */ static int -zstd_bidder_init(struct archive_read_filter *self) +zstd_bidder_init(struct archive_read_filter *f) { int r; - r = __archive_read_program(self, "zstd -d -qq"); + r = __archive_read_program(f, "zstd -d -qq"); /* 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_ZSTD; - self->name = "zstd"; + f->code = ARCHIVE_FILTER_ZSTD; + f->name = "zstd"; return (r); } @@ -208,80 +231,78 @@ zstd_reader_vtable = { * Initialize the filter object */ static int -zstd_bidder_init(struct archive_read_filter *self) +zstd_bidder_init(struct archive_read_filter *f) { - struct private_data *state; + struct zstd *zstd; size_t out_block_size = ZSTD_DStreamOutSize(); void *out_block; ZSTD_DStream *dstream; - self->code = ARCHIVE_FILTER_ZSTD; - self->name = "zstd"; + f->code = ARCHIVE_FILTER_ZSTD; + f->name = "zstd"; - state = calloc(1, sizeof(*state)); + zstd = calloc(1, sizeof(*zstd)); out_block = malloc(out_block_size); dstream = ZSTD_createDStream(); - if (state == NULL || out_block == NULL || dstream == NULL) { + if (zstd == NULL || out_block == NULL || dstream == NULL) { free(out_block); - free(state); + free(zstd); ZSTD_freeDStream(dstream); /* supports free on NULL */ - archive_set_error(&self->archive->archive, ENOMEM, + archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for zstd decompression"); return (ARCHIVE_FATAL); } - self->data = state; + f->data = zstd; - state->out_block_size = out_block_size; - state->out_block = out_block; - state->dstream = dstream; - self->vtable = &zstd_reader_vtable; + zstd->out_block_size = out_block_size; + zstd->out_block = out_block; + zstd->dstream = dstream; + f->vtable = &zstd_reader_vtable; - state->eof = 0; - state->in_frame = 0; + zstd->eof = 0; + zstd->in_frame = 0; return (ARCHIVE_OK); } static ssize_t -zstd_filter_read(struct archive_read_filter *self, const void **p) +zstd_filter_read(struct archive_read_filter *f, const void **p) { - struct private_data *state; + struct zstd *zstd = f->data; size_t decompressed; ssize_t avail_in; ZSTD_outBuffer out; ZSTD_inBuffer in; size_t ret; - state = (struct private_data *)self->data; - - out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 }; + out = (ZSTD_outBuffer) { zstd->out_block, zstd->out_block_size, 0 }; /* Try to fill the output buffer. */ - while (out.pos < out.size && !state->eof) { - if (!state->in_frame) { - ret = ZSTD_initDStream(state->dstream); + while (out.pos < out.size && !zstd->eof) { + if (!zstd->in_frame) { + ret = ZSTD_initDStream(zstd->dstream); if (ZSTD_isError(ret)) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Error initializing zstd decompressor: %s", ZSTD_getErrorName(ret)); return (ARCHIVE_FATAL); } } - in.src = __archive_read_filter_ahead(self->upstream, 1, + in.src = __archive_read_filter_ahead(f->upstream, 1, &avail_in); if (avail_in < 0) { return avail_in; } if (in.src == NULL && avail_in == 0) { - if (!state->in_frame) { + if (!zstd->in_frame) { /* end of stream */ - state->eof = 1; + zstd->eof = 1; break; } else { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Truncated zstd input"); return (ARCHIVE_FATAL); @@ -291,10 +312,10 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) in.pos = 0; { - ret = ZSTD_decompressStream(state->dstream, &out, &in); + ret = ZSTD_decompressStream(zstd->dstream, &out, &in); if (ZSTD_isError(ret)) { - archive_set_error(&self->archive->archive, + archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(ret)); @@ -302,19 +323,18 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) } /* Decompressor made some progress */ - __archive_read_filter_consume(self->upstream, in.pos); + __archive_read_filter_consume(f->upstream, in.pos); /* ret guaranteed to be > 0 if frame isn't done yet */ - state->in_frame = (ret != 0); + zstd->in_frame = (ret != 0); } } decompressed = out.pos; - state->total_out += decompressed; if (decompressed == 0) *p = NULL; else - *p = state->out_block; + *p = zstd->out_block; return (decompressed); } @@ -322,15 +342,13 @@ zstd_filter_read(struct archive_read_filter *self, const void **p) * Clean up the decompressor. */ static int -zstd_filter_close(struct archive_read_filter *self) +zstd_filter_close(struct archive_read_filter *f) { - struct private_data *state; + struct zstd *zstd = f->data; - state = (struct private_data *)self->data; - - ZSTD_freeDStream(state->dstream); - free(state->out_block); - free(state); + ZSTD_freeDStream(zstd->dstream); + free(zstd->out_block); + free(zstd); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 67c0305720..9c9f9a0eef 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -53,6 +53,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" @@ -67,16 +68,17 @@ #define SFX_MIN_ADDR 0x27000 #define SFX_MAX_ADDR 0x60000 #define SFX_MAX_OFFSET (SFX_MAX_ADDR - SFX_MIN_ADDR) +#define SFX_MAX_SEEK 0x800000 /* * PE format */ -#define PE_DOS_HDR_LEN 0x40 +#define PE_DOS_HDR_LEN 0x40 #define PE_DOS_HDR_ELFANEW_OFFSET 0x3c -#define PE_COFF_HDR_LEN 0x18 +#define PE_COFF_HDR_LEN 0x18 #define PE_COFF_HDR_SEC_CNT_OFFSET 0x6 #define PE_COFF_HDR_OPT_SZ_OFFSET 0x14 -#define PE_SEC_HDR_LEN 0x28 +#define PE_SEC_HDR_LEN 0x28 #define PE_SEC_HDR_RAW_ADDR_OFFSET 0x14 #define PE_SEC_HDR_RAW_SZ_OFFSET 0x10 @@ -97,8 +99,8 @@ #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 -#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_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 */ @@ -172,50 +174,48 @@ struct _7z_digests { }; struct _7z_folder { - uint64_t numCoders; + size_t numCoders; struct _7z_coder { - unsigned long codec; - uint64_t numInStreams; - uint64_t numOutStreams; - uint64_t propertiesSize; + int64_t codec; + size_t numInStreams; + size_t numOutStreams; + size_t propertiesSize; unsigned char *properties; } *coders; - uint64_t numBindPairs; + size_t numBindPairs; struct { - uint64_t inIndex; - uint64_t outIndex; + size_t inIndex; + size_t outIndex; } *bindPairs; - uint64_t numPackedStreams; - uint64_t *packedStreams; - uint64_t numInStreams; - uint64_t numOutStreams; - uint64_t *unPackSize; + size_t numPackedStreams; + size_t numInStreams; + size_t numOutStreams; + int64_t *unPackSize; unsigned char digest_defined; uint32_t digest; - uint64_t numUnpackStreams; - uint32_t packIndex; + size_t numUnpackStreams; + size_t packIndex; /* Unoperated bytes. */ - uint64_t skipped_bytes; + int64_t skipped_bytes; }; struct _7z_coders_info { - uint64_t numFolders; + size_t numFolders; struct _7z_folder *folders; - uint64_t dataStreamIndex; }; struct _7z_pack_info { - uint64_t pos; - uint64_t numPackStreams; - uint64_t *sizes; + int64_t pos; + size_t numPackStreams; + int64_t *sizes; struct _7z_digests digest; /* Calculated from pos and numPackStreams. */ - uint64_t *positions; + int64_t *positions; }; struct _7z_substream_info { size_t unpack_streams; - uint64_t *unpackSizes; + int64_t *unpackSizes; unsigned char *digestsDefined; uint32_t *digests; }; @@ -227,8 +227,6 @@ struct _7z_stream_info { }; struct _7z_header_info { - uint64_t dataIndex; - unsigned char *emptyStreamBools; unsigned char *emptyFileBools; unsigned char *antiBools; @@ -241,8 +239,8 @@ struct _7zip_entry { #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) const wchar_t *wname; #endif - uint32_t folderIndex; - uint32_t ssIndex; + size_t folderIndex; + size_t ssIndex; unsigned flg; #define MTIME_IS_SET (1<<0) #define ATIME_IS_SET (1<<1) @@ -266,24 +264,24 @@ struct _7zip { int header_is_being_read; int header_is_encoded; - uint64_t header_bytes_remaining; + int64_t header_bytes_remaining; unsigned long header_crc32; /* Header offset to check that reading points of the file contents * will not exceed the header. */ - uint64_t header_offset; + int64_t header_offset; /* Base offset of the archive file for a seek in case reading SFX. */ - uint64_t seek_base; + int64_t seek_base; /* List of entries */ size_t entries_remaining; - uint64_t numFiles; + size_t numFiles; struct _7zip_entry *entries; struct _7zip_entry *entry; unsigned char *entry_names; /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; - uint64_t entry_bytes_remaining; + int64_t entry_bytes_remaining; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; @@ -304,16 +302,16 @@ struct _7zip { /* * Decompressing control data. */ - unsigned folder_index; - uint64_t folder_outbytes_remaining; - unsigned pack_stream_index; - unsigned pack_stream_remaining; - uint64_t pack_stream_inbytes_remaining; - size_t pack_stream_bytes_unconsumed; + size_t folder_index; + int64_t folder_outbytes_remaining; + size_t pack_stream_index; + size_t pack_stream_remaining; + int64_t pack_stream_inbytes_remaining; + int64_t pack_stream_bytes_unconsumed; /* The codec information of a folder. */ - unsigned long codec; - unsigned long codec2; + int64_t codec; + int64_t codec2; /* * Decompressor controllers. @@ -334,7 +332,7 @@ struct _7zip { int stream_valid; #endif /* Decoding Zstandard data. */ -#if HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD ZSTD_DStream *zstd_dstream; int zstdstream_valid; #endif @@ -345,12 +343,10 @@ struct _7zip { IByteIn bytein; struct { const unsigned char *next_in; - int64_t avail_in; - int64_t total_in; - int64_t stream_in; + size_t avail_in; + size_t stream_in; unsigned char *next_out; - int64_t avail_out; - int64_t total_out; + size_t avail_out; int overconsumed; } ppstream; int ppmd7_valid; @@ -365,7 +361,7 @@ struct _7zip { uint32_t bcj_ip; /* Decoding BCJ2 data. */ - size_t main_stream_bytes_remaining; + int64_t main_stream_bytes_remaining; unsigned char *sub_stream_buff[3]; size_t sub_stream_size[3]; size_t sub_stream_bytes_remaining[3]; @@ -382,13 +378,11 @@ struct _7zip { uint8_t bcj2_prevByte; uint32_t bcj2_range; uint32_t bcj2_code; - uint64_t bcj2_outPos; + size_t bcj2_outPos; /* Filename character-set conversion data. */ 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; }; @@ -396,10 +390,14 @@ struct _7zip { /* 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) +#define UMAX_ENTRY ARCHIVE_LITERAL_LL(100000000) +/* Don't try to read more than 16 MB at a time */ +#define MAX_READ 16 * 1024 * 1024 + +static size_t align_size(size_t); 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_support_format_7zip_capabilities(struct archive_read *); 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 *, @@ -407,14 +405,15 @@ static int archive_read_format_7zip_read_data(struct archive_read *, static int archive_read_format_7zip_read_data_skip(struct archive_read *); static int archive_read_format_7zip_read_header(struct archive_read *, struct archive_entry *); -static int check_7zip_header_in_sfx(const char *); -static unsigned long decode_codec_id(const unsigned char *, size_t); +static size_t check_7zip_header_in_sfx(const unsigned char *); +static int decode_codec_id(const unsigned char *, size_t, int64_t *); static int decode_encoded_header_info(struct archive_read *, struct _7z_stream_info *); static int decompress(struct archive_read *, struct _7zip *, void *, size_t *, const void *, size_t *); static ssize_t extract_pack_stream(struct archive_read *, size_t); -static uint64_t folder_uncompressed_size(struct _7z_folder *); +static int files_info_numfiles_is_sane(const struct _7zip *); +static int64_t folder_uncompressed_size(struct _7z_folder *); static void free_CodersInfo(struct _7z_coders_info *); static void free_Digest(struct _7z_digests *); static void free_Folder(struct _7z_folder *); @@ -425,10 +424,11 @@ static void free_SubStreamsInfo(struct _7z_substream_info *); static int free_decompression(struct archive_read *, struct _7zip *); static ssize_t get_uncompressed_data(struct archive_read *, const void **, size_t, size_t); -static const unsigned char * header_bytes(struct archive_read *, size_t); +static const unsigned char *header_bytes(struct archive_read *, size_t); static int init_decompression(struct archive_read *, struct _7zip *, const struct _7z_coder *, const struct _7z_coder *); -static int parse_7zip_uint64(struct archive_read *, uint64_t *); +static int parse_7zip_size(struct archive_read *, size_t *); +static int parse_7zip_int64(struct archive_read *, int64_t *); static int read_Bools(struct archive_read *, unsigned char *, size_t); static int read_CodersInfo(struct archive_read *, struct _7z_coders_info *); @@ -442,16 +442,15 @@ static int read_StreamsInfo(struct archive_read *, struct _7z_stream_info *); static int read_SubStreamsInfo(struct archive_read *, struct _7z_substream_info *, struct _7z_folder *, size_t); -static int read_Times(struct archive_read *, struct _7z_header_info *, - int); -static void read_consume(struct archive_read *); +static int read_Times(struct archive_read *, int); +static int read_consume(struct archive_read *); static ssize_t read_stream(struct archive_read *, const void **, size_t, size_t); static int seek_pack(struct archive_read *); -static int64_t skip_stream(struct archive_read *, size_t); -static int skip_sfx(struct archive_read *, const ssize_t); -static ssize_t find_pe_overlay(struct archive_read *); -static ssize_t find_elf_data_sec(struct archive_read *); +static int skip_stream(struct archive_read *, int64_t); +static int get_data_offset(struct archive_read *, int64_t *, int); +static int get_pe_sfx_offset(struct archive_read *, int64_t *); +static int get_elf_sfx_offset(struct archive_read *, int64_t *, int); static int slurp_central_directory(struct archive_read *, struct _7zip *, struct _7z_header_info *); static int setup_decode_folder(struct archive_read *, struct _7z_folder *, @@ -461,9 +460,10 @@ static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); static void arm_Init(struct _7zip *); static size_t arm_Convert(struct _7zip *, uint8_t *, size_t); static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); -static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); +static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); static size_t sparc_Convert(struct _7zip *, uint8_t *, size_t); static size_t powerpc_Convert(struct _7zip *, uint8_t *, size_t); +static int64_t seek_compat(struct archive_read *, int64_t, int, int); int @@ -509,19 +509,18 @@ archive_read_support_format_7zip(struct archive *_a) } static int -archive_read_support_format_7zip_capabilities(struct archive_read * a) +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) +archive_read_format_7zip_has_encrypted_entries(struct archive_read *a) { - if (_a && _a->format) { - struct _7zip * zip = (struct _7zip *)_a->format->data; + if (a && a->format) { + struct _7zip *zip = a->format->data; if (zip) { return zip->has_encrypted_entries; } @@ -529,65 +528,93 @@ archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } +static int +get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) +{ + const unsigned char *p; + int64_t offset, sfx_offset; + int r, window; + + if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated 7-Zip file body"); + return (ARCHIVE_FATAL); + } + + /* If first six bytes are the 7-Zip signature, + * return the offset right now. */ + if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) { + *data_offset = 0; + return (ARCHIVE_OK); + } + + /* + * It may be a 7-Zip SFX archive file. If first two bytes are + * 'M' and 'Z' (PE, Windows) or first four bytes are + * "\x7F\x45LF" (ELF, Posix-like systems), seek the 7-Zip + * signature. While get_pe_sfx_offset can be performed without + * performing a seek, get_elf_sfx_offset requires one, + * thus a performance difference between the two is expected. + */ + if ((p[0] == 'M' && p[1] == 'Z')) + r = get_pe_sfx_offset(a, &sfx_offset); + else if (memcmp(p, "\x7F\x45LF", 4) == 0) + r = get_elf_sfx_offset(a, &sfx_offset, compat); + else + r = ARCHIVE_FATAL; + if (r < ARCHIVE_WARN || sfx_offset > SFX_MAX_SEEK) + goto fail; + + offset = sfx_offset; + window = 4096; + while (offset + window <= (sfx_offset + SFX_MAX_OFFSET)) { + ssize_t bytes_avail; + const unsigned char *buff = __archive_read_ahead(a, + offset + window, &bytes_avail); + if (buff == NULL) { + /* Remaining bytes are less than window. */ + window >>= 1; + if (window < 0x40) + goto fail; + continue; + } + p = buff + offset; + while (buff + bytes_avail - p >= 32) { + size_t step = check_7zip_header_in_sfx(p); + if (step == 0) { + *data_offset = p - buff; + return (ARCHIVE_OK); + } + p += step; + } + offset = p - buff; + } +fail: + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Couldn't find out 7-Zip header"); + return (ARCHIVE_FATAL); +} + static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { - const char *p; + int64_t data_offset; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); - if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) + if (get_data_offset(a, &data_offset, 0) < 0) return (0); - /* If first six bytes are the 7-Zip signature, - * return the bid right now. */ - if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) - return (48); - - /* - * It may a 7-Zip SFX archive file. If first two bytes are - * 'M' and 'Z' available on Windows or first four bytes are - * "\x7F\x45LF" available on posix like system, seek the 7-Zip - * signature. While find_pe_overlay can be performed without - * performing a seek, find_elf_data_sec requires one, - * thus a performance difference between the two is expected. - */ - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { - const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) : - find_elf_data_sec(a); - ssize_t offset = min_addr; - ssize_t window = 4096; - ssize_t bytes_avail; - while (offset + window <= (min_addr + SFX_MAX_OFFSET)) { - const char *buff = __archive_read_ahead(a, - offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - return (0); - continue; - } - p = buff + offset; - while (p + 32 < buff + bytes_avail) { - int step = check_7zip_header_in_sfx(p); - if (step == 0) - return (48); - p += step; - } - offset = p - buff; - } - } - return (0); + return (48); } -static int -check_7zip_header_in_sfx(const char *p) +static size_t +check_7zip_header_in_sfx(const unsigned char *p) { - switch ((unsigned char)p[5]) { + switch (p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) return (6); @@ -596,8 +623,7 @@ check_7zip_header_in_sfx(const char *p) * Magic Code, so we should do this in order not to * make a mis-detection. */ - if (crc32(0, (const unsigned char *)p + 12, 20) - != archive_le32dec(p + 8)) + if (crc32(0, p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); @@ -611,79 +637,33 @@ check_7zip_header_in_sfx(const char *p) } static int -skip_sfx(struct archive_read *a, const ssize_t min_addr) -{ - const void *h; - const char *p, *q; - size_t skip, offset; - ssize_t bytes, window; - - if (__archive_read_seek(a, min_addr, SEEK_SET) < 0) - return (ARCHIVE_FATAL); - - offset = 0; - window = 1; - while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) { - h = __archive_read_ahead(a, window, &bytes); - if (h == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - goto fatal; - continue; - } - if (bytes < 6) { - /* This case might happen when window == 1. */ - window = 4096; - continue; - } - p = (const char *)h; - q = p + bytes; - - /* - * Scan ahead until we find something that looks - * like the 7-Zip header. - */ - while (p + 32 < q) { - int step = check_7zip_header_in_sfx(p); - if (step == 0) { - struct _7zip *zip = - (struct _7zip *)a->format->data; - skip = p - (const char *)h; - __archive_read_consume(a, skip); - zip->seek_base = min_addr + offset + skip; - return (ARCHIVE_OK); - } - p += step; - } - skip = p - (const char *)h; - __archive_read_consume(a, skip); - offset += skip; - if (window == 1) - window = 4096; - } -fatal: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Couldn't find out 7-Zip header"); - return (ARCHIVE_FATAL); -} - -static ssize_t -find_pe_overlay(struct archive_read *a) +get_pe_sfx_offset(struct archive_read *a, int64_t *sfx_offset) { const char *h; - ssize_t bytes, max_offset, offset, sec_end; - ssize_t opt_hdr_sz, sec_cnt; + int64_t max_offset, offset; + ssize_t bytes; + uint16_t opt_hdr_sz, sec_cnt; + + /* + * If encounter any weirdness, revert to old brute-force style search + */ + *sfx_offset = SFX_MIN_ADDR; for (;;) { /* * Read Dos header to find e_lfanew */ h = __archive_read_ahead(a, PE_DOS_HDR_LEN, &bytes); - if (h == NULL || h[0] != 'M' || h[1] != 'Z') { + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (h[0] != 'M' || h[1] != 'Z') { break; } offset = archive_le32dec(h + PE_DOS_HDR_ELFANEW_OFFSET); + if (offset > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } /* * Read COFF header to find opt header size and sec cnt @@ -691,8 +671,10 @@ find_pe_overlay(struct archive_read *a) if (bytes < offset + PE_COFF_HDR_LEN) { h = __archive_read_ahead(a, offset + PE_COFF_HDR_LEN, &bytes); - if (h == NULL || h[offset] != 'P' || - h[offset + 1] != 'E') { + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (h[offset] != 'P' || h[offset + 1] != 'E') { break; } } @@ -710,6 +692,10 @@ find_pe_overlay(struct archive_read *a) break; } + if (offset + sec_cnt * PE_SEC_HDR_LEN > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } + /* * Traverse sec table to find max raw offset (i.e., overlay) */ @@ -717,12 +703,14 @@ find_pe_overlay(struct archive_read *a) h = __archive_read_ahead(a, offset + sec_cnt * PE_SEC_HDR_LEN, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } } max_offset = offset; while (sec_cnt > 0) { - sec_end = archive_le32dec( + int64_t sec_end; + + sec_end = (int64_t)archive_le32dec( h + offset + PE_SEC_HDR_RAW_SZ_OFFSET) + archive_le32dec( h + offset + PE_SEC_HDR_RAW_ADDR_OFFSET); @@ -732,34 +720,40 @@ find_pe_overlay(struct archive_read *a) offset += PE_SEC_HDR_LEN; sec_cnt--; } - return (max_offset); + *sfx_offset = max_offset; + break; } - /* - * If encounter any weirdness, revert to old brute-force style search - */ - return (SFX_MIN_ADDR); + return (ARCHIVE_OK); } -static ssize_t -find_elf_data_sec(struct archive_read *a) +static int +get_elf_sfx_offset(struct archive_read *a, int64_t *sfx_offset, int compat) { + int64_t r; const char *h; char big_endian, format_64; - ssize_t bytes, min_addr = SFX_MIN_ADDR; - ssize_t request; + size_t request; uint64_t e_shoff, strtab_offset, strtab_size; uint16_t e_shentsize, e_shnum, e_shstrndx; uint16_t (*dec16)(const void *); uint32_t (*dec32)(const void *); uint64_t (*dec64)(const void *); + /* + * If encounter any weirdness, revert to old brute-force style search + */ + *sfx_offset = SFX_MIN_ADDR; + for (;;) { /* * Read Elf header to find bitness & endianness */ - h = __archive_read_ahead(a, ELF_HDR_MIN_LEN, &bytes); - if (h == NULL || memcmp(h, "\x7F\x45LF", 4) != 0) { + h = __archive_read_ahead(a, ELF_HDR_MIN_LEN, NULL); + if (h == NULL) { + return (ARCHIVE_FATAL); + } + if (memcmp(h, "\x7F\x45LF", 4) != 0) { break; } format_64 = h[ELF_HDR_EI_CLASS_OFFSET] == 0x2; @@ -782,7 +776,7 @@ find_elf_data_sec(struct archive_read *a) e_shentsize = (*dec16)(h + 0x3A); e_shnum = (*dec16)(h + 0x3C); e_shstrndx = (*dec16)(h + 0x3E); - if (e_shnum < e_shstrndx || e_shentsize < 0x28) + if (e_shnum <= e_shstrndx || e_shentsize < 0x28) break; } else { @@ -790,24 +784,32 @@ find_elf_data_sec(struct archive_read *a) e_shentsize = (*dec16)(h + 0x2E); e_shnum = (*dec16)(h + 0x30); e_shstrndx = (*dec16)(h + 0x32); - if (e_shnum < e_shstrndx || e_shentsize < 0x18) + if (e_shnum <= e_shstrndx || e_shentsize < 0x18) break; } + if ((int64_t)e_shoff < 0) { + return (ARCHIVE_FATAL); + } + /* * Reading the section table to find strtab section */ - if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) { - break; + if (seek_compat(a, e_shoff, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } if (format_64) { - request = (size_t)e_shnum * (size_t)e_shentsize + 0x28; + request = (size_t)e_shnum * e_shentsize + 0x28; } else { - request = (size_t)e_shnum * (size_t)e_shentsize + 0x18; + request = (size_t)e_shnum * e_shentsize + 0x18; } - h = __archive_read_ahead(a, request, &bytes); + if (request > SFX_MAX_SEEK) { + return (ARCHIVE_FATAL); + } + h = __archive_read_ahead(a, request, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } if (format_64) { strtab_offset = (*dec64)( @@ -820,45 +822,50 @@ find_elf_data_sec(struct archive_read *a) strtab_size = (*dec32)( h + e_shstrndx * e_shentsize + 0x14); } - if (strtab_size < 6 || strtab_size > SIZE_MAX) - break; + if ((int64_t)strtab_offset < 0 || strtab_size < 6 || + strtab_size > SFX_MAX_SEEK) + return (ARCHIVE_FATAL); /* * Read the STRTAB section to find the .data offset */ - if (__archive_read_seek(a, strtab_offset, SEEK_SET) < 0) { - break; + if (seek_compat(a, strtab_offset, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } h = __archive_read_ahead(a, strtab_size, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } - ssize_t data_sym_offset = -1; - for (size_t offset = 0; offset < strtab_size - 6; offset++) { + size_t data_sym_offset = strtab_size; + for (size_t offset = 0; offset + 6 <= strtab_size; offset++) { if (memcmp(h + offset, ".data\00", 6) == 0) { data_sym_offset = offset; break; } } - if (data_sym_offset == -1) { + if (data_sym_offset == strtab_size) { break; } /* * Find the section with the .data name */ - if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) { - break; + if (seek_compat(a, e_shoff, SEEK_SET, compat) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); } - h = __archive_read_ahead(a, (size_t)e_shnum * (size_t)e_shentsize, NULL); + h = __archive_read_ahead(a, (size_t)e_shnum * e_shentsize, NULL); if (h == NULL) { - break; + return (ARCHIVE_FATAL); } - ssize_t sec_tbl_offset = 0, name_offset; + size_t sec_tbl_offset = 0; while (e_shnum > 0) { + uint32_t name_offset; + name_offset = (*dec32)(h + sec_tbl_offset); if (name_offset == data_sym_offset) { - uint64_t sel_offset; + int64_t sel_offset; if (format_64) { sel_offset = (*dec64)( @@ -867,9 +874,8 @@ find_elf_data_sec(struct archive_read *a) sel_offset = (*dec32)( h + sec_tbl_offset + 0x10); } - if (sel_offset > SSIZE_MAX) - break; - min_addr = (ssize_t)sel_offset; + if (sel_offset >= 0) + *sfx_offset = sel_offset; break; } sec_tbl_offset += e_shentsize; @@ -878,19 +884,20 @@ find_elf_data_sec(struct archive_read *a) break; } - __archive_read_seek(a, 0, SEEK_SET); - return (min_addr); + r = seek_compat(a, 0, SEEK_SET, compat); + if (r < 0) + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (int)r; } static int archive_read_format_7zip_read_header(struct archive_read *a, struct archive_entry *entry) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = 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 @@ -904,8 +911,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, } a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; - if (a->archive.archive_format_name == NULL) - a->archive.archive_format_name = "7-Zip"; + a->archive.archive_format_name = "7-Zip"; if (zip->entries == NULL) { struct _7z_header_info header; @@ -915,20 +921,20 @@ archive_read_format_7zip_read_header(struct archive_read *a, free_Header(&header); if (r != ARCHIVE_OK) return (r); - zip->entries_remaining = (size_t)zip->numFiles; + zip->entries_remaining = zip->numFiles; zip->entry = zip->entries; } else { ++zip->entry; } zip_entry = zip->entry; - if (zip->entries_remaining <= 0 || zip_entry == NULL) + if (zip->entries_remaining == 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; zip->entry_offset = 0; zip->end_of_entry = 0; - zip->entry_crc32 = crc32(0, NULL, 0); + zip->entry_crc32 = 0; /* Setup a string conversion for a filename. */ if (zip->sconv == NULL) { @@ -942,9 +948,11 @@ archive_read_format_7zip_read_header(struct archive_read *a, 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) { + if (zip_entry->folderIndex < zip->si.ci.numFolders) { + size_t fidx = 0; + folder = &(zip->si.ci.folders[zip_entry->folderIndex]); - for (fidx=0; folder && fidxnumCoders; fidx++) { + for (fidx = 0; fidx < folder->numCoders; fidx++) { switch(folder->coders[fidx].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: @@ -957,13 +965,6 @@ archive_read_format_7zip_read_header(struct archive_read *a, } } - /* 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) { @@ -975,7 +976,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name(zip->sconv)); ret = ARCHIVE_WARN; } @@ -991,7 +992,7 @@ archive_read_format_7zip_read_header(struct archive_read *a, if (zip_entry->flg & ATIME_IS_SET) archive_entry_set_atime(entry, zip_entry->atime, zip_entry->atime_ns); - if (zip_entry->ssIndex != (uint32_t)-1) { + if (zip_entry->ssIndex != (size_t)-1) { zip->entry_bytes_remaining = zip->si.ss.unpackSizes[zip_entry->ssIndex]; archive_entry_set_size(entry, zip->entry_bytes_remaining); @@ -1004,29 +1005,19 @@ archive_read_format_7zip_read_header(struct archive_read *a, const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; if (zip_entry->attr & supported_attrs) { - char *fflags_text, *ptr; - /* allocate for ",rdonly,hidden,system" */ - fflags_text = malloc(22 * sizeof(*fflags_text)); - if (fflags_text != NULL) { - ptr = fflags_text; - if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) { - strcpy(ptr, ",rdonly"); - ptr = ptr + 7; - } - if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) { - strcpy(ptr, ",hidden"); - ptr = ptr + 7; - } - if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) { - strcpy(ptr, ",system"); - ptr = ptr + 7; - } - if (ptr > fflags_text) { - archive_entry_copy_fflags_text(entry, - fflags_text + 1); - } - free(fflags_text); - } + char buf[sizeof(",rdonly,hidden,system")]; + const char *fflags[3] = { "", "", "" }; + const char **flag = fflags; + + if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) + *flag++ = ",rdonly"; + if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) + *flag++ = ",hidden"; + if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) + *flag++ = ",system"; + + snprintf(buf, sizeof(buf), "%s%s%s", fflags[0], fflags[1], fflags[2]); + archive_entry_copy_fflags_text(entry, buf + 1); } /* If there's no body, force read_data() to return EOF immediately. */ @@ -1037,6 +1028,13 @@ archive_read_format_7zip_read_header(struct archive_read *a, unsigned char *symname = NULL; size_t symsize = 0; + if (zip->entry_bytes_remaining > 1024 * 1024) { + archive_set_error(&a->archive, ENOMEM, + "Rejecting malformed 7zip archive: " + "symlink contents exceed 1 megabyte"); + return (ARCHIVE_FATAL); + } + /* * Symbolic-name is recorded as its contents. We have to * read the contents at this time. @@ -1090,10 +1088,6 @@ archive_read_format_7zip_read_header(struct archive_read *a, archive_entry_set_size(entry, 0); } - /* Set up a more descriptive format name. */ - snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip"); - a->archive.archive_format_name = zip->format_name; - return (ret); } @@ -1101,12 +1095,10 @@ static int archive_read_format_7zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct _7zip *zip; + struct _7zip *zip = a->format->data; ssize_t bytes; int ret = ARCHIVE_OK; - zip = (struct _7zip *)(a->format->data); - if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } @@ -1124,8 +1116,8 @@ archive_read_format_7zip_read_data(struct archive_read *a, if (zip->end_of_entry) return (ARCHIVE_EOF); - size_t bytes_to_read = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time - if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) { + size_t bytes_to_read = MAX_READ; + if ((int64_t)bytes_to_read > zip->entry_bytes_remaining) { bytes_to_read = (size_t)zip->entry_bytes_remaining; } bytes = read_stream(a, buff, bytes_to_read, 0); @@ -1154,7 +1146,7 @@ archive_read_format_7zip_read_data(struct archive_read *a, zip->entry_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "7-Zip bad CRC: 0x%lx should be 0x%lx", - (unsigned long)zip->entry_crc32, + zip->entry_crc32, (unsigned long)zip->si.ss.digests[ zip->entry->ssIndex]); ret = ARCHIVE_WARN; @@ -1171,10 +1163,8 @@ archive_read_format_7zip_read_data(struct archive_read *a, static int archive_read_format_7zip_read_data_skip(struct archive_read *a) { - struct _7zip *zip; - int64_t bytes_skipped; - - zip = (struct _7zip *)(a->format->data); + struct _7zip *zip = a->format->data; + int r; if (zip->pack_stream_bytes_unconsumed) read_consume(a); @@ -1187,9 +1177,9 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a) * If the length is at the beginning, we can skip the * compressed data much more quickly. */ - bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining); - if (bytes_skipped < 0) - return (ARCHIVE_FATAL); + r = skip_stream(a, zip->entry_bytes_remaining); + if (r < 0) + return (r); zip->entry_bytes_remaining = 0; /* This entry is finished and done. */ @@ -1200,9 +1190,8 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a) static int archive_read_format_7zip_cleanup(struct archive_read *a) { - struct _7zip *zip; + struct _7zip *zip = a->format->data; - zip = (struct _7zip *)(a->format->data); free_StreamsInfo(&(zip->si)); free(zip->entries); free(zip->entry_names); @@ -1213,24 +1202,30 @@ archive_read_format_7zip_cleanup(struct archive_read *a) free(zip->sub_stream_buff[2]); free(zip->tmp_stream_buff); free(zip); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } -static void +static int read_consume(struct archive_read *a) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; if (zip->pack_stream_bytes_unconsumed) { - __archive_read_consume(a, zip->pack_stream_bytes_unconsumed); + int64_t r; + + if ((r = __archive_read_consume(a, + zip->pack_stream_bytes_unconsumed)) < 0) + return ((int)r); + zip->stream_offset += zip->pack_stream_bytes_unconsumed; zip->pack_stream_bytes_unconsumed = 0; } + + return (ARCHIVE_OK); } #ifdef HAVE_LZMA_H - /* * Set an error code and choose an error message for liblzma. */ @@ -1278,52 +1273,49 @@ set_error(struct archive_read *a, int ret) break; } } - #endif -static unsigned long -decode_codec_id(const unsigned char *codecId, size_t id_size) +static int +decode_codec_id(const unsigned char *codecId, size_t id_size, int64_t *id) { - unsigned i; - unsigned long id = 0; + size_t i; + *id = 0; for (i = 0; i < id_size; i++) { - id <<= 8; - id += codecId[i]; + if (archive_ckd_mul_i64(id, *id, 256) || + archive_ckd_add_i64(id, *id, codecId[i])) + return (-1); } - return (id); + return (0); } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; - struct _7zip *zip = (struct _7zip *)(a->format->data); + struct _7zip *zip = a->format->data; Byte b; - if (zip->ppstream.avail_in <= 0) { + if (zip->ppstream.avail_in == 0) { /* * Ppmd7_DecodeSymbol might require reading multiple bytes * and we are on boundary; * last resort to read using __archive_read_ahead. */ - ssize_t bytes_avail = 0; - const uint8_t* data = __archive_read_ahead(a, - (size_t)zip->ppstream.stream_in+1, &bytes_avail); - if(data == NULL || bytes_avail < zip->ppstream.stream_in+1) { + const uint8_t *data = __archive_read_ahead(a, + zip->ppstream.stream_in + 1, NULL); + if (data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7z file data"); zip->ppstream.overconsumed = 1; return (0); } - zip->ppstream.next_in++; b = data[zip->ppstream.stream_in]; } else { b = *zip->ppstream.next_in++; + zip->ppstream.avail_in--; } - zip->ppstream.avail_in--; - zip->ppstream.total_in++; zip->ppstream.stream_in++; return (b); } @@ -1352,8 +1344,9 @@ init_decompression(struct archive_read *a, struct _7zip *zip, coder2->codec != _7Z_SPARC) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unsupported filter %lx for %lx", - coder2->codec, coder1->codec); + "Unsupported filter %jx for %jx", + (uintmax_t)coder2->codec, + (uintmax_t)coder1->codec); return (ARCHIVE_FAILED); } zip->codec2 = coder2->codec; @@ -1438,7 +1431,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, memset(&delta_opt, 0, sizeof(delta_opt)); delta_opt.type = LZMA_DELTA_TYPE_BYTE; delta_opt.dist = - (uint32_t)coder2->properties[0] + 1; + coder2->properties[0] + 1; filters[fi].options = &delta_opt; fi++; break; @@ -1478,7 +1471,8 @@ init_decompression(struct archive_read *a, struct _7zip *zip, default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unexpected codec ID: %lX", zip->codec2); + "Unexpected codec ID: %jX", + (uintmax_t)zip->codec2); return (ARCHIVE_FAILED); } } @@ -1490,7 +1484,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, filters[fi].options = NULL; ff = &filters[fi]; r = lzma_properties_decode(&filters[fi], NULL, - coder1->properties, (size_t)coder1->propertiesSize); + coder1->properties, coder1->propertiesSize); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); @@ -1558,7 +1552,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, #endif case _7Z_ZSTD: { -#if defined(HAVE_ZSTD_H) +#if HAVE_ZSTD_H && HAVE_LIBZSTD if (zip->zstdstream_valid) { ZSTD_freeDStream(zip->zstd_dstream); zip->zstdstream_valid = 0; @@ -1581,7 +1575,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Couldn't initialize zlib stream."); + "Couldn't initialize zlib stream"); return (ARCHIVE_FAILED); } zip->stream_valid = 1; @@ -1632,8 +1626,6 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->ppmd7_valid = 1; zip->ppmd7_stat = 0; zip->ppstream.overconsumed = 0; - zip->ppstream.total_in = 0; - zip->ppstream.total_out = 0; break; } case _7Z_X86: @@ -1647,7 +1639,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, case _7Z_SPARC: case _7Z_DELTA: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unexpected codec ID: %lX", zip->codec); + "Unexpected codec ID: %jX", (uintmax_t)zip->codec); return (ARCHIVE_FAILED); case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: @@ -1658,11 +1650,12 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->has_encrypted_entries = 1; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); + "Crypto codec not supported yet (ID: 0x%jX)", + (uintmax_t)zip->codec); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unknown codec ID: %lX", zip->codec); + "Unknown codec ID: %jX", (uintmax_t)zip->codec); return (ARCHIVE_FAILED); } @@ -1714,7 +1707,7 @@ decompress(struct archive_read *a, struct _7zip *zip, bcj2_avail_out = t_avail_out; if (zip->codec2 == _7Z_X86_BCJ2) { /* - * Decord a remaining decompressed main stream for BCJ2. + * Decode a remaining decompressed main stream for BCJ2. */ if (zip->tmp_stream_bytes_remaining) { ssize_t bytes; @@ -1723,7 +1716,7 @@ decompress(struct archive_read *a, struct _7zip *zip, if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "BCJ2 conversion Failed"); + "BCJ2 conversion failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= @@ -1748,8 +1741,8 @@ decompress(struct archive_read *a, struct _7zip *zip, switch (zip->codec) { case _7Z_COPY: { - size_t bytes = - (t_avail_in > t_avail_out)?t_avail_out:t_avail_in; + size_t bytes = (t_avail_in > t_avail_out) ? + t_avail_out : t_avail_in; memcpy(t_next_out, t_next_in, bytes); t_avail_in -= bytes; @@ -1777,7 +1770,7 @@ decompress(struct archive_read *a, struct _7zip *zip, default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "Decompression failed(%d)", + "Decompression failed (%d)", r); return (ARCHIVE_FAILED); } @@ -1787,10 +1780,14 @@ decompress(struct archive_read *a, struct _7zip *zip, #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: + if (t_avail_in > UINT_MAX) + t_avail_in = UINT_MAX; + if (t_avail_out > UINT_MAX) + t_avail_out = UINT_MAX; zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; - zip->bzstream.avail_in = (uint32_t)t_avail_in; + zip->bzstream.avail_in = (unsigned int)t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; - zip->bzstream.avail_out = (uint32_t)t_avail_out; + zip->bzstream.avail_out = (unsigned int)t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ @@ -1820,6 +1817,10 @@ decompress(struct archive_read *a, struct _7zip *zip, #endif #ifdef HAVE_ZLIB_H case _7Z_DEFLATE: + if (t_avail_in > UINT_MAX) + t_avail_in = UINT_MAX; + if (t_avail_out > UINT_MAX) + t_avail_out = UINT_MAX; zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in; zip->stream.avail_in = (uInt)t_avail_in; zip->stream.next_out = t_next_out; @@ -1840,15 +1841,20 @@ decompress(struct archive_read *a, struct _7zip *zip, t_avail_out = zip->stream.avail_out; break; #endif -#ifdef HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD case _7Z_ZSTD: { - ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos - ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos + /* src, size, pos */ + ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; + /* dst, size, pos */ + ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; - size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input); + size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, + &output, &input); if (ZSTD_isError(zret)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret)); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Zstd decompression failed: %s", + ZSTD_getErrorName(zret)); return ARCHIVE_FAILED; } t_avail_in -= input.pos; @@ -1858,7 +1864,7 @@ decompress(struct archive_read *a, struct _7zip *zip, #endif case _7Z_PPMD: { - uint64_t flush_bytes; + int64_t flush_bytes; if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 || t_avail_out <= 0) { @@ -1916,14 +1922,13 @@ decompress(struct archive_read *a, struct _7zip *zip, } *zip->ppstream.next_out++ = (unsigned char)sym; zip->ppstream.avail_out--; - zip->ppstream.total_out++; - if (flush_bytes) + if (flush_bytes > 0) flush_bytes--; } while (zip->ppstream.avail_out && (zip->ppstream.avail_in || flush_bytes)); - t_avail_in = (size_t)zip->ppstream.avail_in; - t_avail_out = (size_t)zip->ppstream.avail_out; + t_avail_in = zip->ppstream.avail_in; + t_avail_out = zip->ppstream.avail_out; break; } default: @@ -1938,7 +1943,7 @@ decompress(struct archive_read *a, struct _7zip *zip, *outbytes = o_avail_out - t_avail_out; /* - * Decord BCJ. + * Decode BCJ. */ if (zip->codec != _7Z_LZMA2) { if (zip->codec2 == _7Z_X86) { @@ -1946,9 +1951,10 @@ decompress(struct archive_read *a, struct _7zip *zip, zip->odd_bcj_size = *outbytes - l; if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && - o_avail_in && ret != ARCHIVE_EOF) { - memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, - zip->odd_bcj_size); + o_avail_in && ret != ARCHIVE_EOF) { + memcpy(zip->odd_bcj, + ((unsigned char *)buff) + l, + zip->odd_bcj_size); *outbytes = l; } else zip->odd_bcj_size = 0; @@ -1964,7 +1970,7 @@ decompress(struct archive_read *a, struct _7zip *zip, } /* - * Decord BCJ2 with a decompressed main stream. + * Decode BCJ2 with a decompressed main stream. */ if (zip->codec2 == _7Z_X86_BCJ2) { ssize_t bytes; @@ -1972,14 +1978,14 @@ decompress(struct archive_read *a, struct _7zip *zip, zip->tmp_stream_bytes_avail = zip->tmp_stream_buff_size - t_avail_out; if (zip->tmp_stream_bytes_avail > - zip->main_stream_bytes_remaining) + (uint64_t)zip->main_stream_bytes_remaining) zip->tmp_stream_bytes_avail = zip->main_stream_bytes_remaining; zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail; bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), - ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); + ARCHIVE_ERRNO_MISC, "BCJ2 conversion failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= @@ -2027,7 +2033,7 @@ free_decompression(struct archive_read *a, struct _7zip *zip) zip->stream_valid = 0; } #endif -#ifdef HAVE_ZSTD_H +#if HAVE_ZSTD_H && HAVE_LIBZSTD if (zip->zstdstream_valid) ZSTD_freeDStream(zip->zstd_dstream); #endif @@ -2040,8 +2046,21 @@ free_decompression(struct archive_read *a, struct _7zip *zip) } static int -parse_7zip_uint64(struct archive_read *a, uint64_t *val) +parse_7zip_size(struct archive_read *a, size_t *val) { + int64_t v; + + if (parse_7zip_int64(a, &v) < 0 || v > UMAX_ENTRY) + return (-1); + + *val = (size_t)v; + return (0); +} + +static int +parse_7zip_int64(struct archive_read *a, int64_t *val) +{ + uint64_t v; const unsigned char *p; unsigned char avail, mask; int i; @@ -2050,18 +2069,21 @@ parse_7zip_uint64(struct archive_read *a, uint64_t *val) return (-1); avail = *p; mask = 0x80; - *val = 0; + v = 0; for (i = 0; i < 8; i++) { if (avail & mask) { if ((p = header_bytes(a, 1)) == NULL) return (-1); - *val |= ((uint64_t)*p) << (8 * i); + v |= ((uint64_t)*p) << (8 * i); mask >>= 1; continue; } - *val += ((uint64_t)(avail & (mask -1))) << (8 * i); + v += ((uint64_t)(avail & (mask - 1))) << (8 * i); break; } + if (v > (uint64_t)INT64_MAX) + return (-1); + *val = (int64_t)v; return (0); } @@ -2069,7 +2091,8 @@ static int read_Bools(struct archive_read *a, unsigned char *data, size_t num) { const unsigned char *p; - unsigned i, mask = 0, avail = 0; + size_t i; + unsigned mask = 0, avail = 0; for (i = 0; i < num; i++) { if (mask == 0) { @@ -2078,7 +2101,7 @@ read_Bools(struct archive_read *a, unsigned char *data, size_t num) avail = *p; mask = 0x80; } - data[i] = (avail & mask)?1:0; + data[i] = (avail & mask) ? 1 : 0; mask >>= 1; } return (0); @@ -2095,7 +2118,7 @@ static int read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num) { const unsigned char *p; - unsigned i; + size_t i; if (num == 0) return (-1); @@ -2142,25 +2165,23 @@ static int read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) { const unsigned char *p; - unsigned i; + size_t i; memset(pi, 0, sizeof(*pi)); /* * Read PackPos. */ - if (parse_7zip_uint64(a, &(pi->pos)) < 0) + if (parse_7zip_int64(a, &(pi->pos)) < 0) return (-1); /* * Read NumPackStreams. */ - if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0) + if (parse_7zip_size(a, &(pi->numPackStreams)) < 0) return (-1); if (pi->numPackStreams == 0) return (-1); - if (UMAX_ENTRY < pi->numPackStreams) - return (-1); /* * Read PackSizes[num] @@ -2172,13 +2193,13 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) return (0); if (*p != kSize) return (-1); - pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); - pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); + pi->sizes = calloc(pi->numPackStreams, sizeof(int64_t)); + pi->positions = calloc(pi->numPackStreams, sizeof(int64_t)); if (pi->sizes == NULL || pi->positions == NULL) return (-1); for (i = 0; i < pi->numPackStreams; i++) { - if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0) + if (parse_7zip_int64(a, &(pi->sizes[i])) < 0) return (-1); } @@ -2190,9 +2211,9 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) if (*p == kEnd) { /* PackStreamDigests[num] are not present. */ pi->digest.defineds = - calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds)); + calloc(pi->numPackStreams, sizeof(*pi->digest.defineds)); pi->digest.digests = - calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests)); + calloc(pi->numPackStreams, sizeof(*pi->digest.digests)); if (pi->digest.defineds == NULL || pi->digest.digests == NULL) return (-1); return (0); @@ -2201,7 +2222,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) if (*p != kCRC) return (-1); - if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) + if (read_Digests(a, &(pi->digest), pi->numPackStreams) < 0) return (-1); /* @@ -2217,40 +2238,39 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) static void free_Folder(struct _7z_folder *f) { - unsigned i; - if (f->coders) { + size_t i; + for (i = 0; i< f->numCoders; i++) { free(f->coders[i].properties); } free(f->coders); } free(f->bindPairs); - free(f->packedStreams); free(f->unPackSize); } static int read_Folder(struct archive_read *a, struct _7z_folder *f) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const unsigned char *p; - uint64_t numInStreamsTotal = 0; - uint64_t numOutStreamsTotal = 0; - unsigned i; + size_t numInStreamsTotal = 0; + size_t numOutStreamsTotal = 0; + size_t i; memset(f, 0, sizeof(*f)); /* * Read NumCoders. */ - if (parse_7zip_uint64(a, &(f->numCoders)) < 0) + if (parse_7zip_size(a, &(f->numCoders)) < 0) return (-1); if (f->numCoders > 4) /* Too many coders. */ return (-1); - f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders)); + f->coders = calloc(f->numCoders, sizeof(*f->coders)); if (f->coders == NULL) return (-1); for (i = 0; i< f->numCoders; i++) { @@ -2268,7 +2288,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) * 7: Must be zero. */ codec_size = *p & 0xf; - simple = (*p & 0x10)?0:1; + simple = (*p & 0x10) ? 0 : 1; attr = *p & 0x20; if (*p & 0x80) return (-1);/* Not supported. */ @@ -2279,43 +2299,41 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) if ((p = header_bytes(a, codec_size)) == NULL) return (-1); - f->coders[i].codec = decode_codec_id(p, codec_size); + if (decode_codec_id(p, codec_size, &f->coders[i].codec) < 0) + return (-1); if (simple) { f->coders[i].numInStreams = 1; f->coders[i].numOutStreams = 1; } else { - if (parse_7zip_uint64( + if (parse_7zip_size( a, &(f->coders[i].numInStreams)) < 0) return (-1); - if (UMAX_ENTRY < f->coders[i].numInStreams) - return (-1); - if (parse_7zip_uint64( + if (parse_7zip_size( a, &(f->coders[i].numOutStreams)) < 0) return (-1); - if (UMAX_ENTRY < f->coders[i].numOutStreams) - return (-1); } if (attr) { - if (parse_7zip_uint64( + if (parse_7zip_size( a, &(f->coders[i].propertiesSize)) < 0) return (-1); - if (UMAX_ENTRY < f->coders[i].propertiesSize) - return (-1); if ((p = header_bytes( - a, (size_t)f->coders[i].propertiesSize)) == NULL) + a, f->coders[i].propertiesSize)) == NULL) return (-1); f->coders[i].properties = - malloc((size_t)f->coders[i].propertiesSize); + malloc(f->coders[i].propertiesSize); if (f->coders[i].properties == NULL) return (-1); memcpy(f->coders[i].properties, p, - (size_t)f->coders[i].propertiesSize); + f->coders[i].propertiesSize); } - numInStreamsTotal += f->coders[i].numInStreams; - numOutStreamsTotal += f->coders[i].numOutStreams; + if (archive_ckd_add_size(&numInStreamsTotal, + numInStreamsTotal, f->coders[i].numInStreams) || + archive_ckd_add_size(&numOutStreamsTotal, + numOutStreamsTotal, f->coders[i].numOutStreams)) + return (-1); } if (numOutStreamsTotal == 0 || @@ -2323,34 +2341,27 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) return (-1); f->numBindPairs = numOutStreamsTotal - 1; - if (zip->header_bytes_remaining < f->numBindPairs) + if ((uint64_t)zip->header_bytes_remaining < f->numBindPairs) return (-1); if (f->numBindPairs > 0) { f->bindPairs = - calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs)); + calloc(f->numBindPairs, sizeof(*f->bindPairs)); if (f->bindPairs == NULL) return (-1); } else f->bindPairs = NULL; for (i = 0; i < f->numBindPairs; i++) { - if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) + if (parse_7zip_size(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); - if (UMAX_ENTRY < f->bindPairs[i].inIndex) - return (-1); - if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) - return (-1); - if (UMAX_ENTRY < f->bindPairs[i].outIndex) + if (parse_7zip_size(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); } f->numPackedStreams = numInStreamsTotal - f->numBindPairs; - f->packedStreams = - calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams)); - if (f->packedStreams == NULL) - return (-1); + /* packedStreams are not needed; parse/verify nonetheless */ if (f->numPackedStreams == 1) { for (i = 0; i < numInStreamsTotal; i++) { - unsigned j; + size_t j; for (j = 0; j < f->numBindPairs; j++) { if (f->bindPairs[j].inIndex == i) break; @@ -2360,12 +2371,10 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) } if (i == numInStreamsTotal) return (-1); - f->packedStreams[0] = i; } else { for (i = 0; i < f->numPackedStreams; i++) { - if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) - return (-1); - if (UMAX_ENTRY < f->packedStreams[i]) + size_t packedStream; + if (parse_7zip_size(a, &packedStream) < 0) return (-1); } } @@ -2378,7 +2387,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f) static void free_CodersInfo(struct _7z_coders_info *ci) { - unsigned i; + size_t i; if (ci->folders) { for (i = 0; i < ci->numFolders; i++) @@ -2390,9 +2399,10 @@ free_CodersInfo(struct _7z_coders_info *ci) static int read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) { + struct _7zip *zip = a->format->data; const unsigned char *p; struct _7z_digests digest; - unsigned i; + size_t dataStreamIndex, i; memset(ci, 0, sizeof(*ci)); memset(&digest, 0, sizeof(digest)); @@ -2405,10 +2415,16 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) /* * Read NumFolders. */ - if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) + if (parse_7zip_size(a, &(ci->numFolders)) < 0) + goto failed; + /* + * Each folder is encoded by at least one byte in the coders + * list that follows, so a folder count larger than the bytes + * left in the header cannot be honored and is rejected here + * before it is used to size the folders allocation. + */ + if (ci->numFolders > (uint64_t)zip->header_bytes_remaining) goto failed; - if (UMAX_ENTRY < ci->numFolders) - return (-1); /* * Read External. @@ -2418,7 +2434,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) switch (*p) { case 0: ci->folders = - calloc((size_t)ci->numFolders, sizeof(*ci->folders)); + calloc(ci->numFolders, sizeof(*ci->folders)); if (ci->folders == NULL) return (-1); for (i = 0; i < ci->numFolders; i++) { @@ -2427,9 +2443,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) } break; case 1: - if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) - return (-1); - if (UMAX_ENTRY < ci->dataStreamIndex) + if (parse_7zip_size(a, &dataStreamIndex) < 0) return (-1); if (ci->numFolders > 0) { archive_set_error(&a->archive, -1, @@ -2450,14 +2464,14 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) for (i = 0; i < ci->numFolders; i++) { struct _7z_folder *folder = &(ci->folders[i]); - unsigned j; + size_t j; folder->unPackSize = - calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize)); + calloc(folder->numOutStreams, sizeof(*folder->unPackSize)); if (folder->unPackSize == NULL) goto failed; for (j = 0; j < folder->numOutStreams; j++) { - if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0) + if (parse_7zip_int64(a, &(folder->unPackSize[j])) < 0) goto failed; } } @@ -2471,7 +2485,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) return (0); if (*p != kCRC) goto failed; - if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0) + if (read_Digests(a, &digest, ci->numFolders) < 0) goto failed; for (i = 0; i < ci->numFolders; i++) { ci->folders[i].digest_defined = digest.defineds[i]; @@ -2492,16 +2506,16 @@ failed: return (-1); } -static uint64_t +static int64_t folder_uncompressed_size(struct _7z_folder *f) { - int n = (int)f->numOutStreams; - unsigned pairs = (unsigned)f->numBindPairs; + size_t n = f->numOutStreams; + size_t pairs = f->numBindPairs; - while (--n >= 0) { - unsigned i; + while (n-- > 0) { + size_t i; for (i = 0; i < pairs; i++) { - if (f->bindPairs[i].outIndex == (uint64_t)n) + if (f->bindPairs[i].outIndex == n) break; } if (i >= pairs) @@ -2523,11 +2537,11 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, struct _7z_folder *f, size_t numFolders) { const unsigned char *p; - uint64_t *usizes; + int64_t *usizes; + size_t numDigests; size_t unpack_streams; + size_t i; int type; - unsigned i; - uint32_t numDigests; memset(ss, 0, sizeof(*ss)); @@ -2541,14 +2555,11 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, if (type == kNumUnPackStream) { unpack_streams = 0; for (i = 0; i < numFolders; i++) { - if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) + if (parse_7zip_size(a, &(f[i].numUnpackStreams)) < 0) return (-1); - if (UMAX_ENTRY < f[i].numUnpackStreams) + if (archive_ckd_add_size(&unpack_streams, + unpack_streams, f[i].numUnpackStreams)) return (-1); - if (unpack_streams > SIZE_MAX - UMAX_ENTRY) { - return (-1); - } - unpack_streams += (size_t)f[i].numUnpackStreams; } if ((p = header_bytes(a, 1)) == NULL) return (-1); @@ -2556,6 +2567,16 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, } else unpack_streams = numFolders; + if (unpack_streams > UMAX_ENTRY) + return (-1); + + if (type != kSize) { + for (i = 0; i < numFolders; i++) { + if (f[i].numUnpackStreams > 1) + return (-1); + } + } + ss->unpack_streams = unpack_streams; if (unpack_streams) { ss->unpackSizes = calloc(unpack_streams, @@ -2571,8 +2592,8 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, usizes = ss->unpackSizes; for (i = 0; i < numFolders; i++) { - unsigned pack; - uint64_t size, sum; + int64_t size, sum; + size_t pack; if (f[i].numUnpackStreams == 0) continue; @@ -2580,11 +2601,10 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, sum = 0; if (type == kSize) { for (pack = 1; pack < f[i].numUnpackStreams; pack++) { - if (parse_7zip_uint64(a, usizes) < 0) + if (parse_7zip_int64(a, usizes) < 0) return (-1); - if (*usizes > UINT64_MAX - sum) + if (archive_ckd_add_i64(&sum, sum, *usizes++)) return (-1); - sum += *usizes++; } } size = folder_uncompressed_size(&f[i]); @@ -2599,22 +2619,21 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, type = *p; } - for (i = 0; i < unpack_streams; i++) { - ss->digestsDefined[i] = 0; - ss->digests[i] = 0; - } - numDigests = 0; for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams != 1 || !f[i].digest_defined) - numDigests += (uint32_t)f[i].numUnpackStreams; + if (archive_ckd_add_size(&numDigests, + numDigests, f[i].numUnpackStreams)) { + errno = ENOMEM; + return (-1); + } } if (type == kCRC) { struct _7z_digests tmpDigests; unsigned char *digestsDefined = ss->digestsDefined; - uint32_t * digests = ss->digests; - int di = 0; + uint32_t *digests = ss->digests; + size_t di = 0; memset(&tmpDigests, 0, sizeof(tmpDigests)); if (read_Digests(a, &(tmpDigests), numDigests) < 0) { @@ -2626,7 +2645,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, *digestsDefined++ = 1; *digests++ = f[i].digest; } else { - unsigned j; + size_t j; for (j = 0; j < f[i].numUnpackStreams; j++, di++) { @@ -2662,16 +2681,16 @@ free_StreamsInfo(struct _7z_stream_info *si) static int read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const unsigned char *p; - unsigned i; + size_t i; memset(si, 0, sizeof(*si)); if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kPackInfo) { - uint64_t packPos; + int64_t packPos; if (read_PackInfo(a, &(si->pi)) < 0) return (-1); @@ -2684,9 +2703,9 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) packPos = si->pi.pos; for (i = 0; i < si->pi.numPackStreams; i++) { si->pi.positions[i] = packPos; - if (packPos > UINT64_MAX - si->pi.sizes[i]) + if (archive_ckd_add_i64(&packPos, + packPos, si->pi.sizes[i])) return (-1); - packPos += si->pi.sizes[i]; if (packPos > zip->header_offset) return (-1); } @@ -2694,7 +2713,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) return (-1); } if (*p == kUnPackInfo) { - uint32_t packIndex; + size_t packIndex; struct _7z_folder *f; if (read_CodersInfo(a, &(si->ci)) < 0) @@ -2707,12 +2726,9 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) f = si->ci.folders; for (i = 0; i < si->ci.numFolders; i++) { f[i].packIndex = packIndex; - if (f[i].numPackedStreams > UINT32_MAX) - return (-1); - if (packIndex > UINT32_MAX - (uint32_t)f[i].numPackedStreams) - return (-1); - packIndex += (uint32_t)f[i].numPackedStreams; - if (packIndex > si->pi.numPackStreams) + if (archive_ckd_add_size(&packIndex, + packIndex, f[i].numPackedStreams) || + packIndex > si->pi.numPackStreams) return (-1); } if ((p = header_bytes(a, 1)) == NULL) @@ -2721,7 +2737,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) if (*p == kSubStreamsInfo) { if (read_SubStreamsInfo(a, &(si->ss), - si->ci.folders, (size_t)si->ci.numFolders) < 0) + si->ci.folders, si->ci.numFolders) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); @@ -2744,18 +2760,38 @@ free_Header(struct _7z_header_info *h) free(h->attrBools); } +/* + * Files without unpack streams must be described by the EmptyStream bitmap, + * which consumes one bit for every file entry in FilesInfo. + */ +static int +files_info_numfiles_is_sane(const struct _7zip *zip) +{ + int64_t empty_stream_map_bytes; + + if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries)) + return (0); + + if (zip->numFiles <= zip->si.ss.unpack_streams) + return (1); + + empty_stream_map_bytes = (zip->numFiles + 7) / 8; + return (empty_stream_map_bytes <= zip->header_bytes_remaining); +} + static int read_Header(struct archive_read *a, struct _7z_header_info *h, int check_header_id) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const unsigned char *p; struct _7z_folder *folders; struct _7z_stream_info *si = &(zip->si); struct _7zip_entry *entries; - uint32_t folderIndex, indexInFolder; - unsigned i; - int eindex, empty_streams, sindex; + size_t folderIndex, indexInFolder; + size_t i; + size_t eindex, empty_streams, sindex; + int attr_seen = 0; if (check_header_id) { /* @@ -2774,13 +2810,26 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, return (-1); if (*p == kArchiveProperties) { for (;;) { - uint64_t size; + int64_t size; if ((p = header_bytes(a, 1)) == NULL) return (-1); - if (*p == 0) + if (*p == kEnd) break; - if (parse_7zip_uint64(a, &size) < 0) + if (parse_7zip_int64(a, &size) < 0) return (-1); + if (size < 0 || zip->header_bytes_remaining < size) + return (-1); + + /* Skip the property data to keep header parsing aligned. */ + while (size > 0) { + int64_t skip = size; + + if (skip > UBUFF_SIZE) + skip = UBUFF_SIZE; + if (header_bytes(a, (size_t)skip) == NULL) + return (-1); + size -= skip; + } } if ((p = header_bytes(a, 1)) == NULL) return (-1); @@ -2804,12 +2853,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (*p != kFilesInfo) return (-1); - if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) + if (parse_7zip_size(a, &(zip->numFiles)) < 0) return (-1); - if (UMAX_ENTRY < zip->numFiles) + if (!files_info_numfiles_is_sane(zip)) return (-1); - zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); + zip->entries = calloc(zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) return (-1); entries = zip->entries; @@ -2817,7 +2866,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, empty_streams = 0; for (;;) { int type; - uint64_t size; + int64_t size; size_t ll; if ((p = header_bytes(a, 1)) == NULL) @@ -2826,9 +2875,10 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if (type == kEnd) break; - if (parse_7zip_uint64(a, &size) < 0) + if (parse_7zip_int64(a, &size) < 0) return (-1); - if (zip->header_bytes_remaining < size) + if (zip->header_bytes_remaining < size || + size > (int64_t)(SIZE_MAX / 4)) return (-1); ll = (size_t)size; @@ -2836,12 +2886,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, case kEmptyStream: if (h->emptyStreamBools != NULL) return (-1); - h->emptyStreamBools = calloc((size_t)zip->numFiles, + h->emptyStreamBools = calloc(zip->numFiles, sizeof(*h->emptyStreamBools)); if (h->emptyStreamBools == NULL) return (-1); if (read_Bools( - a, h->emptyStreamBools, (size_t)zip->numFiles) < 0) + a, h->emptyStreamBools, zip->numFiles) < 0) return (-1); empty_streams = 0; for (i = 0; i < zip->numFiles; i++) { @@ -2884,7 +2934,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, case kCTime: case kATime: case kMTime: - if (read_Times(a, h, type) < 0) + if (read_Times(a, type) < 0) return (-1); break; case kName: @@ -2954,21 +3004,20 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, if ((p = header_bytes(a, 2)) == NULL) return (-1); allAreDefined = *p; - if (h->attrBools != NULL) + if (attr_seen) return (-1); - h->attrBools = calloc((size_t)zip->numFiles, - sizeof(*h->attrBools)); - if (h->attrBools == NULL) - return (-1); - if (allAreDefined) - memset(h->attrBools, 1, (size_t)zip->numFiles); - else { + attr_seen = 1; + if (!allAreDefined) { + h->attrBools = calloc(zip->numFiles, + sizeof(*h->attrBools)); + if (h->attrBools == NULL) + return (-1); if (read_Bools(a, h->attrBools, - (size_t)zip->numFiles) < 0) + zip->numFiles) < 0) return (-1); } for (i = 0; i < zip->numFiles; i++) { - if (h->attrBools[i]) { + if (allAreDefined || h->attrBools[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); entries[i].attr = archive_le32dec(p); @@ -3021,7 +3070,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, } if (entries[i].flg & HAS_STREAM) { - if ((size_t)sindex >= si->ss.unpack_streams) + if (sindex >= si->ss.unpack_streams) return (-1); if (entries[i].mode == 0) entries[i].mode = AE_IFREG | 0666; @@ -3030,14 +3079,10 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, entries[i].ssIndex = sindex; sindex++; } else { - int dir; - if (h->emptyFileBools == NULL) - dir = 1; - else { - if (h->emptyFileBools[eindex]) - dir = 0; - else - dir = 1; + int dir = 1; + + if (h->emptyFileBools != NULL) { + dir = !h->emptyFileBools[eindex]; eindex++; } if (entries[i].mode == 0) { @@ -3094,27 +3139,25 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, } static int -read_Times(struct archive_read *a, struct _7z_header_info *h, int type) +read_Times(struct archive_read *a, int type) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const unsigned char *p; struct _7zip_entry *entries = zip->entries; - unsigned char *timeBools; + unsigned char *timeBools = NULL; int allAreDefined; - unsigned i; - - timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools)); - if (timeBools == NULL) - return (-1); + size_t dataIndex, i; /* Read allAreDefined. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; allAreDefined = *p; - if (allAreDefined) - memset(timeBools, 1, (size_t)zip->numFiles); - else { - if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0) + + if (!allAreDefined) { + timeBools = calloc(zip->numFiles, sizeof(*timeBools)); + if (timeBools == NULL) + goto failed; + if (read_Bools(a, timeBools, zip->numFiles) < 0) goto failed; } @@ -3122,14 +3165,12 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type) if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p) { - if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) - goto failed; - if (UMAX_ENTRY < h->dataIndex) + if (parse_7zip_size(a, &dataIndex) < 0) goto failed; } for (i = 0; i < zip->numFiles; i++) { - if (!timeBools[i]) + if (!allAreDefined && !timeBools[i]) continue; if ((p = header_bytes(a, 8)) == NULL) goto failed; @@ -3165,7 +3206,8 @@ failed: static int decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; + int64_t pi_end; errno = 0; if (read_StreamsInfo(a, si) < 0) { @@ -3183,9 +3225,12 @@ decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) return (ARCHIVE_FATAL); } - if (zip->header_offset < si->pi.pos + si->pi.sizes[0] || - (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 || - si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) { + if (archive_ckd_add_i64(&pi_end, si->pi.pos, si->pi.sizes[0])) { + archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); + return (ARCHIVE_FATAL); + } + + if (zip->header_offset < pi_end) { archive_set_error(&a->archive, -1, "Malformed Header offset"); return (ARCHIVE_FATAL); } @@ -3196,10 +3241,10 @@ decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) static const unsigned char * header_bytes(struct archive_read *a, size_t rbytes) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const unsigned char *p; - if (zip->header_bytes_remaining < rbytes) + if ((uint64_t)zip->header_bytes_remaining < rbytes) return (NULL); if (zip->pack_stream_bytes_unconsumed) read_consume(a); @@ -3231,26 +3276,29 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, struct _7z_header_info *header) { const unsigned char *p; - uint64_t next_header_offset; - uint64_t next_header_size; + int64_t next_header_offset; + int64_t next_header_size; uint32_t next_header_crc; ssize_t bytes_avail; + int64_t data_offset; int check_header_crc, r; - if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) + if (get_data_offset(a, &data_offset, 1) < 0) + return (ARCHIVE_FATAL); + if (__archive_read_consume(a, data_offset) < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); + } + if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated 7-Zip file header"); + return (ARCHIVE_FATAL); + } + + if (archive_ckd_add_i64(&zip->seek_base, data_offset, 32)) { + archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); - - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { - /* This is an executable ? Must be self-extracting... */ - const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) : - find_elf_data_sec(a); - r = skip_sfx(a, min_addr); - if (r < ARCHIVE_WARN) - return (r); - if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) - return (ARCHIVE_FATAL); } - zip->seek_base += 32; if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) { archive_set_error(&a->archive, -1, "Not 7-Zip archive file"); @@ -3258,7 +3306,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, } /* CRC check. */ - if (crc32(0, (const unsigned char *)p + 12, 20) + if (crc32(0, p + 12, 20) != archive_le32dec(p + 8)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Header CRC error"); @@ -3267,24 +3315,36 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, } next_header_offset = archive_le64dec(p + 12); - next_header_size = archive_le64dec(p + 20); - next_header_crc = archive_le32dec(p + 28); - - if (next_header_size == 0) - /* There is no entry in an archive file. */ - return (ARCHIVE_EOF); - - if (((int64_t)next_header_offset) < 0) { + if (next_header_offset < 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } + next_header_size = archive_le64dec(p + 20); + if (next_header_size < 0) { + archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); + return (ARCHIVE_FATAL); + } + next_header_crc = archive_le32dec(p + 28); + + if (next_header_size == 0) + /* There is no entry in the archive file. */ + return (ARCHIVE_EOF); + __archive_read_consume(a, 32); if (next_header_offset != 0) { - if (bytes_avail >= (ssize_t)next_header_offset) + if (bytes_avail >= next_header_offset) __archive_read_consume(a, next_header_offset); - else if (__archive_read_seek(a, - next_header_offset + zip->seek_base, SEEK_SET) < 0) - return (ARCHIVE_FATAL); + else { + int64_t target; + + if (archive_ckd_add_i64(&target, + zip->seek_base, next_header_offset) || + seek_compat(a, target, SEEK_SET, 1) < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, "Seek error"); + return (ARCHIVE_FATAL); + } + } } zip->stream_offset = next_header_offset; zip->header_offset = next_header_offset; @@ -3395,10 +3455,10 @@ static ssize_t get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, size_t minimum) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; ssize_t bytes_avail; - if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { + if (zip->codec == _7Z_COPY && zip->codec2 == -1) { /* Copy mode. */ *buff = __archive_read_ahead(a, minimum, &bytes_avail); @@ -3444,14 +3504,25 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, return (bytes_avail); } +static size_t +align_size(size_t s) +{ + size_t r; + + if (archive_ckd_add_size(&r, s, 1023)) + return (s); + r &= ~0x3ff; + return (r); +} + static ssize_t extract_pack_stream(struct archive_read *a, size_t minimum) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; ssize_t bytes_avail; int r; - if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { + if (zip->codec == _7Z_COPY && zip->codec2 == -1) { if (minimum == 0) minimum = 1; if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL @@ -3461,10 +3532,10 @@ extract_pack_stream(struct archive_read *a, size_t minimum) "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } - if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining) + if (bytes_avail > zip->pack_stream_inbytes_remaining) bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining; zip->pack_stream_inbytes_remaining -= bytes_avail; - if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining) + if (bytes_avail > zip->folder_outbytes_remaining) bytes_avail = (ssize_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_avail; zip->uncompressed_buffer_bytes_remaining = bytes_avail; @@ -3473,18 +3544,17 @@ extract_pack_stream(struct archive_read *a, size_t minimum) /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { - zip->uncompressed_buffer_size = UBUFF_SIZE; - if (zip->uncompressed_buffer_size < minimum) { - zip->uncompressed_buffer_size = minimum + 1023; - zip->uncompressed_buffer_size &= ~0x3ff; - } - zip->uncompressed_buffer = - malloc(zip->uncompressed_buffer_size); + size_t new_size = UBUFF_SIZE; + + if (new_size < minimum) + new_size = align_size(minimum); + zip->uncompressed_buffer = malloc(new_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } + zip->uncompressed_buffer_size = new_size; zip->uncompressed_buffer_bytes_remaining = 0; } else if (zip->uncompressed_buffer_size < minimum || zip->uncompressed_buffer_bytes_remaining < minimum) { @@ -3505,10 +3575,8 @@ extract_pack_stream(struct archive_read *a, size_t minimum) * the minimum size. */ void *p; - size_t new_size; + size_t new_size = align_size(minimum); - new_size = minimum + 1023; - new_size &= ~0x3ff; p = realloc(zip->uncompressed_buffer, new_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -3554,7 +3622,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum) bytes_out = zip->uncompressed_buffer_size - zip->uncompressed_buffer_bytes_remaining; bytes_in = bytes_avail; - if (bytes_in > zip->pack_stream_inbytes_remaining) + if (bytes_in > (uint64_t)zip->pack_stream_inbytes_remaining) bytes_in = (size_t)zip->pack_stream_inbytes_remaining; /* Drive decompression. */ r = decompress(a, zip, buff_out, &bytes_out, @@ -3570,7 +3638,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum) return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining -= bytes_in; - if (bytes_out > zip->folder_outbytes_remaining) + if (bytes_out > (uint64_t)zip->folder_outbytes_remaining) bytes_out = (size_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_out; zip->uncompressed_buffer_bytes_remaining += bytes_out; @@ -3608,10 +3676,10 @@ extract_pack_stream(struct archive_read *a, size_t minimum) static int seek_pack(struct archive_read *a) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; int64_t pack_offset; - if (zip->pack_stream_remaining <= 0) { + if (zip->pack_stream_remaining == 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); @@ -3620,9 +3688,14 @@ seek_pack(struct archive_read *a) zip->si.pi.sizes[zip->pack_stream_index]; pack_offset = zip->si.pi.positions[zip->pack_stream_index]; if (zip->stream_offset != pack_offset) { - if (0 > __archive_read_seek(a, pack_offset + zip->seek_base, - SEEK_SET)) + int64_t target; + + if (archive_ckd_add_i64(&target, + zip->seek_base, pack_offset) || + 0 > seek_compat(a, target, SEEK_SET, 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error"); return (ARCHIVE_FATAL); + } zip->stream_offset = pack_offset; } zip->pack_stream_index++; @@ -3634,8 +3707,8 @@ static ssize_t read_stream(struct archive_read *a, const void **buff, size_t size, size_t minimum) { - struct _7zip *zip = (struct _7zip *)a->format->data; - uint64_t skip_bytes = 0; + struct _7zip *zip = a->format->data; + int64_t skip_bytes = 0; ssize_t r; if (zip->uncompressed_buffer_bytes_remaining == 0) { @@ -3688,7 +3761,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, r = setup_decode_folder(a, &(zip->si.ci.folders[zip->folder_index]), 0); if (r != ARCHIVE_OK) - return (ARCHIVE_FATAL); + return (r); zip->folder_index++; } @@ -3706,9 +3779,10 @@ read_stream(struct archive_read *a, const void **buff, size_t size, return (r); /* - * Skip the bytes we already has skipped in skip_stream(). + * Skip the bytes we already have skipped in skip_stream(). */ while (1) { + size_t request; ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { @@ -3732,8 +3806,12 @@ read_stream(struct archive_read *a, const void **buff, size_t size, if (!skip_bytes) break; - skipped = get_uncompressed_data( - a, buff, (size_t)skip_bytes, 0); + if (skip_bytes > MAX_READ) + request = MAX_READ; + else + request = (size_t)skip_bytes; + + skipped = get_uncompressed_data(a, buff, request, 0); if (skipped < 0) return (skipped); skip_bytes -= skipped; @@ -3748,10 +3826,10 @@ static int setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, int header) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const struct _7z_coder *coder1, *coder2; const char *cname = (header)?"archive header":"file content"; - unsigned i; + size_t i; int r, found_bcj2 = 0; /* @@ -3763,15 +3841,9 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, } /* - * Initialize a stream reader. - */ - zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; - zip->pack_stream_index = (unsigned)folder->packIndex; - zip->folder_outbytes_remaining = folder_uncompressed_size(folder); - zip->uncompressed_buffer_bytes_remaining = 0; - - /* - * Check coder types. + * Check coder types before modifying any stream-reader state, so that + * an early return leaves zip unchanged (avoids partially-initialized + * state that callers would have to reason about). */ for (i = 0; i < folder->numCoders; i++) { switch(folder->coders[i].codec) { @@ -3789,7 +3861,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, ARCHIVE_ERRNO_MISC, "The %s is encrypted, " "but currently not supported", cname); - return (ARCHIVE_FATAL); + return (header ? ARCHIVE_FATAL : ARCHIVE_FAILED); } case _7Z_X86_BCJ2: { found_bcj2++; @@ -3809,8 +3881,16 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, ARCHIVE_ERRNO_MISC, "The %s is encoded with many filters, " "but currently not supported", cname); - return (ARCHIVE_FATAL); + return (header ? ARCHIVE_FATAL : ARCHIVE_FAILED); } + + /* + * Initialize a stream reader. + */ + zip->pack_stream_remaining = folder->numPackedStreams; + zip->pack_stream_index = folder->packIndex; + zip->folder_outbytes_remaining = folder_uncompressed_size(folder); + zip->uncompressed_buffer_bytes_remaining = 0; coder1 = &(folder->coders[0]); if (folder->numCoders == 2) coder2 = &(folder->coders[1]); @@ -3830,7 +3910,8 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, const void *buff; ssize_t bytes; unsigned char *b[3] = {NULL, NULL, NULL}; - uint64_t sunpack[3] ={-1, -1, -1}; + int64_t sunpack[3] ={-1, -1, -1}; + int64_t remaining; size_t s[3] = {0, 0, 0}; int idx[3] = {0, 1, 2}; @@ -3883,14 +3964,12 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, } } coder2 = &(fc[3]); - zip->main_stream_bytes_remaining = - (size_t)folder->unPackSize[2]; + remaining = folder->unPackSize[2]; } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 && zip->pack_stream_remaining == 4 && folder->numInStreams == 5 && folder->numOutStreams == 2) { /* Source type 0 made by 7z */ - zip->main_stream_bytes_remaining = - (size_t)folder->unPackSize[0]; + remaining = folder->unPackSize[0]; } else { /* We got an unexpected form. */ archive_set_error(&(a->archive), @@ -3898,13 +3977,16 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, "Unsupported form of BCJ2 streams"); return (ARCHIVE_FATAL); } + zip->main_stream_bytes_remaining = remaining; + /* Skip the main stream at this time. */ if ((r = seek_pack(a)) < 0) return (r); zip->pack_stream_bytes_unconsumed = - (size_t)zip->pack_stream_inbytes_remaining; - read_consume(a); + zip->pack_stream_inbytes_remaining; + if ((r = read_consume(a)) < 0) + return (r); /* Read following three sub streams. */ for (i = 0; i < 3; i++) { @@ -3915,7 +3997,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, return (r); } - if (sunpack[i] == (uint64_t)-1) + if (sunpack[i] == -1) zip->folder_outbytes_remaining = zip->pack_stream_inbytes_remaining; else @@ -3929,6 +4011,14 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, /* Allocate memory for the decoded data of a sub * stream. */ + if ((uint64_t)zip->folder_outbytes_remaining > SIZE_MAX) { + free(b[0]); free(b[1]); free(b[2]); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "7-Zip sub-stream size exceeds " + "platform maximum"); + return (ARCHIVE_FATAL); + } b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { free(b[0]); free(b[1]); free(b[2]); @@ -3986,7 +4076,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, * of BCJ2. */ zip->pack_stream_remaining = 1; - zip->pack_stream_index = (unsigned)folder->packIndex; + zip->pack_stream_index = folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; @@ -4001,26 +4091,40 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, return (ARCHIVE_OK); } -static int64_t -skip_stream(struct archive_read *a, size_t skip_bytes) +static int +skip_stream(struct archive_read *a, int64_t skip_bytes) { - struct _7zip *zip = (struct _7zip *)a->format->data; + struct _7zip *zip = a->format->data; const void *p; int64_t skipped_bytes; - size_t bytes = skip_bytes; + int64_t bytes = skip_bytes; if (zip->folder_index == 0) { + int64_t *v; + /* * Optimization for a list mode. * Avoid unnecessary decoding operations. */ - zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes - += skip_bytes; - return (skip_bytes); + v = &zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes; + if (archive_ckd_add_i64(v, *v, skip_bytes)) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Too many bytes to skip"); + return (ARCHIVE_FATAL); + } + return (ARCHIVE_OK); } while (bytes) { - skipped_bytes = read_stream(a, &p, bytes, 0); + size_t request; + + if (bytes > MAX_READ) + request = MAX_READ; + else + request = (size_t)bytes; + + skipped_bytes = read_stream(a, &p, request, 0); if (skipped_bytes < 0) return (skipped_bytes); if (skipped_bytes == 0) { @@ -4029,11 +4133,11 @@ skip_stream(struct archive_read *a, size_t skip_bytes) "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } - bytes -= (size_t)skipped_bytes; + bytes -= skipped_bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } - return (skip_bytes); + return (ARCHIVE_OK); } /* @@ -4082,7 +4186,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) if (p >= limit) break; prevPosT = bufferPos - prevPosT; - if (prevPosT > 3) + if (prevPosT == 0 || prevPosT > 3) prevMask = 0; else { prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; @@ -4101,9 +4205,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) prevPosT = bufferPos; if (Test86MSByte(p[4])) { - uint32_t src = ((uint32_t)p[4] << 24) | - ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | - ((uint32_t)p[1]); + uint32_t src = archive_le32dec(p + 1); uint32_t dest; for (;;) { uint8_t b; @@ -4163,16 +4265,13 @@ arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) for (i = 0; i + 4 <= size; i += 4) { if (buf[i + 3] == 0xEB) { // Calculate the transformed addr. - addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) - | ((uint32_t)buf[i + 2] << 16); + addr = archive_le24dec(buf + i); addr <<= 2; addr -= zip->bcj_ip + (uint32_t)i; addr >>= 2; // Store the transformed addr in buf. - buf[i] = (uint8_t)addr; - buf[i + 1] = (uint8_t)(addr >> 8); - buf[i + 2] = (uint8_t)(addr >> 16); + archive_le24enc(buf + i, addr); } } @@ -4203,20 +4302,14 @@ arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) uint32_t addr; for (i = 0; i + 4 <= size; i += 4) { - instr = (uint32_t)buf[i] - | ((uint32_t)buf[i+1] << 8) - | ((uint32_t)buf[i+2] << 16) - | ((uint32_t)buf[i+3] << 24); + instr = archive_le32dec(buf + i); if ((instr >> 26) == 0x25) { /* BL instruction */ addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2); instr = 0x94000000 | (addr & 0x03FFFFFF); - buf[i] = (uint8_t)instr; - buf[i+1] = (uint8_t)(instr >> 8); - buf[i+2] = (uint8_t)(instr >> 16); - buf[i+3] = (uint8_t)(instr >> 24); + archive_le32enc(buf + i, instr); } else if ((instr & 0x9F000000) == 0x90000000) { /* ADRP instruction */ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC); @@ -4232,10 +4325,7 @@ arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) instr |= (addr & 0x03FFFC) << 3; instr |= (0U - (addr & 0x020000)) & 0xE00000; - buf[i] = (uint8_t)instr; - buf[i+1] = (uint8_t)(instr >> 8); - buf[i+2] = (uint8_t)(instr >> 16); - buf[i+3] = (uint8_t)(instr >> 24); + archive_le32enc(buf + i, instr); } } @@ -4278,10 +4368,7 @@ sparc_Convert(struct _7zip *zip, uint8_t *buf, size_t size) size &= ~(size_t)3; for (i = 0; i < size; i += 4) { - instr = (uint32_t)(buf[i] << 24) - | ((uint32_t)buf[i+1] << 16) - | ((uint32_t)buf[i+2] << 8) - | (uint32_t)buf[i+3]; + instr = archive_be32dec(buf + i); if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) { instr <<= 2; @@ -4290,10 +4377,7 @@ sparc_Convert(struct _7zip *zip, uint8_t *buf, size_t size) instr = ((uint32_t)0x40000000 - (instr & 0x400000)) | 0x40000000 | (instr & 0x3FFFFF); - buf[i] = (uint8_t)(instr >> 24); - buf[i+1] = (uint8_t)(instr >> 16); - buf[i+2] = (uint8_t)(instr >> 8); - buf[i+3] = (uint8_t)instr; + archive_be32enc(buf + i, instr); } } @@ -4406,7 +4490,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) const uint8_t *buf0, *buf1, *buf2, *buf3; size_t size0, size1, size2, size3; const uint8_t *buffer, *bufferLim; - unsigned int i, j; + size_t i, j; size0 = zip->tmp_stream_bytes_remaining; buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0; @@ -4504,15 +4588,10 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) buf2 += 4; size2 -= 4; } - dest = (((uint32_t)v[0] << 24) | - ((uint32_t)v[1] << 16) | - ((uint32_t)v[2] << 8) | - ((uint32_t)v[3])) - - ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); - out[0] = (uint8_t)dest; - out[1] = (uint8_t)(dest >> 8); - out[2] = (uint8_t)(dest >> 16); - out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24); + dest = archive_be32dec(v) - + (zip->bcj2_outPos + outPos + 4); + archive_le32enc(out, dest); + zip->bcj2_prevByte = out[3]; for (i = 0; i < 4 && outPos < outSize; i++) outBuf[outPos++] = out[i]; @@ -4521,7 +4600,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) * Save odd bytes which we could not add into * the output buffer because of out of space. */ - zip->odd_bcj_size = 4 -i; + zip->odd_bcj_size = 4 - i; for (; i < 4; i++) { j = i - 4 + (unsigned)zip->odd_bcj_size; zip->odd_bcj[j] = out[i]; @@ -4538,3 +4617,34 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) return ((ssize_t)outPos); } + +/* + * Perform a seek to given position. If seeking is not supported, + * target position is in front of current position, and compat is requested, + * try to consume bytes until position is reached. + */ +int64_t +seek_compat(struct archive_read *a, int64_t offset, int whence, int compat) +{ + int64_t ret = ARCHIVE_FAILED; + + if (a->filter->can_seek) + ret = __archive_read_seek(a, offset, whence); + else if (compat) { + switch (whence) { + case SEEK_CUR: + ret = __archive_read_consume(a, offset); + break; + case SEEK_SET: + if (a->filter->position > offset) + break; + ret = __archive_read_consume(a, + offset - a->filter->position); + break; + default: + break; + } + } + + return (ret); +} diff --git a/libarchive/archive_read_support_format_all.c b/libarchive/archive_read_support_format_all.c index 3b53c9ad5f..a2c5a4c6f5 100644 --- a/libarchive/archive_read_support_format_all.c +++ b/libarchive/archive_read_support_format_all.c @@ -80,7 +80,7 @@ archive_read_support_format_all(struct archive *a) /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable * "as much as possible." Clients who need specific - * compression should enable those individually so they can + * formats should enable those individually so they can * verify the level of support. */ /* Clear any warning messages set by the above functions. */ archive_clear_error(a); diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 6dfe2939d0..8585de28fd 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -45,6 +45,7 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -54,7 +55,7 @@ struct ar { * but haven't yet marked as consumed. Must be paired with * entry_bytes_remaining usage/modification. */ - size_t entry_bytes_unconsumed; + int64_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; char *strtab; @@ -80,18 +81,18 @@ struct ar { #define AR_fmag_offset 58 #define AR_fmag_size 2 -static int archive_read_format_ar_bid(struct archive_read *a, int); -static int archive_read_format_ar_cleanup(struct archive_read *a); -static int archive_read_format_ar_read_data(struct archive_read *a, - const void **buff, size_t *size, int64_t *offset); -static int archive_read_format_ar_skip(struct archive_read *a); -static int archive_read_format_ar_read_header(struct archive_read *a, - struct archive_entry *e); -static uint64_t ar_atol8(const char *p, unsigned char_cnt); -static uint64_t ar_atol10(const char *p, unsigned char_cnt); -static int ar_parse_gnu_filename_table(struct archive_read *a); -static int ar_parse_common_header(struct ar *ar, struct archive_entry *, - const char *h); +static int archive_read_format_ar_bid(struct archive_read *, int); +static int archive_read_format_ar_cleanup(struct archive_read *); +static int archive_read_format_ar_read_data(struct archive_read *, + const void **, size_t *, int64_t *); +static int archive_read_format_ar_skip(struct archive_read *); +static int archive_read_format_ar_read_header(struct archive_read *, + struct archive_entry *); +static uint64_t ar_atol8(const char *, size_t); +static uint64_t ar_atol10(const char *, size_t); +static int ar_parse_gnu_filename_table(struct archive_read *); +static void ar_parse_common_header(struct ar *, struct archive_entry *, + const char *); int archive_read_support_format_ar(struct archive *_a) @@ -134,12 +135,12 @@ archive_read_support_format_ar(struct archive *_a) static int archive_read_format_ar_cleanup(struct archive_read *a) { - struct ar *ar; + struct ar *ar = a->format->data; - ar = (struct ar *)(a->format->data); free(ar->strtab); free(ar); - (a->format->data) = NULL; + a->format->data = NULL; + return (ARCHIVE_OK); } @@ -164,14 +165,12 @@ archive_read_format_ar_bid(struct archive_read *a, int best_bid) static int _ar_read_header(struct archive_read *a, struct archive_entry *entry, - struct ar *ar, const char *h, size_t *unconsumed) + struct ar *ar, const char *h, int64_t *unconsumed) { char filename[AR_name_size + 1]; uint64_t number; /* Used to hold parsed numbers before validation. */ - size_t bsd_name_length, entry_size; - char *p, *st; + char *p; const void *b; - int r; /* Verify the magic signature on the file header. */ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { @@ -246,15 +245,21 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } + /* + * Parse the time, owner, mode, size fields. + * This must come before any call to _read_ahead. + */ + ar_parse_common_header(ar, entry, h); + /* * '//' is the GNU filename table. * Later entries can refer to names in this table. */ if (strcmp(filename, "//") == 0) { - /* This must come before any call to _read_ahead. */ - ar_parse_common_header(ar, entry, h); + char *st; + size_t entry_size; + archive_entry_copy_pathname(entry, filename); - archive_entry_set_filetype(entry, AE_IFREG); /* Get the size of the filename table. */ number = ar_atol10(h + AR_size_offset, AR_size_size); if (number > SIZE_MAX || number > 1024 * 1024 * 1024) { @@ -309,22 +314,15 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, */ if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') { number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1); - /* - * If we can't look up the real name, warn and return - * the entry with the wrong name. - */ + /* Fail if we can't look up the real name. */ if (ar->strtab == NULL || number >= ar->strtab_size) { archive_set_error(&a->archive, EINVAL, "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_FATAL); } archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); - /* Parse the time, owner, mode, size fields. */ - return (ar_parse_common_header(ar, entry, h)); + return (ARCHIVE_OK); } /* @@ -333,9 +331,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, * the filename to the file contents. */ if (strncmp(filename, "#1/", 3) == 0) { - /* Parse the time, owner, mode, size fields. */ - /* This must occur before _read_ahead is called again. */ - ar_parse_common_header(ar, entry, h); + size_t bsd_name_length; /* Parse the size of the name, adjust the file size. */ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); @@ -346,7 +342,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, */ if (number > SIZE_MAX - 1 || number > 1024 * 1024 - || (int64_t)number > ar->entry_bytes_remaining) { + || number > (uint64_t)ar->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad input file size"); return (ARCHIVE_FATAL); @@ -384,50 +380,34 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_OK); } - /* - * "/" is the SVR4/GNU archive symbol table. - * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table. - */ - if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) { - archive_entry_copy_pathname(entry, filename); - /* Parse the time, owner, mode, size fields. */ - r = ar_parse_common_header(ar, entry, h); - /* Force the file type to a regular file. */ - archive_entry_set_filetype(entry, AE_IFREG); - return (r); - } - - /* - * "__.SYMDEF" is a BSD archive symbol table. - */ - if (strcmp(filename, "__.SYMDEF") == 0) { - archive_entry_copy_pathname(entry, filename); - /* Parse the time, owner, mode, size fields. */ - return (ar_parse_common_header(ar, entry, h)); - } - /* * Otherwise, this is a standard entry. The filename * has already been trimmed as much as possible, based * on our current knowledge of the format. + * + * Note for future implementations: + * + * - "/" is the SVR4/GNU archive symbol table. + * - "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table. + * - "__.SYMDEF" is a BSD archive symbol table. */ archive_entry_copy_pathname(entry, filename); - return (ar_parse_common_header(ar, entry, h)); + return (ARCHIVE_OK); } static int archive_read_format_ar_read_header(struct archive_read *a, struct archive_entry *entry) { - struct ar *ar = (struct ar*)(a->format->data); - size_t unconsumed; + struct ar *ar = a->format->data; + int64_t unconsumed; const void *header_data; int ret; if (!ar->read_global_header) { /* - * We are now at the beginning of the archive, - * so we need first consume the ar global header. + * We are at the beginning of the archive now, + * so we have to consume the ar global header first. */ __archive_read_consume(a, 8); ar->read_global_header = 1; @@ -451,11 +431,11 @@ archive_read_format_ar_read_header(struct archive_read *a, } -static int +static void ar_parse_common_header(struct ar *ar, struct archive_entry *entry, const char *h) { - uint64_t n; + int64_t n; /* Copy remaining header */ archive_entry_set_mtime(entry, @@ -467,23 +447,20 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry, archive_entry_set_mode(entry, (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size)); archive_entry_set_filetype(entry, AE_IFREG); - n = ar_atol10(h + AR_size_offset, AR_size_size); + n = (int64_t)ar_atol10(h + AR_size_offset, AR_size_size); ar->entry_offset = 0; ar->entry_padding = n % 2; archive_entry_set_size(entry, n); ar->entry_bytes_remaining = n; - return (ARCHIVE_OK); } static int archive_read_format_ar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct ar *ar = a->format->data; ssize_t bytes_read; - struct ar *ar; - - ar = (struct ar *)(a->format->data); if (ar->entry_bytes_unconsumed) { __archive_read_consume(a, ar->entry_bytes_unconsumed); @@ -508,17 +485,9 @@ archive_read_format_ar_read_data(struct archive_read *a, ar->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { - int64_t skipped = __archive_read_consume(a, ar->entry_padding); - if (skipped >= 0) { - ar->entry_padding -= skipped; - } - if (ar->entry_padding) { - if (skipped >= 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Truncated ar archive - failed consuming padding"); - } + if (__archive_read_consume(a, ar->entry_padding) < 0) return (ARCHIVE_FATAL); - } + ar->entry_padding = 0; *buff = NULL; *size = 0; *offset = ar->entry_offset; @@ -529,15 +498,11 @@ archive_read_format_ar_read_data(struct archive_read *a, static int archive_read_format_ar_skip(struct archive_read *a) { - int64_t bytes_skipped; - struct ar* ar; + struct ar *ar = a->format->data; - ar = (struct ar *)(a->format->data); - - bytes_skipped = __archive_read_consume(a, - ar->entry_bytes_remaining + ar->entry_padding - + ar->entry_bytes_unconsumed); - if (bytes_skipped < 0) + if (__archive_read_consume(a, + ar->entry_bytes_remaining + ar->entry_padding + + ar->entry_bytes_unconsumed) < 0) return (ARCHIVE_FATAL); ar->entry_bytes_remaining = 0; @@ -550,11 +515,10 @@ archive_read_format_ar_skip(struct archive_read *a) static int ar_parse_gnu_filename_table(struct archive_read *a) { - struct ar *ar; + struct ar *ar = a->format->data; char *p; size_t size; - ar = (struct ar*)(a->format->data); size = ar->strtab_size; for (p = ar->strtab; p < ar->strtab + size - 1; ++p) { @@ -586,51 +550,47 @@ bad_string_table: } static uint64_t -ar_atol8(const char *p, unsigned char_cnt) +ar_atol8(const char *p, size_t char_cnt) { - uint64_t l, limit, last_digit_limit; + uint64_t l; unsigned int digit, base; base = 8; - limit = UINT64_MAX / base; - last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; - while (*p >= '0' && digit < base && char_cnt-- > 0) { - if (l>limit || (l == limit && digit > last_digit_limit)) { + while (*p >= '0' && digit < base && char_cnt-- > 0) { + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } - l = (l * base) + digit; digit = *++p - '0'; } return (l); } static uint64_t -ar_atol10(const char *p, unsigned char_cnt) +ar_atol10(const char *p, size_t char_cnt) { - uint64_t l, limit, last_digit_limit; + uint64_t l; unsigned int base, digit; base = 10; - limit = UINT64_MAX / base; - last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { - if (l > limit || (l == limit && digit > last_digit_limit)) { + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } - l = (l * base) + digit; digit = *++p - '0'; } return (l); diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index c4a67a7268..f90250e195 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -49,85 +49,186 @@ #include "archive_endian.h" +/* + * Huffman coding. + * + * Array representation of a Huffman tree for codes of up to 16 bit lengths. + * Lookups are performed through a direct, expanded lookup table. + * + * An expanded table has as many elements as needed to cover all possible + * indices formable with bit patterns of given lookup_bits length. + * + * If less codes exist, these span multiple entries for all possible + * combinations of following bits. + * + * Example of a Huffman tree with len_size = 3, lookup_bits = 2: + * + * Symbol | Code + * -------+----- + * A | 0b0 + * B | 0b10 + * C | 0b11 + * + * The bit sequences 0b00 and 0b01 are rightfully not covered by a code, + * since code 0b0 already maps to symbol A. The table will contain two + * entries for symbol A instead: + * + * idx | tbl[idx] + * -----+--------- + * 0b00 | A + * 0b01 | A + * 0b10 | B + * 0b11 | C + * + * By using lookup_bits bits as a lookup, it becomes apparent that 0b00 and + * 0b01 point to a symbol which actually has the code 0b0. A user of this + * data structure must check the code bit length of a retrieved symbol after + * the lookup to properly advance the bit stream: + * + * idx | bitlen[idx] + * ----+------------ + * A | 1 + * B | 2 + * C | 2 + * + * Thus, a proper code sequence would be: + * + * symbol = tbl[read_bits(lookup_bits)] + * consume_bits(bitlen[symbol]) + */ +struct huffman { + /* + * Amount of symbols. + * + * This implementation keeps track of unused symbols as well, + * thus symbols start with 0x0000 up to given symbol amount: + * [0..symbol_count) + * + * Used to construct tbl. + */ + uint16_t symbol_count; + /* + * Frequency of code bit lengths. + * + * Represents the amount of occurrences of given bit lengths. + * Index 0 is used for "empty" codes (aka unused symbols), + * otherwise index represents the bit length + * (index 1 is bit length 1 and so on). + * + * Used to construct tbl. + */ + uint16_t freq[17]; + /* Map of symbols to their code bit lengths. */ + uint8_t *bitlen; + /* Amount of bits to use for lookup (<= tbl_bits). */ + uint8_t lookup_bits; + /* + * Code bit length used for allocation (<= 16). + * + * Used to construct tbl. + */ + uint8_t tbl_bits; + /* Direct, expanded lookup table. */ + uint16_t *tbl; +}; + +/* + * Bit stream reader. + */ +struct lzx_br { +#define CACHE_TYPE uint64_t +#define CACHE_BITS (8 * sizeof(CACHE_TYPE)) + /* Cache buffer. */ + CACHE_TYPE cache_buffer; + /* Indicates how many bits avail in cache_buffer. */ + size_t cache_avail; + uint8_t odd; + int have_odd; +}; + +struct lzx_pos_tbl { + uint32_t base; + uint8_t footer_bits; +}; + struct lzx_dec { /* Decoding status. */ int state; +#define ST_RD_TRANSLATION 0 +#define ST_RD_TRANSLATION_SIZE 1 +#define ST_RD_BLOCK_TYPE 2 +#define ST_RD_BLOCK_SIZE 3 +#define ST_RD_ALIGNMENT 4 +#define ST_RD_R0 5 +#define ST_RD_R1 6 +#define ST_RD_R2 7 +#define ST_COPY_UNCOMP1 8 +#define ST_COPY_UNCOMP2 9 +#define ST_RD_ALIGNED_OFFSET 10 +#define ST_RD_VERBATIM 11 +#define ST_RD_PRE_MAIN_TREE_256 12 +#define ST_MAIN_TREE_256 13 +#define ST_RD_PRE_MAIN_TREE_REM 14 +#define ST_MAIN_TREE_REM 15 +#define ST_RD_PRE_LENGTH_TREE 16 +#define ST_LENGTH_TREE 17 +#define ST_MAIN 18 +#define ST_LENGTH 19 +#define ST_OFFSET 20 +#define ST_REAL_POS 21 +#define ST_COPY 22 /* - * Window to see last decoded data, from 32KBi to 2MBi. + * Window to see last decoded data, from 32 KiB to 2 MiB. */ - int w_size; - int w_mask; + size_t w_size; + size_t w_mask; /* Window buffer, which is a loop buffer. */ - unsigned char *w_buff; + uint8_t *w_buff; /* The insert position to the window. */ - int w_pos; + size_t w_pos; /* The position where we can copy decoded code from the window. */ - int copy_pos; + size_t copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ - int copy_len; + size_t copy_len; /* Translation reversal for x86 processor CALL byte sequence(E8). * This is used for LZX only. */ - uint32_t translation_size; - char translation; - char block_type; + int32_t translation_size; + int translation; + uint8_t block_type; #define VERBATIM_BLOCK 1 #define ALIGNED_OFFSET_BLOCK 2 #define UNCOMPRESSED_BLOCK 3 size_t block_size; size_t block_bytes_avail; /* Repeated offset. */ - int r0, r1, r2; - unsigned char rbytes[4]; - int rbytes_avail; - int length_header; - int position_slot; - int offset_bits; + size_t r0, r1, r2; + uint8_t rbytes[4]; + size_t rbytes_avail; + uint8_t length_header; + uint16_t position_slot; + uint8_t offset_bits; - struct lzx_pos_tbl { - int base; - int footer_bits; - } *pos_tbl; + struct lzx_pos_tbl *pos_tbl; /* * Bit stream reader. */ - struct lzx_br { -#define CACHE_TYPE uint64_t -#define CACHE_BITS (8 * sizeof(CACHE_TYPE)) - /* Cache buffer. */ - CACHE_TYPE cache_buffer; - /* Indicates how many bits avail in cache_buffer. */ - int cache_avail; - unsigned char odd; - char have_odd; - } br; + struct lzx_br br; /* * Huffman coding. */ - struct huffman { - int len_size; - int freq[17]; - unsigned char *bitlen; + struct huffman at; + struct huffman lt; + struct huffman mt; + struct huffman pt; - /* - * Use a index table. It's faster than searching a huffman - * coding tree, which is a binary tree. But a use of a large - * index table causes L1 cache read miss many times. - */ - int max_bits; - int tbl_bits; - int tree_used; - /* Direct access table. */ - uint16_t *tbl; - } at, lt, mt, pt; - - int loop; + uint16_t loop; int error; }; -static const int slots[] = { +static const size_t slots[] = { 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 }; #define SLOT_BASE 15 @@ -135,11 +236,11 @@ static const int slots[] = { struct lzx_stream { const unsigned char *next_in; - int64_t avail_in; - int64_t total_in; + size_t avail_in; + size_t total_in; unsigned char *next_out; - int64_t avail_out; - int64_t total_out; + size_t avail_out; + size_t total_out; struct lzx_dec *ds; }; @@ -179,6 +280,11 @@ struct lzx_stream { #define CFDATA_cbData 4 #define CFDATA_cbUncomp 6 +/* Limits */ +#define MAX_UNCOMPRESS_SIZE 0x8000 +#define MAX_FILE_SIZE (UINT16_MAX * MAX_UNCOMPRESS_SIZE) +#define MAX_E8_TRANSLATION (0x8000 * MAX_UNCOMPRESS_SIZE) + static const char * const compression_name[] = { "NONE", "MSZIP", @@ -240,8 +346,6 @@ struct cffile { }; struct cfheader { - /* Total bytes of all file size in a Cabinet. */ - uint32_t total_bytes; uint32_t files_offset; uint16_t folder_count; uint16_t file_count; @@ -249,7 +353,6 @@ struct cfheader { #define PREV_CABINET 0x0001 #define NEXT_CABINET 0x0002 #define RESERVE_PRESENT 0x0004 - uint16_t setid; uint16_t cabinet; /* Version number. */ unsigned char major; @@ -264,12 +367,10 @@ struct cfheader { }; struct cab { - /* entry_bytes_remaining is the number of bytes we expect. */ + /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; - int64_t entry_compressed_bytes_read; - int64_t entry_uncompressed_bytes_read; struct cffolder *entry_cffolder; struct cffile *entry_cffile; struct cfdata *entry_cfdata; @@ -279,7 +380,7 @@ struct cab { struct cfheader cfheader; struct archive_wstring ws; - /* Flag to mark progress that an archive was read their first header.*/ + /* Flag to mark progress that first header of an archive was read.*/ char found_header; char end_of_archive; char end_of_entry; @@ -314,7 +415,7 @@ static int archive_read_format_cab_read_data_skip(struct archive_read *); static int archive_read_format_cab_cleanup(struct archive_read *); static int cab_skip_sfx(struct archive_read *); -static time_t cab_dos_time(const unsigned char *); +static time_t cab_dos_time(const char *); static int cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int cab_read_header(struct archive_read *); @@ -335,15 +436,15 @@ static int lzx_decode_init(struct lzx_stream *, int); static int lzx_read_blocks(struct lzx_stream *, int); static int lzx_decode_blocks(struct lzx_stream *, int); static void lzx_decode_free(struct lzx_stream *); -static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t); +static void lzx_translation(struct lzx_stream *, unsigned char *, size_t, int32_t); static void lzx_cleanup_bitstream(struct lzx_stream *); static int lzx_decode(struct lzx_stream *, int); static int lzx_read_pre_tree(struct lzx_stream *); -static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int); -static int lzx_huffman_init(struct huffman *, size_t, int); +static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, uint16_t); +static int lzx_huffman_init(struct huffman *, uint16_t, uint8_t); static void lzx_huffman_free(struct huffman *); static int lzx_make_huffman_table(struct huffman *); -static inline int lzx_decode_huffman(struct huffman *, unsigned); +static uint16_t lzx_decode_huffman(struct huffman *, uint16_t); int @@ -433,7 +534,7 @@ archive_read_format_cab_bid(struct archive_read *a, int best_bid) /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward - * up to 128k for a 'MSCF' marker. + * up to 128k for an 'MSCF' marker. */ if (p[0] == 'M' && p[1] == 'Z') { offset = 0; @@ -465,10 +566,9 @@ static int archive_read_format_cab_options(struct archive_read *a, const char *key, const char *val) { - struct cab *cab; + struct cab *cab = a->format->data; int ret = ARCHIVE_FAILED; - cab = (struct cab *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -501,7 +601,7 @@ cab_skip_sfx(struct archive_read *a) for (;;) { const char *h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { - /* Remaining size are less than window. */ + /* Remaining size is less than window. */ window >>= 1; if (window < 128) { archive_set_error(&a->archive, @@ -540,33 +640,34 @@ truncated_error(struct archive_read *a) return (ARCHIVE_FATAL); } -static ssize_t -cab_strnlen(const unsigned char *p, size_t maxlen) +#ifdef HAVE_STRNLEN +#define cab_strnlen(a,b) strnlen(a,b) +#else +static size_t +cab_strnlen(const char *p, size_t maxlen) { size_t i; - for (i = 0; i <= maxlen; i++) { + for (i = 0; i < maxlen; i++) { if (p[i] == 0) break; } - if (i > maxlen) - return (-1);/* invalid */ - return ((ssize_t)i); + return (i); } +#endif -/* Read bytes as much as remaining. */ +/* Read up to max remaining bytes. */ static const void * -cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail) +cab_read_ahead_remaining(struct archive_read *a, size_t max, ssize_t *avail) { - const void *p; + const void *p = __archive_read_ahead(a, max, avail); - while (min > 0) { - p = __archive_read_ahead(a, min, avail); - if (p != NULL) - return (p); - min--; - } - return (NULL); + if (p == NULL && *avail > 0) + p = __archive_read_ahead(a, *avail, avail); + if (p != NULL && (size_t)*avail > max) + *avail = max; + + return (p); } /* Convert a path separator '\' -> '/' */ @@ -623,11 +724,11 @@ cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry) static int cab_read_header(struct archive_read *a) { - const unsigned char *p; - struct cab *cab; + struct cab *cab = a->format->data; + const char *p; struct cfheader *hd; - size_t bytes, used; - ssize_t len; + size_t bytes, len, maxlen, used; + ssize_t avail; int64_t skip; int err, i; int cur_folder, prev_folder; @@ -640,7 +741,6 @@ cab_read_header(struct archive_read *a) if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); - cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { /* This is an executable? Must be self-extracting... */ @@ -664,7 +764,6 @@ cab_read_header(struct archive_read *a) "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } - hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet); hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles); hd->minor = p[CFHEADER_versionMinor]; hd->major = p[CFHEADER_versionMajor]; @@ -675,7 +774,6 @@ cab_read_header(struct archive_read *a) if (hd->file_count == 0) goto invalid; hd->flags = archive_le16dec(p + CFHEADER_flags); - hd->setid = archive_le16dec(p + CFHEADER_setID); hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet); used = CFHEADER_iCabinet + 2; if (hd->flags & RESERVE_PRESENT) { @@ -691,29 +789,42 @@ cab_read_header(struct archive_read *a) hd->cffolder = 0;/* Avoid compiling warning. */ if (hd->flags & PREV_CABINET) { /* How many bytes are used for szCabinetPrev. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + maxlen = avail - used; + len = cab_strnlen(p + used, maxlen); + if (len == 0 || len == maxlen) { goto invalid; + } used += len + 1; /* How many bytes are used for szDiskPrev. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + maxlen = avail - used; + len = cab_strnlen(p + used, maxlen); + if (len == maxlen) goto invalid; used += len + 1; } if (hd->flags & NEXT_CABINET) { /* How many bytes are used for szCabinetNext. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + maxlen = avail - used; + len = cab_strnlen(p + used, maxlen); + if (len == 0 || len == maxlen) goto invalid; used += len + 1; /* How many bytes are used for szDiskNext. */ - if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) + if ((p = cab_read_ahead_remaining(a, used + 256, + &avail)) == NULL || (size_t)avail <= used) return (truncated_error(a)); - if ((len = cab_strnlen(p + used, 255)) <= 0) + maxlen = avail - used; + len = cab_strnlen(p + used, maxlen); + if (len == maxlen) goto invalid; used += len + 1; } @@ -776,14 +887,15 @@ cab_read_header(struct archive_read *a) */ /* Seek read pointer to the offset of CFFILE if needed. */ skip = (int64_t)hd->files_offset - cab->cab_offset; - if (skip < 0) { + if (skip < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFFILE %jd < %jd", (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip) { - __archive_read_consume(a, skip); + if (__archive_read_consume(a, skip) < 0) + return (truncated_error(a)); cab->cab_offset += skip; } /* Allocate memory for CFDATA */ @@ -795,7 +907,6 @@ cab_read_header(struct archive_read *a) prev_folder = -1; for (i = 0; i < hd->file_count; i++) { struct cffile *file = &(hd->file_array[i]); - ssize_t avail; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) return (truncated_error(a)); @@ -809,7 +920,9 @@ cab_read_header(struct archive_read *a) cab->cab_offset += 16; if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL) return (truncated_error(a)); - if ((len = cab_strnlen(p, avail-1)) <= 0) + maxlen = avail; + len = cab_strnlen(p, maxlen); + if (len == 0 || len == maxlen) goto invalid; /* Copy a pathname. */ @@ -821,10 +934,10 @@ cab_read_header(struct archive_read *a) /* * Sanity check if each data is acceptable. */ - if (file->uncompressed_size > 0x7FFF8000) + if (file->uncompressed_size > MAX_FILE_SIZE) goto invalid;/* Too large */ if ((int64_t)file->offset + (int64_t)file->uncompressed_size - > ARCHIVE_LITERAL_LL(0x7FFF8000)) + > (int64_t)MAX_FILE_SIZE) goto invalid;/* Too large */ switch (file->folder) { case iFoldCONTINUED_TO_NEXT: @@ -890,14 +1003,13 @@ static int archive_read_format_cab_read_header(struct archive_read *a, struct archive_entry *entry) { - struct cab *cab; + struct cab *cab = a->format->data; struct cfheader *hd; struct cffolder *prev_folder; struct cffile *file; struct archive_string_conv *sconv; int err = ARCHIVE_OK, r; - - cab = (struct cab *)(a->format->data); + if (cab->found_header == 0) { err = cab_read_header(a); if (err < ARCHIVE_WARN) @@ -915,8 +1027,6 @@ archive_read_format_cab_read_header(struct archive_read *a, cab->end_of_entry = 0; cab->end_of_entry_cleanup = 0; - cab->entry_compressed_bytes_read = 0; - cab->entry_uncompressed_bytes_read = 0; cab->entry_unconsumed = 0; cab->entry_cffile = file; @@ -980,7 +1090,7 @@ archive_read_format_cab_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name(sconv)); err = ARCHIVE_WARN; } @@ -1014,7 +1124,7 @@ static int archive_read_format_cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; int r; switch (cab->entry_cffile->folder) { @@ -1026,7 +1136,7 @@ archive_read_format_cab_read_data(struct archive_read *a, *offset = 0; archive_clear_error(&a->archive); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Cannot restore this file split in multivolume."); + "Cannot restore this file split in multivolume"); return (ARCHIVE_FAILED); default: break; @@ -1114,7 +1224,7 @@ cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed) static void cab_checksum_update(struct archive_read *a, size_t bytes) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata = cab->entry_cfdata; const unsigned char *p; size_t sumbytes; @@ -1154,7 +1264,7 @@ cab_checksum_update(struct archive_read *a, size_t bytes) static int cab_checksum_finish(struct archive_read *a) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata = cab->entry_cfdata; int l; @@ -1175,6 +1285,9 @@ cab_checksum_finish(struct archive_read *a) l = 4; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; + if (cfdata->memimage == NULL) { + return (ARCHIVE_FAILED); + } cfdata->sum_calculated = cab_checksum_cfdata( cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated); if (cfdata->sum_calculated != cfdata->sum) { @@ -1196,7 +1309,7 @@ cab_checksum_finish(struct archive_read *a) static int cab_next_cfdata(struct archive_read *a) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata = cab->entry_cfdata; /* There are remaining bytes in current CFDATA, use it first. */ @@ -1274,9 +1387,9 @@ cab_next_cfdata(struct archive_read *a) * Sanity check if data size is acceptable. */ if (cfdata->compressed_size == 0 || - cfdata->compressed_size > (0x8000+6144)) + cfdata->compressed_size > (MAX_UNCOMPRESS_SIZE + 6144)) goto invalid; - if (cfdata->uncompressed_size > 0x8000) + if (cfdata->uncompressed_size > MAX_UNCOMPRESS_SIZE) goto invalid; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { @@ -1289,10 +1402,10 @@ cab_next_cfdata(struct archive_read *a) } } /* If CFDATA is not last in a folder, an uncompressed - * size must be 0x8000(32KBi) */ + * size must be 0x8000 (32 KiB) */ if ((cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) && - cfdata->uncompressed_size != 0x8000) + cfdata->uncompressed_size != MAX_UNCOMPRESS_SIZE) goto invalid; /* A compressed data size and an uncompressed data size must @@ -1344,7 +1457,7 @@ invalid: static const void * cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; int err; err = cab_next_cfdata(a); @@ -1362,7 +1475,7 @@ cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) return (cab_read_ahead_cfdata_lzx(a, avail)); default: /* Unsupported compression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported CAB compression : %s", + "Unsupported CAB compression: %s", cab->entry_cffolder->compname); *avail = ARCHIVE_FAILED; return (NULL); @@ -1375,7 +1488,7 @@ cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) static const void * cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata; const void *d; @@ -1407,7 +1520,7 @@ cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail) static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata; const void *d; int r, mszip; @@ -1417,7 +1530,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { - cab->uncompressed_buffer_size = 0x8000; + cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE; cab->uncompressed_buffer = malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { @@ -1449,7 +1562,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize deflate decompression."); + "Can't initialize deflate decompression"); *avail = ARCHIVE_FATAL; return (NULL); } @@ -1562,11 +1675,9 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) * correctly compute the sum of CFDATA accordingly. */ if (cfdata->compressed_bytes_remaining > 0) { - ssize_t bytes_avail; - d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, - &bytes_avail); - if (bytes_avail <= 0) { + NULL); + if (d == NULL) { *avail = truncated_error(a); return (NULL); } @@ -1637,7 +1748,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) static const void * cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata; const void *d; int r; @@ -1646,7 +1757,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { - cab->uncompressed_buffer_size = 0x8000; + cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE; cab->uncompressed_buffer = malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { @@ -1669,7 +1780,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) cab->entry_cffolder->compdata); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize LZX decompression."); + "Can't initialize LZX decompression"); *avail = ARCHIVE_FATAL; return (NULL); } @@ -1727,11 +1838,9 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) * Make sure a read pointer advances to next CFDATA. */ if (cfdata->compressed_bytes_remaining > 0) { - ssize_t bytes_avail; - d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, - &bytes_avail); - if (bytes_avail <= 0) { + NULL); + if (d == NULL) { *avail = truncated_error(a); return (NULL); } @@ -1748,7 +1857,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) */ lzx_translation(&cab->xstrm, cab->uncompressed_buffer, cfdata->uncompressed_size, - (cab->entry_cffolder->cfdata_index-1) * 0x8000); + (cab->entry_cffolder->cfdata_index - 1) * MAX_UNCOMPRESS_SIZE); d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; @@ -1769,7 +1878,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) static int64_t cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata; int64_t cbytes, rbytes; int err; @@ -1862,7 +1971,7 @@ cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) static int64_t cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfdata *cfdata; int64_t cbytes, rbytes; int err; @@ -1919,7 +2028,7 @@ static int cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; ssize_t bytes_avail; if (cab->entry_bytes_remaining == 0) { @@ -1965,12 +2074,10 @@ cab_read_data(struct archive_read *a, const void **buff, static int archive_read_format_cab_read_data_skip(struct archive_read *a) { - struct cab *cab; + struct cab *cab = a->format->data; int64_t bytes_skipped; int r; - cab = (struct cab *)(a->format->data); - if (cab->end_of_archive) return (ARCHIVE_EOF); @@ -2020,9 +2127,9 @@ archive_read_format_cab_read_data_skip(struct archive_read *a) static int archive_read_format_cab_cleanup(struct archive_read *a) { - struct cab *cab = (struct cab *)(a->format->data); + struct cab *cab = a->format->data; struct cfheader *hd = &cab->cfheader; - int i; + uint16_t i; if (hd->folder_array != NULL) { for (i = 0; i < hd->folder_count; i++) @@ -2042,13 +2149,13 @@ archive_read_format_cab_cleanup(struct archive_read *a) archive_wstring_free(&cab->ws); free(cab->uncompressed_buffer); free(cab); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t -cab_dos_time(const unsigned char *p) +cab_dos_time(const char *p) { int msTime, msDate; struct tm ts; @@ -2084,10 +2191,11 @@ cab_dos_time(const unsigned char *p) static int lzx_decode_init(struct lzx_stream *strm, int w_bits) { - struct lzx_dec *ds; - int slot, w_size, w_slot; - int base, footer; int base_inc[18]; + struct lzx_dec *ds; + uint32_t base; + uint16_t slot, w_size, w_slot; + uint8_t footer; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); @@ -2097,7 +2205,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits) ds = strm->ds; ds->error = ARCHIVE_FAILED; - /* Allow bits from 15(32KBi) up to 21(2MBi) */ + /* Allow bits from 15 (32 KiB) up to 21 (2 MiB) */ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX) return (ARCHIVE_FAILED); @@ -2109,7 +2217,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits) w_size = ds->w_size; w_slot = slots[w_bits - SLOT_BASE]; ds->w_size = 1U << w_bits; - ds->w_mask = ds->w_size -1; + ds->w_mask = ds->w_size - 1; if (ds->w_buff == NULL || w_size != ds->w_size) { free(ds->w_buff); ds->w_buff = malloc(ds->w_size); @@ -2131,18 +2239,20 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits) else base += base_inc[footer]; if (footer < 17) { - footer = -2; + footer = 0; for (n = base; n; n >>= 1) footer++; - if (footer <= 0) + if (footer <= 2) footer = 0; + else + footer -= 2; } ds->pos_tbl[slot].base = base; ds->pos_tbl[slot].footer_bits = footer; } ds->w_pos = 0; - ds->state = 0; + ds->state = ST_RD_TRANSLATION; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; ds->r0 = ds->r1 = ds->r2 = 1; @@ -2156,7 +2266,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits) return (ARCHIVE_FATAL); /* Initialize Main tree. */ - if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16) + if (lzx_huffman_init(&(ds->mt), 256 + (w_slot << 3), 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -2192,29 +2302,36 @@ lzx_decode_free(struct lzx_stream *strm) * E8 Call Translation reversal. */ static void -lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) +lzx_translation(struct lzx_stream *strm, unsigned char *buffer, size_t size, + int32_t offset) { struct lzx_dec *ds = strm->ds; - unsigned char *b, *end; + unsigned char *p, *end; - if (!ds->translation || size <= 10) + if (!ds->translation || offset >= MAX_E8_TRANSLATION || size <= 10) return; - b = p; - end = b + size - 10; - while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) { - size_t i = b - (unsigned char *)p; - int32_t cp, displacement, value; - cp = (int32_t)(offset + (uint32_t)i); - value = archive_le32dec(&b[1]); - if (value >= -cp && value < (int32_t)ds->translation_size) { - if (value >= 0) - displacement = value - cp; + p = buffer; + end = buffer + size - 10; + + while (p < end && (p = memchr(p, 0xE8, end - p)) != NULL) { + int32_t address, position; + + address = archive_le32dec(p + 1); + position = offset + (p - buffer); + + if (address >= -position && address < ds->translation_size) { + uint32_t relative; + + if (address >= 0) + relative = address - position; else - displacement = value + ds->translation_size; - archive_le32enc(&b[1], (uint32_t)displacement); + relative = address + ds->translation_size; + + archive_le32enc(p + 1, relative); } - b += 5; + + p += 5; } } @@ -2236,7 +2353,7 @@ lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzx_br_read_ahead_0(strm, br, n) \ - (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br)) + (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br) == ARCHIVE_OK) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ @@ -2265,8 +2382,8 @@ static const uint32_t cache_masks[] = { * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * - * Returns 1 if the cache buffer is full. - * Returns 0 if the cache buffer is not full; input buffer is empty. + * Returns ARCHIVE_OK if the cache buffer is full. + * Returns ARCHIVE_EOF if the cache buffer is not full; input buffer is empty. */ static int lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) @@ -2274,7 +2391,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) /* * x86 processor family can read misaligned data without an access error. */ - int n = CACHE_BITS - br->cache_avail; + ssize_t n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 4) { @@ -2292,7 +2409,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; - return (1); + return (ARCHIVE_OK); } break; case 3: @@ -2308,13 +2425,13 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; - return (1); + return (ARCHIVE_OK); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ - return (1); + return (ARCHIVE_EOF); default: break; } @@ -2326,7 +2443,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) strm->avail_in--; br->have_odd = 1; } - return (0); + return (ARCHIVE_EOF); } br->cache_buffer = (br->cache_buffer << 16) | @@ -2341,7 +2458,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) static void lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br) { - int n = CACHE_BITS - br->cache_avail; + ssize_t n = CACHE_BITS - br->cache_avail; if (br->have_odd && n >= 16 && strm->avail_in > 0) { br->cache_buffer = @@ -2370,35 +2487,11 @@ lzx_cleanup_bitstream(struct lzx_stream *strm) * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. */ -#define ST_RD_TRANSLATION 0 -#define ST_RD_TRANSLATION_SIZE 1 -#define ST_RD_BLOCK_TYPE 2 -#define ST_RD_BLOCK_SIZE 3 -#define ST_RD_ALIGNMENT 4 -#define ST_RD_R0 5 -#define ST_RD_R1 6 -#define ST_RD_R2 7 -#define ST_COPY_UNCOMP1 8 -#define ST_COPY_UNCOMP2 9 -#define ST_RD_ALIGNED_OFFSET 10 -#define ST_RD_VERBATIM 11 -#define ST_RD_PRE_MAIN_TREE_256 12 -#define ST_MAIN_TREE_256 13 -#define ST_RD_PRE_MAIN_TREE_REM 14 -#define ST_MAIN_TREE_REM 15 -#define ST_RD_PRE_LENGTH_TREE 16 -#define ST_LENGTH_TREE 17 -#define ST_MAIN 18 -#define ST_LENGTH 19 -#define ST_OFFSET 20 -#define ST_REAL_POS 21 -#define ST_COPY 22 - static int lzx_decode(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; - int64_t avail_in; + size_t avail_in; int r; if (ds->error) @@ -2410,7 +2503,8 @@ lzx_decode(struct lzx_stream *strm, int last) if (ds->state < ST_MAIN) r = lzx_read_blocks(strm, last); else { - int64_t bytes_written = strm->avail_out; + size_t bytes_written = strm->avail_out; + r = lzx_decode_blocks(strm, last); bytes_written -= strm->avail_out; strm->next_out += bytes_written; @@ -2426,7 +2520,8 @@ lzx_read_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); - int i, r; + int r; + uint16_t i; for (;;) { switch (ds->state) { @@ -2442,16 +2537,21 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* FALL THROUGH */ case ST_RD_TRANSLATION_SIZE: if (ds->translation) { + uint32_t v; + if (!lzx_br_read_ahead(strm, br, 32)) { ds->state = ST_RD_TRANSLATION_SIZE; if (last) goto failed; return (ARCHIVE_OK); } - ds->translation_size = lzx_br_bits(br, 16); + v = lzx_br_bits(br, 16); lzx_br_consume(br, 16); - ds->translation_size <<= 16; - ds->translation_size |= lzx_br_bits(br, 16); + v <<= 16; + v |= lzx_br_bits(br, 16); + if (v > MAX_FILE_SIZE) + goto failed; + ds->translation_size = (int32_t)v; lzx_br_consume(br, 16); } /* FALL THROUGH */ @@ -2558,17 +2658,17 @@ lzx_read_blocks(struct lzx_stream *strm, int last) ds->rbytes_avail = 0; if (ds->state == ST_RD_R0) { ds->r0 = archive_le32dec(ds->rbytes); - if (ds->r0 < 0) + if (ds->r0 > (size_t)INT32_MAX) goto failed; ds->state = ST_RD_R1; } else if (ds->state == ST_RD_R1) { ds->r1 = archive_le32dec(ds->rbytes); - if (ds->r1 < 0) + if (ds->r1 > (size_t)INT32_MAX) goto failed; ds->state = ST_RD_R2; } else if (ds->state == ST_RD_R2) { ds->r2 = archive_le32dec(ds->rbytes); - if (ds->r2 < 0) + if (ds->r2 > (size_t)INT32_MAX) goto failed; /* We've gotten all repeated offsets. */ ds->state = ST_COPY_UNCOMP1; @@ -2577,10 +2677,10 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* FALL THROUGH */ case ST_COPY_UNCOMP1: /* - * Copy bytes form next_in to next_out directly. + * Copy bytes from next_in to next_out directly. */ while (ds->block_bytes_avail) { - int l; + size_t l; if (strm->avail_out <= 0) /* Output buffer is empty. */ @@ -2591,13 +2691,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last) goto failed; return (ARCHIVE_OK); } - l = (int)ds->block_bytes_avail; + l = ds->block_bytes_avail; if (l > ds->w_size - ds->w_pos) l = ds->w_size - ds->w_pos; if (l > strm->avail_out) - l = (int)strm->avail_out; + l = strm->avail_out; if (l > strm->avail_in) - l = (int)strm->avail_in; + l = strm->avail_in; memcpy(strm->next_out, strm->next_in, l); memcpy(&(ds->w_buff[ds->w_pos]), strm->next_in, l); @@ -2631,19 +2731,19 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Aligned offset tree. */ - if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) { + if (!lzx_br_read_ahead(strm, br, 3 * ds->at.symbol_count)) { ds->state = ST_RD_ALIGNED_OFFSET; if (last) goto failed; return (ARCHIVE_OK); } memset(ds->at.freq, 0, sizeof(ds->at.freq)); - for (i = 0; i < ds->at.len_size; i++) { + for (i = 0; i < ds->at.symbol_count; i++) { ds->at.bitlen[i] = lzx_br_bits(br, 3); ds->at.freq[ds->at.bitlen[i]]++; lzx_br_consume(br, 3); } - if (!lzx_make_huffman_table(&ds->at)) + if (lzx_make_huffman_table(&ds->at) < 0) goto failed; /* FALL THROUGH */ case ST_RD_VERBATIM: @@ -2653,13 +2753,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Pre-tree for first 256 elements of main tree. */ - if (!lzx_read_pre_tree(strm)) { + if (lzx_read_pre_tree(strm) < 0) { ds->state = ST_RD_PRE_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } - if (!lzx_make_huffman_table(&ds->pt)) + if (lzx_make_huffman_table(&ds->pt) < 0) goto failed; ds->loop = 0; /* FALL THROUGH */ @@ -2668,27 +2768,26 @@ lzx_read_blocks(struct lzx_stream *strm, int last) * Get path lengths of first 256 elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, 256); - if (r < 0) - goto failed; - else if (!r) { + if (r == ARCHIVE_EOF) { ds->state = ST_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); - } + } else if (r < 0) + goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_REM: /* * Read Pre-tree for remaining elements of main tree. */ - if (!lzx_read_pre_tree(strm)) { + if (lzx_read_pre_tree(strm) < 0) { ds->state = ST_RD_PRE_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } - if (!lzx_make_huffman_table(&ds->pt)) + if (lzx_make_huffman_table(&ds->pt) < 0) goto failed; ds->loop = 256; /* FALL THROUGH */ @@ -2696,16 +2795,15 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Get path lengths of remaining elements of main tree. */ - r = lzx_read_bitlen(strm, &ds->mt, -1); - if (r < 0) - goto failed; - else if (!r) { + r = lzx_read_bitlen(strm, &ds->mt, 0); + if (r == ARCHIVE_EOF) { ds->state = ST_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); - } - if (!lzx_make_huffman_table(&ds->mt)) + } else if (r < 0) + goto failed; + if (lzx_make_huffman_table(&ds->mt) < 0) goto failed; ds->loop = 0; /* FALL THROUGH */ @@ -2713,13 +2811,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Pre-tree for remaining elements of main tree. */ - if (!lzx_read_pre_tree(strm)) { + if (lzx_read_pre_tree(strm) < 0) { ds->state = ST_RD_PRE_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } - if (!lzx_make_huffman_table(&ds->pt)) + if (lzx_make_huffman_table(&ds->pt) < 0) goto failed; ds->loop = 0; /* FALL THROUGH */ @@ -2727,16 +2825,15 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Get path lengths of remaining elements of main tree. */ - r = lzx_read_bitlen(strm, &ds->lt, -1); - if (r < 0) - goto failed; - else if (!r) { + r = lzx_read_bitlen(strm, &ds->lt, 0); + if (r == ARCHIVE_EOF) { ds->state = ST_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); - } - if (!lzx_make_huffman_table(&ds->lt)) + } else if (r < 0) + goto failed; + if (lzx_make_huffman_table(&ds->lt) < 0) goto failed; ds->state = ST_MAIN; return (100); @@ -2755,22 +2852,23 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl; unsigned char *noutp = strm->next_out; unsigned char *endp = noutp + strm->avail_out; - unsigned char *w_buff = ds->w_buff; - unsigned char *at_bitlen = at->bitlen; - unsigned char *lt_bitlen = lt->bitlen; - unsigned char *mt_bitlen = mt->bitlen; + uint8_t *w_buff = ds->w_buff; + uint8_t *at_bitlen = at->bitlen; + uint8_t *lt_bitlen = lt->bitlen; + uint8_t *mt_bitlen = mt->bitlen; size_t block_bytes_avail = ds->block_bytes_avail; - int at_max_bits = at->max_bits; - int lt_max_bits = lt->max_bits; - int mt_max_bits = mt->max_bits; - int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos; - int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; - int length_header = ds->length_header; - int offset_bits = ds->offset_bits; - int position_slot = ds->position_slot; - int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2; + uint8_t at_lookup_bits = at->lookup_bits; + uint8_t lt_lookup_bits = lt->lookup_bits; + uint8_t mt_lookup_bits = mt->lookup_bits; + size_t copy_len = ds->copy_len, copy_pos = ds->copy_pos; + size_t w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; + uint8_t length_header = ds->length_header; + uint8_t offset_bits = ds->offset_bits; + uint16_t position_slot = ds->position_slot; + size_t r0 = ds->r0, r1 = ds->r1, r2 = ds->r2; int state = ds->state; - char block_type = ds->block_type; + uint16_t c; + uint8_t block_type = ds->block_type; for (;;) { switch (state) { @@ -2796,26 +2894,26 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) goto next_data; if (!lzx_br_read_ahead(strm, &bre, - mt_max_bits)) { + mt_lookup_bits)) { if (!last) goto next_data; /* Remaining bits are less than - * maximum bits(mt.max_bits) but maybe - * it still remains as much as we need, - * so we should try to use it with - * dummy bits. */ + * maximum bits (mt.lookup_bits) but + * maybe it still remains as much as we + * need, so we should try to use it + * with dummy bits. */ c = lzx_decode_huffman(mt, lzx_br_bits_forced( - &bre, mt_max_bits)); - lzx_br_consume(&bre, mt_bitlen[c]); - if (!lzx_br_has(&bre, 0)) + &bre, mt_lookup_bits)); + if (!lzx_br_has(&bre, mt_bitlen[c])) goto failed;/* Over read. */ + lzx_br_consume(&bre, mt_bitlen[c]); } else { c = lzx_decode_huffman(mt, - lzx_br_bits(&bre, mt_max_bits)); + lzx_br_bits(&bre, mt_lookup_bits)); lzx_br_consume(&bre, mt_bitlen[c]); } - if ((unsigned int)c > UCHAR_MAX) + if (c > UCHAR_MAX) break; /* * 'c' is exactly literal code. @@ -2841,26 +2939,26 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) */ if (length_header == 7) { if (!lzx_br_read_ahead(strm, &bre, - lt_max_bits)) { + lt_lookup_bits)) { if (!last) { state = ST_LENGTH; goto next_data; } c = lzx_decode_huffman(lt, lzx_br_bits_forced( - &bre, lt_max_bits)); - lzx_br_consume(&bre, lt_bitlen[c]); - if (!lzx_br_has(&bre, 0)) + &bre, lt_lookup_bits)); + if (!lzx_br_has(&bre, lt_bitlen[c])) goto failed;/* Over read. */ + lzx_br_consume(&bre, lt_bitlen[c]); } else { c = lzx_decode_huffman(lt, - lzx_br_bits(&bre, lt_max_bits)); + lzx_br_bits(&bre, lt_lookup_bits)); lzx_br_consume(&bre, lt_bitlen[c]); } copy_len = c + 7 + 2; } else copy_len = length_header + 2; - if ((size_t)copy_len > block_bytes_avail) + if (copy_len > block_bytes_avail) goto failed; /* * Get an offset. @@ -2897,7 +2995,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) */ if (block_type == ALIGNED_OFFSET_BLOCK && offset_bits >= 3) { - int offbits = offset_bits - 3; + unsigned offbits = offset_bits - 3; if (!lzx_br_read_ahead(strm, &bre, offbits)) { state = ST_OFFSET; @@ -2909,7 +3007,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) /* Get an aligned number. */ if (!lzx_br_read_ahead(strm, &bre, - offbits + at_max_bits)) { + offbits + at_lookup_bits)) { if (!last) { state = ST_OFFSET; goto next_data; @@ -2917,14 +3015,14 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits_forced(&bre, - at_max_bits)); - lzx_br_consume(&bre, at_bitlen[c]); - if (!lzx_br_has(&bre, 0)) + at_lookup_bits)); + if (!lzx_br_has(&bre, at_bitlen[c])) goto failed;/* Over read. */ + lzx_br_consume(&bre, at_bitlen[c]); } else { lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, - lzx_br_bits(&bre, at_max_bits)); + lzx_br_bits(&bre, at_lookup_bits)); lzx_br_consume(&bre, at_bitlen[c]); } /* Add an aligned number. */ @@ -2940,7 +3038,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) copy_pos = lzx_br_bits(&bre, offset_bits); lzx_br_consume(&bre, offset_bits); } - copy_pos += pos_tbl[position_slot].base -2; + copy_pos += pos_tbl[position_slot].base - 2; /* Update repeated offset LRU queue. */ r2 = r1; @@ -2959,8 +3057,8 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) * into the output buffer. */ for (;;) { - const unsigned char *s; - int l; + const uint8_t *s; + size_t l; l = copy_len; if (copy_pos > w_pos) { @@ -2971,15 +3069,15 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) l = w_size - w_pos; } if (noutp + l >= endp) - l = (int)(endp - noutp); + l = endp - noutp; s = w_buff + copy_pos; if (l >= 8 && ((copy_pos + l < w_pos) || (w_pos + l < copy_pos))) { memcpy(w_buff + w_pos, s, l); memcpy(noutp, s, l); } else { - unsigned char *d; - int li; + uint8_t *d; + size_t li; d = w_buff + w_pos; for (li = 0; li < l; li++) @@ -3025,122 +3123,124 @@ lzx_read_pre_tree(struct lzx_stream *strm) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); - int i; + uint16_t i; if (ds->loop == 0) memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); - for (i = ds->loop; i < ds->pt.len_size; i++) { + for (i = ds->loop; i < ds->pt.symbol_count; i++) { if (!lzx_br_read_ahead(strm, br, 4)) { ds->loop = i; - return (0); + return (ARCHIVE_EOF); } ds->pt.bitlen[i] = lzx_br_bits(br, 4); ds->pt.freq[ds->pt.bitlen[i]]++; lzx_br_consume(br, 4); } ds->loop = i; - return (1); + return (ARCHIVE_OK); } /* * Read a bunch of bit-lengths from pre-tree. */ static int -lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end) +lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, uint16_t end) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); - int c, i, j, ret, same; - unsigned rbits; + int ret; + uint16_t c, i, j, rbits, same; i = ds->loop; if (i == 0) memset(d->freq, 0, sizeof(d->freq)); - ret = 0; - if (end < 0) - end = d->len_size; + ret = ARCHIVE_EOF; + if (end == 0) + end = d->symbol_count; while (i < end) { ds->loop = i; - if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits)) + if (!lzx_br_read_ahead(strm, br, (unsigned)ds->pt.lookup_bits)) goto getdata; - rbits = lzx_br_bits(br, ds->pt.max_bits); + rbits = lzx_br_bits(br, ds->pt.lookup_bits); c = lzx_decode_huffman(&(ds->pt), rbits); switch (c) { case 17:/* several zero lengths, from 4 to 19. */ - if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4)) + if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 4U)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 4) + 4; - if (i + same > end) - return (-1);/* Invalid */ + if (same > end - i) + return (ARCHIVE_FATAL); lzx_br_consume(br, 4); for (j = 0; j < same; j++) d->bitlen[i++] = 0; break; case 18:/* many zero lengths, from 20 to 51. */ - if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5)) + if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 5U)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 5) + 20; - if (i + same > end) - return (-1);/* Invalid */ + if (same > end - i) + return (ARCHIVE_FATAL); lzx_br_consume(br, 5); memset(d->bitlen + i, 0, same); i += same; break; case 19:/* a few same lengths. */ if (!lzx_br_read_ahead(strm, br, - ds->pt.bitlen[c]+1+ds->pt.max_bits)) + ds->pt.bitlen[c] + 1U + ds->pt.lookup_bits)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 1) + 4; - if (i + same > end) - return (-1); + if (same > end - i) + return (ARCHIVE_FATAL); lzx_br_consume(br, 1); - rbits = lzx_br_bits(br, ds->pt.max_bits); + rbits = lzx_br_bits(br, ds->pt.lookup_bits); c = lzx_decode_huffman(&(ds->pt), rbits); lzx_br_consume(br, ds->pt.bitlen[c]); - c = (d->bitlen[i] - c + 17) % 17; - if (c < 0) - return (-1);/* Invalid */ + if (c > d->bitlen[i] + 17) + return (ARCHIVE_FATAL); + c = (d->bitlen[i] + 17 - c) % 17; for (j = 0; j < same; j++) d->bitlen[i++] = c; d->freq[c] += same; break; default: lzx_br_consume(br, ds->pt.bitlen[c]); - c = (d->bitlen[i] - c + 17) % 17; - if (c < 0) - return (-1);/* Invalid */ + if (c > d->bitlen[i] + 17) + return (ARCHIVE_FATAL); + c = (d->bitlen[i] + 17 - c) % 17; d->freq[c]++; d->bitlen[i++] = c; break; } } - ret = 1; + ret = ARCHIVE_OK; getdata: ds->loop = i; return (ret); } static int -lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) +lzx_huffman_init(struct huffman *hf, uint16_t symbol_count, uint8_t tbl_bits) { + size_t tbl_size = (size_t)1 << tbl_bits; - if (hf->bitlen == NULL || hf->len_size != (int)len_size) { + if (hf->bitlen == NULL || hf->symbol_count != symbol_count) { free(hf->bitlen); - hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0])); + hf->bitlen = calloc(symbol_count, sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); - hf->len_size = (int)len_size; + hf->symbol_count = symbol_count; } else - memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0])); + memset(hf->bitlen, 0, symbol_count * sizeof(hf->bitlen[0])); if (hf->tbl == NULL) { - hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0])); + hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; - } + } else + memset(hf->tbl, 0, tbl_size * sizeof(hf->bitlen[0])); return (ARCHIVE_OK); } @@ -3152,19 +3252,47 @@ lzx_huffman_free(struct huffman *hf) } /* - * Make a huffman coding table. + * Create a direct, expanded Huffman lookup table based on + * canonical representation. */ static int lzx_make_huffman_table(struct huffman *hf) { + uint16_t bitptn[17], weight[17]; uint16_t *tbl; - const unsigned char *bitlen; - int bitptn[17], weight[17]; - int i, maxbits = 0, ptn, tbl_size, w; - int len_avail; + const uint8_t *bitlen; + uint8_t maxbits = 0; + uint32_t ptn; + uint16_t i, symbol_count, w; /* * Initialize bit patterns. + * + * Each bitptn element represents the smallest possible + * code sequence allowed. The weight represents the amount + * of lookup bit patterns covered by a code of this length. + * + * Example of a Huffman tree: + * + * idx | freq[idx] + * ----+---------- + * 1 | 1 + * 2 | 2 + * + * The result will be: + * + * idx | bitptn[idx] | weight[idx] + * ----+-------------+------------ + * 1 | 0x0000 | 32768 + * 2 | 0x8000 | 16384 + * + * This means that a code with bit length 1 will take 32768 + * possible combinations starting with 0b0. Since one such code exists + * (freq[1] = 1), codes with bit length 2 must start with 0b1. Since + * two codes with bit length 2 exist (freq[2] = 2), no more codes can + * be added. + * + * In this example, maxbits will be 2 (longest code length is 2). */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { @@ -3175,61 +3303,108 @@ lzx_make_huffman_table(struct huffman *hf) maxbits = i; } } - if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits) - return (0);/* Invalid */ - - hf->max_bits = maxbits; + /* Verify Kraft's inequality. */ + if (ptn != 0 && ptn != 0x10000) + return (ARCHIVE_FATAL); /* - * Cut out extra bits which we won't house in the table. - * This preparation reduces the same calculation in the for-loop - * making the table. + * Shrink codes to smallest size by removing extra bits after + * the actual code sequences as good as possible. + * + * Continuing the example above: + * + * idx | bitptn[idx] | weight[idx] + * ----+-------------+------------- + * 1 | 0x0000 | 32768 + * 2 | 0x8000 | 16384 + * + * As can be seen, a total of 65536 entries must be created even + * though 4 would be sufficient (indices 0b00 to 0b11). Right shift + * the pattern and divide weight as much as possible: + * + * idx | bitptn[idx] | weight[idx] + * ----+-------------+------------- + * 1 | 0x0000 | 2 + * 2 | 0x0002 | 1 + * + * Thus, the direct, expanded lookup table only needs 4 entries. */ if (maxbits < 16) { - int ebits = 16 - maxbits; + uint8_t ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } + /* Grow table if necessary. */ + if (maxbits > hf->tbl_bits) { + size_t tbl_size; + + hf->tbl_bits = 16; + tbl_size = (size_t)1 << hf->tbl_bits; + + free(hf->tbl); + hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0])); + if (hf->tbl == NULL) + return (ARCHIVE_FATAL); + } + hf->lookup_bits = maxbits; + /* - * Make the table. + * Construct the direct, expanded lookup table. + * + * Store each symbol in table for every possible bit patterns starting + * with their code. + * + * Following the example with len_avail being 4: + * + * idx | bitlen[idx] + * ----+------------ + * 0 | 0 + * 1 | 1 + * 2 | 0 + * 3 | 2 + * 4 | 2 + * + * The resulting table contains all used symbols (1, 3, 4) for up to + * max_len bit patterns: + * + * idx | tbl[idx] + * ----------+--------- + * 0 (0b00) | 1 + * 1 (0b01) | 1 + * 2 (0b10) | 3 + * 3 (0b11) | 4 */ - tbl_size = 1 << hf->tbl_bits; tbl = hf->tbl; bitlen = hf->bitlen; - len_avail = hf->len_size; - hf->tree_used = 0; - for (i = 0; i < len_avail; i++) { + symbol_count = hf->symbol_count; + for (i = 0; i < symbol_count; i++) { uint16_t *p; - int len, cnt; + uint16_t cnt; + uint8_t len; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; - if (len > tbl_size) - return (0); + if (len > maxbits) + return (ARCHIVE_FATAL); ptn = bitptn[len]; cnt = weight[len]; /* Calculate next bit pattern */ - if ((bitptn[len] = ptn + cnt) > tbl_size) - return (0);/* Invalid */ + bitptn[len] = ptn + cnt; /* Update the table */ - p = &(tbl[ptn]); - while (--cnt >= 0) - p[cnt] = (uint16_t)i; + p = tbl + ptn; + while (cnt-- > 0) + *p++ = i; } - return (1); + return (ARCHIVE_OK); } -static inline int -lzx_decode_huffman(struct huffman *hf, unsigned rbits) +static uint16_t +lzx_decode_huffman(struct huffman *hf, uint16_t rbits) { - int c; - c = hf->tbl[rbits]; - if (c < hf->len_size) - return (c); - return (0); + return hf->tbl[rbits]; } diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 526096b39f..7902cdf849 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -38,6 +38,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" @@ -160,6 +161,8 @@ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 +/* CPIO name fields store a full pathname, including the terminating NUL. */ +#define CPIO_PATHNAME_MAX (1024 * 1024) struct links_entry { struct links_entry *next; @@ -200,7 +203,7 @@ static int archive_read_format_cpio_read_data(struct archive_read *, static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); -static int64_t be4(const unsigned char *); +static int64_t be32dec(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, @@ -215,7 +218,7 @@ static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); -static int64_t le4(const unsigned char *); +static int64_t le32dec(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); @@ -258,14 +261,12 @@ archive_read_support_format_cpio(struct archive *_a) static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { + struct cpio *cpio = a->format->data; const unsigned char *p; - struct cpio *cpio; int bid; (void)best_bid; /* UNUSED */ - cpio = (struct cpio *)(a->format->data); - if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); @@ -303,12 +304,12 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ - } else if (p[0] * 256 + p[1] == 070707) { + } else if (archive_be16dec(p) == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ - } else if (p[0] + p[1] * 256 == 070707) { + } else if (archive_le16dec(p) == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; @@ -323,10 +324,9 @@ static int archive_read_format_cpio_options(struct archive_read *a, const char *key, const char *val) { - struct cpio *cpio; + struct cpio *cpio = a->format->data; int ret = ARCHIVE_FAILED; - cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ cpio->init_default_conversion = (val != NULL)?1:0; @@ -361,14 +361,14 @@ static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { - struct cpio *cpio; + struct cpio *cpio = a->format->data; const void *h, *hl; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; + int is_trailer; int r; - cpio = (struct cpio *)(a->format->data); sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { @@ -385,8 +385,15 @@ archive_read_format_cpio_read_header(struct archive_read *a, if (r < ARCHIVE_WARN) return (r); + if (namelength > CPIO_PATHNAME_MAX) { + archive_set_error(&a->archive, ENOMEM, + "Rejecting malformed cpio archive: " + "pathname exceeds 1 megabyte"); + return (ARCHIVE_FATAL); + } + /* Read name from buffer. */ - h = __archive_read_ahead(a, namelength + name_pad, NULL); + h = __archive_read_ahead(a, namelength, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, @@ -397,13 +404,17 @@ archive_read_format_cpio_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Pathname can't be converted from %s to current locale.", + "Pathname can't be converted from %s to current locale", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } + /* Save this before consuming the name buffer below. */ + is_trailer = (namelength == 11 && + memcmp((const char *)h, "TRAILER!!!", 10) == 0); cpio->entry_offset = 0; - __archive_read_consume(a, namelength + name_pad); + __archive_read_consume(a, namelength); + __archive_read_consume(a, name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { @@ -426,7 +437,7 @@ archive_read_format_cpio_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname can't be converted from %s to " - "current locale.", + "current locale", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } @@ -440,8 +451,7 @@ archive_read_format_cpio_read_header(struct archive_read *a, * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ - if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", - 10) == 0) { + if (is_trailer) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); @@ -459,10 +469,8 @@ static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct cpio *cpio = a->format->data; ssize_t bytes_read; - struct cpio *cpio; - - cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_unconsumed) { __archive_read_consume(a, cpio->entry_bytes_unconsumed); @@ -497,7 +505,7 @@ archive_read_format_cpio_read_data(struct archive_read *a, static int archive_read_format_cpio_skip(struct archive_read *a) { - struct cpio *cpio = (struct cpio *)(a->format->data); + struct cpio *cpio = a->format->data; int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding + cpio->entry_bytes_unconsumed; @@ -534,7 +542,8 @@ find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, skipped = 0; + int64_t skip; + uintmax_t skipped = 0; ssize_t bytes; for (;;) { @@ -566,9 +575,9 @@ find_newc_header(struct archive_read *a) if (skipped > 0) { archive_set_error(&a->archive, 0, - "Skipped %d bytes before " + "Skipped %ju bytes before " "finding valid header", - (int)skipped); + skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); @@ -703,13 +712,24 @@ find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; - size_t skip, skipped = 0; + int64_t skip; + uintmax_t skipped = 0; ssize_t bytes; for (;;) { - h = __archive_read_ahead(a, odc_header_size, &bytes); - if (h == NULL) - return (ARCHIVE_FATAL); + size_t header_size; + + header_size = afiol_header_size; + h = __archive_read_ahead(a, afiol_header_size, &bytes); + if (h == NULL) { + if (bytes >= odc_header_size) { + header_size = odc_header_size; + h = __archive_read_ahead(a, odc_header_size, + &bytes); + } + if (h == NULL) + return (ARCHIVE_FATAL); + } p = h; q = p + bytes; @@ -725,7 +745,7 @@ find_odc_header(struct archive_read *a) * Scan ahead until we find something that looks * like an odc header. */ - while (p + odc_header_size <= q) { + while (p + header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 @@ -741,9 +761,9 @@ find_odc_header(struct archive_read *a) if (skipped > 0) { archive_set_error(&a->archive, 0, - "Skipped %d bytes before " + "Skipped %ju bytes before " "finding valid header", - (int)skipped); + skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); @@ -828,7 +848,7 @@ header_odc(struct archive_read *a, struct cpio *cpio, * NOTE: if a filename suffix is ".z", it is a file gzipped by afio. * it would be nice if we could show uncompressed file size and * uncompress file contents automatically, unfortunately we have nothing - * to get a uncompressed file size while reading each header. It means + * to get an uncompressed file size while reading each header. It means * we also cannot uncompress file contents under our framework. */ static int @@ -873,7 +893,8 @@ header_afiol(struct archive_read *a, struct cpio *cpio, t = atol16(header + afiol_filesize_offset, afiol_filesize_size); if (t < 0) { - archive_set_error(&a->archive, 0, "Nonsensical file size"); + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Nonsensical file size"); return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = t; @@ -897,7 +918,7 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } @@ -905,24 +926,24 @@ header_bin_le(struct archive_read *a, struct cpio *cpio, /* Parse out binary fields. */ header = (const unsigned char *)h; - archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); - archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); - archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); + archive_entry_set_dev(entry, archive_le16dec(header + bin_dev_offset)); + archive_entry_set_ino(entry, archive_le16dec(header + bin_ino_offset)); + archive_entry_set_mode(entry, archive_le16dec(header + bin_mode_offset)); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } - archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); - archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); - archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); - archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); - archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); - *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; + archive_entry_set_uid(entry, archive_le16dec(header + bin_uid_offset)); + archive_entry_set_gid(entry, archive_le16dec(header + bin_gid_offset)); + archive_entry_set_nlink(entry, archive_le16dec(header + bin_nlink_offset)); + archive_entry_set_rdev(entry, archive_le16dec(header + bin_rdev_offset)); + archive_entry_set_mtime(entry, le32dec(header + bin_mtime_offset), 0); + *namelength = archive_le16dec(header + bin_namesize_offset); *name_pad = *namelength & 1; /* Pad to even. */ - cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); + cpio->entry_bytes_remaining = le32dec(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); @@ -942,7 +963,7 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } @@ -950,24 +971,24 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, /* Parse out binary fields. */ header = (const unsigned char *)h; - archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); - archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); - archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); + archive_entry_set_dev(entry, archive_be16dec(header + bin_dev_offset)); + archive_entry_set_ino(entry, archive_be16dec(header + bin_ino_offset)); + archive_entry_set_mode(entry, archive_be16dec(header + bin_mode_offset)); if (cpio->option_pwb) { /* turn off random bits left over from V6 inode */ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777); if ((archive_entry_mode(entry) & AE_IFMT) == 0) archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG); } - archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); - archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); - archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); - archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); - archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); - *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; + archive_entry_set_uid(entry, archive_be16dec(header + bin_uid_offset)); + archive_entry_set_gid(entry, archive_be16dec(header + bin_gid_offset)); + archive_entry_set_nlink(entry, archive_be16dec(header + bin_nlink_offset)); + archive_entry_set_rdev(entry, archive_be16dec(header + bin_rdev_offset)); + archive_entry_set_mtime(entry, be32dec(header + bin_mtime_offset), 0); + *namelength = archive_be16dec(header + bin_namesize_offset); *name_pad = *namelength & 1; /* Pad to even. */ - cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); + cpio->entry_bytes_remaining = be32dec(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); @@ -977,9 +998,8 @@ header_bin_be(struct archive_read *a, struct cpio *cpio, static int archive_read_format_cpio_cleanup(struct archive_read *a) { - struct cpio *cpio; + struct cpio *cpio = a->format->data; - cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; @@ -989,21 +1009,20 @@ archive_read_format_cpio_cleanup(struct archive_read *a) cpio->links_head = lp; } free(cpio); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } static int64_t -le4(const unsigned char *p) +le32dec(const unsigned char *p) { - return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8)); + return ((int64_t)archive_le16dec(p) << 16) | archive_le16dec(p + 2); } - static int64_t -be4(const unsigned char *p) +be32dec(const unsigned char *p) { - return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3])); + return ((int64_t)archive_be16dec(p) << 16) | archive_be16dec(p + 2); } /* @@ -1051,7 +1070,7 @@ atol16u(const char *p, unsigned char_cnt) else if (*p >= '0' && *p <= '9') digit = *p - '0'; else - return ((int64_t)l); + return (l); p++; l <<= 4; l |= digit; @@ -1102,20 +1121,32 @@ record_hardlink(struct archive_read *a, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } + + const char *pathname = archive_entry_pathname(entry); + if (pathname == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid hardlink entry with no pathname"); + free(le); + return (ARCHIVE_FATAL); + } + + le->dev = dev; + le->ino = ino; + le->links = archive_entry_nlink(entry) - 1; + le->name = strdup(pathname); + if (le->name == NULL) { + archive_set_error(&a->archive, + ENOMEM, "Out of memory adding file to list"); + free(le); + return (ARCHIVE_FATAL); + } + if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; - le->dev = dev; - le->ino = ino; - le->links = archive_entry_nlink(entry) - 1; - le->name = strdup(archive_entry_pathname(entry)); - if (le->name == NULL) { - archive_set_error(&a->archive, - ENOMEM, "Out of memory adding file to list"); - return (ARCHIVE_FATAL); - } return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index 1635228d50..c3da44c826 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -47,6 +47,7 @@ #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" @@ -268,6 +269,7 @@ struct file_info { uint64_t size; /* File size in bytes. */ uint32_t ce_offset; /* Offset of CE. */ uint32_t ce_size; /* Size of CE. */ + uint64_t ce_processed_end;/* End offset of processed CE. */ char rr_moved; /* Flag to rr_moved. */ char rr_moved_has_re_only; char re; /* Having RRIP "RE" extension. */ @@ -313,8 +315,8 @@ struct file_info { struct heap_queue { struct file_info **files; - int allocated; - int used; + size_t allocated; + size_t used; }; struct iso9660 { @@ -336,8 +338,8 @@ struct iso9660 { uint64_t offset;/* Offset of CE on disk. */ struct file_info *file; } *reqs; - int cnt; - int allocated; + size_t cnt; + size_t allocated; } read_ce_req; int64_t previous_number; @@ -378,8 +380,6 @@ struct iso9660 { size_t utf16be_path_len; unsigned char *utf16be_previous_path; size_t utf16be_previous_path_len; - /* Null buffer used in bidder to improve its performance. */ - unsigned char null[2048]; }; static int archive_read_format_iso9660_bid(struct archive_read *, int); @@ -402,12 +402,12 @@ static time_t isodate17(const unsigned char *); static int isodate17_valid(const unsigned char *); static time_t isodate7(const unsigned char *); static int isodate7_valid(const unsigned char *); -static int isBootRecord(struct iso9660 *, const unsigned char *); +static int isBootRecord(const unsigned char *); static int isVolumePartition(struct iso9660 *, const unsigned char *); -static int isVDSetTerminator(struct iso9660 *, const unsigned char *); +static int isVDSetTerminator(const unsigned char *); static int isJolietSVD(struct iso9660 *, const unsigned char *); -static int isSVD(struct iso9660 *, const unsigned char *); -static int isEVD(struct iso9660 *, const unsigned char *); +static int isSVD(const unsigned char *); +static int isEVD(const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); static int isRootDirectoryRecord(const unsigned char *); static int isValid723Integer(const unsigned char *); @@ -436,7 +436,6 @@ static void parse_rockridge_ZF1(struct file_info *, const unsigned char *, int); static void register_file(struct iso9660 *, struct file_info *); static void release_files(struct iso9660 *); -static unsigned toi(const void *p, int n); static inline void re_add_entry(struct iso9660 *, struct file_info *); static inline struct file_info * re_get_entry(struct iso9660 *); static inline int rede_add_entry(struct file_info *); @@ -503,7 +502,7 @@ archive_read_support_format_iso9660(struct archive *_a) static int archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; ssize_t bytes_read; const unsigned char *p; int seenTerminator; @@ -513,8 +512,6 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) if (best_bid > 48) return (-1); - iso9660 = (struct iso9660 *)(a->format->data); - /* * Skip the first 32k (reserved area) and get the first * 8 sectors of the volume descriptor table. Of course, @@ -547,15 +544,15 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) if (isJolietSVD(iso9660, p)) continue; } - if (isBootRecord(iso9660, p)) + if (isBootRecord(p)) continue; - if (isEVD(iso9660, p)) + if (isEVD(p)) continue; - if (isSVD(iso9660, p)) + if (isSVD(p)) continue; if (isVolumePartition(iso9660, p)) continue; - if (isVDSetTerminator(iso9660, p)) { + if (isVDSetTerminator(p)) { seenTerminator = 1; break; } @@ -576,9 +573,7 @@ static int archive_read_format_iso9660_options(struct archive_read *a, const char *key, const char *val) { - struct iso9660 *iso9660; - - iso9660 = (struct iso9660 *)(a->format->data); + struct iso9660 *iso9660 = a->format->data; if (strcmp(key, "joliet") == 0) { if (val == NULL || strcmp(val, "off") == 0 || @@ -603,26 +598,23 @@ archive_read_format_iso9660_options(struct archive_read *a, } static int -isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset, -unsigned bytes) +isNull(const unsigned char *h, unsigned offset, unsigned bytes) { - - while (bytes >= sizeof(iso9660->null)) { - if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null))) - return (0); - offset += sizeof(iso9660->null); - bytes -= sizeof(iso9660->null); - } - if (bytes) - return memcmp(iso9660->null, h + offset, bytes) == 0; - else + if (bytes == 0) return (1); + + /* + * If the first byte is zero and every byte equals the following + * byte, the entire range is zero. + */ + return (h[offset] == 0 && + (bytes == 1 || + memcmp(h + offset, h + offset + 1, bytes - 1) == 0)); } static int -isBootRecord(struct iso9660 *iso9660, const unsigned char *h) +isBootRecord(const unsigned char *h) { - (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Boot Record must be 0. */ if (h[0] != 0) @@ -662,9 +654,8 @@ isVolumePartition(struct iso9660 *iso9660, const unsigned char *h) } static int -isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) +isVDSetTerminator(const unsigned char *h) { - (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Set Terminator must be 255. */ if (h[0] != 255) @@ -675,7 +666,7 @@ isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, 7, 2048-7)) + if (!isNull(h, 7, 2048-7)) return (0); return (1); @@ -690,7 +681,7 @@ isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) /* Check if current sector is a kind of Supplementary Volume * Descriptor. */ - if (!isSVD(iso9660, h)) + if (!isSVD(h)) return (0); /* FIXME: do more validations according to joliet spec. */ @@ -731,25 +722,24 @@ isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) } static int -isSVD(struct iso9660 *iso9660, const unsigned char *h) +isSVD(const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; - (void)iso9660; /* UNUSED */ /* Type 2 means it's a SVD. */ if (h[SVD_type_offset] != 2) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size)) + if (!isNull(h, SVD_reserved1_offset, SVD_reserved1_size)) return (0); - if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size)) + if (!isNull(h, SVD_reserved2_offset, SVD_reserved2_size)) return (0); - if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size)) + if (!isNull(h, SVD_reserved3_offset, SVD_reserved3_size)) return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ @@ -791,14 +781,13 @@ isSVD(struct iso9660 *iso9660, const unsigned char *h) } static int -isEVD(struct iso9660 *iso9660, const unsigned char *h) +isEVD(const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; - (void)iso9660; /* UNUSED */ /* Type of the Enhanced Volume Descriptor must be 2. */ if (h[PVD_type_offset] != 2) @@ -813,11 +802,11 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) + if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) + if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Logical block size must be > 0. */ @@ -853,11 +842,11 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size)) + if (!isNull(h, PVD_reserved4_offset, PVD_reserved4_size)) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) + if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* Read Root Directory Record in Volume Descriptor. */ @@ -891,7 +880,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) + if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Volume space size must be encoded according to 7.3.3 */ @@ -903,7 +892,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) + if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Volume set size must be encoded according to 7.2.3 */ @@ -961,7 +950,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) + if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* XXX TODO: Check other values for sanity; reject more @@ -1034,12 +1023,11 @@ isRootDirectoryRecord(const unsigned char *p) { static int read_children(struct archive_read *a, struct file_info *parent) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; const unsigned char *b, *p; struct file_info *multi; size_t step, skip_size; - iso9660 = (struct iso9660 *)(a->format->data); /* flush any remaining bytes from the last round to ensure * we're positioned */ if (iso9660->entry_bytes_unconsumed) { @@ -1086,7 +1074,7 @@ read_children(struct archive_read *a, struct file_info *parent) p = b; b += iso9660->logical_block_size; step -= iso9660->logical_block_size; - for (; *p != 0 && p + DR_name_offset < b && p + *p <= b; + for (; p < b && b - p > DR_name_offset && *p != 0 && *p <= b - p; p += *p) { struct file_info *child; @@ -1247,12 +1235,10 @@ static int archive_read_format_iso9660_read_header(struct archive_read *a, struct archive_entry *entry) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; struct file_info *file; int r, rd_r = ARCHIVE_OK; - iso9660 = (struct iso9660 *)(a->format->data); - if (!a->archive.archive_format) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; @@ -1322,7 +1308,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name( iso9660->sconv_utf16be)); @@ -1400,7 +1386,7 @@ archive_read_format_iso9660_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name( iso9660->sconv_utf16be)); rd_r = ARCHIVE_WARN; @@ -1499,7 +1485,7 @@ static int zisofs_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; struct zisofs *zisofs; const unsigned char *p; size_t avail; @@ -1507,7 +1493,6 @@ zisofs_read_data(struct archive_read *a, size_t uncompressed_size; int r; - iso9660 = (struct iso9660 *)(a->format->data); zisofs = &iso9660->entry_zisofs; p = __archive_read_ahead(a, 1, &bytes_read); @@ -1663,7 +1648,7 @@ zisofs_read_data(struct archive_read *a, r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize zisofs decompression."); + "Can't initialize zisofs decompression"); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; @@ -1728,7 +1713,7 @@ zisofs_read_data(struct archive_read *a, (void)size;/* UNUSED */ (void)offset;/* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "zisofs is not supported on this platform."); + "zisofs is not supported on this platform"); return (ARCHIVE_FAILED); } @@ -1738,10 +1723,8 @@ static int archive_read_format_iso9660_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct iso9660 *iso9660 = a->format->data; ssize_t bytes_read; - struct iso9660 *iso9660; - - iso9660 = (struct iso9660 *)(a->format->data); if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); @@ -1805,10 +1788,9 @@ archive_read_format_iso9660_read_data(struct archive_read *a, static int archive_read_format_iso9660_cleanup(struct archive_read *a) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; int r = ARCHIVE_OK; - iso9660 = (struct iso9660 *)(a->format->data); release_files(iso9660); free(iso9660->read_ce_req.reqs); archive_string_free(&iso9660->pathname); @@ -1828,7 +1810,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a) free(iso9660->utf16be_path); free(iso9660->utf16be_previous_path); free(iso9660); - (a->format->data) = NULL; + a->format->data = NULL; return (r); } @@ -1840,7 +1822,7 @@ static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec, size_t reclen) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; struct file_info *file, *filep; size_t name_len; const unsigned char *rr_start, *rr_end; @@ -1850,8 +1832,6 @@ parse_file_info(struct archive_read *a, struct file_info *parent, int32_t location; int flags; - iso9660 = (struct iso9660 *)(a->format->data); - if (reclen != 0) dr_len = (size_t)isodirrec[DR_length_offset]; /* @@ -1865,7 +1845,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, } name_len = (size_t)isodirrec[DR_name_len_offset]; location = archive_le32dec(isodirrec + DR_extent_offset); - fsize = toi(isodirrec + DR_size_offset, DR_size_size); + fsize = archive_le32dec(isodirrec + DR_size_offset); /* Sanity check that name_len doesn't exceed dr_len. */ if (dr_len - 33 < name_len || name_len == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2162,7 +2142,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, fprintf(stderr, "\n ** Unrecognized flag: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); - } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { + } else if (archive_le16dec(isodirrec + DR_volume_sequence_number_offset) != 1) { fprintf(stderr, "\n ** Unrecognized sequence number: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); @@ -2192,11 +2172,9 @@ static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *p, const unsigned char *end) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; int entry_seen = 0; - iso9660 = (struct iso9660 *)(a->format->data); - while (p + 4 <= end /* Enough space for another entry. */ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ @@ -2255,9 +2233,10 @@ parse_rockridge(struct archive_read *a, struct file_info *file, */ if (p[1] == 'N') { if (version == 1 && data_length == 16) { - file->rdev = toi(data,4); + file->rdev = archive_le32dec(data); file->rdev <<= 32; - file->rdev |= toi(data + 8, 4); + file->rdev |= + archive_le32dec(data + 8); iso9660->seenRockridge = 1; } } @@ -2272,20 +2251,20 @@ parse_rockridge(struct archive_read *a, struct file_info *file, */ if (version == 1) { if (data_length >= 8) - file->mode - = (__LA_MODE_T)toi(data, 4); + file->mode = (__LA_MODE_T) + archive_le32dec(data); if (data_length >= 16) - file->nlinks - = toi(data + 8, 4); + file->nlinks = + archive_le32dec(data + 8); if (data_length >= 24) - file->uid - = toi(data + 16, 4); + file->uid = + archive_le32dec(data + 16); if (data_length >= 32) - file->gid - = toi(data + 24, 4); + file->gid = + archive_le32dec(data + 24); if (data_length >= 40) - file->number - = toi(data + 32, 4); + file->number = + archive_le32dec(data + 32); iso9660->seenRockridge = 1; } } @@ -2365,13 +2344,11 @@ static int register_CE(struct archive_read *a, int32_t location, struct file_info *file) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format->data; struct read_ce_queue *heap; - struct read_ce_req *p; uint64_t offset, parent_offset; - int hole, parent; + size_t hole, parent; - iso9660 = (struct iso9660 *)(a->format->data); offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size; if (((file->mode & AE_IFMT) == AE_IFREG && offset >= file->offset) || @@ -2388,14 +2365,13 @@ register_CE(struct archive_read *a, int32_t location, /* Expand our CE list as necessary. */ heap = &(iso9660->read_ce_req); if (heap->cnt >= heap->allocated) { - int new_size; + struct read_ce_req *p; + size_t new_size; if (heap->allocated < 16) new_size = 16; - else - new_size = heap->allocated * 2; - /* Overflow might keep us from growing the list. */ - if (new_size <= heap->allocated) { + else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) { + /* Overflow keeps us from growing the list. */ archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } @@ -2437,7 +2413,7 @@ static void next_CE(struct read_ce_queue *heap) { uint64_t a_offset, b_offset, c_offset; - int a, b, c; + size_t a, b, c; struct read_ce_req tmp; if (heap->cnt < 1) @@ -2483,6 +2459,7 @@ read_CE(struct archive_read *a, struct iso9660 *iso9660) const unsigned char *b, *p, *end; struct file_info *file; size_t step; + uint64_t ce_start, ce_end; int r; /* Read data which RRIP "CE" extension points. */ @@ -2506,8 +2483,16 @@ read_CE(struct archive_read *a, struct iso9660 *iso9660) "Malformed CE information"); return (ARCHIVE_FATAL); } + ce_start = heap->reqs[0].offset + file->ce_offset; + ce_end = ce_start + file->ce_size; + if (ce_start < file->ce_processed_end) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid parameter in SUSP \"CE\" extension"); + return (ARCHIVE_FATAL); + } p = b + file->ce_offset; end = p + file->ce_size; + file->ce_processed_end = ce_end; next_CE(heap); r = parse_rockridge(a, file, p, end); if (r != ARCHIVE_OK) @@ -2755,12 +2740,20 @@ parse_rockridge_ZF1(struct file_info *file, const unsigned char *data, int data_length) { - if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) { - /* paged zlib */ - file->pz = 1; - file->pz_log2_bs = data[3]; - file->pz_uncompressed_size = archive_le32dec(&data[4]); - } + if (data_length == 12 && data[0] == 0x70 && data[1] == 0x7a) { + /* paged zlib */ + file->pz = 1; + file->pz_log2_bs = data[3]; + if (file->pz_log2_bs < 15 || file->pz_log2_bs > 17) { + /* TODO: Return an error here instead of silently + * disabling zisofs. That requires propagating an + * error return through parse_rockridge() and its + * callers. */ + file->pz = 0; + return; + } + file->pz_uncompressed_size = archive_le32dec(&data[4]); + } } static void @@ -3126,7 +3119,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct file_info *file, uint64_t key) { uint64_t file_key, parent_key; - int hole, parent; + size_t hole, parent; /* Reserve 16 bits for possible key collisions (needed for linked items) */ /* For ISO files with more than 65535 entries, reordering will still occur */ @@ -3136,12 +3129,12 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct file_info **new_pending_files; - int new_size = heap->allocated * 2; + size_t new_size; if (heap->allocated < 1024) new_size = 1024; - /* Overflow might keep us from growing the list. */ - if (new_size <= heap->allocated) { + else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) { + /* Overflow keeps us from growing the list. */ archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); @@ -3168,7 +3161,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, */ hole = heap->used++; while (hole > 0) { - parent = (hole - 1)/2; + parent = (hole - 1) / 2; parent_key = heap->files[parent]->key; if (file_key >= parent_key) { heap->files[hole] = file; @@ -3187,7 +3180,7 @@ static struct file_info * heap_get_entry(struct heap_queue *heap) { uint64_t a_key, b_key, c_key; - int a, b, c; + size_t a, b, c; struct file_info *r, *tmp; if (heap->used < 1) @@ -3230,17 +3223,6 @@ heap_get_entry(struct heap_queue *heap) } } -static unsigned int -toi(const void *p, int n) -{ - const unsigned char *v = (const unsigned char *)p; - if (n > 1) - return v[0] + 256 * toi(v + 1, n - 1); - if (n == 1) - return v[0]; - return (0); -} - /* * ECMA119/ISO9660 stores multi-byte integers in one of * three different formats: @@ -3485,6 +3467,8 @@ build_pathname_utf16be(unsigned char *p, size_t max, size_t *len, if (file->parent != NULL && file->parent->utf16be_bytes > 0) { if (build_pathname_utf16be(p, max, len, file->parent) != 0) return (-1); + if (*len + 2 > max) + return (-1);/* Path is too long! */ p[*len] = 0; p[*len + 1] = '/'; *len += 2; @@ -3509,26 +3493,24 @@ static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { fprintf(out, " l %d,", - toi(isodirrec + DR_length_offset, DR_length_size)); + isodirrec[DR_length_offset]); fprintf(out, " a %d,", - toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); + isodirrec[DR_ext_attr_length_offset]); fprintf(out, " ext 0x%x,", - toi(isodirrec + DR_extent_offset, DR_extent_size)); + archive_le32dec(isodirrec + DR_extent_offset)); fprintf(out, " s %d,", - toi(isodirrec + DR_size_offset, DR_extent_size)); + archive_le32dec(isodirrec + DR_size_offset)); fprintf(out, " f 0x%x,", - toi(isodirrec + DR_flags_offset, DR_flags_size)); + isodirrec[DR_flags_offset]); fprintf(out, " u %d,", - toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); + isodirrec[DR_file_unit_size_offset]); fprintf(out, " ilv %d,", - toi(isodirrec + DR_interleave_offset, DR_interleave_size)); + isodirrec[DR_interleave_offset]); fprintf(out, " seq %d,", - toi(isodirrec + DR_volume_sequence_number_offset, - DR_volume_sequence_number_size)); + archive_le16dec(isodirrec + DR_volume_sequence_number_offset)); fprintf(out, " nl %d:", - toi(isodirrec + DR_name_len_offset, DR_name_len_size)); + isodirrec[DR_name_len_offset]); fprintf(out, " `%.*s'", - toi(isodirrec + DR_name_len_offset, DR_name_len_size), - isodirrec + DR_name_offset); + isodirrec[DR_name_len_offset], isodirrec + DR_name_offset); } #endif diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index ccdf49007c..0a47546157 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -107,8 +107,8 @@ struct lzh_dec { unsigned char *bitlen; /* - * Use a index table. It's faster than searching a huffman - * coding tree, which is a binary tree. But a use of a large + * Use an index table. It's faster than searching a huffman + * coding tree, which is a binary tree. But usage of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 @@ -142,7 +142,6 @@ struct lzh_stream { int64_t total_in; const unsigned char *ref_ptr; int avail_out; - int64_t total_out; struct lzh_dec *ds; }; @@ -239,7 +238,6 @@ 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 *); @@ -392,10 +390,9 @@ static int archive_read_format_lha_options(struct archive_read *a, const char *key, const char *val) { - struct lha *lha; + struct lha *lha = a->format->data; int ret = ARCHIVE_FAILED; - lha = (struct lha *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -474,22 +471,19 @@ static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { + struct lha *lha = a->format->data; struct archive_wstring linkname; struct archive_wstring pathname; - struct lha *lha; const unsigned char *p; const char *signature; int err; struct archive_mstring conv_buffer; const wchar_t *conv_buffer_p; - lha_crc16_init(); - a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; - lha = (struct lha *)(a->format->data); lha->decompress_init = 0; lha->end_of_entry = 0; lha->end_of_entry_cleanup = 0; @@ -613,7 +607,7 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to Unicode.", + "from %s to Unicode", archive_string_conversion_charset_name(lha->sconv_dir)); err = ARCHIVE_FATAL; } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) @@ -634,7 +628,7 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to Unicode.", + "from %s to Unicode", archive_string_conversion_charset_name(lha->sconv_fname)); err = ARCHIVE_FATAL; } @@ -904,7 +898,7 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; - int i, err, err2; + int err, err2; int namelen, padding; unsigned char headersum, sum_calculated; @@ -929,10 +923,9 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha) if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); - for (i = 0; i < namelen; i++) { - if (p[i + H1_FILE_NAME_OFFSET] == 0xff) - goto invalid;/* Invalid filename. */ - } + if (memchr(p + H1_FILE_NAME_OFFSET, 0xff, + (size_t)namelen) != NULL) + goto invalid; /* Invalid filename. */ archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen); lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; @@ -1101,7 +1094,7 @@ lha_read_file_header_3(struct archive_read *a, struct lha *lha) header_crc = lha_crc16(0, p, H3_FIXED_SIZE); __archive_read_consume(a, H3_FIXED_SIZE); - /* Reject rediculously large header */ + /* Reject ridiculously large header */ if (lha->header_size > 65536) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header size too large"); @@ -1456,7 +1449,7 @@ invalid: static int lha_end_of_entry(struct archive_read *a) { - struct lha *lha = (struct lha *)(a->format->data); + struct lha *lha = a->format->data; int r = ARCHIVE_EOF; if (!lha->end_of_entry_cleanup) { @@ -1477,7 +1470,7 @@ static int archive_read_format_lha_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct lha *lha = (struct lha *)(a->format->data); + struct lha *lha = a->format->data; int r; if (lha->entry_unconsumed) { @@ -1510,7 +1503,7 @@ static int lha_read_data_none(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct lha *lha = (struct lha *)(a->format->data); + struct lha *lha = a->format->data; ssize_t bytes_avail; if (lha->entry_bytes_remaining == 0) { @@ -1557,7 +1550,7 @@ static int lha_read_data_lzh(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct lha *lha = (struct lha *)(a->format->data); + struct lha *lha = a->format->data; ssize_t bytes_avail; int r; @@ -1588,7 +1581,6 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, /* We've initialized decompression for this stream. */ lha->decompress_init = 1; lha->strm.avail_out = 0; - lha->strm.total_out = 0; } /* @@ -1648,11 +1640,9 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, static int archive_read_format_lha_read_data_skip(struct archive_read *a) { - struct lha *lha; + struct lha *lha = a->format->data; int64_t bytes_skipped; - lha = (struct lha *)(a->format->data); - if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); @@ -1679,7 +1669,7 @@ archive_read_format_lha_read_data_skip(struct archive_read *a) static int archive_read_format_lha_cleanup(struct archive_read *a) { - struct lha *lha = (struct lha *)(a->format->data); + struct lha *lha = a->format->data; lzh_decode_free(&(lha->strm)); archive_string_free(&(lha->dirname)); @@ -1688,7 +1678,7 @@ archive_read_format_lha_cleanup(struct archive_read *a) archive_string_free(&(lha->gname)); archive_wstring_free(&(lha->ws)); free(lha); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } @@ -1733,30 +1723,86 @@ lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) return (sum); } -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; +static const uint16_t crc16tbl[2][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 + }, + { + 0x0000, 0x9001, 0x6001, 0xf000, 0xc002, 0x5003, 0xa003, + 0x3002, 0xc007, 0x5006, 0xa006, 0x3007, 0x0005, 0x9004, + 0x6004, 0xf005, 0xc00d, 0x500c, 0xa00c, 0x300d, 0x000f, + 0x900e, 0x600e, 0xf00f, 0x000a, 0x900b, 0x600b, 0xf00a, + 0xc008, 0x5009, 0xa009, 0x3008, 0xc019, 0x5018, 0xa018, + 0x3019, 0x001b, 0x901a, 0x601a, 0xf01b, 0x001e, 0x901f, + 0x601f, 0xf01e, 0xc01c, 0x501d, 0xa01d, 0x301c, 0x0014, + 0x9015, 0x6015, 0xf014, 0xc016, 0x5017, 0xa017, 0x3016, + 0xc013, 0x5012, 0xa012, 0x3013, 0x0011, 0x9010, 0x6010, + 0xf011, 0xc031, 0x5030, 0xa030, 0x3031, 0x0033, 0x9032, + 0x6032, 0xf033, 0x0036, 0x9037, 0x6037, 0xf036, 0xc034, + 0x5035, 0xa035, 0x3034, 0x003c, 0x903d, 0x603d, 0xf03c, + 0xc03e, 0x503f, 0xa03f, 0x303e, 0xc03b, 0x503a, 0xa03a, + 0x303b, 0x0039, 0x9038, 0x6038, 0xf039, 0x0028, 0x9029, + 0x6029, 0xf028, 0xc02a, 0x502b, 0xa02b, 0x302a, 0xc02f, + 0x502e, 0xa02e, 0x302f, 0x002d, 0x902c, 0x602c, 0xf02d, + 0xc025, 0x5024, 0xa024, 0x3025, 0x0027, 0x9026, 0x6026, + 0xf027, 0x0022, 0x9023, 0x6023, 0xf022, 0xc020, 0x5021, + 0xa021, 0x3020, 0xc061, 0x5060, 0xa060, 0x3061, 0x0063, + 0x9062, 0x6062, 0xf063, 0x0066, 0x9067, 0x6067, 0xf066, + 0xc064, 0x5065, 0xa065, 0x3064, 0x006c, 0x906d, 0x606d, + 0xf06c, 0xc06e, 0x506f, 0xa06f, 0x306e, 0xc06b, 0x506a, + 0xa06a, 0x306b, 0x0069, 0x9068, 0x6068, 0xf069, 0x0078, + 0x9079, 0x6079, 0xf078, 0xc07a, 0x507b, 0xa07b, 0x307a, + 0xc07f, 0x507e, 0xa07e, 0x307f, 0x007d, 0x907c, 0x607c, + 0xf07d, 0xc075, 0x5074, 0xa074, 0x3075, 0x0077, 0x9076, + 0x6076, 0xf077, 0x0072, 0x9073, 0x6073, 0xf072, 0xc070, + 0x5071, 0xa071, 0x3070, 0x0050, 0x9051, 0x6051, 0xf050, + 0xc052, 0x5053, 0xa053, 0x3052, 0xc057, 0x5056, 0xa056, + 0x3057, 0x0055, 0x9054, 0x6054, 0xf055, 0xc05d, 0x505c, + 0xa05c, 0x305d, 0x005f, 0x905e, 0x605e, 0xf05f, 0x005a, + 0x905b, 0x605b, 0xf05a, 0xc058, 0x5059, 0xa059, 0x3058, + 0xc049, 0x5048, 0xa048, 0x3049, 0x004b, 0x904a, 0x604a, + 0xf04b, 0x004e, 0x904f, 0x604f, 0xf04e, 0xc04c, 0x504d, + 0xa04d, 0x304c, 0x0044, 0x9045, 0x6045, 0xf044, 0xc046, + 0x5047, 0xa047, 0x3046, 0xc043, 0x5042, 0xa042, 0x3043, + 0x0041, 0x9040, 0x6040, 0xf041 } - - 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) @@ -2084,7 +2130,6 @@ lzh_emit_window(struct lzh_stream *strm, size_t s) { strm->ref_ptr = strm->ds->w_buff; strm->avail_out = (int)s; - strm->total_out += s; } static int diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index 4a5a49ca81..dab580a303 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -51,6 +51,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_private.h" +#include "archive_integer.h" #include "archive_platform_stat.h" #include "archive_private.h" #include "archive_rb.h" @@ -140,56 +141,6 @@ static int64_t mtree_atol(char **, int base); static size_t mtree_strnlen(const char *, size_t); #endif -/* - * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them - * here. TODO: Move this to configure time, but be careful - * about cross-compile environments. - */ -static int64_t -get_time_t_max(void) -{ -#if defined(TIME_T_MAX) - return TIME_T_MAX; -#else - /* 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; - } - } -#endif -} - -static int64_t -get_time_t_min(void) -{ -#if defined(TIME_T_MIN) - return TIME_T_MIN; -#else - 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; - } - } -#endif -} - #ifdef HAVE_STRNLEN #define mtree_strnlen(a,b) strnlen(a,b) #else @@ -198,12 +149,10 @@ mtree_strnlen(const char *p, size_t maxlen) { size_t i; - for (i = 0; i <= maxlen; i++) { + for (i = 0; i < maxlen; i++) { if (p[i] == 0) break; } - if (i > maxlen) - return (-1);/* invalid */ return (i); } #endif @@ -212,9 +161,8 @@ static int archive_read_format_mtree_options(struct archive_read *a, const char *key, const char *val) { - struct mtree *mtree; + struct mtree *mtree = a->format->data; - 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) { @@ -296,11 +244,9 @@ archive_read_support_format_mtree(struct archive *_a) static int cleanup(struct archive_read *a) { - struct mtree *mtree; + struct mtree *mtree = a->format->data; struct mtree_entry *p, *q; - mtree = (struct mtree *)(a->format->data); - /* Close any dangling file descriptor before freeing */ if (mtree->fd >= 0) { close(mtree->fd); @@ -321,7 +267,7 @@ cleanup(struct archive_read *a) free(mtree->buff); free(mtree); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } @@ -386,7 +332,7 @@ next_line(struct archive_read *a, */ while (*nl == 0 && len == *avail && !quit) { ssize_t diff = *ravail - *avail; - size_t nbytes_req = (*ravail+1023) & ~1023U; + size_t nbytes_req = ((size_t)*ravail + 1023) & ~1023U; ssize_t tested; /* @@ -875,7 +821,7 @@ process_global_set(struct archive_read *a, line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); - if (eq > next) + if (eq == NULL || eq > next) len = next - line; else len = eq - line; @@ -1120,12 +1066,10 @@ read_mtree(struct archive_read *a, struct mtree *mtree) static int read_header(struct archive_read *a, struct archive_entry *entry) { - struct mtree *mtree; + struct mtree *mtree = a->format->data; char *p; int r, use_next; - mtree = (struct mtree *)(a->format->data); - if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; @@ -1780,8 +1724,6 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, } if (strcmp(key, "time") == 0) { 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 = 0; *parsed_kws |= MTREE_HAS_MTIME; @@ -1790,17 +1732,21 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { + int64_t v; + ++val; - ns = (long)mtree_atol(&val, 10); - if (ns < 0) + v = mtree_atol(&val, 10); + if (v < 0) ns = 0; - else if (ns > 999999999) + else if (v > 999999999) ns = 999999999; + else + ns = (long)v; } - if (m > my_time_t_max) - m = my_time_t_max; - else if (m < my_time_t_min) - m = my_time_t_min; + if (m > TIME_MAX) + m = TIME_MAX; + else if (m < TIME_MIN) + m = TIME_MIN; archive_entry_set_mtime(entry, (time_t)m, ns); return (ARCHIVE_OK); } @@ -1887,11 +1833,10 @@ static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct mtree *mtree = a->format->data; size_t bytes_to_read; ssize_t bytes_read; - struct mtree *mtree; - mtree = (struct mtree *)(a->format->data); if (mtree->fd < 0) { *buff = NULL; *offset = 0; @@ -1932,9 +1877,8 @@ read_data(struct archive_read *a, const void **buff, size_t *size, static int skip(struct archive_read *a) { - struct mtree *mtree; + struct mtree *mtree = a->format->data; - mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; @@ -2029,9 +1973,9 @@ parsedigit(char c) if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') - return c - 'a'; + return 10 + c - 'a'; else if (c >= 'A' && c <= 'F') - return c - 'A'; + return 10 + c - 'A'; else return -1; } @@ -2044,8 +1988,8 @@ parsedigit(char c) static int64_t mtree_atol(char **p, int base) { - int64_t l, limit; - int digit, last_digit_limit; + int64_t l; + int digit; if (base == 0) { if (**p != '0') @@ -2059,29 +2003,24 @@ mtree_atol(char **p, int base) } if (**p == '-') { - limit = INT64_MIN / base; - last_digit_limit = -(INT64_MIN % base); ++(*p); l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { - if (l < limit || (l == limit && digit >= last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_sub_i64(&l, l, digit)) return INT64_MIN; - l = (l * base) - digit; digit = parsedigit(*++(*p)); } return l; } else { - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; - l = 0; digit = parsedigit(**p); while (digit >= 0 && digit < base) { - if (l > limit || (l == limit && digit > last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, digit)) return INT64_MAX; - l = (l * base) + digit; digit = parsedigit(*++(*p)); } return l; @@ -2100,8 +2039,7 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; - const void *t; - void *nl; + const void *nl, *t; char *u; /* Accumulate line in a line buffer. */ diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c index 9b401c00ba..ff764f5030 100644 --- a/libarchive/archive_read_support_format_rar.c +++ b/libarchive/archive_read_support_format_rar.c @@ -42,6 +42,7 @@ #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" @@ -154,7 +155,7 @@ #define UNP_BUFFER_SIZE (128 * 1024) /* Define this here for non-Windows platforms */ -#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)) +#ifndef FILE_ATTRIBUTE_DIRECTORY #define FILE_ATTRIBUTE_DIRECTORY 0x10 #endif @@ -201,8 +202,8 @@ struct huffman_table_entry struct huffman_code { struct huffman_tree_node *tree; - int numentries; - int numallocatedentries; + size_t numentries; + size_t numallocatedentries; int minlength; int maxlength; int tablesize; @@ -227,8 +228,6 @@ struct rar_program_code { uint8_t *staticdata; uint32_t staticdatalen; - uint8_t *globalbackup; - uint32_t globalbackuplen; uint64_t fingerprint; uint32_t usagecount; uint32_t oldfilterlength; @@ -499,7 +498,7 @@ static const uint32_t cache_masks[] = { static int rar_br_fillup(struct archive_read *a, struct rar_br *br) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; int n = CACHE_BITS - br->cache_avail; for (;;) { @@ -594,7 +593,7 @@ rar_br_fillup(struct archive_read *a, struct rar_br *br) static int rar_br_preparation(struct archive_read *a, struct rar_br *br) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; if (rar->bytes_remaining > 0) { br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); @@ -709,7 +708,7 @@ static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; struct rar_br *br = &(rar->br); Byte b; if (!rar_br_read_ahead(a, br, 8)) @@ -777,7 +776,7 @@ 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; + struct rar *rar = _a->format->data; if (rar) { return rar->has_encrypted_entries; } @@ -877,10 +876,9 @@ static int archive_read_format_rar_options(struct archive_read *a, const char *key, const char *val) { - struct rar *rar; + struct rar *rar = a->format->data; int ret = ARCHIVE_FAILED; - rar = (struct rar *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -907,9 +905,9 @@ static int archive_read_format_rar_read_header(struct archive_read *a, struct archive_entry *entry) { + struct rar *rar = a->format->data; const void *h; const char *p; - struct rar *rar; int64_t skip; char head_type; int ret; @@ -920,8 +918,6 @@ archive_read_format_rar_read_header(struct archive_read *a, if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "RAR"; - 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 @@ -955,7 +951,7 @@ archive_read_format_rar_read_header(struct archive_read *a, if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to read next header."); + "Failed to read next header"); return (ARCHIVE_FATAL); } p = h; @@ -1005,7 +1001,7 @@ archive_read_format_rar_read_header(struct archive_read *a, 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."); + "RAR encryption support unavailable"); return (ARCHIVE_FATAL); } @@ -1100,7 +1096,7 @@ static int archive_read_format_rar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; int ret; if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { @@ -1141,8 +1137,8 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported compression method for RAR file."); - ret = ARCHIVE_FATAL; + "Unsupported compression method for RAR file"); + ret = ARCHIVE_FAILED; break; } return (ret); @@ -1151,12 +1147,10 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, static int archive_read_format_rar_read_data_skip(struct archive_read *a) { - struct rar *rar; + struct rar *rar = a->format->data; int64_t bytes_skipped; int ret; - rar = (struct rar *)(a->format->data); - if (rar->bytes_unconsumed > 0) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, rar->bytes_unconsumed); @@ -1189,9 +1183,9 @@ static int64_t archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, int whence) { + struct rar *rar = a->format->data; int64_t client_offset, ret; size_t i; - struct rar *rar = (struct rar *)(a->format->data); if (rar->compression_method == COMPRESS_METHOD_STORE) { @@ -1294,6 +1288,12 @@ archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, "Error during seek of RAR file"); return (ARCHIVE_FAILED); } + if (rar->cursor == 0) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid RAR seek cursor state"); + return (ARCHIVE_FAILED); + } client_offset += rar->dbo[rar->cursor].start_offset - rar->dbo[rar->cursor-1].end_offset; continue; @@ -1342,9 +1342,8 @@ archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, static int archive_read_format_rar_cleanup(struct archive_read *a) { - struct rar *rar; + struct rar *rar = a->format->data; - rar = (struct rar *)(a->format->data); free_codes(a); clear_filters(&rar->filters); free(rar->filename); @@ -1354,7 +1353,7 @@ archive_read_format_rar_cleanup(struct archive_read *a) free(rar->lzss.window); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); free(rar); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } @@ -1362,9 +1361,9 @@ static int read_header(struct archive_read *a, struct archive_entry *entry, char head_type) { + struct rar *rar = a->format->data; const void *h; const char *p, *endp; - struct rar *rar; struct rar_header rar_header; struct rar_file_header file_header; int64_t header_size; @@ -1380,8 +1379,6 @@ read_header(struct archive_read *a, struct archive_entry *entry, char *newptr; size_t newsize; - rar = (struct rar *)(a->format->data); - /* Setup a string conversion object for non-rar-unicode filenames. */ sconv = rar->opt_sconv; if (sconv == NULL) { @@ -1432,14 +1429,14 @@ read_header(struct archive_read *a, struct archive_entry *entry, else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "RAR solid archive support unavailable."); + "RAR solid archive support unavailable"); return (ARCHIVE_FATAL); } if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to read full header content."); + "Failed to read full header content"); return (ARCHIVE_FATAL); } @@ -1471,7 +1468,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, 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."); + "RAR encryption support unavailable"); /* 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. */ @@ -1503,7 +1500,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, if (rar->packed_size < 0 || rar->unp_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid sizes specified."); + "Invalid sizes specified"); return (ARCHIVE_FATAL); } @@ -1513,27 +1510,23 @@ read_header(struct archive_read *a, struct archive_entry *entry, * consumed at the end. */ if (head_type == NEWSUB_HEAD) { - size_t distance = p - (const char *)h; if (rar->packed_size > INT64_MAX - header_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Extended header size too large."); + "Invalid RAR file: Overlarge extended header"); return (ARCHIVE_FATAL); } - header_size += rar->packed_size; - if ((uintmax_t)header_size > SIZE_MAX) { + if (__archive_read_consume(a, header_size + rar->packed_size - 7) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unable to read extended header data."); + "Invalid RAR file: Cannot read extended header data"); return (ARCHIVE_FATAL); } - /* Make sure we have the extended data. */ - if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to read extended header data."); - return (ARCHIVE_FATAL); - } - p = h; - endp = p + header_size - 7; - p += distance; + + /* + * NEWSUB records are metadata-only in this reader. The block header + * has already been validated, so it is safe to skip exactly the + * remaining header bytes and the associated data payload. + */ + return ret; } filename_size = archive_le16dec(file_header.name_size); @@ -1547,7 +1540,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, newptr = realloc(rar->filename, newsize); if (newptr == NULL) { archive_set_error(&a->archive, ENOMEM, - "Couldn't allocate memory."); + "Couldn't allocate memory"); return (ARCHIVE_FATAL); } rar->filename = newptr; @@ -1672,16 +1665,18 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } fn_sconv = rar->sconv_utf8; - while ((strp = strchr(filename, '\\')) != NULL) - *strp = '/'; + strp = filename; + while ((strp = strchr(strp, '\\')) != NULL) + *strp++ = '/'; p += filename_size; } } else { fn_sconv = sconv; - while ((strp = strchr(filename, '\\')) != NULL) - *strp = '/'; + strp = filename; + while ((strp = strchr(strp, '\\')) != NULL) + *strp++ = '/'; p += filename_size; } @@ -1701,7 +1696,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, newsize = sizeof(*rar->dbo) * (rar->nodes + 1); if ((newdbo = realloc(rar->dbo, newsize)) == NULL) { - archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); return (ARCHIVE_FATAL); } rar->dbo = newdbo; @@ -1715,7 +1710,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, if (rar->packed_size > INT64_MAX - a->filter->position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unable to store offsets."); + "Unable to store offsets"); return (ARCHIVE_FATAL); } rar->dbo[rar->cursor].start_offset = a->filter->position; @@ -1734,7 +1729,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, newsize = filename_size + 1; if ((newptr = realloc(rar->filename_save, newsize)) == NULL) { - archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); return (ARCHIVE_FATAL); } rar->filename_save = newptr; @@ -1745,7 +1740,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, free(rar->dbo); if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL) { - archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); return (ARCHIVE_FATAL); } rar->dbo[0].header_size = header_size; @@ -1776,7 +1771,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, __archive_read_consume(a, header_size - 7); if (rar->packed_size > INT64_MAX - a->filter->position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unable to store offsets."); + "Unable to store offsets"); return (ARCHIVE_FATAL); } rar->dbo[0].start_offset = a->filter->position; @@ -1829,10 +1824,6 @@ read_header(struct archive_read *a, struct archive_entry *entry, rar->ppmd_valid = rar->ppmd_eod = 0; rar->filters.filterstart = INT64_MAX; - /* Don't set any archive entries for non-file header types */ - if (head_type == NEWSUB_HEAD) - return ret; - archive_entry_set_mtime(entry, rar->mtime, rar->mnsec); archive_entry_set_ctime(entry, rar->ctime, rar->cnsec); archive_entry_set_atime(entry, rar->atime, rar->ansec); @@ -1848,7 +1839,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Pathname cannot be converted from %s to current locale.", + "Pathname cannot be converted from %s to current locale", archive_string_conversion_charset_name(fn_sconv)); ret = (ARCHIVE_WARN); } @@ -1970,22 +1961,21 @@ static int read_symlink_stored(struct archive_read *a, struct archive_entry *entry, struct archive_string_conv *sconv) { + struct rar *rar = a->format->data; const void *h; const char *p; - struct rar *rar; int ret = (ARCHIVE_OK); - rar = (struct rar *)(a->format->data); if ((uintmax_t)rar->packed_size > SIZE_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unable to read link."); + "Unable to read link"); return (ARCHIVE_FATAL); } if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Failed to read link."); + "Failed to read link"); return (ARCHIVE_FATAL); } p = h; @@ -2000,7 +1990,7 @@ read_symlink_stored(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "link cannot be converted from %s to current locale.", + "link cannot be converted from %s to current locale", archive_string_conversion_charset_name(sconv)); ret = (ARCHIVE_WARN); } @@ -2012,10 +2002,9 @@ static int read_data_stored(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct rar *rar; + struct rar *rar = a->format->data; ssize_t bytes_avail; - rar = (struct rar *)(a->format->data); if (rar->bytes_remaining == 0 && !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)) { @@ -2026,7 +2015,7 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); #endif } rar->entry_eof = 1; @@ -2038,7 +2027,7 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } *size = bytes_avail; @@ -2058,18 +2047,16 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, int64_t *offset, size_t looper) { if (looper++ > MAX_COMPRESS_DEPTH) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); - struct rar *rar; + struct rar *rar = a->format->data; int64_t start, end; size_t bs; int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i; - rar = (struct rar *)(a->format->data); - do { if (!rar->valid) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); if (rar->filters.bytes_ready > 0) { @@ -2121,7 +2108,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "File CRC error"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); #endif } rar->entry_eof = 1; @@ -2155,7 +2142,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, if (rar->filters.lastend == rar->filters.filterstart) { if (!run_filters(a)) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); continue; } @@ -2172,7 +2159,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if(sym != rar->ppmd_escape) { @@ -2186,7 +2173,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } switch(code) @@ -2201,7 +2188,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, case 3: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Parsing filters is unsupported."); + "Parsing filters is unsupported"); return (ARCHIVE_FAILED); case 4: @@ -2213,7 +2200,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_offset |= code << (i * 8); } @@ -2222,7 +2209,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_emit_match(rar, lzss_offset + 2, length + 32); rar->bytes_uncopied += length + 32; @@ -2234,7 +2221,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid symbol"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } lzss_emit_match(rar, 1, length + 4); rar->bytes_uncopied += length + 4; @@ -2280,7 +2267,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, * what we would do to solve it. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Internal error extracting RAR file"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } } if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset)) @@ -2311,11 +2298,11 @@ ending_block: static int parse_codes(struct archive_read *a) { + struct rar *rar = a->format->data; int i, j, val, n, r; unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags; unsigned int maxorder; struct huffman_code precode; - struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); free_codes(a); @@ -2363,7 +2350,7 @@ parse_codes(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* Make sure ppmd7_context is freed before Ppmd7_Construct @@ -2379,7 +2366,7 @@ parse_codes(struct archive_read *a) if (rar->dictionary_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid zero dictionary size"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, @@ -2393,7 +2380,7 @@ parse_codes(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder); rar->ppmd_valid = 1; @@ -2403,13 +2390,13 @@ parse_codes(struct archive_read *a) if (!rar->ppmd_valid) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid PPMd sequence"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unable to initialize PPMd range decoder"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } } } @@ -2417,6 +2404,13 @@ parse_codes(struct archive_read *a) { rar_br_consume(br, 1); + /* + * Low-distance repeat state belongs to the current LZ table and + * must not be reused after starting a new table. + */ + rar->lastlowoffset = 0; + rar->numlowoffsetrepeats = 0; + /* Keep existing table flag */ if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; @@ -2459,7 +2453,7 @@ parse_codes(struct archive_read *a) if ((val = read_next_symbol(a, &precode)) < 0) { free(precode.tree); free(precode.table); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (val < 16) { @@ -2473,8 +2467,8 @@ parse_codes(struct archive_read *a) free(precode.tree); free(precode.table); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Internal error extracting RAR file."); - return (ARCHIVE_FATAL); + "Internal error extracting RAR file"); + return (ARCHIVE_FAILED); } if(val == 16) { @@ -2548,7 +2542,8 @@ parse_codes(struct archive_read *a) return (r); } - if (!rar->dictionary_size || !rar->lzss.window) + if (!rar->dictionary_size || !rar->lzss.window || + (unsigned int)(rar->lzss.mask + 1) < rar->dictionary_size) { /* Seems as though dictionary sizes are not used. Even so, minimize * memory usage as much as possible. @@ -2562,13 +2557,13 @@ parse_codes(struct archive_read *a) new_size = rar_fls((unsigned int)rar->unp_size) << 1; if (new_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Zero window size is invalid."); - return (ARCHIVE_FATAL); + "Zero window size is invalid"); + return (ARCHIVE_FAILED); } new_window = realloc(rar->lzss.window, new_size); if (new_window == NULL) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for uncompressed data."); + "Unable to allocate memory for uncompressed data"); return (ARCHIVE_FATAL); } rar->lzss.window = (unsigned char *)new_window; @@ -2583,13 +2578,13 @@ truncated_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static void free_codes(struct archive_read *a) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; free(rar->maincode.tree); free(rar->offsetcode.tree); free(rar->lowoffsetcode.tree); @@ -2608,10 +2603,10 @@ free_codes(struct archive_read *a) static int read_next_symbol(struct archive_read *a, struct huffman_code *code) { + struct rar *rar = a->format->data; unsigned char bit; unsigned int bits; int length, value, node; - struct rar *rar; struct rar_br *br; if (!code->table) @@ -2620,7 +2615,6 @@ read_next_symbol(struct archive_read *a, struct huffman_code *code) return -1; } - rar = (struct rar *)(a->format->data); br = &(rar->br); /* Look ahead (peek) at bits */ @@ -2686,7 +2680,7 @@ create_code(struct archive_read *a, struct huffman_code *code, code->numallocatedentries = 0; if (new_node(code) < 0) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for node data."); + "Unable to allocate memory for node data"); return (ARCHIVE_FATAL); } code->numentries = 1; @@ -2699,7 +2693,7 @@ create_code(struct archive_read *a, struct huffman_code *code, { if (lengths[j] != i) continue; if (add_value(a, code, j, codebits, i) != ARCHIVE_OK) - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); codebits++; if (--symbolsleft <= 0) break; @@ -2751,7 +2745,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* @@ -2769,12 +2763,12 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, if ((repeatnode = new_node(code)) < 0) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for node data."); + "Unable to allocate memory for node data"); return (ARCHIVE_FATAL); } if ((nextnode = new_node(code)) < 0) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for node data."); + "Unable to allocate memory for node data"); return (ARCHIVE_FATAL); } @@ -2794,7 +2788,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, { if (new_node(code) < 0) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for node data."); + "Unable to allocate memory for node data"); return (ARCHIVE_FATAL); } code->tree[lastnode].branches[bit] = code->numentries++; @@ -2810,7 +2804,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Prefix found"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } /* Set leaf value */ @@ -2825,11 +2819,16 @@ new_node(struct huffman_code *code) { void *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)); + size_t size, new_num_entries; + + if (code->numentries == 0) + new_num_entries = 256; + else if (archive_ckd_mul_size(&new_num_entries, code->numentries, 2) + || new_num_entries > INT_MAX) + return -1; + if (archive_ckd_mul_size(&size, new_num_entries, sizeof(*code->tree))) + return -1; + new_tree = realloc(code->tree, size); if (new_tree == NULL) return (-1); code->tree = (struct huffman_tree_node *)new_tree; @@ -2849,6 +2848,10 @@ make_table(struct archive_read *a, struct huffman_code *code) code->tablesize = code->maxlength; code->table = calloc(((size_t)1U) << code->tablesize, sizeof(*code->table)); + if (code->table == NULL) { + archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } return make_table_recurse(a, code, 0, code->table, 0, code->tablesize); } @@ -2863,14 +2866,14 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node, if (!code->tree) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Huffman tree was not created."); - return (ARCHIVE_FATAL); + "Huffman tree was not created"); + return (ARCHIVE_FAILED); } - if (node < 0 || node >= code->numentries) + if (node < 0 || (size_t)node >= code->numentries) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid location to Huffman tree specified."); - return (ARCHIVE_FATAL); + "Invalid location to Huffman tree specified"); + return (ARCHIVE_FAILED); } currtablesize = 1 << (maxdepth - depth); @@ -2954,9 +2957,9 @@ expand(struct archive_read *a, int64_t *end) static const unsigned char shortbits[] = { 2, 2, 3, 4, 5, 6, 6, 6 }; + struct rar *rar = a->format->data; int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol; unsigned char newfile; - struct rar *rar = (struct rar *)(a->format->data); struct rar_br *br = &(rar->br); if (rar->filters.filterstart < *end) @@ -3133,26 +3136,31 @@ truncated_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); rar->valid = 0; - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); bad_data: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static int copy_from_lzss_window(struct archive_read *a, uint8_t *buffer, int64_t startpos, int length) { + struct rar *rar = a->format->data; int windowoffs, firstpart; - struct rar *rar = (struct rar *)(a->format->data); windowoffs = lzss_offset_for_position(&rar->lzss, startpos); firstpart = lzss_size(&rar->lzss) - windowoffs; + if (length > lzss_size(&rar->lzss)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Bad RAR file data"); + return (ARCHIVE_FAILED); + } if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if (firstpart < length) { memcpy(buffer, &rar->lzss.window[windowoffs], firstpart); @@ -3167,8 +3175,8 @@ static int copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, int64_t startpos, size_t length) { + struct rar *rar = a->format->data; int windowoffs, firstpart; - struct rar *rar = (struct rar *)(a->format->data); if (length > rar->unp_buffer_size) { @@ -3180,7 +3188,7 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL) { archive_set_error(&a->archive, ENOMEM, - "Unable to allocate memory for uncompressed data."); + "Unable to allocate memory for uncompressed data"); return (ARCHIVE_FATAL); } } @@ -3194,7 +3202,7 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, if (firstpart < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } if ((size_t)firstpart < length) { memcpy(&rar->unp_buffer[rar->unp_offset], @@ -3218,13 +3226,13 @@ copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer, fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data"); - return (ARCHIVE_FATAL); + return (ARCHIVE_FAILED); } static const void * rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; const void *h; int ret; @@ -3261,7 +3269,7 @@ again: static int parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; struct rar_filters *filters = &rar->filters; struct memory_bit_reader br = { 0 }; @@ -3315,7 +3323,8 @@ parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint else blocklength = prog ? prog->oldfilterlength : 0; - if (blocklength > rar->dictionary_size) + if (blocklength > rar->dictionary_size || + blocklength > (uint32_t)(rar->lzss.mask + 1)) return 0; registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS; @@ -3428,7 +3437,7 @@ create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t static int run_filters(struct archive_read *a) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; struct rar_filters *filters = &rar->filters; struct rar_filter *filter = filters->stack; struct rar_filter *f; @@ -3549,7 +3558,13 @@ compile_program(const uint8_t *bytes, size_t length) if (membr_bits(&br, 1)) { - prog->staticdatalen = membr_next_rarvm_number(&br) + 1; + uint32_t staticdatalen = membr_next_rarvm_number(&br); + if (staticdatalen >= VM_MEMORY_SIZE) + { + delete_program_code(prog); + return NULL; + } + prog->staticdatalen = staticdatalen + 1; prog->staticdata = malloc(prog->staticdatalen); if (!prog->staticdata) { @@ -3590,7 +3605,6 @@ delete_program_code(struct rar_program_code *prog) { struct rar_program_code *next = prog->next; free(prog->staticdata); - free(prog->globalbackup); free(prog); prog = next; } @@ -3643,7 +3657,7 @@ membr_fill(struct memory_bit_reader *br, int bits) static int read_filter(struct archive_read *a, int64_t *end) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; uint8_t flags, val, *code; uint16_t length, i; @@ -3839,7 +3853,7 @@ execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm) state.delta[0] = state.lastdelta; predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF; byte = (predbyte - delta) & 0xFF; - prederror = delta << 3; + prederror = delta * 8; state.error[0] += abs(prederror); state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]); state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]); @@ -3896,7 +3910,7 @@ execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_vir static int rar_decode_byte(struct archive_read *a, uint8_t *byte) { - struct rar *rar = (struct rar *)(a->format->data); + struct rar *rar = a->format->data; struct rar_br *br = &(rar->br); if (!rar_br_read_ahead(a, br, 8)) return 0; diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index 778537be93..faddad9ff4 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -44,6 +44,7 @@ #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_ppmd7_private.h" #include "archive_entry_private.h" #include "archive_time_private.h" @@ -81,7 +82,9 @@ * * The array itself is decrypted in `rar5_init` function. */ -static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; +static const unsigned char rar5_signature_xor[] = { + 243, 192, 211, 128, 187, 166, 160, 161 +}; static const size_t g_unpack_window_size = 0x20000; /* These could have been static const's, but they aren't, because of @@ -165,11 +168,9 @@ enum FILTER_TYPE { struct filter_info { int type; int channels; - int pos_r; int64_t block_start; ssize_t block_length; - uint16_t width; }; struct data_ready { @@ -372,10 +373,10 @@ struct rar5 { static void rar5_signature(char *buf); static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); -static int push_data_ready(struct archive_read* a, struct rar5* rar, +static int push_data_ready(struct archive_read* a, struct rar5 *rar5, const uint8_t* buf, size_t size, int64_t offset); -static void clear_data_ready_stack(struct rar5* rar); -static void rar5_deinit(struct rar5* rar); +static void clear_data_ready_stack(struct rar5 *rar5); +static void rar5_deinit(struct rar5 *rar5); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { @@ -514,10 +515,6 @@ uint8_t bf_is_last_block(const struct compressed_block_header* hdr) { return (hdr->block_flags_u8 >> 6) & 1; } -static inline struct rar5* get_context(struct archive_read* a) { - return (struct rar5*) a->format->data; -} - /* Convenience functions used by filter implementations. */ static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask, int64_t start, int64_t end) @@ -533,28 +530,28 @@ static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask, } } -static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { +static uint32_t read_filter_data(struct rar5 *rar5, uint32_t offset) { uint8_t linear_buf[4]; - circular_memcpy(linear_buf, rar->cstate.window_buf, - rar->cstate.window_mask, offset, offset + 4); + circular_memcpy(linear_buf, rar5->cstate.window_buf, + rar5->cstate.window_mask, offset, offset + 4); return archive_le32dec(linear_buf); } -static void write_filter_data(struct rar5* rar, uint32_t offset, +static void write_filter_data(struct rar5 *rar5, uint32_t offset, uint32_t value) { - archive_le32enc(&rar->cstate.filtered_buf[offset], value); + archive_le32enc(&rar5->cstate.filtered_buf[offset], value); } /* Allocates a new filter descriptor and adds it to the filter array. */ -static struct filter_info* add_new_filter(struct rar5* rar) { +static struct filter_info* add_new_filter(struct rar5 *rar5) { struct filter_info* f = calloc(1, sizeof(*f)); if(!f) { return NULL; } - if (CDE_OK != cdeque_push_back(&rar->cstate.filters, cdeque_filter(f))) { + if (CDE_OK != cdeque_push_back(&rar5->cstate.filters, cdeque_filter(f))) { free(f); return NULL; } @@ -562,7 +559,7 @@ static struct filter_info* add_new_filter(struct rar5* rar) { return f; } -static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { +static int run_delta_filter(struct rar5 *rar5, struct filter_info* flt) { int i; ssize_t dest_pos, src_pos = 0; @@ -574,12 +571,12 @@ static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { { uint8_t byte; - byte = rar->cstate.window_buf[ - (rar->cstate.solid_offset + flt->block_start + - src_pos) & rar->cstate.window_mask]; + byte = rar5->cstate.window_buf[ + (rar5->cstate.solid_offset + flt->block_start + + src_pos) & rar5->cstate.window_mask]; prev_byte -= byte; - rar->cstate.filtered_buf[dest_pos] = prev_byte; + rar5->cstate.filtered_buf[dest_pos] = prev_byte; src_pos++; } } @@ -587,21 +584,21 @@ static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { return ARCHIVE_OK; } -static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, +static int run_e8e9_filter(struct rar5 *rar5, struct filter_info* flt, int extended) { const uint32_t file_size = 0x1000000; ssize_t i; - circular_memcpy(rar->cstate.filtered_buf, - rar->cstate.window_buf, rar->cstate.window_mask, - rar->cstate.solid_offset + flt->block_start, - rar->cstate.solid_offset + flt->block_start + flt->block_length); + circular_memcpy(rar5->cstate.filtered_buf, + rar5->cstate.window_buf, rar5->cstate.window_mask, + rar5->cstate.solid_offset + flt->block_start, + rar5->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 4;) { - uint8_t b = rar->cstate.window_buf[ - (rar->cstate.solid_offset + flt->block_start + - i++) & rar->cstate.window_mask]; + uint8_t b = rar5->cstate.window_buf[ + (rar5->cstate.solid_offset + flt->block_start + + i++) & rar5->cstate.window_mask]; /* * 0xE8 = x86's call (function call) @@ -612,19 +609,19 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, uint32_t addr; uint32_t offset = (i + flt->block_start) % file_size; - addr = read_filter_data(rar, - (uint32_t)(rar->cstate.solid_offset + - flt->block_start + i) & rar->cstate.window_mask); + addr = read_filter_data(rar5, + (uint32_t)(rar5->cstate.solid_offset + + flt->block_start + i) & rar5->cstate.window_mask); if(addr & 0x80000000) { if(((addr + offset) & 0x80000000) == 0) { - write_filter_data(rar, (uint32_t)i, + write_filter_data(rar5, (uint32_t)i, addr + file_size); } } else { if((addr - file_size) & 0x80000000) { uint32_t naddr = addr - offset; - write_filter_data(rar, (uint32_t)i, + write_filter_data(rar5, (uint32_t)i, naddr); } } @@ -636,29 +633,29 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, return ARCHIVE_OK; } -static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { +static int run_arm_filter(struct rar5 *rar5, struct filter_info* flt) { ssize_t i = 0; uint32_t offset; - circular_memcpy(rar->cstate.filtered_buf, - rar->cstate.window_buf, rar->cstate.window_mask, - rar->cstate.solid_offset + flt->block_start, - rar->cstate.solid_offset + flt->block_start + flt->block_length); + circular_memcpy(rar5->cstate.filtered_buf, + rar5->cstate.window_buf, rar5->cstate.window_mask, + rar5->cstate.solid_offset + flt->block_start, + rar5->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 3; i += 4) { - uint8_t* b = &rar->cstate.window_buf[ - (rar->cstate.solid_offset + - flt->block_start + i + 3) & rar->cstate.window_mask]; + uint8_t* b = &rar5->cstate.window_buf[ + (rar5->cstate.solid_offset + + flt->block_start + i + 3) & rar5->cstate.window_mask]; if(*b == 0xEB) { /* 0xEB = ARM's BL (branch + link) instruction. */ - offset = read_filter_data(rar, - (rar->cstate.solid_offset + flt->block_start + i) & - (uint32_t)rar->cstate.window_mask) & 0x00ffffff; + offset = read_filter_data(rar5, + (rar5->cstate.solid_offset + flt->block_start + i) & + (uint32_t)rar5->cstate.window_mask) & 0x00ffffff; offset -= (uint32_t) ((i + flt->block_start) / 4); offset = (offset & 0x00ffffff) | 0xeb000000; - write_filter_data(rar, (uint32_t)i, offset); + write_filter_data(rar5, (uint32_t)i, offset); } } @@ -666,33 +663,33 @@ static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { } static int run_filter(struct archive_read* a, struct filter_info* flt) { + struct rar5 *rar5 = a->format->data; int ret; - struct rar5* rar = get_context(a); - clear_data_ready_stack(rar); - free(rar->cstate.filtered_buf); + clear_data_ready_stack(rar5); + free(rar5->cstate.filtered_buf); - rar->cstate.filtered_buf = malloc(flt->block_length); - if(!rar->cstate.filtered_buf) { + rar5->cstate.filtered_buf = malloc(flt->block_length); + if(!rar5->cstate.filtered_buf) { archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for filter data."); + "Can't allocate memory for filter data"); return ARCHIVE_FATAL; } switch(flt->type) { case FILTER_DELTA: - ret = run_delta_filter(rar, flt); + ret = run_delta_filter(rar5, flt); break; case FILTER_E8: /* fallthrough */ case FILTER_E8E9: - ret = run_e8e9_filter(rar, flt, + ret = run_e8e9_filter(rar5, flt, flt->type == FILTER_E8E9); break; case FILTER_ARM: - ret = run_arm_filter(rar, flt); + ret = run_arm_filter(rar5, flt); break; default: @@ -700,7 +697,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported filter type: 0x%x", (unsigned int)flt->type); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(ret != ARCHIVE_OK) { @@ -708,8 +705,8 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { return ret; } - if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, - flt->block_length, rar->cstate.last_write_ptr)) + if(ARCHIVE_OK != push_data_ready(a, rar5, rar5->cstate.filtered_buf, + flt->block_length, rar5->cstate.last_write_ptr)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Stack overflow when submitting unpacked data"); @@ -717,7 +714,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { return ARCHIVE_FATAL; } - rar->cstate.last_write_ptr += flt->block_length; + rar5->cstate.last_write_ptr += flt->block_length; return ARCHIVE_OK; } @@ -725,15 +722,15 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { * Next call of `use_data` will use the pointer, size and offset arguments * that are specified here. These arguments are pushed to the FIFO stack here, * and popped from the stack by the `use_data` function. */ -static void push_data(struct archive_read* a, struct rar5* rar, +static void push_data(struct archive_read* a, struct rar5 *rar5, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { - const ssize_t wmask = rar->cstate.window_mask; - const ssize_t solid_write_ptr = (rar->cstate.solid_offset + - rar->cstate.last_write_ptr) & wmask; + const ssize_t wmask = rar5->cstate.window_mask; + const ssize_t solid_write_ptr = (rar5->cstate.solid_offset + + rar5->cstate.last_write_ptr) & wmask; - idx_begin += rar->cstate.solid_offset; - idx_end += rar->cstate.solid_offset; + idx_begin += rar5->cstate.solid_offset; + idx_end += rar5->cstate.solid_offset; /* Check if our unpacked data is wrapped inside the window circular * buffer. If it's not wrapped, it can be copied out by using @@ -743,57 +740,57 @@ static void push_data(struct archive_read* a, struct rar5* rar, if((idx_begin & wmask) > (idx_end & wmask)) { /* The data is wrapped (begin offset sis bigger than end * offset). */ - const ssize_t frag1_size = rar->cstate.window_size - + const ssize_t frag1_size = rar5->cstate.window_size - (idx_begin & wmask); const ssize_t frag2_size = idx_end & wmask; /* Copy the first part of the buffer first. */ - push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, - rar->cstate.last_write_ptr); + push_data_ready(a, rar5, buf + solid_write_ptr, frag1_size, + rar5->cstate.last_write_ptr); /* Copy the second part of the buffer. */ - push_data_ready(a, rar, buf, frag2_size, - rar->cstate.last_write_ptr + frag1_size); + push_data_ready(a, rar5, buf, frag2_size, + rar5->cstate.last_write_ptr + frag1_size); - rar->cstate.last_write_ptr += frag1_size + frag2_size; + rar5->cstate.last_write_ptr += frag1_size + frag2_size; } else { /* Data is not wrapped, so we can just use one call to copy the * data. */ - push_data_ready(a, rar, + push_data_ready(a, rar5, buf + solid_write_ptr, (idx_end - idx_begin) & wmask, - rar->cstate.last_write_ptr); + rar5->cstate.last_write_ptr); - rar->cstate.last_write_ptr += idx_end - idx_begin; + rar5->cstate.last_write_ptr += idx_end - idx_begin; } } /* Convenience function that submits the data to the user. It uses the * unpack window buffer as a source location. */ -static void push_window_data(struct archive_read* a, struct rar5* rar, +static void push_window_data(struct archive_read* a, struct rar5 *rar5, int64_t idx_begin, int64_t idx_end) { - push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); + push_data(a, rar5, rar5->cstate.window_buf, idx_begin, idx_end); } static int apply_filters(struct archive_read* a) { + struct rar5 *rar5 = a->format->data; struct filter_info* flt; - struct rar5* rar = get_context(a); int ret; - rar->cstate.all_filters_applied = 0; + rar5->cstate.all_filters_applied = 0; /* Get the first filter that can be applied to our data. The data * needs to be fully unpacked before the filter can be run. */ - if(CDE_OK == cdeque_front(&rar->cstate.filters, + if(CDE_OK == cdeque_front(&rar5->cstate.filters, cdeque_filter_p(&flt))) { /* Check if our unpacked data fully covers this filter's * range. */ - if(rar->cstate.write_ptr > flt->block_start && - rar->cstate.write_ptr >= flt->block_start + + if(rar5->cstate.write_ptr > flt->block_start && + rar5->cstate.write_ptr >= flt->block_start + flt->block_length) { /* Check if we have some data pending to be written * right before the filter's start offset. */ - if(rar->cstate.last_write_ptr == flt->block_start) { + if(rar5->cstate.last_write_ptr == flt->block_start) { /* Run the filter specified by descriptor * `flt`. */ ret = run_filter(a, flt); @@ -805,15 +802,15 @@ static int apply_filters(struct archive_read* a) { /* Filter descriptor won't be needed anymore * after it's used, * so remove it from the * filter list and free its memory. */ - (void) cdeque_pop_front(&rar->cstate.filters, + (void) cdeque_pop_front(&rar5->cstate.filters, cdeque_filter_p(&flt)); free(flt); } else { /* We can't run filters yet, dump the memory * right before the filter. */ - push_window_data(a, rar, - rar->cstate.last_write_ptr, + push_window_data(a, rar5, + rar5->cstate.last_write_ptr, flt->block_start); } @@ -823,12 +820,12 @@ static int apply_filters(struct archive_read* a) { } } - rar->cstate.all_filters_applied = 1; + rar5->cstate.all_filters_applied = 1; return ARCHIVE_OK; } -static void dist_cache_push(struct rar5* rar, int value) { - int* q = rar->cstate.dist_cache; +static void dist_cache_push(struct rar5 *rar5, int value) { + int* q = rar5->cstate.dist_cache; q[3] = q[2]; q[2] = q[1]; @@ -836,8 +833,8 @@ static void dist_cache_push(struct rar5* rar, int value) { q[0] = value; } -static int dist_cache_touch(struct rar5* rar, int idx) { - int* q = rar->cstate.dist_cache; +static int dist_cache_touch(struct rar5 *rar5, int idx) { + int* q = rar5->cstate.dist_cache; int i, dist = q[idx]; for(i = idx; i > 0; i--) @@ -847,8 +844,8 @@ static int dist_cache_touch(struct rar5* rar, int idx) { return dist; } -static void free_filters(struct rar5* rar) { - struct cdeque* d = &rar->cstate.filters; +static void free_filters(struct rar5 *rar5) { + struct cdeque* d = &rar5->cstate.filters; /* Free any remaining filters. All filters should be naturally * consumed by the unpacking function, so remaining filters after @@ -868,28 +865,28 @@ static void free_filters(struct rar5* rar) { cdeque_clear(d); /* Also clear out the variables needed for sanity checking. */ - rar->cstate.last_block_start = 0; - rar->cstate.last_block_length = 0; + rar5->cstate.last_block_start = 0; + rar5->cstate.last_block_length = 0; } -static void reset_file_context(struct rar5* rar) { - memset(&rar->file, 0, sizeof(rar->file)); - blake2sp_init(&rar->file.b2state, 32); +static void reset_file_context(struct rar5 *rar5) { + memset(&rar5->file, 0, sizeof(rar5->file)); + blake2sp_init(&rar5->file.b2state, 32); - if(rar->main.solid) { - rar->cstate.solid_offset += rar->cstate.write_ptr; + if(rar5->main.solid) { + rar5->cstate.solid_offset += rar5->cstate.write_ptr; } else { - rar->cstate.solid_offset = 0; + rar5->cstate.solid_offset = 0; } - rar->cstate.write_ptr = 0; - rar->cstate.last_write_ptr = 0; - rar->cstate.last_unstore_ptr = 0; + rar5->cstate.write_ptr = 0; + rar5->cstate.last_write_ptr = 0; + rar5->cstate.last_unstore_ptr = 0; - rar->file.redir_type = REDIR_TYPE_NONE; - rar->file.redir_flags = 0; + rar5->file.redir_type = REDIR_TYPE_NONE; + rar5->file.redir_flags = 0; - free_filters(rar); + free_filters(rar5); } static inline int get_archive_read(struct archive* a, @@ -905,11 +902,10 @@ static inline int get_archive_read(struct archive* a, static int read_ahead(struct archive_read* a, size_t how_many, const uint8_t** ptr) { - ssize_t avail = -1; if(!ptr) return 0; - *ptr = __archive_read_ahead(a, how_many, &avail); + *ptr = __archive_read_ahead(a, how_many, NULL); if(*ptr == NULL) { return 0; } @@ -947,23 +943,30 @@ static int consume(struct archive_read* a, int64_t how_many) { static int read_var(struct archive_read* a, uint64_t* pvalue, uint64_t* pvalue_len) { + uint64_t multiplier; uint64_t result = 0; - size_t shift, i; + size_t i; const uint8_t* p; - uint8_t b; - /* We will read maximum of 8 bytes. We don't have to handle the + /* We will read maximum of 10 bytes. We don't have to handle the * situation to read the RAR5 variable-sized value stored at the end of * the file, because such situation will never happen. */ - if(!read_ahead(a, 8, &p)) + if(!read_ahead(a, 10, &p)) return 0; - for(shift = 0, i = 0; i < 8; i++, shift += 7) { + for(multiplier = 1, i = 0; i < 10; i++, multiplier *= 128) { + uint64_t val; + uint8_t b; + b = p[i]; /* Strip the MSB from the input byte and add the resulting * number to the `result`. */ - result += (b & (uint64_t)0x7F) << shift; + if(archive_ckd_mul_u64(&val, b & 0x7F, multiplier) || + archive_ckd_add_u64(&result, result, val)) { + /* Integer overflow occurred. */ + return 0; + } /* MSB set to 1 means we need to continue decoding process. * MSB set to 0 means we're done. @@ -997,22 +1000,8 @@ static int read_var(struct archive_read* a, uint64_t* pvalue, } } - /* The decoded value takes the maximum number of 8 bytes. - * It's a maximum number of bytes, so end decoding process here - * even if the first bit of last byte is 1. */ - if(pvalue) { - *pvalue = result; - } - - if(pvalue_len) { - *pvalue_len = 9; - } else { - if(ARCHIVE_OK != consume(a, 9)) { - return 0; - } - } - - return 1; + /* All continuation bits were set. This is an error. */ + return 0; } static int read_var_sized(struct archive_read* a, size_t* pvalue, @@ -1036,52 +1025,47 @@ static int read_var_sized(struct archive_read* a, size_t* pvalue, return ret; } -static int read_bits_32(struct archive_read* a, struct rar5* rar, +static int read_bits_32(struct archive_read* a, struct rar5 *rar5, const uint8_t* p, uint32_t* value) { - if(rar->bits.in_addr >= rar->cstate.cur_block_size) { + if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Premature end of stream during extraction of data (#1)"); return ARCHIVE_FATAL; } - uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; - bits |= p[rar->bits.in_addr + 1] << 16; - bits |= p[rar->bits.in_addr + 2] << 8; - bits |= p[rar->bits.in_addr + 3]; - bits <<= rar->bits.bit_addr; - bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); + uint32_t bits = archive_be32dec(p + rar5->bits.in_addr); + bits <<= rar5->bits.bit_addr; + bits |= p[rar5->bits.in_addr + 4] >> (8 - rar5->bits.bit_addr); *value = bits; return ARCHIVE_OK; } -static int read_bits_16(struct archive_read* a, struct rar5* rar, +static int read_bits_16(struct archive_read* a, struct rar5 *rar5, const uint8_t* p, uint16_t* value) { - if(rar->bits.in_addr >= rar->cstate.cur_block_size) { + if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Premature end of stream during extraction of data (#2)"); return ARCHIVE_FATAL; } - int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; - bits |= (int) p[rar->bits.in_addr + 1] << 8; - bits |= (int) p[rar->bits.in_addr + 2]; - bits >>= (8 - rar->bits.bit_addr); + uint32_t bits = archive_be24dec(p + (unsigned)rar5->bits.in_addr); + bits >>= (8 - rar5->bits.bit_addr); *value = bits & 0xffff; return ARCHIVE_OK; } -static void skip_bits(struct rar5* rar, int bits) { - const int new_bits = rar->bits.bit_addr + bits; - rar->bits.in_addr += new_bits >> 3; - rar->bits.bit_addr = new_bits & 7; +static void skip_bits(struct rar5 *rar5, int bits) { + const int new_bits = rar5->bits.bit_addr + bits; + rar5->bits.in_addr += new_bits >> 3; + rar5->bits.bit_addr = new_bits & 7; } /* n = up to 16 */ -static int read_consume_bits(struct archive_read* a, struct rar5* rar, +static int read_consume_bits(struct archive_read* a, struct rar5 *rar5, const uint8_t* p, int n, int* value) { uint16_t v; @@ -1093,14 +1077,14 @@ static int read_consume_bits(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - ret = read_bits_16(a, rar, p, &v); + ret = read_bits_16(a, rar5, p, &v); if(ret != ARCHIVE_OK) return ret; num = (int) v; num >>= 16 - n; - skip_bits(rar, n); + skip_bits(rar5, n); if(value) *value = num; @@ -1215,11 +1199,11 @@ static void init_header(struct archive_read* a) { a->archive.archive_format_name = "RAR5"; } -static void init_window_mask(struct rar5* rar) { - if (rar->cstate.window_size) - rar->cstate.window_mask = rar->cstate.window_size - 1; +static void init_window_mask(struct rar5 *rar5) { + if (rar5->cstate.window_size) + rar5->cstate.window_mask = rar5->cstate.window_size - 1; else - rar->cstate.window_mask = 0; + rar5->cstate.window_mask = 0; } enum HEADER_FLAGS { @@ -1233,7 +1217,7 @@ enum HEADER_FLAGS { }; static int process_main_locator_extra_block(struct archive_read* a, - struct rar5* rar) + struct rar5 *rar5) { uint64_t locator_flags; @@ -1246,7 +1230,7 @@ static int process_main_locator_extra_block(struct archive_read* a, } if(locator_flags & QLIST) { - if(!read_var(a, &rar->qlist_offset, NULL)) { + if(!read_var(a, &rar5->qlist_offset, NULL)) { return ARCHIVE_EOF; } @@ -1254,7 +1238,7 @@ static int process_main_locator_extra_block(struct archive_read* a, } if(locator_flags & RECOVERY) { - if(!read_var(a, &rar->rr_offset, NULL)) { + if(!read_var(a, &rar5->rr_offset, NULL)) { return ARCHIVE_EOF; } @@ -1264,7 +1248,7 @@ static int process_main_locator_extra_block(struct archive_read* a, return ARCHIVE_OK; } -static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, +static int parse_file_extra_hash(struct archive_read* a, struct rar5 *rar5, int64_t* extra_data_size) { size_t hash_type = 0; @@ -1286,13 +1270,13 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, * CRC32. */ if(hash_type == BLAKE2sp) { const uint8_t* p; - const int hash_size = sizeof(rar->file.blake2sp); + const int hash_size = sizeof(rar5->file.blake2sp); if(!read_ahead(a, hash_size, &p)) return ARCHIVE_EOF; - rar->file.has_blake2 = 1; - memcpy(&rar->file.blake2sp, p, hash_size); + rar5->file.has_blake2 = 1; + memcpy(&rar5->file.blake2sp, p, hash_size); if(ARCHIVE_OK != consume(a, hash_size)) { return ARCHIVE_EOF; @@ -1385,7 +1369,7 @@ static int parse_file_extra_version(struct archive_read* a, } static int parse_file_extra_htime(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size) + struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size) { char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime; size_t flags = 0; @@ -1412,39 +1396,39 @@ static int parse_file_extra_htime(struct archive_read* a, has_mtime = flags & HAS_MTIME; has_atime = flags & HAS_ATIME; has_ctime = flags & HAS_CTIME; - rar->file.e_atime_ns = rar->file.e_ctime_ns = rar->file.e_mtime_ns = 0; + rar5->file.e_atime_ns = rar5->file.e_ctime_ns = rar5->file.e_mtime_ns = 0; if(has_mtime) { - parse_htime_item(a, unix_time, &rar->file.e_mtime, - &rar->file.e_mtime_ns, extra_data_size); + parse_htime_item(a, unix_time, &rar5->file.e_mtime, + &rar5->file.e_mtime_ns, extra_data_size); } if(has_ctime) { - parse_htime_item(a, unix_time, &rar->file.e_ctime, - &rar->file.e_ctime_ns, extra_data_size); + parse_htime_item(a, unix_time, &rar5->file.e_ctime, + &rar5->file.e_ctime_ns, extra_data_size); } if(has_atime) { - parse_htime_item(a, unix_time, &rar->file.e_atime, - &rar->file.e_atime_ns, extra_data_size); + parse_htime_item(a, unix_time, &rar5->file.e_atime, + &rar5->file.e_atime_ns, extra_data_size); } if(has_mtime && has_unix_ns) { - if(!read_u32(a, &rar->file.e_mtime_ns)) + if(!read_u32(a, &rar5->file.e_mtime_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; } if(has_ctime && has_unix_ns) { - if(!read_u32(a, &rar->file.e_ctime_ns)) + if(!read_u32(a, &rar5->file.e_ctime_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; } if(has_atime && has_unix_ns) { - if(!read_u32(a, &rar->file.e_atime_ns)) + if(!read_u32(a, &rar5->file.e_atime_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; @@ -1453,35 +1437,35 @@ static int parse_file_extra_htime(struct archive_read* a, /* The seconds and nanoseconds are either together, or separated in two * fields so we parse them, then set the archive_entry's times. */ if(has_mtime) { - archive_entry_set_mtime(e, rar->file.e_mtime, rar->file.e_mtime_ns); + archive_entry_set_mtime(e, rar5->file.e_mtime, rar5->file.e_mtime_ns); } if(has_ctime) { - archive_entry_set_ctime(e, rar->file.e_ctime, rar->file.e_ctime_ns); + archive_entry_set_ctime(e, rar5->file.e_ctime, rar5->file.e_ctime_ns); } if(has_atime) { - archive_entry_set_atime(e, rar->file.e_atime, rar->file.e_atime_ns); + archive_entry_set_atime(e, rar5->file.e_atime, rar5->file.e_atime_ns); } return ARCHIVE_OK; } static int parse_file_extra_redir(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size) + struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size) { uint64_t value_size = 0; size_t target_size = 0; char target_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; - if(!read_var(a, &rar->file.redir_type, &value_size)) + if(!read_var(a, &rar5->file.redir_type, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; - if(!read_var(a, &rar->file.redir_flags, &value_size)) + if(!read_var(a, &rar5->file.redir_flags, &value_size)) return ARCHIVE_EOF; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; @@ -1512,12 +1496,12 @@ static int parse_file_extra_redir(struct archive_read* a, if(ARCHIVE_OK != consume(a, (int64_t)target_size)) return ARCHIVE_EOF; - switch(rar->file.redir_type) { + switch(rar5->file.redir_type) { case REDIR_TYPE_UNIXSYMLINK: case REDIR_TYPE_WINSYMLINK: archive_entry_set_filetype(e, AE_IFLNK); archive_entry_update_symlink_utf8(e, target_utf8_buf); - if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { + if (rar5->file.redir_flags & REDIR_SYMLINK_IS_DIR) { archive_entry_set_symlink_type(e, AE_SYMLINK_TYPE_DIRECTORY); } else { @@ -1619,7 +1603,7 @@ static int parse_file_extra_owner(struct archive_read* a, } static int process_head_file_extra(struct archive_read* a, - struct archive_entry* e, struct rar5* rar, int64_t extra_data_size) + struct archive_entry* e, struct rar5 *rar5, int64_t extra_data_size) { uint64_t extra_field_size; uint64_t extra_field_id = 0; @@ -1649,15 +1633,15 @@ static int process_head_file_extra(struct archive_read* a, switch(extra_field_id) { case EX_HASH: - ret = parse_file_extra_hash(a, rar, + ret = parse_file_extra_hash(a, rar5, &extra_data_size); break; case EX_HTIME: - ret = parse_file_extra_htime(a, e, rar, + ret = parse_file_extra_htime(a, e, rar5, &extra_data_size); break; case EX_REDIR: - ret = parse_file_extra_redir(a, e, rar, + ret = parse_file_extra_redir(a, e, rar5, &extra_data_size); break; case EX_UOWNER: @@ -1671,8 +1655,8 @@ static int process_head_file_extra(struct archive_read* a, case EX_CRYPT: /* Mark the entry as encrypted */ archive_entry_set_is_data_encrypted(e, 1); - rar->has_encrypted_entries = 1; - rar->cstate.data_encrypted = 1; + rar5->has_encrypted_entries = 1; + rar5->cstate.data_encrypted = 1; /* fallthrough */ case EX_SUBDATA: /* fallthrough */ @@ -1734,7 +1718,7 @@ static int file_entry_sanity_checks(struct archive_read* a, return ARCHIVE_OK; } -static int process_head_file(struct archive_read* a, struct rar5* rar, +static int process_head_file(struct archive_read* a, struct rar5 *rar5, struct archive_entry* entry, size_t block_flags) { int64_t extra_data_size = 0; @@ -1773,8 +1757,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, archive_entry_clear(entry); /* Do not reset file context if we're switching archives. */ - if(!rar->cstate.switch_multivolume) { - reset_file_context(rar); + if(!rar5->cstate.switch_multivolume) { + reset_file_context(rar5); } if(block_flags & HFL_EXTRA_DATA) { @@ -1790,9 +1774,16 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(!read_var_sized(a, &data_size, NULL)) return ARCHIVE_EOF; - rar->file.bytes_remaining = data_size; + if(data_size > SSIZE_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "File data size is too large"); + return ARCHIVE_FATAL; + } + + rar5->file.bytes_remaining = data_size; } else { - rar->file.bytes_remaining = 0; + rar5->file.bytes_remaining = 0; } if(!read_var_sized(a, &file_flags, NULL)) @@ -1807,9 +1798,9 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); + rar5->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); - sanity_ret = file_entry_sanity_checks(a, block_flags, rar->file.dir, + sanity_ret = file_entry_sanity_checks(a, block_flags, rar5->file.dir, unpacked_size, data_size); if (sanity_ret != ARCHIVE_OK) { @@ -1836,90 +1827,90 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, c_version = (int) (compression_info & 0x3f); /* RAR5 seems to limit the dictionary size to 64MB. */ - window_size = (rar->file.dir > 0) ? + window_size = (rar5->file.dir > 0) ? 0 : g_unpack_window_size << ((compression_info >> 10) & 15); - rar->cstate.method = c_method; - rar->cstate.version = c_version + 50; - rar->file.solid = (compression_info & SOLID) > 0; + rar5->cstate.method = c_method; + rar5->cstate.version = c_version + 50; + rar5->file.solid = (compression_info & SOLID) > 0; /* Archives which declare solid files without initializing the window * buffer first are invalid, unless previous data was encrypted, in * which case we may never have had the chance */ - if(rar->file.solid > 0 && rar->cstate.data_encrypted == 0 && - rar->cstate.window_buf == NULL) { + if(rar5->file.solid > 0 && rar5->cstate.data_encrypted == 0 && + rar5->cstate.window_buf == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Declared solid file, but no window buffer " - "initialized yet."); + "initialized yet"); return ARCHIVE_FATAL; } /* Check if window_size is a sane value. Also, if the file is not * declared as a directory, disallow window_size == 0. */ if(window_size > (64 * 1024 * 1024) || - (rar->file.dir == 0 && window_size == 0)) + (rar5->file.dir == 0 && window_size == 0)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Declared dictionary size is not supported."); + "Declared dictionary size is not supported"); return ARCHIVE_FATAL; } - if(rar->file.solid > 0) { + if(rar5->file.solid > 0) { /* Re-check if current window size is the same as previous * window size (for solid files only). */ - if(rar->file.solid_window_size > 0 && - rar->file.solid_window_size != (ssize_t) window_size) + if(rar5->file.solid_window_size > 0 && + rar5->file.solid_window_size != (ssize_t) window_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Window size for this solid file doesn't match " - "the window size used in previous solid file. "); + "the window size used in previous solid file"); return ARCHIVE_FATAL; } } else - rar->cstate.data_encrypted = 0; /* Reset for new buffer */ + rar5->cstate.data_encrypted = 0; /* Reset for new buffer */ - if(rar->cstate.window_size < (ssize_t) window_size && - rar->cstate.window_buf) + if(rar5->cstate.window_size < (ssize_t) window_size && + rar5->cstate.window_buf) { /* The `data_ready` stack contains pointers to the `window_buf` or * `filtered_buf` buffers. Since we're about to reallocate the first * buffer, some of those pointers could become invalid. Therefore, we * need to dispose of all entries from the stack before attempting the * realloc. */ - clear_data_ready_stack(rar); + clear_data_ready_stack(rar5); /* If window_buf has been allocated before, reallocate it, so * that its size will match new window_size. */ uint8_t* new_window_buf = - realloc(rar->cstate.window_buf, (size_t) window_size); + realloc(rar5->cstate.window_buf, (size_t) window_size); if(!new_window_buf) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Not enough memory when trying to realloc the window " - "buffer."); + "buffer"); return ARCHIVE_FATAL; } - rar->cstate.window_buf = new_window_buf; + rar5->cstate.window_buf = new_window_buf; } /* Values up to 64M should fit into ssize_t on every * architecture. */ - rar->cstate.window_size = (ssize_t) window_size; + rar5->cstate.window_size = (ssize_t) window_size; - if(rar->file.solid > 0 && rar->file.solid_window_size == 0) { + if(rar5->file.solid > 0 && rar5->file.solid_window_size == 0) { /* Solid files have to have the same window_size across whole archive. Remember the window_size parameter for first solid file found. */ - rar->file.solid_window_size = rar->cstate.window_size; + rar5->file.solid_window_size = rar5->cstate.window_size; } - init_window_mask(rar); + init_window_mask(rar5); - rar->file.service = 0; + rar5->file.service = 0; if(!read_var_sized(a, &host_os, NULL)) return ARCHIVE_EOF; @@ -1946,29 +1937,20 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, archive_entry_set_mode(entry, mode); if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) { - char *fflags_text, *ptr; - /* allocate for ",rdonly,hidden,system" */ - fflags_text = malloc(22 * sizeof(*fflags_text)); - if (fflags_text != NULL) { - ptr = fflags_text; - if (file_attr & ATTR_READONLY) { - strcpy(ptr, ",rdonly"); - ptr = ptr + 7; - } - if (file_attr & ATTR_HIDDEN) { - strcpy(ptr, ",hidden"); - ptr = ptr + 7; - } - if (file_attr & ATTR_SYSTEM) { - strcpy(ptr, ",system"); - ptr = ptr + 7; - } - if (ptr > fflags_text) { - archive_entry_copy_fflags_text(entry, - fflags_text + 1); - } - free(fflags_text); - } + char buf[sizeof(",rdonly,hidden,system")]; + const char *fflags[3] = { "", "", "" }; + const char **flag = fflags; + + if (file_attr & ATTR_READONLY) + *flag++ = ",rdonly"; + if (file_attr & ATTR_HIDDEN) + *flag++ = ",hidden"; + if (file_attr & ATTR_SYSTEM) + *flag++ = ",system"; + + snprintf(buf, sizeof(buf), "%s%s%s", + fflags[0], fflags[1], fflags[2]); + archive_entry_copy_fflags_text(entry, buf + 1); } } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ @@ -2011,7 +1993,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, archive_entry_update_pathname_utf8(entry, name_utf8_buf); if(extra_data_size > 0) { - int ret = process_head_file_extra(a, entry, rar, + int ret = process_head_file_extra(a, entry, rar5, extra_data_size); /* @@ -2030,8 +2012,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, } if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { - rar->file.unpacked_size = (ssize_t) unpacked_size; - if(rar->file.redir_type == REDIR_TYPE_NONE) + rar5->file.unpacked_size = (ssize_t) unpacked_size; + if(rar5->file.redir_type == REDIR_TYPE_NONE) archive_entry_set_size(entry, unpacked_size); } @@ -2040,18 +2022,18 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, } if(file_flags & CRC32) { - rar->file.stored_crc32 = crc; + rar5->file.stored_crc32 = crc; } - if(!rar->cstate.switch_multivolume) { + if(!rar5->cstate.switch_multivolume) { /* Do not reinitialize unpacking state if we're switching * archives. */ - rar->cstate.block_parsing_finished = 1; - rar->cstate.all_filters_applied = 1; - rar->cstate.initialized = 0; + rar5->cstate.block_parsing_finished = 1; + rar5->cstate.all_filters_applied = 1; + rar5->cstate.initialized = 0; } - if(rar->generic.split_before > 0) { + if(rar5->generic.split_before > 0) { /* If now we're standing on a header that has a 'split before' * mark, it means we're standing on a 'continuation' file * header. Signal the caller that if it wants to move to @@ -2064,15 +2046,15 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, } } -static int process_head_service(struct archive_read* a, struct rar5* rar, +static int process_head_service(struct archive_read* a, struct rar5 *rar5, struct archive_entry* entry, size_t block_flags) { /* Process this SERVICE block the same way as FILE blocks. */ - int ret = process_head_file(a, rar, entry, block_flags); + int ret = process_head_file(a, rar5, entry, block_flags); if(ret != ARCHIVE_OK) return ret; - rar->file.service = 1; + rar5->file.service = 1; /* But skip the data part automatically. It's no use for the user * anyway. It contains only service data, not even needed to @@ -2085,7 +2067,7 @@ static int process_head_service(struct archive_read* a, struct rar5* rar, return ARCHIVE_RETRY; } -static int process_head_main(struct archive_read* a, struct rar5* rar, +static int process_head_main(struct archive_read* a, struct rar5 *rar5, struct archive_entry* entry, size_t block_flags) { int ret; @@ -2121,8 +2103,8 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, return ARCHIVE_EOF; } - rar->main.volume = (archive_flags & VOLUME) > 0; - rar->main.solid = (archive_flags & SOLID) > 0; + rar5->main.volume = (archive_flags & VOLUME) > 0; + rar5->main.solid = (archive_flags & SOLID) > 0; if(archive_flags & VOLUME_NUMBER) { size_t v = 0; @@ -2137,13 +2119,13 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - rar->main.vol_no = (unsigned int) v; + rar5->main.vol_no = (unsigned int) v; } else { - rar->main.vol_no = 0; + rar5->main.vol_no = 0; } - if(rar->vol.expected_vol_no > 0 && - rar->main.vol_no != rar->vol.expected_vol_no) + if(rar5->vol.expected_vol_no > 0 && + rar5->main.vol_no != rar5->vol.expected_vol_no) { /* Returning EOF instead of FATAL because of strange * libarchive behavior. When opening multiple files via @@ -2174,7 +2156,7 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, switch(extra_field_id) { case LOCATOR: - ret = process_main_locator_extra_block(a, rar); + ret = process_main_locator_extra_block(a, rar5); if(ret != ARCHIVE_OK) { /* Error while parsing main locator extra * block. */ @@ -2194,25 +2176,25 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, } static int skip_unprocessed_bytes(struct archive_read* a) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; int ret; - if(rar->file.bytes_remaining) { + if(rar5->file.bytes_remaining) { /* Use different skipping method in block merging mode than in * normal mode. If merge mode is active, rar5_read_data_skip * can't be used, because it could allow recursive use of * merge_block() * function, and this function doesn't support * recursive use. */ - if(rar->merge_mode) { + if(rar5->merge_mode) { /* Discard whole merged block. This is valid in solid * mode as well, because the code will discard blocks * only if those blocks are safe to discard (i.e. * they're not FILE blocks). */ - ret = consume(a, rar->file.bytes_remaining); + ret = consume(a, rar5->file.bytes_remaining); if(ret != ARCHIVE_OK) { return ret; } - rar->file.bytes_remaining = 0; + rar5->file.bytes_remaining = 0; } else { /* If we're not in merge mode, use safe skipping code. * This will ensure we'll handle solid archives @@ -2274,17 +2256,45 @@ static int scan_for_signature(struct archive_read* a); * block. */ +/* + * A header that carries no file data (HEAD_MAIN, or an unknown block + * flagged HFL_SKIP_IF_UNKNOWN) may leave bytes in its body that the + * sub-parser did not read. Skip them before returning ARCHIVE_RETRY, + * otherwise rar5_read_header() re-parses the same block region O(N) + * times instead of O(1), letting a crafted RAR5 file stall the reader + * (GHSA-9h2c-464f-j3hj). + * + * Safe because read_ahead(a, hdr_size, &p) pre-loaded the whole block + * into one contiguous buffer with no compaction until we return, so + * body_start stays valid and (cur - body_start) is the exact number of + * body bytes consumed so far. + */ +static void +rar5_skip_remaining_block(struct archive_read* a, + const uint8_t* body_start, size_t raw_hdr_size) +{ + const uint8_t* cur; + + if(read_ahead(a, 1, &cur)) { + size_t body_used = (size_t)(cur - body_start); + + if(body_used < raw_hdr_size) + (void)consume(a, raw_hdr_size - body_used); + } +} + static int process_base_block(struct archive_read* a, struct archive_entry* entry) { const size_t SMALLEST_RAR5_BLOCK_SIZE = 3; - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; uint32_t hdr_crc, computed_crc; size_t raw_hdr_size = 0, hdr_size_len, hdr_size; size_t header_id = 0; size_t header_flags = 0; const uint8_t* p; + const uint8_t* body_start; int ret; enum HEADER_TYPE { @@ -2346,6 +2356,10 @@ static int process_base_block(struct archive_read* a, #endif } + /* Remember the first byte of the block body so we can later skip + * any bytes the sub-parser leaves unconsumed. */ + body_start = p + hdr_size_len; + /* If the checksum is OK, we proceed with parsing. */ if(ARCHIVE_OK != consume(a, hdr_size_len)) { return ARCHIVE_EOF; @@ -2357,47 +2371,50 @@ static int process_base_block(struct archive_read* a, if(!read_var_sized(a, &header_flags, NULL)) return ARCHIVE_EOF; - rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; - rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; - rar->generic.size = (int)hdr_size; - rar->generic.last_header_id = (int)header_id; - rar->main.endarc = 0; + rar5->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; + rar5->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; + rar5->generic.size = (int)hdr_size; + rar5->generic.last_header_id = (int)header_id; + rar5->main.endarc = 0; /* Those are possible header ids in RARv5. */ switch(header_id) { case HEAD_MAIN: - ret = process_head_main(a, rar, entry, header_flags); + ret = process_head_main(a, rar5, entry, header_flags); /* Main header doesn't have any files in it, so it's * pointless to return to the caller. Retry to next * header, which should be HEAD_FILE/HEAD_SERVICE. */ - if(ret == ARCHIVE_OK) + if(ret == ARCHIVE_OK) { + rar5_skip_remaining_block(a, body_start, + raw_hdr_size); return ARCHIVE_RETRY; + } return ret; case HEAD_SERVICE: - ret = process_head_service(a, rar, entry, header_flags); + ret = process_head_service(a, rar5, entry, header_flags); return ret; case HEAD_FILE: - ret = process_head_file(a, rar, entry, header_flags); + ret = process_head_file(a, rar5, entry, header_flags); return ret; case HEAD_CRYPT: archive_entry_set_is_metadata_encrypted(entry, 1); archive_entry_set_is_data_encrypted(entry, 1); - rar->has_encrypted_entries = 1; - rar->headers_are_encrypted = 1; + rar5->has_encrypted_entries = 1; + rar5->headers_are_encrypted = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encryption is not supported"); return ARCHIVE_FATAL; case HEAD_ENDARC: - rar->main.endarc = 1; + rar5->main.endarc = 1; /* After encountering an end of file marker, we need * to take into consideration if this archive is * continued in another file (i.e. is it part01.rar: * is there a part02.rar?) */ - if(rar->main.volume) { + if(rar5->main.volume) { /* In case there is part02.rar, position the * read pointer in a proper place, so we can * resume parsing. */ @@ -2405,7 +2422,7 @@ static int process_base_block(struct archive_read* a, if(ret == ARCHIVE_FATAL) { return ARCHIVE_EOF; } else { - if(rar->vol.expected_vol_no == + if(rar5->vol.expected_vol_no == UINT_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2413,8 +2430,8 @@ static int process_base_block(struct archive_read* a, return ARCHIVE_FATAL; } - rar->vol.expected_vol_no = - rar->main.vol_no + 1; + rar5->vol.expected_vol_no = + rar5->main.vol_no + 1; return ARCHIVE_OK; } } else { @@ -2432,6 +2449,8 @@ static int process_base_block(struct archive_read* a, /* If the block is marked as 'skip if unknown', * do as the flag says: skip the block * instead on failing on it. */ + rar5_skip_remaining_block(a, body_start, + raw_hdr_size); return ARCHIVE_RETRY; } } @@ -2445,13 +2464,16 @@ static int process_base_block(struct archive_read* a, } static int skip_base_block(struct archive_read* a) { + struct rar5 *rar5 = a->format->data; int ret; - struct rar5* rar = get_context(a); /* Create a new local archive_entry structure that will be operated on * by header reader; operations on this archive_entry will be discarded. */ struct archive_entry* entry = archive_entry_new(); + if (entry == NULL) + return ARCHIVE_FATAL; + ret = process_base_block(a, entry); /* Discard operations on this archive_entry structure. */ @@ -2459,7 +2481,7 @@ static int skip_base_block(struct archive_read* a) { if(ret == ARCHIVE_FATAL) return ret; - if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) + if(rar5->generic.last_header_id == 2 && rar5->generic.split_before > 0) return ARCHIVE_OK; if(ret == ARCHIVE_OK) @@ -2527,71 +2549,77 @@ fatal: static int rar5_read_header(struct archive_read *a, struct archive_entry *entry) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; int ret; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. */ - if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { - rar->has_encrypted_entries = 0; + if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + rar5->has_encrypted_entries = 0; } - if(rar->header_initialized == 0) { + if(rar5->header_initialized == 0) { init_header(a); if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN) return ret; - rar->header_initialized = 1; + rar5->header_initialized = 1; } - if(rar->skipped_magic == 0) { + if(rar5->skipped_magic == 0) { if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) { return ARCHIVE_EOF; } - rar->skipped_magic = 1; + rar5->skipped_magic = 1; } do { ret = process_base_block(a, entry); } while(ret == ARCHIVE_RETRY || - (rar->main.endarc > 0 && ret == ARCHIVE_OK)); + (rar5->main.endarc > 0 && ret == ARCHIVE_OK)); return ret; } -static void init_unpack(struct rar5* rar) { - rar->file.calculated_crc32 = 0; - init_window_mask(rar); +static int init_unpack(struct rar5 *rar5) { + rar5->file.calculated_crc32 = 0; + init_window_mask(rar5); - free(rar->cstate.window_buf); - free(rar->cstate.filtered_buf); + free(rar5->cstate.window_buf); + free(rar5->cstate.filtered_buf); - if(rar->cstate.window_size > 0) { - rar->cstate.window_buf = calloc(1, rar->cstate.window_size); - rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); - } else { - rar->cstate.window_buf = NULL; - rar->cstate.filtered_buf = NULL; + rar5->cstate.window_buf = NULL; + rar5->cstate.filtered_buf = NULL; + + if(rar5->cstate.window_size > 0) { + rar5->cstate.window_buf = calloc(1, rar5->cstate.window_size); + if(rar5->cstate.window_buf == NULL) + return ARCHIVE_FATAL; + rar5->cstate.filtered_buf = calloc(1, + rar5->cstate.window_size); + if(rar5->cstate.filtered_buf == NULL) + return ARCHIVE_FATAL; } - clear_data_ready_stack(rar); + clear_data_ready_stack(rar5); - rar->cstate.write_ptr = 0; - rar->cstate.last_write_ptr = 0; + rar5->cstate.write_ptr = 0; + rar5->cstate.last_write_ptr = 0; - memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); - memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); - memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); - memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); - memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); + memset(&rar5->cstate.bd, 0, sizeof(rar5->cstate.bd)); + memset(&rar5->cstate.ld, 0, sizeof(rar5->cstate.ld)); + memset(&rar5->cstate.dd, 0, sizeof(rar5->cstate.dd)); + memset(&rar5->cstate.ldd, 0, sizeof(rar5->cstate.ldd)); + memset(&rar5->cstate.rd, 0, sizeof(rar5->cstate.rd)); + return ARCHIVE_OK; } -static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { +static void update_crc(struct rar5 *rar5, const uint8_t* p, size_t to_read) { int verify_crc; - if(rar->skip_mode) { + if(rar5->skip_mode) { #if defined CHECK_CRC_ON_SOLID_SKIP verify_crc = 1; #else @@ -2603,17 +2631,19 @@ static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { if(verify_crc) { /* Don't update CRC32 if the file doesn't have the * `stored_crc32` info filled in. */ - if(rar->file.stored_crc32 > 0) { - rar->file.calculated_crc32 = - crc32(rar->file.calculated_crc32, p, (unsigned int)to_read); + if(rar5->file.stored_crc32 > 0) { + rar5->file.calculated_crc32 = + crc32(rar5->file.calculated_crc32, p, + (unsigned int)to_read); } /* Check if the file uses an optional BLAKE2sp checksum * algorithm. */ - if(rar->file.has_blake2 > 0) { + if(rar5->file.has_blake2 > 0) { /* Return value of the `update` function is always 0, * so we can explicitly ignore it here. */ - (void) blake2sp_update(&rar->file.b2state, p, to_read); + (void) blake2sp_update(&rar5->file.b2state, p, + to_read); } } } @@ -2647,6 +2677,15 @@ static int create_decode_tables(uint8_t* bit_length, upper_limit <<= 1; } + /* Verify the code-length distribution is not over-subscribed. + * After the loop above, upper_limit == sum(lc[i] * 2^(16-i)). + * For a valid prefix-free code this must be <= 2^16 = 65536. + * An over-subscribed table (> 65536) cannot produce a valid + * decode table and must be rejected. */ + if(upper_limit > 65536) { + return ARCHIVE_FAILED; + } + memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); for(i = 0; i < size; i++) { @@ -2688,12 +2727,12 @@ static int create_decode_tables(uint8_t* bit_length, static int decode_number(struct archive_read* a, struct decode_table* table, const uint8_t* p, uint16_t* num) { + struct rar5 *rar5 = a->format->data; int i, bits, dist, ret; uint16_t bitfield; uint32_t pos; - struct rar5* rar = get_context(a); - if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) { + if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &bitfield))) { return ret; } @@ -2701,7 +2740,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table, if(bitfield < table->decode_len[table->quick_bits]) { int code = bitfield >> (16 - table->quick_bits); - skip_bits(rar, table->quick_len[code]); + skip_bits(rar5, table->quick_len[code]); *num = table->quick_num[code]; return ARCHIVE_OK; } @@ -2715,7 +2754,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table, } } - skip_bits(rar, bits); + skip_bits(rar5, bits); dist = bitfield - table->decode_len[bits - 1]; dist >>= (16 - bits); @@ -2729,7 +2768,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table, } /* Reads and parses Huffman tables from the beginning of the block. */ -static int parse_tables(struct archive_read* a, struct rar5* rar, +static int parse_tables(struct archive_read* a, struct rar5 *rar5, const uint8_t* p) { int ret, value, i, w, idx = 0; @@ -2743,12 +2782,12 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, /* The data for table generation is compressed using a simple RLE-like * algorithm when storing zeroes, so we need to unpack it first. */ for(w = 0, i = 0; w < HUFF_BC;) { - if(i >= rar->cstate.cur_block_size) { + if(i >= rar5->cstate.cur_block_size) { /* Truncated data, can't continue. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated data in huffman tables"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } value = (p[i] & nibble_mask) >> nibble_shift; @@ -2787,25 +2826,25 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, } } - rar->bits.in_addr = i; - rar->bits.bit_addr = nibble_shift ^ 4; + rar5->bits.in_addr = i; + rar5->bits.bit_addr = nibble_shift ^ 4; - ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); + ret = create_decode_tables(bit_length, &rar5->cstate.bd, HUFF_BC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; - ret = decode_number(a, &rar->cstate.bd, p, &num); + ret = decode_number(a, &rar5->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } if(num < 16) { @@ -2816,17 +2855,17 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, /* 16..17: repeat previous code */ uint16_t n; - if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n))) + if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n))) return ret; if(num == 16) { n >>= 13; n += 3; - skip_bits(rar, 3); + skip_bits(rar5, 3); } else { n >>= 9; n += 11; - skip_bits(rar, 7); + skip_bits(rar5, 7); } if(i > 0) { @@ -2839,23 +2878,23 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, ARCHIVE_ERRNO_FILE_FORMAT, "Unexpected error when decoding " "huffman tables"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } } else { /* other codes: fill with zeroes `n` times */ uint16_t n; - if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n))) + if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n))) return ret; if(num == 18) { n >>= 13; n += 3; - skip_bits(rar, 3); + skip_bits(rar5, 3); } else { n >>= 9; n += 11; - skip_bits(rar, 7); + skip_bits(rar5, 7); } while(n-- > 0 && i < HUFF_TABLE_SIZE) @@ -2863,38 +2902,38 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, } } - ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); + ret = create_decode_tables(&table[idx], &rar5->cstate.ld, HUFF_NC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create literal table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_NC; - ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); + ret = create_decode_tables(&table[idx], &rar5->cstate.dd, HUFF_DC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create distance table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_DC; - ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); + ret = create_decode_tables(&table[idx], &rar5->cstate.ldd, HUFF_LDC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create lower bits of distances table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } idx += HUFF_LDC; - ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); + ret = create_decode_tables(&table[idx], &rar5->cstate.rd, HUFF_RC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create repeating distances table"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } return ARCHIVE_OK; @@ -2912,7 +2951,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block header size (was %d, max is 2)", bf_byte_count(hdr)); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } /* This should probably use bit reader interface in order to be more @@ -2956,7 +2995,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } @@ -2964,13 +3003,13 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, } /* Convenience function used during filter processing. */ -static int parse_filter_data(struct archive_read* a, struct rar5* rar, +static int parse_filter_data(struct archive_read* a, struct rar5 *rar5, const uint8_t* p, uint32_t* filter_data) { int i, bytes, ret; uint32_t data = 0; - if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes))) + if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5, p, 2, &bytes))) return ret; bytes++; @@ -2978,14 +3017,14 @@ static int parse_filter_data(struct archive_read* a, struct rar5* rar, for(i = 0; i < bytes; i++) { uint16_t byte; - if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) { + if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &byte))) { return ret; } /* Cast to uint32_t will ensure the shift operation will not * produce undefined result. */ data += ((uint32_t) byte >> 8) << (i * 8); - skip_bits(rar, 8); + skip_bits(rar5, 8); } *filter_data = data; @@ -2993,12 +3032,12 @@ static int parse_filter_data(struct archive_read* a, struct rar5* rar, } /* Function is used during sanity checking. */ -static int is_valid_filter_block_start(struct rar5* rar, +static int is_valid_filter_block_start(struct rar5 *rar5, uint32_t start) { - const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; - const int64_t last_bs = rar->cstate.last_block_start; - const ssize_t last_bl = rar->cstate.last_block_length; + const int64_t block_start = (ssize_t) start + rar5->cstate.write_ptr; + const int64_t last_bs = rar5->cstate.last_block_start; + const ssize_t last_bl = rar5->cstate.last_block_length; if(last_bs == 0 || last_bl == 0) { /* We didn't have any filters yet, so accept this offset. */ @@ -3018,55 +3057,52 @@ static int is_valid_filter_block_start(struct rar5* rar, /* The function will create a new filter, read its parameters from the input * stream and add it to the filter collection. */ static int parse_filter(struct archive_read* ar, const uint8_t* p) { + struct rar5 *rar5 = ar->format->data; uint32_t block_start, block_length; uint16_t filter_type; struct filter_info* filt = NULL; - struct rar5* rar = get_context(ar); int ret; /* Read the parameters from the input stream. */ - if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start))) + if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_start))) return ret; - if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length))) + if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_length))) return ret; - if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type))) + if(ARCHIVE_OK != (ret = read_bits_16(ar, rar5, p, &filter_type))) return ret; filter_type >>= 13; - skip_bits(rar, 3); + skip_bits(rar5, 3); - /* Perform some sanity checks on this filter parameters. Note that we - * allow only DELTA, E8/E9 and ARM filters here, because rest of - * filters are not used in RARv5. */ + /* Perform some sanity checks on this filter parameters. */ if(block_length < 4 || block_length > 0x400000 || - filter_type > FILTER_ARM || - !is_valid_filter_block_start(rar, block_start) || - (rar->cstate.window_size > 0 && - (ssize_t)block_length > rar->cstate.window_size >> 1)) + !is_valid_filter_block_start(rar5, block_start) || + (rar5->cstate.window_size > 0 && + (ssize_t)block_length > rar5->cstate.window_size >> 1)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid filter encountered"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } /* Allocate a new filter. */ - filt = add_new_filter(rar); + filt = add_new_filter(rar5); if(filt == NULL) { archive_set_error(&ar->archive, ENOMEM, - "Can't allocate memory for a filter descriptor."); + "Can't allocate memory for a filter descriptor"); return ARCHIVE_FATAL; } filt->type = filter_type; - filt->block_start = rar->cstate.write_ptr + block_start; + filt->block_start = rar5->cstate.write_ptr + block_start; filt->block_length = block_length; - rar->cstate.last_block_start = filt->block_start; - rar->cstate.last_block_length = filt->block_length; + rar5->cstate.last_block_start = filt->block_start; + rar5->cstate.last_block_length = filt->block_length; /* Read some more data in case this is a DELTA filter. Other filter * types don't require any additional data over what was already @@ -3074,7 +3110,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) { if(filter_type == FILTER_DELTA) { int channels; - if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels))) + if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar5, p, 5, &channels))) return ret; filt->channels = channels + 1; @@ -3083,7 +3119,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) { return ARCHIVE_OK; } -static int decode_code_length(struct archive_read* a, struct rar5* rar, +static int decode_code_length(struct archive_read* a, struct rar5 *rar5, const uint8_t* p, uint16_t code) { int lbits, length = 2; @@ -3099,7 +3135,7 @@ static int decode_code_length(struct archive_read* a, struct rar5* rar, if(lbits > 0) { int add; - if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add)) + if(ARCHIVE_OK != read_consume_bits(a, rar5, p, lbits, &add)) return -1; length += add; @@ -3109,15 +3145,22 @@ static int decode_code_length(struct archive_read* a, struct rar5* rar, } static int copy_string(struct archive_read* a, int len, int dist) { - struct rar5* rar = get_context(a); - const ssize_t cmask = rar->cstate.window_mask; - const uint64_t write_ptr = rar->cstate.write_ptr + - rar->cstate.solid_offset; + struct rar5 *rar5 = a->format->data; + const ssize_t cmask = rar5->cstate.window_mask; + const uint64_t write_ptr = rar5->cstate.write_ptr + + rar5->cstate.solid_offset; int i; - if (rar->cstate.window_buf == NULL) + if (rar5->cstate.window_buf == NULL) return ARCHIVE_FATAL; + if (rar5->cstate.write_ptr > rar5->file.unpacked_size || + len > rar5->file.unpacked_size - rar5->cstate.write_ptr) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Uncompressed data exceeds declared size"); + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; + } + /* The unpacker spends most of the time in this function. It would be * a good idea to introduce some optimizations here. * @@ -3128,26 +3171,26 @@ static int copy_string(struct archive_read* a, int len, int dist) { for(i = 0; i < len; i++) { const ssize_t write_idx = (write_ptr + i) & cmask; const ssize_t read_idx = (write_ptr + i - dist) & cmask; - rar->cstate.window_buf[write_idx] = - rar->cstate.window_buf[read_idx]; + rar5->cstate.window_buf[write_idx] = + rar5->cstate.window_buf[read_idx]; } - rar->cstate.write_ptr += len; + rar5->cstate.write_ptr += len; return ARCHIVE_OK; } static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; uint16_t num; int ret; - const uint64_t cmask = rar->cstate.window_mask; - const struct compressed_block_header* hdr = &rar->last_block_hdr; + const uint64_t cmask = rar5->cstate.window_mask; + const struct compressed_block_header* hdr = &rar5->last_block_hdr; const uint8_t bit_size = 1 + bf_bit_size(hdr); while(1) { - if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > - (rar->cstate.window_size >> 1)) { + if(rar5->cstate.write_ptr - rar5->cstate.last_write_ptr > + (rar5->cstate.window_size >> 1)) { /* Don't allow growing data by more than half of the * window size at a time. In such case, break the loop; * next call to this function will continue processing @@ -3155,18 +3198,18 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { break; } - if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || - (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && - rar->bits.bit_addr >= bit_size)) + if(rar5->bits.in_addr > rar5->cstate.cur_block_size - 1 || + (rar5->bits.in_addr == rar5->cstate.cur_block_size - 1 && + rar5->bits.bit_addr >= bit_size)) { /* If the program counter is here, it means the * function has finished processing the block. */ - rar->cstate.block_parsing_finished = 1; + rar5->cstate.block_parsing_finished = 1; break; } /* Decode the next literal. */ - if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { + if(ARCHIVE_OK != decode_number(a, &rar5->cstate.ld, p, &num)) { return ARCHIVE_EOF; } @@ -3186,15 +3229,25 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { if(num < 256) { /* Directly store the byte. */ - int64_t write_idx = rar->cstate.solid_offset + - rar->cstate.write_ptr++; + int64_t write_idx; - rar->cstate.window_buf[write_idx & cmask] = + /* A literal write emits one byte; copy_string() checks len. */ + if(rar5->cstate.write_ptr >= rar5->file.unpacked_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Uncompressed data exceeds declared size"); + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; + } + + write_idx = rar5->cstate.solid_offset + + rar5->cstate.write_ptr++; + + rar5->cstate.window_buf[write_idx & cmask] = (uint8_t) num; continue; } else if(num >= 262) { uint16_t dist_slot; - int len = decode_code_length(a, rar, p, num - 262), + int len = decode_code_length(a, rar5, p, num - 262), dbits, dist = 1; @@ -3203,17 +3256,17 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the code length"); - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } - if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, + if(ARCHIVE_OK != decode_number(a, &rar5->cstate.dd, p, &dist_slot)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } if(dist_slot < 4) { @@ -3237,28 +3290,28 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { if(dbits > 4) { if(ARCHIVE_OK != (ret = read_bits_32( - a, rar, p, &add))) { + a, rar5, p, &add))) { /* Return EOF if we * can't read more * data. */ return ret; } - skip_bits(rar, dbits - 4); + skip_bits(rar5, dbits - 4); add = (add >> ( 36 - dbits)) << 4; dist += add; } if(ARCHIVE_OK != decode_number(a, - &rar->cstate.ldd, p, &low_dist)) + &rar5->cstate.ldd, p, &low_dist)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the " "distance slot"); - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } if(dist >= INT_MAX - low_dist - 1) { @@ -3268,7 +3321,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { ARCHIVE_ERRNO_FILE_FORMAT, "Distance pointer " "overflow"); - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } dist += low_dist; @@ -3276,7 +3329,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { /* dbits is one of [0,1,2,3] */ int add; - if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, + if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5, p, dbits, &add))) { /* Return EOF if we can't read * more data. */ @@ -3299,11 +3352,12 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { } } - dist_cache_push(rar, dist); - rar->cstate.last_len = len; + dist_cache_push(rar5, dist); + rar5->cstate.last_len = len; - if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; + ret = copy_string(a, len, dist); + if(ret != ARCHIVE_OK) + return ret; continue; } else if(num == 256) { @@ -3314,38 +3368,38 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { continue; } else if(num == 257) { - if(rar->cstate.last_len != 0) { - if(ARCHIVE_OK != copy_string(a, - rar->cstate.last_len, - rar->cstate.dist_cache[0])) - { - return ARCHIVE_FATAL; - } + if(rar5->cstate.last_len != 0) { + ret = copy_string(a, + rar5->cstate.last_len, + rar5->cstate.dist_cache[0]); + if(ret != ARCHIVE_OK) + return ret; } continue; } else { /* num < 262 */ const int idx = num - 258; - const int dist = dist_cache_touch(rar, idx); + const int dist = dist_cache_touch(rar5, idx); uint16_t len_slot; int len; - if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, + if(ARCHIVE_OK != decode_number(a, &rar5->cstate.rd, p, &len_slot)) { - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } - len = decode_code_length(a, rar, p, len_slot); + len = decode_code_length(a, rar5, p, len_slot); if (len == -1) { - return ARCHIVE_FATAL; + return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED; } - rar->cstate.last_len = len; + rar5->cstate.last_len = len; - if(ARCHIVE_OK != copy_string(a, len, dist)) - return ARCHIVE_FATAL; + ret = copy_string(a, len, dist); + if(ret != ARCHIVE_OK) + return ret; continue; } @@ -3403,15 +3457,15 @@ static int scan_for_signature(struct archive_read* a) { /* This function will switch the multivolume archive file to another file, * i.e. from part03 to part 04. */ static int advance_multivolume(struct archive_read* a) { + struct rar5 *rar5 = a->format->data; int lret; - struct rar5* rar = get_context(a); /* A small state machine that will skip unnecessary data, needed to * switch from one multivolume to another. Such skipping is needed if * we want to be an stream-oriented (instead of file-oriented) * unpacker. * - * The state machine starts with `rar->main.endarc` == 0. It also + * The state machine starts with `rar5->main.endarc` == 0. It also * assumes that current stream pointer points to some base block * header. * @@ -3420,10 +3474,10 @@ static int advance_multivolume(struct archive_read* a) { */ while(1) { - if(rar->main.endarc == 1) { + if(rar5->main.endarc == 1) { int looping = 1; - rar->main.endarc = 0; + rar5->main.endarc = 0; while(looping) { lret = skip_base_block(a); @@ -3461,7 +3515,7 @@ static int advance_multivolume(struct archive_read* a) { /* If there was an error during skipping, or we * have just skipped a FILE base block... */ - if(rar->main.endarc == 0) { + if(rar5->main.endarc == 0) { return lret; } else { continue; @@ -3480,12 +3534,12 @@ static int advance_multivolume(struct archive_read* a) { static int merge_block(struct archive_read* a, ssize_t block_size, const uint8_t** p) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; ssize_t cur_block_size, partial_offset = 0; const uint8_t* lp; int ret; - if(rar->merge_mode) { + if(rar5->merge_mode) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Recursive merge is not allowed"); @@ -3493,26 +3547,27 @@ static int merge_block(struct archive_read* a, ssize_t block_size, } /* Set a flag that we're in the switching mode. */ - rar->cstate.switch_multivolume = 1; + rar5->cstate.switch_multivolume = 1; /* Reallocate the memory which will hold the whole block. */ - if(rar->vol.push_buf) - free((void*) rar->vol.push_buf); + if(rar5->vol.push_buf) + free((void*) rar5->vol.push_buf); /* Increasing the allocation block by 8 is due to bit reading functions, * which are using additional 2 or 4 bytes. Allocating the block size * by exact value would make bit reader perform reads from invalid * memory block when reading the last byte from the buffer. */ - rar->vol.push_buf = malloc(block_size + 8); - if(!rar->vol.push_buf) { + rar5->vol.push_buf = malloc(block_size + 8); + if(!rar5->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for a merge block buffer."); + "Can't allocate memory for a merge block buffer"); + rar5->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } /* Valgrind complains if the extension block for bit reader is not * initialized, so initialize it. */ - memset(&rar->vol.push_buf[block_size], 0, 8); + memset(&rar5->vol.push_buf[block_size], 0, 8); /* A single block can span across multiple multivolume archive files, * so we use a loop here. This loop will consume enough multivolume @@ -3521,41 +3576,52 @@ static int merge_block(struct archive_read* a, ssize_t block_size, while(1) { /* Get the size of current block chunk in this multivolume * archive file and read it. */ - cur_block_size = rar5_min(rar->file.bytes_remaining, + cur_block_size = rar5_min(rar5->file.bytes_remaining, block_size - partial_offset); - if(cur_block_size == 0) { + if(cur_block_size < 1) { + /* bytes_remaining is less than 1 at the wrong point in + * the merge loop, indicating corrupt volume + * accounting. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Encountered block size == 0 during block merge"); + "Encountered invalid block size during block merge"); + rar5->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } - if(!read_ahead(a, cur_block_size, &lp)) + if(!read_ahead(a, cur_block_size, &lp)) { + rar5->cstate.switch_multivolume = 0; return ARCHIVE_EOF; + } /* Sanity check; there should never be a situation where this * function reads more data than the block's size. */ if(partial_offset + cur_block_size > block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Consumed too much data when merging blocks."); + "Consumed too much data when merging blocks"); + rar5->cstate.switch_multivolume = 0; return ARCHIVE_FATAL; } /* Merge previous block chunk with current block chunk, * or create first block chunk if this is our first * iteration. */ - memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); + memcpy(&rar5->vol.push_buf[partial_offset], lp, cur_block_size); /* Advance the stream read pointer by this block chunk size. */ - if(ARCHIVE_OK != consume(a, cur_block_size)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, cur_block_size)) { + /* Data was copied but stream pointer didn't advance; + * stream position is unrecoverable. */ + rar5->cstate.switch_multivolume = 0; + return ARCHIVE_FATAL; + } /* Update the pointers. `partial_offset` contains information * about the sum of merged block chunks. */ partial_offset += cur_block_size; - rar->file.bytes_remaining -= cur_block_size; + rar5->file.bytes_remaining -= cur_block_size; /* If `partial_offset` is the same as `block_size`, this means * we've merged all block chunks and we have a valid full @@ -3566,17 +3632,18 @@ static int merge_block(struct archive_read* a, ssize_t block_size, /* If we don't have any bytes to read, this means we should * switch to another multivolume archive file. */ - if(rar->file.bytes_remaining == 0) { - rar->merge_mode++; + if(rar5->file.bytes_remaining == 0) { + rar5->merge_mode++; ret = advance_multivolume(a); - rar->merge_mode--; + rar5->merge_mode--; if(ret != ARCHIVE_OK) { + rar5->cstate.switch_multivolume = 0; return ret; } } } - *p = rar->vol.push_buf; + *p = rar5->vol.push_buf; /* If we're here, we can resume unpacking by processing the block * pointed to by the `*p` memory pointer. */ @@ -3585,19 +3652,19 @@ static int merge_block(struct archive_read* a, ssize_t block_size, } static int process_block(struct archive_read* a) { + struct rar5 *rar5 = a->format->data; const uint8_t* p; - struct rar5* rar = get_context(a); int ret; /* If we don't have any data to be processed, this most probably means * we need to switch to the next volume. */ - if(rar->main.volume && rar->file.bytes_remaining == 0) { + if(rar5->main.volume && rar5->file.bytes_remaining == 0) { ret = advance_multivolume(a); if(ret != ARCHIVE_OK) return ret; } - if(rar->cstate.block_parsing_finished) { + if(rar5->cstate.block_parsing_finished) { ssize_t block_size; ssize_t to_skip; ssize_t cur_block_size; @@ -3617,7 +3684,7 @@ static int process_block(struct archive_read* a) { * `parse_block_header` as the second argument. */ ret = parse_block_header(a, p, &block_size, - &rar->last_block_hdr); + &rar5->last_block_hdr); if(ret != ARCHIVE_OK) { return ret; } @@ -3625,12 +3692,21 @@ static int process_block(struct archive_read* a) { /* Skip block header. Next data is huffman tables, * if present. */ to_skip = sizeof(struct compressed_block_header) + - bf_byte_count(&rar->last_block_hdr) + 1; + bf_byte_count(&rar5->last_block_hdr) + 1; + + /* If the block header's to_skip value exceeds the declared + * remaining data, the archive is malformed. */ + if(to_skip > rar5->file.bytes_remaining) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Block header size exceeds remaining file data"); + return ARCHIVE_FATAL; + } if(ARCHIVE_OK != consume(a, to_skip)) return ARCHIVE_EOF; - rar->file.bytes_remaining -= to_skip; + rar5->file.bytes_remaining -= to_skip; /* The block size gives information about the whole block size, * but the block could be stored in split form when using @@ -3639,9 +3715,9 @@ static int process_block(struct archive_read* a) { * part of the data will be in another file. */ cur_block_size = - rar5_min(rar->file.bytes_remaining, block_size); + rar5_min(rar5->file.bytes_remaining, block_size); - if(block_size > rar->file.bytes_remaining) { + if(block_size > rar5->file.bytes_remaining) { /* If current blocks' size is bigger than our data * size, this means we have a multivolume archive. * In this case, skip all base headers until the end @@ -3665,7 +3741,7 @@ static int process_block(struct archive_read* a) { * the *whole* block (merged from partial blocks * stored in multiple archives files). */ } else { - rar->cstate.switch_multivolume = 0; + rar5->cstate.switch_multivolume = 0; /* Read the whole block size into memory. This can take * up to 8 megabytes of memory in theoretical cases. @@ -3677,16 +3753,16 @@ static int process_block(struct archive_read* a) { } } - rar->cstate.block_buf = p; - rar->cstate.cur_block_size = cur_block_size; - rar->cstate.block_parsing_finished = 0; + rar5->cstate.block_buf = p; + rar5->cstate.cur_block_size = cur_block_size; + rar5->cstate.block_parsing_finished = 0; - rar->bits.in_addr = 0; - rar->bits.bit_addr = 0; + rar5->bits.in_addr = 0; + rar5->bits.bit_addr = 0; - if(bf_is_table_present(&rar->last_block_hdr)) { + if(bf_is_table_present(&rar5->last_block_hdr)) { /* Load Huffman tables. */ - ret = parse_tables(a, rar, p); + ret = parse_tables(a, rar5, p); if(ret != ARCHIVE_OK) { /* Error during decompression of Huffman * tables. */ @@ -3695,7 +3771,7 @@ static int process_block(struct archive_read* a) { } } else { /* Block parsing not finished, reuse previous memory buffer. */ - p = rar->cstate.block_buf; + p = rar5->cstate.block_buf; } /* Uncompress the block, or a part of it, depending on how many bytes @@ -3708,22 +3784,22 @@ static int process_block(struct archive_read* a) { return ret; } - if(rar->cstate.block_parsing_finished && - rar->cstate.switch_multivolume == 0 && - rar->cstate.cur_block_size > 0) + if(rar5->cstate.block_parsing_finished && + rar5->cstate.switch_multivolume == 0 && + rar5->cstate.cur_block_size > 0) { /* If we're processing a normal block, consume the whole * block. We can do this because we've already read the whole * block to memory. */ - if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) + if(ARCHIVE_OK != consume(a, rar5->cstate.cur_block_size)) return ARCHIVE_FATAL; - rar->file.bytes_remaining -= rar->cstate.cur_block_size; - } else if(rar->cstate.switch_multivolume) { + rar5->file.bytes_remaining -= rar5->cstate.cur_block_size; + } else if(rar5->cstate.switch_multivolume) { /* Don't consume the block if we're doing multivolume * processing. The volume switching function will consume * the proper count of bytes instead. */ - rar->cstate.switch_multivolume = 0; + rar5->cstate.switch_multivolume = 0; } return ARCHIVE_OK; @@ -3733,13 +3809,13 @@ static int process_block(struct archive_read* a) { * * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY * when there is no data on the stack. */ -static int use_data(struct rar5* rar, const void** buf, size_t* size, +static int use_data(struct rar5 *rar5, const void** buf, size_t* size, int64_t* offset) { int i; - for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { - struct data_ready *d = &rar->cstate.dready[i]; + for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) { + struct data_ready *d = &rar5->cstate.dready[i]; if(d->used) { if(buf) *buf = d->buf; @@ -3754,14 +3830,14 @@ static int use_data(struct rar5* rar, const void** buf, size_t* size, return ARCHIVE_RETRY; } -static void clear_data_ready_stack(struct rar5* rar) { - memset(&rar->cstate.dready, 0, sizeof(rar->cstate.dready)); +static void clear_data_ready_stack(struct rar5 *rar5) { + memset(&rar5->cstate.dready, 0, sizeof(rar5->cstate.dready)); } -/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready +/* Pushes the `buf`, `size` and `offset` arguments to the rar5->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ -static int push_data_ready(struct archive_read* a, struct rar5* rar, +static int push_data_ready(struct archive_read* a, struct rar5 *rar5, const uint8_t* buf, size_t size, int64_t offset) { int i; @@ -3772,18 +3848,18 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar, * because we're interested only in the side effect: building up the * internal window circular buffer. This window buffer will be used * later during unpacking of requested data. */ - if(rar->skip_mode) + if(rar5->skip_mode) return ARCHIVE_OK; /* Sanity check. */ - if(offset != rar->file.last_offset + rar->file.last_size) { + if(offset != rar5->file.last_offset + rar5->file.last_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity check error: output stream is not continuous"); return ARCHIVE_FATAL; } - for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { - struct data_ready* d = &rar->cstate.dready[i]; + for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) { + struct data_ready* d = &rar5->cstate.dready[i]; if(!d->used) { d->used = 1; d->buf = buf; @@ -3791,24 +3867,24 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar, d->offset = offset; /* These fields are used only in sanity checking. */ - rar->file.last_offset = offset; - rar->file.last_size = size; + rar5->file.last_offset = offset; + rar5->file.last_size = size; /* Calculate the checksum of this new block before * submitting data to libarchive's engine. */ - update_crc(rar, d->buf, d->size); + update_crc(rar5, d->buf, d->size); return ARCHIVE_OK; } } - /* Program counter will reach this code if the `rar->cstate.data_ready` - * stack will be filled up so that no new entries will be allowed. The - * code shouldn't allow such situation to occur. So we treat this case - * as an internal error. */ + /* Program counter will reach this code if the + * `rar5->cstate.data_ready` stack will be filled up so that no new + * entries will be allowed. The code shouldn't allow such situation to + * occur. So we treat this case as an internal error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Error: premature end of data_ready stack"); + "Premature end of data_ready stack"); return ARCHIVE_FATAL; } @@ -3855,31 +3931,32 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar, * */ static int do_uncompress_file(struct archive_read* a) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; int ret; int64_t max_end_pos; - if(!rar->cstate.initialized) { + if(!rar5->cstate.initialized) { /* Don't perform full context reinitialization if we're * processing a solid archive. */ - if(!rar->main.solid || !rar->cstate.window_buf) { - init_unpack(rar); + if(!rar5->main.solid || !rar5->cstate.window_buf) { + if((ret = init_unpack(rar5)) != ARCHIVE_OK) + return ret; } - rar->cstate.initialized = 1; + rar5->cstate.initialized = 1; } /* Don't allow extraction if window_size is invalid. */ - if(rar->cstate.window_size == 0) { + if(rar5->cstate.window_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid window size declaration in this file"); /* This should never happen in valid files. */ - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } - if(rar->cstate.all_filters_applied == 1) { + if(rar5->cstate.all_filters_applied == 1) { /* We use while(1) here, but standard case allows for just 1 * iteration. The loop will iterate if process_block() didn't * generate any data at all. This can happen if the block @@ -3887,15 +3964,15 @@ static int do_uncompress_file(struct archive_read* a) { * files). */ while(1) { ret = process_block(a); - if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) + if(ret != ARCHIVE_OK) return ret; - if(rar->cstate.last_write_ptr == - rar->cstate.write_ptr) { + if(rar5->cstate.last_write_ptr == + rar5->cstate.write_ptr) { /* The block didn't generate any new data, * so just process a new block if this one * wasn't the last block in the file. */ - if (bf_is_last_block(&rar->last_block_hdr)) { + if (bf_is_last_block(&rar5->last_block_hdr)) { return ARCHIVE_EOF; } @@ -3913,19 +3990,17 @@ static int do_uncompress_file(struct archive_read* a) { ret = apply_filters(a); if(ret == ARCHIVE_RETRY) { return ARCHIVE_OK; - } else if(ret == ARCHIVE_FATAL) { - return ARCHIVE_FATAL; + } else if(ret != ARCHIVE_OK) { + return ret; } - /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ - - if(cdeque_size(&rar->cstate.filters) > 0) { + if(cdeque_size(&rar5->cstate.filters) > 0) { /* Check if we can write something before hitting first * filter. */ struct filter_info* flt; /* Get the block_start offset from the first filter. */ - if(CDE_OK != cdeque_front(&rar->cstate.filters, + if(CDE_OK != cdeque_front(&rar5->cstate.filters, cdeque_filter_p(&flt))) { archive_set_error(&a->archive, @@ -3935,15 +4010,15 @@ static int do_uncompress_file(struct archive_read* a) { } max_end_pos = rar5_min(flt->block_start, - rar->cstate.write_ptr); + rar5->cstate.write_ptr); } else { /* There are no filters defined, or all filters were applied. * This means we can just store the data without any * postprocessing. */ - max_end_pos = rar->cstate.write_ptr; + max_end_pos = rar5->cstate.write_ptr; } - if(max_end_pos == rar->cstate.last_write_ptr) { + if(max_end_pos == rar5->cstate.last_write_ptr) { /* We can't write anything yet. The block uncompression * function did not generate enough data, and no filter can be * applied. At the same time we don't have any data that can be @@ -3960,9 +4035,9 @@ static int do_uncompress_file(struct archive_read* a) { * So let's do it. The push_window_data() function will * effectively return the selected data block to the user * application. */ - push_window_data(a, rar, rar->cstate.last_write_ptr, + push_window_data(a, rar5, rar5->cstate.last_write_ptr, max_end_pos); - rar->cstate.last_write_ptr = max_end_pos; + rar5->cstate.last_write_ptr = max_end_pos; } return ARCHIVE_OK; @@ -3983,19 +4058,19 @@ static int uncompress_file(struct archive_read* a) { static int do_unstore_file(struct archive_read* a, - struct rar5* rar, const void** buf, size_t* size, int64_t* offset) + struct rar5 *rar5, const void** buf, size_t* size, int64_t* offset) { size_t to_read; const uint8_t* p; - if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && - rar->generic.split_after > 0) + if(rar5->file.bytes_remaining == 0 && rar5->main.volume > 0 && + rar5->generic.split_after > 0) { int ret; - rar->cstate.switch_multivolume = 1; + rar5->cstate.switch_multivolume = 1; ret = advance_multivolume(a); - rar->cstate.switch_multivolume = 0; + rar5->cstate.switch_multivolume = 0; if(ret != ARCHIVE_OK) { /* Failed to advance to next multivolume archive @@ -4004,7 +4079,7 @@ static int do_unstore_file(struct archive_read* a, } } - to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); + to_read = rar5_min(rar5->file.bytes_remaining, 64 * 1024); if(to_read == 0) { return ARCHIVE_EOF; } @@ -4021,16 +4096,16 @@ static int do_unstore_file(struct archive_read* a, if(buf) *buf = p; if(size) *size = to_read; - if(offset) *offset = rar->cstate.last_unstore_ptr; + if(offset) *offset = rar5->cstate.last_unstore_ptr; - rar->file.bytes_remaining -= to_read; - rar->cstate.last_unstore_ptr += to_read; + rar5->file.bytes_remaining -= to_read; + rar5->cstate.last_unstore_ptr += to_read; - update_crc(rar, p, to_read); + update_crc(rar5, p, to_read); return ARCHIVE_OK; } -static int do_unpack(struct archive_read* a, struct rar5* rar, +static int do_unpack(struct archive_read* a, struct rar5 *rar5, const void** buf, size_t* size, int64_t* offset) { enum COMPRESSION_METHOD { @@ -4038,12 +4113,12 @@ static int do_unpack(struct archive_read* a, struct rar5* rar, BEST = 5 }; - if(rar->file.service > 0) { - return do_unstore_file(a, rar, buf, size, offset); + if(rar5->file.service > 0) { + return do_unstore_file(a, rar5, buf, size, offset); } else { - switch(rar->cstate.method) { + switch(rar5->cstate.method) { case STORE: - return do_unstore_file(a, rar, buf, size, + return do_unstore_file(a, rar5, buf, size, offset); case FASTEST: /* fallthrough */ @@ -4060,13 +4135,13 @@ static int do_unpack(struct archive_read* a, struct rar5* rar, * accordingly. At this point the decoder doesn't have any * pending uncompressed data blocks, so the current position in * the output file should be last_write_ptr. */ - if (offset) *offset = rar->cstate.last_write_ptr; + if (offset) *offset = rar5->cstate.last_write_ptr; return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Compression method not supported: 0x%x", - (unsigned int)rar->cstate.method); + (unsigned int)rar5->cstate.method); return ARCHIVE_FATAL; } @@ -4079,15 +4154,15 @@ static int do_unpack(struct archive_read* a, struct rar5* rar, } static int verify_checksums(struct archive_read* a) { + struct rar5 *rar5 = a->format->data; int verify_crc; - struct rar5* rar = get_context(a); /* Check checksums only when actually unpacking the data. There's no * need to calculate checksum when we're skipping data in solid archives * (skipping in solid archives is the same thing as unpacking compressed * data and discarding the result). */ - if(!rar->skip_mode) { + if(!rar5->skip_mode) { /* Always check checksums if we're not in skip mode */ verify_crc = 1; } else { @@ -4111,39 +4186,39 @@ static int verify_checksums(struct archive_read* a) { * process is already over and we can check if calculated * checksum (CRC32 or BLAKE2sp) is the same as what is stored * in the archive. */ - if(rar->file.stored_crc32 > 0) { + if(rar5->file.stored_crc32 > 0) { /* Check CRC32 only when the file contains a CRC32 * value for this file. */ - if(rar->file.calculated_crc32 != - rar->file.stored_crc32) { + if(rar5->file.calculated_crc32 != + rar5->file.stored_crc32) { /* Checksums do not match; the unpacked file * is corrupted. */ DEBUG_CODE { printf("Checksum error: CRC32 " "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n", - rar->file.calculated_crc32, - rar->file.stored_crc32); + rar5->file.calculated_crc32, + rar5->file.stored_crc32); } #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: CRC32"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } else { DEBUG_CODE { printf("Checksum OK: CRC32 " "(%08" PRIx32 "/%08" PRIx32 ")\n", - rar->file.stored_crc32, - rar->file.calculated_crc32); + rar5->file.stored_crc32, + rar5->file.calculated_crc32); } } } - if(rar->file.has_blake2 > 0) { + if(rar5->file.has_blake2 > 0) { /* BLAKE2sp is an optional checksum algorithm that is * added to RARv5 archives when using the `-htb` switch * during creation of archive. @@ -4158,15 +4233,15 @@ static int verify_checksums(struct archive_read* a) { * This is why we're explicitly ignoring it. */ uint8_t b2_buf[32]; - (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); + (void) blake2sp_final(&rar5->file.b2state, b2_buf, 32); - if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { + if(memcmp(&rar5->file.blake2sp, b2_buf, 32) != 0) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: BLAKE2"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; #endif } } @@ -4194,23 +4269,23 @@ static void rar5_signature(char *buf) { static int rar5_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct rar5 *rar5 = a->format->data; int ret; - struct rar5* rar = get_context(a); if (size) *size = 0; - if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { - rar->has_encrypted_entries = 0; + if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { + rar5->has_encrypted_entries = 0; } - if (rar->headers_are_encrypted || rar->cstate.data_encrypted) { + if (rar5->headers_are_encrypted || rar5->cstate.data_encrypted) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Reading encrypted data is not currently supported"); - return ARCHIVE_FATAL; + return ARCHIVE_FAILED; } - if(rar->file.dir > 0) { + if(rar5->file.dir > 0) { /* Don't process any data if this file entry was declared * as a directory. This is needed, because entries marked as * directory doesn't have any dictionary buffer allocated, so @@ -4220,28 +4295,28 @@ static int rar5_read_data(struct archive_read *a, const void **buff, return ARCHIVE_FATAL; } - if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { + if(!rar5->skip_mode && (rar5->cstate.last_write_ptr > rar5->file.unpacked_size)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Unpacker has written too many bytes"); return ARCHIVE_FATAL; } - ret = use_data(rar, buff, size, offset); + ret = use_data(rar5, buff, size, offset); if(ret == ARCHIVE_OK) { return ret; } - if(rar->file.eof == 1) { + if(rar5->file.eof == 1) { return ARCHIVE_EOF; } - ret = do_unpack(a, rar, buff, size, offset); + ret = do_unpack(a, rar5, buff, size, offset); if(ret != ARCHIVE_OK) { return ret; } - if(rar->file.bytes_remaining == 0 && - rar->cstate.last_write_ptr == rar->file.unpacked_size) + if(rar5->file.bytes_remaining == 0 && + rar5->cstate.last_write_ptr == rar5->file.unpacked_size) { /* If all bytes of current file were processed, run * finalization. @@ -4251,7 +4326,7 @@ static int rar5_read_data(struct archive_read *a, const void **buff, * value in the last `archive_read_data` call to signal an error * to the user. */ - rar->file.eof = 1; + rar5->file.eof = 1; return verify_global_checksums(a); } @@ -4259,21 +4334,21 @@ static int rar5_read_data(struct archive_read *a, const void **buff, } static int rar5_read_data_skip(struct archive_read *a) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; - if(rar->main.solid && (rar->cstate.data_encrypted == 0)) { + if(rar5->main.solid && (rar5->cstate.data_encrypted == 0)) { /* In solid archives, instead of skipping the data, we need to * extract it, and dispose the result. The side effect of this * operation will be setting up the initial window buffer state * needed to be able to extract the selected file. Note that - * this is only possible when data withing this solid block is + * this is only possible when data within this solid block is * not encrypted, in which case we'll skip and fail if the user * tries to read data. */ int ret; /* Make sure to process all blocks in the compressed stream. */ - while(rar->file.bytes_remaining > 0) { + while(rar5->file.bytes_remaining > 0) { /* Setting the "skip mode" will allow us to skip * checksum checks during data skipping. Checking the * checksum of skipped data isn't really necessary and @@ -4282,14 +4357,14 @@ static int rar5_read_data_skip(struct archive_read *a) { * This is incremented instead of setting to 1 because * this data skipping function can be called * recursively. */ - rar->skip_mode++; + rar5->skip_mode++; /* We're disposing 1 block of data, so we use triple * NULLs in arguments. */ ret = rar5_read_data(a, NULL, NULL, NULL); /* Turn off "skip mode". */ - rar->skip_mode--; + rar5->skip_mode--; if(ret < 0 || ret == ARCHIVE_EOF) { /* Propagate any potential error conditions @@ -4302,11 +4377,11 @@ static int rar5_read_data_skip(struct archive_read *a) { * stream. Each file in non-solid archives starts from an empty * window buffer. */ - if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { + if(ARCHIVE_OK != consume(a, rar5->file.bytes_remaining)) { return ARCHIVE_FATAL; } - rar->file.bytes_remaining = 0; + rar5->file.bytes_remaining = 0; } return ARCHIVE_OK; @@ -4315,28 +4390,30 @@ static int rar5_read_data_skip(struct archive_read *a) { static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, int whence) { - (void) a; (void) offset; (void) whence; - /* We're a streaming unpacker, and we don't support seeking. */ + /* We're a streaming unpacker, and we don't support seeking. + * That's a capability gap, not a fatal error. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Seeking of RAR5 files is unsupported"); - return ARCHIVE_FATAL; + return (ARCHIVE_FAILED); } static int rar5_cleanup(struct archive_read *a) { - struct rar5* rar = get_context(a); + struct rar5 *rar5 = a->format->data; - free(rar->cstate.window_buf); - free(rar->cstate.filtered_buf); - clear_data_ready_stack(rar); + free(rar5->cstate.window_buf); + free(rar5->cstate.filtered_buf); + clear_data_ready_stack(rar5); - free(rar->vol.push_buf); + free(rar5->vol.push_buf); - free_filters(rar); - rar5_deinit(rar); + free_filters(rar5); + rar5_deinit(rar5); - free(rar); + free(rar5); a->format->data = NULL; return ARCHIVE_OK; @@ -4350,9 +4427,9 @@ static int rar5_capabilities(struct archive_read * a) { static int rar5_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { - struct rar5 *rar = (struct rar5 *)_a->format->data; - if (rar) { - return rar->has_encrypted_entries; + struct rar5 *rar5 = _a->format->data; + if (rar5) { + return rar5->has_encrypted_entries; } } @@ -4360,50 +4437,50 @@ static int rar5_has_encrypted_entries(struct archive_read *_a) { } /* Must match deallocations in rar5_deinit */ -static int rar5_init(struct rar5* rar) { - memset(rar, 0, sizeof(struct rar5)); +static int rar5_init(struct rar5 *rar5) { + memset(rar5, 0, sizeof(struct rar5)); - if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) + if(CDE_OK != cdeque_init(&rar5->cstate.filters, 8192)) return ARCHIVE_FATAL; /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ - rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; + rar5->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; return ARCHIVE_OK; } /* Must match allocations in rar5_init */ -static void rar5_deinit(struct rar5* rar) { - cdeque_free(&rar->cstate.filters); +static void rar5_deinit(struct rar5 *rar5) { + cdeque_free(&rar5->cstate.filters); } int archive_read_support_format_rar5(struct archive *_a) { struct archive_read* ar; int ret; - struct rar5* rar; + struct rar5 *rar5; if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) return ret; - rar = malloc(sizeof(*rar)); - if(rar == NULL) { + rar5 = malloc(sizeof(*rar5)); + if(rar5 == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data"); return ARCHIVE_FATAL; } - if(ARCHIVE_OK != rar5_init(rar)) { + if(ARCHIVE_OK != rar5_init(rar5)) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter buffer"); - free(rar); + free(rar5); return ARCHIVE_FATAL; } ret = __archive_read_register_format(ar, - rar, + rar5, "rar5", rar5_bid, rar5_options, @@ -4416,8 +4493,8 @@ int archive_read_support_format_rar5(struct archive *_a) { rar5_has_encrypted_entries); if(ret != ARCHIVE_OK) { - rar5_deinit(rar); - free(rar); + rar5_deinit(rar5); + free(rar5); } return ARCHIVE_OK; diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c index e935396dda..cda3c1e49c 100644 --- a/libarchive/archive_read_support_format_raw.c +++ b/libarchive/archive_read_support_format_raw.c @@ -34,10 +34,11 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" -struct raw_info { +struct raw { int64_t offset; /* Current position in the file. */ int64_t unconsumed; int end_of_file; @@ -54,22 +55,22 @@ static int archive_read_format_raw_read_header(struct archive_read *, int archive_read_support_format_raw(struct archive *_a) { - struct raw_info *info; struct archive_read *a = (struct archive_read *)_a; + struct raw *raw; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_raw"); - info = calloc(1, sizeof(*info)); - if (info == NULL) { + raw = calloc(1, sizeof(*raw)); + if (raw == NULL) { archive_set_error(&a->archive, ENOMEM, - "Can't allocate raw_info data"); + "Can't allocate raw data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format(a, - info, + raw, "raw", archive_read_format_raw_bid, NULL, @@ -81,7 +82,7 @@ archive_read_support_format_raw(struct archive *_a) NULL, NULL); if (r != ARCHIVE_OK) - free(info); + free(raw); return (r); } @@ -107,10 +108,9 @@ static int archive_read_format_raw_read_header(struct archive_read *a, struct archive_entry *entry) { - struct raw_info *info; + struct raw *raw = a->format->data; - info = (struct raw_info *)(a->format->data); - if (info->end_of_file) + if (raw->end_of_file) return (ARCHIVE_EOF); a->archive.archive_format = ARCHIVE_FORMAT_RAW; @@ -128,39 +128,43 @@ static int archive_read_format_raw_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct raw_info *info; + struct raw *raw = a->format->data; ssize_t avail; - info = (struct raw_info *)(a->format->data); - /* Consume the bytes we read last time. */ - if (info->unconsumed) { - __archive_read_consume(a, info->unconsumed); - info->unconsumed = 0; + if (raw->unconsumed) { + __archive_read_consume(a, raw->unconsumed); + raw->unconsumed = 0; } - if (info->end_of_file) + if (raw->end_of_file) return (ARCHIVE_EOF); /* Get whatever bytes are immediately available. */ *buff = __archive_read_ahead(a, 1, &avail); if (avail > 0) { /* Return the bytes we just read */ + *offset = raw->offset; + if (archive_ckd_add_i64(&raw->offset, raw->offset, avail)) { + avail = INT64_MAX - *offset; + if (avail == 0) { + *size = 0; + return (ARCHIVE_FATAL); + } + } *size = avail; - *offset = info->offset; - info->offset += *size; - info->unconsumed = avail; + raw->unconsumed = avail; return (ARCHIVE_OK); } else if (0 == avail) { /* Record and return end-of-file. */ - info->end_of_file = 1; + raw->end_of_file = 1; *size = 0; - *offset = info->offset; + *offset = raw->offset; return (ARCHIVE_EOF); } else { /* Record and return an error. */ *size = 0; - *offset = info->offset; + *offset = raw->offset; return ((int)avail); } } @@ -168,24 +172,23 @@ archive_read_format_raw_read_data(struct archive_read *a, static int archive_read_format_raw_read_data_skip(struct archive_read *a) { - struct raw_info *info = (struct raw_info *)(a->format->data); + struct raw *raw = a->format->data; /* Consume the bytes we read last time. */ - if (info->unconsumed) { - __archive_read_consume(a, info->unconsumed); - info->unconsumed = 0; + if (raw->unconsumed) { + __archive_read_consume(a, raw->unconsumed); + raw->unconsumed = 0; } - info->end_of_file = 1; + raw->end_of_file = 1; return (ARCHIVE_OK); } static int archive_read_format_raw_cleanup(struct archive_read *a) { - struct raw_info *info; + struct raw *raw = a->format->data; - info = (struct raw_info *)(a->format->data); - free(info); + free(raw); a->format->data = NULL; return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 98f7d69957..b73c0d9372 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -42,6 +42,7 @@ #include "archive_acl_private.h" /* For ACL parsing routines. */ #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -153,6 +154,8 @@ struct tar { int compat_2x; int process_mac_extensions; int read_concatenated_archives; + int default_inode; + int default_dev; }; /* Track which size fields were present in the headers */ @@ -297,9 +300,8 @@ archive_read_support_format_tar(struct archive *_a) static int archive_read_format_tar_cleanup(struct archive_read *a) { - struct tar *tar; + struct tar *tar = a->format->data; - tar = (struct tar *)(a->format->data); gnu_clear_sparse_list(tar); archive_string_free(&tar->entry_pathname); archive_string_free(&tar->entry_pathname_override); @@ -309,7 +311,7 @@ archive_read_format_tar_cleanup(struct archive_read *a) archive_string_free(&tar->line); archive_string_free(&tar->localname); free(tar); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } @@ -436,10 +438,9 @@ static int archive_read_format_tar_options(struct archive_read *a, const char *key, const char *val) { - struct tar *tar; + struct tar *tar = a->format->data; int ret = ARCHIVE_FAILED; - tar = (struct tar *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle UTF-8 filenames as libarchive 2.x */ tar->compat_2x = (val != NULL && val[0] != 0); @@ -522,11 +523,7 @@ archive_read_format_tar_read_header(struct archive_read *a, * probably not worthwhile just to support the relatively * obscure tar->cpio conversion case. */ - /* TODO: Move this into `struct tar` to avoid conflicts - * when reading multiple archives */ - static int default_inode; - static int default_dev; - struct tar *tar; + struct tar *tar = a->format->data; const char *p; const wchar_t *wp; int r; @@ -534,15 +531,14 @@ archive_read_format_tar_read_header(struct archive_read *a, int64_t unconsumed = 0; /* Assign default device/inode values. */ - archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */ - archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */ + archive_entry_set_dev(entry, 1 + tar->default_dev); /* Don't use zero. */ + archive_entry_set_ino(entry, ++tar->default_inode); /* Don't use zero. */ /* Limit generated st_ino number to 16 bits. */ - if (default_inode >= 0xffff) { - ++default_dev; - default_inode = 0; + if (tar->default_inode >= 0xffff) { + ++tar->default_dev; + tar->default_inode = 0; } - tar = (struct tar *)(a->format->data); tar->entry_offset = 0; gnu_clear_sparse_list(tar); tar->size_fields = 0; /* We don't have any size info yet */ @@ -586,16 +582,16 @@ archive_read_format_tar_read_header(struct archive_read *a, * directory: This is needed for certain old tar * variants and even for some broken newer ones. */ - if ((wp = archive_entry_pathname_w(entry)) != NULL) { - l = wcslen(wp); - if (l > 0 && wp[l - 1] == L'/') { + if ((p = archive_entry_pathname(entry)) != NULL) { + l = strlen(p); + if (l > 0 && p[l - 1] == '/') { archive_entry_set_filetype(entry, AE_IFDIR); tar->entry_bytes_remaining = 0; tar->entry_padding = 0; } - } else if ((p = archive_entry_pathname(entry)) != NULL) { - l = strlen(p); - if (l > 0 && p[l - 1] == '/') { + } else if ((wp = archive_entry_pathname_w(entry)) != NULL) { + l = wcslen(wp); + if (l > 0 && wp[l - 1] == L'/') { archive_entry_set_filetype(entry, AE_IFDIR); tar->entry_bytes_remaining = 0; tar->entry_padding = 0; @@ -609,12 +605,10 @@ static int archive_read_format_tar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct tar *tar = a->format->data; ssize_t bytes_read; - struct tar *tar; struct sparse_block *p; - tar = (struct tar *)(a->format->data); - for (;;) { /* Remove exhausted entries from sparse list. */ while (tar->sparse_list != NULL && @@ -673,10 +667,8 @@ archive_read_format_tar_read_data(struct archive_read *a, static int archive_read_format_tar_skip(struct archive_read *a) { + struct tar *tar = a->format->data; int64_t request; - struct tar* tar; - - tar = (struct tar *)(a->format->data); request = tar->entry_bytes_remaining + tar->entry_padding + tar->entry_bytes_unconsumed; @@ -1074,21 +1066,19 @@ static int header_Solaris_ACL(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, int64_t *unconsumed) { - const struct archive_entry_header_ustar *header; struct archive_string acl_text; size_t size; int err, acl_type; uint64_t type; char *acl, *p; - header = (const struct archive_entry_header_ustar *)h; - size = (size_t)tar_atol(header->size, sizeof(header->size)); archive_string_init(&acl_text); err = read_body_to_string(a, tar, &acl_text, h, unconsumed); if (err != ARCHIVE_OK) { archive_string_free(&acl_text); return (err); } + size = archive_strlen(&acl_text); /* TODO: Examine the first characters to see if this * is an AIX ACL descriptor. We'll likely never support @@ -1202,7 +1192,7 @@ set_conversion_failed_error(struct archive_read *a, return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "%s can't be converted from %s to current locale.", + "%s can't be converted from %s to current locale", name, archive_string_conversion_charset_name(sconv)); return (ARCHIVE_WARN); } @@ -1347,6 +1337,7 @@ header_common(struct archive_read *a, struct tar *tar, const struct archive_entry_header_ustar *header; const char *existing_linkpath; const wchar_t *existing_wcs_linkpath; + mode_t header_mode; int err = ARCHIVE_OK; header = (const struct archive_entry_header_ustar *)h; @@ -1354,12 +1345,10 @@ header_common(struct archive_read *a, struct tar *tar, /* Parse out the numeric fields (all are octal) */ /* Split mode handling: Set filetype always, perm only if not already set */ - archive_entry_set_filetype(entry, - (mode_t)tar_atol(header->mode, sizeof(header->mode))); - if (!archive_entry_perm_is_set(entry)) { - archive_entry_set_perm(entry, - (mode_t)tar_atol(header->mode, sizeof(header->mode))); - } + header_mode = (mode_t)tar_atol(header->mode, sizeof(header->mode)); + archive_entry_set_filetype(entry, header_mode); + if (!archive_entry_perm_is_set(entry)) + archive_entry_set_perm(entry, header_mode); /* Set uid, gid, mtime if not already set */ if (!archive_entry_uid_is_set(entry)) { @@ -1369,7 +1358,12 @@ header_common(struct archive_read *a, struct tar *tar, archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid))); } if (!archive_entry_mtime_is_set(entry)) { - archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0); + int64_t t64 = tar_atol(header->mtime, sizeof(header->mtime)); + time_t t = (time_t)t64; + if ((int64_t)t != t64) { /* time_t overflowed */ + t = TIME_MAX; + } + archive_entry_set_mtime(entry, t, 0); } /* Reconcile the size info. */ @@ -1644,24 +1638,9 @@ is_mac_metadata_entry(struct archive_entry *entry) { const char *p, *name; const wchar_t *wp, *wname; - wname = wp = archive_entry_pathname_w(entry); - if (wp != NULL) { + name = p = archive_entry_pathname(entry); + if (p != NULL) { /* Find the last path element. */ - for (; *wp != L'\0'; ++wp) { - if (wp[0] == '/' && wp[1] != L'\0') - wname = wp + 1; - } - /* - * If last path element starts with "._", then - * this is a Mac extension. - */ - if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0') - return 1; - } else { - /* Find the last path element. */ - name = p = archive_entry_pathname(entry); - if (p == NULL) - return (ARCHIVE_FAILED); for (; *p != '\0'; ++p) { if (p[0] == '/' && p[1] != '\0') name = p + 1; @@ -1672,6 +1651,21 @@ is_mac_metadata_entry(struct archive_entry *entry) { */ if (name[0] == '.' && name[1] == '_' && name[2] != '\0') return 1; + } else { + /* Find the last path element. */ + wname = wp = archive_entry_pathname_w(entry); + if (wp == NULL) + return 0; + for (; *wp != L'\0'; ++wp) { + if (wp[0] == L'/' && wp[1] != L'\0') + wname = wp + 1; + } + /* + * If last path element starts with "._", then + * this is a Mac extension. + */ + if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0') + return 1; } /* Not a mac extension */ return 0; @@ -1752,7 +1746,6 @@ header_pax_global(struct archive_read *a, struct tar *tar, const struct archive_entry_header_ustar *header; int64_t size, to_consume; - (void)a; /* UNUSED */ (void)tar; /* UNUSED */ (void)entry; /* UNUSED */ @@ -1764,6 +1757,11 @@ header_pax_global(struct archive_read *a, struct tar *tar, (long long)size); return (ARCHIVE_FATAL); } + if (size == 0) { + archive_set_error(&a->archive, EINVAL, + "Invalid empty pax global extended header"); + return (ARCHIVE_FATAL); + } to_consume = ((size + 511) & ~511); *unconsumed += to_consume; return (ARCHIVE_OK); @@ -1782,32 +1780,42 @@ header_ustar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h) { const struct archive_entry_header_ustar *header; - struct archive_string as; int err = ARCHIVE_OK, r; header = (const struct archive_entry_header_ustar *)h; - /* Copy name into an internal buffer to ensure null-termination. */ + /* + * The name field is fixed-width and may not be NUL-terminated. + * Use a temporary string only when prefix/name joining is required. + */ const char *existing_pathname = archive_entry_pathname(entry); const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry); if ((existing_pathname == NULL || existing_pathname[0] == '\0') && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == '\0')) { + struct archive_string as; + const char *pathname; + size_t pathname_length; + archive_string_init(&as); if (header->prefix[0]) { archive_strncpy(&as, header->prefix, sizeof(header->prefix)); if (as.s[archive_strlen(&as) - 1] != '/') archive_strappend_char(&as, '/'); archive_strncat(&as, header->name, sizeof(header->name)); + pathname = as.s; + pathname_length = archive_strlen(&as); } else { - archive_strncpy(&as, header->name, sizeof(header->name)); + pathname = header->name; + pathname_length = sizeof(header->name); } - if (archive_entry_copy_pathname_l(entry, as.s, archive_strlen(&as), - tar->sconv) != 0) { + r = archive_entry_copy_pathname_l(entry, pathname, + pathname_length, tar->sconv); + archive_string_free(&as); + if (r != 0) { err = set_conversion_failed_error(a, tar->sconv, "Pathname"); if (err == ARCHIVE_FATAL) return (err); } - archive_string_free(&as); } /* Handle rest of common fields. */ @@ -1900,6 +1908,11 @@ header_pax_extension(struct archive_read *a, struct tar *tar, (long long)ext_size); return (ARCHIVE_FATAL); } + if (ext_size == 0) { + archive_set_error(&a->archive, EINVAL, + "Invalid empty pax extended header"); + return (ARCHIVE_FATAL); + } ext_padding = 0x1ff & (-ext_size); if (ext_size > ext_size_limit) { @@ -2009,6 +2022,7 @@ header_pax_extension(struct archive_read *a, struct tar *tar, /* Consume size, name, and `=` */ *unconsumed += p - attr_start; if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { + archive_string_free(&attr_name); return (ARCHIVE_FATAL); } @@ -2016,6 +2030,7 @@ header_pax_extension(struct archive_read *a, struct tar *tar, archive_set_error(&a->archive, EINVAL, "Malformed pax attributes"); *unconsumed += ext_size + ext_padding; + archive_string_free(&attr_name); return (ARCHIVE_WARN); } @@ -2255,18 +2270,18 @@ pax_attribute_SCHILY_acl(struct archive_read *a, struct tar *tar, if (r != ARCHIVE_OK) { if (r == ARCHIVE_FATAL) { archive_set_error(&a->archive, ENOMEM, - "%s %s", "Can't allocate memory for ", + "%s %s", "Can't allocate memory for", errstr); return (r); } archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr); + ARCHIVE_ERRNO_MISC, "%s %s", "Parse error:", errstr); } return (r); } static int -pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps, long *pn, int64_t *unconsumed) { +pax_attribute_read_time(struct archive_read *a, size_t value_length, __LA_TIME_T *ps, long *pn, int64_t *unconsumed) { struct archive_string as; int r; @@ -2286,12 +2301,16 @@ pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps return (r); } - pax_time(as.s, archive_strlen(&as), ps, pn); + int64_t sec = 0; + pax_time(as.s, archive_strlen(&as), &sec, pn); archive_string_free(&as); - if (*ps == INT64_MIN) { + + if (sec == INT64_MIN) { *ps = 0; *pn = 0; return (ARCHIVE_WARN); + } else { + *ps = (__LA_TIME_T)sec; } return (ARCHIVE_OK); } @@ -2348,7 +2367,6 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent int64_t t; long n; const char *p; - ssize_t bytes_read; int err = ARCHIVE_OK; switch (key[0]) { @@ -2432,7 +2450,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent (unsigned long long)sparse_map_limit); err = ARCHIVE_FAILED; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2507,15 +2525,20 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent */ if (key_length == 12 && memcmp(key, "creationtime", 12) == 0) { /* LIBARCHIVE.creationtime */ - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_birthtime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_birthtime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax creationtime"); } return (err); } else if (key_length == 11 && memcmp(key, "symlinktype", 11) == 0) { /* LIBARCHIVE.symlinktype */ if (value_length < 16) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -2549,7 +2572,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent if (value_length > xattr_limit) { err = ARCHIVE_WARN; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2579,7 +2602,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent /* TODO: Should this be FAILED instead? */ err = ARCHIVE_WARN; } else { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2632,7 +2655,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent } else if (key_length == 6 && memcmp(key, "fflags", 6) == 0) { if (value_length < fflags_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { /* Truncated archive */ archive_set_error(&a->archive, EINVAL, @@ -2679,7 +2702,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent key_length -= 6; key += 6; if (value_length < xattr_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2709,7 +2732,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent if (key_length == 9 && memcmp(key, "holesdata", 9) == 0) { /* SUN.holesdata */ if (value_length < sparse_map_limit) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, EINVAL, "Truncated archive" @@ -2736,16 +2759,26 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent break; case 'a': if (key_length == 5 && memcmp(key, "atime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_atime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_atime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax atime"); } return (err); } break; case 'c': if (key_length == 5 && memcmp(key, "ctime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_ctime(entry, t, n); + __LA_TIME_T sec = 0; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_ctime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax ctime"); } return (err); } else if (key_length == 7 && memcmp(key, "charset", 7) == 0) { @@ -2773,7 +2806,7 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent case 'h': if (key_length == 10 && memcmp(key, "hdrcharset", 10) == 0) { if (value_length < 64) { - p = __archive_read_ahead(a, value_length, &bytes_read); + p = __archive_read_ahead(a, value_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -2817,8 +2850,13 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent break; case 'm': if (key_length == 5 && memcmp(key, "mtime", 5) == 0) { - if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) { - archive_entry_set_mtime(entry, t, n); + __LA_TIME_T sec; + if ((err = pax_attribute_read_time(a, value_length, &sec, &n, unconsumed)) == ARCHIVE_OK) { + archive_entry_set_mtime(entry, sec, n); + } else { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Ignoring malformed pax mtime"); } return (err); } @@ -2885,7 +2923,8 @@ pax_attribute(struct archive_read *a, struct tar *tar, struct archive_entry *ent /* * Parse a decimal time value, which may include a fractional portion * - * Sets ps to INT64_MIN on error. + * Sets ps to INT64_MIN on error, including syntax issues such as non-digits, + * or a time value that's outside the range of time_t. */ static void pax_time(const char *p, size_t length, int64_t *ps, long *pn) @@ -2894,10 +2933,6 @@ pax_time(const char *p, size_t length, int64_t *ps, long *pn) int64_t s; unsigned long l; int sign; - int64_t limit, last_digit_limit; - - limit = INT64_MAX / 10; - last_digit_limit = INT64_MAX % 10; if (length <= 0) { *ps = 0; @@ -2913,34 +2948,73 @@ pax_time(const char *p, size_t length, int64_t *ps, long *pn) } while (length > 0 && *p >= '0' && *p <= '9') { digit = *p - '0'; - if (s > limit || - (s == limit && digit > last_digit_limit)) { + if (archive_ckd_mul_i64(&s, s, 10) || + archive_ckd_add_i64(&s, s, digit)) { *ps = INT64_MIN; *pn = 0; return; } - s = (s * 10) + digit; ++p; --length; } *ps = s * sign; +#if ARCHIVE_VERSION_NUMBER < 4000000 + /* Libarchive 4.0 will have __LA_TIME_T == int64_t, so + this will be unnecessary. */ + /* Test whether it overflows __LA_TIME_T */ + __LA_TIME_T sec = (__LA_TIME_T)*ps; + if ((int64_t)sec != *ps) { + *ps = INT64_MIN; + *pn = 0; + return; + } +#endif + /* Calculate nanoseconds. */ *pn = 0; - if (length <= 0 || *p != '.') + if (length <= 0) { return; + } + + /* Skip `.` */ + if (*p != '.') { + *ps = INT64_MIN; + *pn = 0; + return; + } + ++p; + --length; l = 100000000UL; do { + if (length <= 0) { + return; + } + if (*p >= '0' && *p <= '9') { + *pn += (*p - '0') * l; + } else { + *ps = INT64_MIN; + *pn = 0; + return; + } ++p; --length; - if (length > 0 && *p >= '0' && *p <= '9') - *pn += (*p - '0') * l; - else - break; } while (l /= 10); + + /* Ignore resolution beyond nanoseconds, + but verify it's all decimal digits. */ + while (length > 0) { + if (*p < '0' || *p > '9') { + *ps = INT64_MIN; + *pn = 0; + return; + } + ++p; + --length; + } } /* @@ -2950,6 +3024,7 @@ static int header_gnutar(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, int64_t *unconsumed) { + struct archive_entry_header_gnutar header_copy; const struct archive_entry_header_gnutar *header; int64_t t; int err = ARCHIVE_OK; @@ -2960,8 +3035,14 @@ header_gnutar(struct archive_read *a, struct tar *tar, * filename is stored as in old-style archives. */ + /* + * Sparse extension reads can reuse the callback buffer containing h, + * so preserve the main header until common fields are parsed. + */ + memcpy(&header_copy, h, sizeof(header_copy)); + header = &header_copy; + /* Copy filename over (to ensure null termination). */ - header = (const struct archive_entry_header_gnutar *)h; const char *existing_pathname = archive_entry_pathname(entry); const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry); if ((existing_pathname == NULL || existing_pathname[0] == '\0') @@ -3040,7 +3121,7 @@ header_gnutar(struct archive_read *a, struct tar *tar, } /* Grab fields common to all tar variants. */ - err = header_common(a, tar, entry, h); + err = header_common(a, tar, entry, header); if (err == ARCHIVE_FATAL) return (err); @@ -3103,7 +3184,6 @@ static int gnu_sparse_old_read(struct archive_read *a, struct tar *tar, const struct archive_entry_header_gnutar *header, int64_t *unconsumed) { - ssize_t bytes_read; const void *data; struct extended { struct gnu_sparse sparse[21]; @@ -3121,7 +3201,7 @@ gnu_sparse_old_read(struct archive_read *a, struct tar *tar, if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } - data = __archive_read_ahead(a, 512, &bytes_read); + data = __archive_read_ahead(a, 512, NULL); if (data == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated tar archive " @@ -3239,14 +3319,12 @@ static int64_t gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, int64_t *remaining, int64_t *unconsumed) { - int64_t l, limit, last_digit_limit; + int64_t l; const char *p; ssize_t bytes_read; int base, digit; base = 10; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; /* * Skip any lines starting with '#'; GNU tar specs @@ -3267,10 +3345,10 @@ gnu_sparse_10_atol(struct archive_read *a, struct tar *tar, if (*p < '0' || *p >= '0' + base) return (ARCHIVE_WARN); digit = *p - '0'; - if (l > limit || (l == limit && digit > last_digit_limit)) + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, digit)) { l = INT64_MAX; /* Truncate on overflow. */ - else - l = (l * base) + digit; + } p++; bytes_read--; } @@ -3366,13 +3444,10 @@ pax_attribute_SUN_holesdata(struct archive_read *a, struct tar *tar, return (ARCHIVE_FATAL); tar->sparse_last->hole = hole; } - if (length == 0 || *e == '\n') { - if (length == 0 && *e == '\n') { - return (ARCHIVE_OK); - } else { - return (ARCHIVE_WARN); - } - } + if (length == 0) + return (ARCHIVE_OK); + if (*e == '\n') + return (ARCHIVE_WARN); p = e + 1; length--; hole = hole == 0; @@ -3416,13 +3491,9 @@ 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, maxval, limit, last_digit_limit; + int64_t l; int digit, sign; - maxval = INT64_MAX; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; - /* the pointer will not be dereferenced if char_cnt is zero * due to the way the && operator is evaluated. */ @@ -3436,25 +3507,22 @@ 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; - if (char_cnt != 0) { + while (char_cnt != 0) { digit = *p - '0'; - while (digit >= 0 && digit < base && char_cnt != 0) { - if (l>limit || (l == limit && digit >= last_digit_limit)) { - return maxval; /* Truncate on overflow. */ - } - l = (l * base) + digit; - digit = *++p - '0'; - char_cnt--; + if (digit < 0 || digit >= base) + break; + if (archive_ckd_mul_i64(&l, l, base) || + archive_ckd_add_i64(&l, l, sign * digit)) { + /* Truncate on overflow. */ + return sign < 0 ? INT64_MIN : INT64_MAX; } + p++; + char_cnt--; } - return (sign < 0) ? -l : l; + return l; } static int64_t @@ -3534,9 +3602,8 @@ readline(struct archive_read *a, struct tar *tar, const char **start, { ssize_t bytes_read; ssize_t total_size = 0; - const void *t; + const void *p, *t; const char *s; - void *p; if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) { return (ARCHIVE_FATAL); @@ -3608,24 +3675,20 @@ readline(struct archive_read *a, struct tar *tar, const char **start, static char * base64_decode(const char *s, size_t len, size_t *out_len) { - static const unsigned char digits[64] = { - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', - 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b', - 'c','d','e','f','g','h','i','j','k','l','m','n','o','p', - 'q','r','s','t','u','v','w','x','y','z','0','1','2','3', - '4','5','6','7','8','9','+','/' }; - static unsigned char decode_table[128]; + static const unsigned char decode_table[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, + 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 255, 255, 255, 255, 255 }; char *out, *d; const unsigned char *src = (const unsigned char *)s; - /* If the decode table is not yet initialized, prepare it. */ - if (decode_table[digits[1]] != 1) { - unsigned i; - memset(decode_table, 0xff, sizeof(decode_table)); - for (i = 0; i < sizeof(digits); i++) - decode_table[digits[i]] = i; - } - /* Allocate enough space to hold the entire output. */ /* Note that we may not use all of this... */ out = malloc(len - len / 4 + 1); diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c index d8f188cf0b..d70c718320 100644 --- a/libarchive/archive_read_support_format_warc.c +++ b/libarchive/archive_read_support_format_warc.c @@ -25,28 +25,35 @@ #include "archive_platform.h" -/** - * 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: +/* + * An overview of WARC format: + * + * WARC files are laid out as a sequence of records. Each record has + * a text header followed by a content block whose size is given by + * Content-Length. This reader supports WARC/0.12 through WARC/1.0 + * and was written using the final draft that became ISO 28500:2009: * 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. + * This reader exposes resource and response records as regular files + * when they have a usable WARC-Target-URI. WARC-Date is exposed as + * ctime, and a Last-Modified record header is exposed as mtime when + * present. * - **/ + * TODO: Real-world WARCs can contain resources at endpoints ending in + * a slash, for example http://bibnum.bnf.fr/warc/. Some responses + * include a Content-Location header that points to a Unix-compatible + * filename such as http://bibnum.bnf.fr/warc/index.html, but WARC does + * not require that convention and some sites do not follow it. Until + * archive options exist to control these entries, this reader skips + * them instead of creating directory endpoints as files. + */ #ifdef HAVE_SYS_STAT_H #include #endif +#ifdef HAVE_CTYPE_H +#include +#endif #ifdef HAVE_ERRNO_H #include #endif @@ -56,40 +63,35 @@ #ifdef HAVE_STRING_H #include #endif -#ifdef HAVE_LIMITS_H -#include -#endif -#ifdef HAVE_CTYPE_H -#include -#endif #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" typedef enum { WT_NONE, - /* warcinfo */ + /* WARC info */ WT_INFO, - /* metadata */ + /* Metadata */ WT_META, - /* resource */ + /* Resource */ WT_RSRC, - /* request, unsupported */ + /* Request, unsupported */ WT_REQ, - /* response, unsupported */ + /* Response */ WT_RSP, - /* revisit, unsupported */ + /* Revisit, unsupported */ WT_RVIS, - /* conversion, unsupported */ + /* Conversion, unsupported */ WT_CONV, - /* continuation, unsupported at the moment */ + /* Continuation, currently unsupported */ WT_CONT, - /* invalid type */ + /* Invalid type */ LAST_WT } warc_type_t; @@ -103,82 +105,92 @@ typedef struct { 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; +struct warc { + /* Content length of the current record */ + int64_t cntlen; + /* Bytes processed from the current record */ + int64_t cntoff; + /* Bytes to consume before the next read */ + int64_t unconsumed; - /* string pool */ + /* String pool */ warc_strbuf_t pool; - /* previous version */ + /* Previous version */ unsigned int pver; - /* stringified format name */ + /* 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); +static int archive_read_format_warc_bid(struct archive_read *, int); +static int archive_read_format_warc_cleanup(struct archive_read *); +static int archive_read_format_warc_read_data(struct archive_read *, + const void **, size_t *, int64_t *); +static int archive_read_format_warc_skip(struct archive_read *); +static int archive_read_format_warc_read_header(struct archive_read *, + struct archive_entry *); -/* private routines */ -static unsigned int _warc_rdver(const char *buf, 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); -static const char *_warc_find_eol(const char *buf, size_t bsz); +/* Private routines */ +static unsigned int warc_read_version(const char *, size_t); +static unsigned int warc_read_type(const char *, size_t); +static warc_string_t warc_read_uri(const char *, size_t); +static int64_t warc_read_length(const char *, size_t); +static time_t warc_read_date(const char *, size_t); +static time_t warc_read_last_modified(const char *, size_t); +static const char *warc_find_eoh(const char *, size_t); +static const char *warc_find_eol(const char *, size_t); int archive_read_support_format_warc(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - struct warc_s *w; + struct warc *warc; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); - if ((w = calloc(1, sizeof(*w))) == NULL) { + if ((warc = calloc(1, sizeof(*warc))) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } - r = __archive_read_register_format( - a, w, "warc", - _warc_bid, NULL, _warc_rdhdr, _warc_read, - _warc_skip, NULL, _warc_cleanup, NULL, NULL); + r = __archive_read_register_format(a, + warc, + "warc", + archive_read_format_warc_bid, + NULL, + archive_read_format_warc_read_header, + archive_read_format_warc_read_data, + archive_read_format_warc_skip, + NULL, + archive_read_format_warc_cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) { - free(w); + free(warc); return (r); } return (ARCHIVE_OK); } static int -_warc_cleanup(struct archive_read *a) +archive_read_format_warc_cleanup(struct archive_read *a) { - struct warc_s *w = a->format->data; + struct warc *warc = a->format->data; - if (w->pool.len > 0U) { - free(w->pool.str); + if (warc->pool.len > 0U) { + free(warc->pool.str); } - archive_string_free(&w->sver); - free(w); + archive_string_free(&warc->sver); + free(warc); a->format->data = NULL; return (ARCHIVE_OK); } static int -_warc_bid(struct archive_read *a, int best_bid) +archive_read_format_warc_bid(struct archive_read *a, int best_bid) { const char *hdr; ssize_t nrd; @@ -186,55 +198,52 @@ _warc_bid(struct archive_read *a, int best_bid) (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 */ + /* Check the first line, which should already be a record header. */ + if ((hdr = __archive_read_ahead(a, 12, &nrd)) == NULL) { + /* Not enough data to identify this format. */ return -1; } - /* otherwise snarf the record's version number */ - ver = _warc_rdver(hdr, nrd); + /* Parse the record version number. */ + ver = warc_read_version(hdr, nrd); if (ver < 1200U || ver > 10000U) { - /* we only support WARC 0.12 to 1.0 */ + /* Only WARC 0.12 through WARC 1.0 are supported. */ return -1; } - /* otherwise be confident */ + /* WARC magic and version checks passed. */ return (64); } static int -_warc_rdhdr(struct archive_read *a, struct archive_entry *entry) +archive_read_format_warc_read_header(struct archive_read *a, + struct archive_entry *entry) { #define HDR_PROBE_LEN (12U) - struct warc_s *w = a->format->data; + struct warc *warc = a->format->data; unsigned int ver; const char *buf; ssize_t nrd; const char *eoh; char *tmp; - /* for the file name, saves some strndup()'ing */ + /* Reuse the header buffer while parsing the file name. */ warc_string_t fnam; - /* warc record type, not that we really use it a lot */ + /* WARC record type */ warc_type_t ftyp; - /* content-length+error monad */ - ssize_t cntlen; - /* record time is the WARC-Date time we reinterpret it as ctime */ + /* Content length, or a negative error indicator */ + int64_t cntlen; + /* WARC-Date is exposed as the entry ctime. */ time_t rtime; - /* mtime is the Last-Modified time which will be the entry's mtime */ + /* A Last-Modified record header is exposed as the entry 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 */ + /* Use read_ahead(); it already tracks unconsumed bytes, so this + * reader does not need a separate shift buffer. */ buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd); if (nrd < 0) { - /* no good */ + /* I/O or stream error. */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); @@ -244,19 +253,17 @@ start_over: * 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); + /* Locate the end of the record header. */ + 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 */ + /* The header terminator was not found in the probed data. */ archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, "Bad record header"); return (ARCHIVE_FATAL); } - ver = _warc_rdver(buf, eoh - buf); - /* we currently support WARC 0.12 to 1.0 */ + ver = warc_read_version(buf, eoh - buf); + /* Only WARC 0.12 through WARC 1.0 are supported. */ if (ver == 0U) { archive_set_error( &a->archive, ARCHIVE_ERRNO_MISC, @@ -269,78 +276,73 @@ start_over: ver / 10000, (ver % 10000) / 100); return (ARCHIVE_FATAL); } - cntlen = _warc_rdlen(buf, eoh - buf); + cntlen = warc_read_length(buf, eoh - buf); if (cntlen < 0) { - /* nightmare! the specs say content-length is mandatory - * so I don't feel overly bad stopping the reader here */ + /* This reader requires Content-Length before processing a record. */ archive_set_error( &a->archive, EINVAL, "Bad content length"); return (ARCHIVE_FATAL); } - rtime = _warc_rdrtm(buf, eoh - buf); + rtime = warc_read_date(buf, eoh - buf); if (rtime == (time_t)-1) { - /* record time is mandatory as per WARC/1.0, - * so just barf here, fast and loud */ + /* This reader requires WARC-Date before processing a record. */ archive_set_error( &a->archive, EINVAL, "Bad record time"); return (ARCHIVE_FATAL); } - /* let the world know we're a WARC archive */ + /* Report this archive as WARC. */ a->archive.archive_format = ARCHIVE_FORMAT_WARC; - if (ver != w->pver) { - /* stringify this entry's version */ - archive_string_sprintf(&w->sver, + if (ver != warc->pver) { + /* Format this entry's WARC version. */ + archive_string_sprintf(&warc->sver, "WARC/%u.%u", ver / 10000, (ver % 10000) / 100); - /* remember the version */ - w->pver = ver; + /* Remember the version for later entries. */ + warc->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. */ + /* Parse the record type. */ + ftyp = warc_read_type(buf, eoh - buf); + /* Save content state for subsequent read calls. */ + warc->cntlen = cntlen; + warc->cntoff = 0; + mtime = 0;/* Avoid compiler warnings on some platforms. */ 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 */ + /* Read the filename only for record types that are expected to + * have a target URI. */ + fnam = warc_read_uri(buf, eoh - buf); + /* Avoid creating directory endpoints as files. */ if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') { - /* break here for now */ + /* Skip this record. */ 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; - tmp = realloc(w->pool.str, w->pool.len); + /* Copy the name into the reusable string pool to avoid a malloc/free + * roundtrip for each entry. */ + if (fnam.len + 1U > warc->pool.len) { + warc->pool.len = ((fnam.len + 64U) / 64U) * 64U; + tmp = realloc(warc->pool.str, warc->pool.len); if (tmp == NULL) { archive_set_error( &a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - w->pool.str = tmp; + warc->pool.str = tmp; } - memcpy(w->pool.str, fnam.str, fnam.len); - w->pool.str[fnam.len] = '\0'; - /* let no one else know about the pool, it's a secret, shhh */ - fnam.str = w->pool.str; + memcpy(warc->pool.str, fnam.str, fnam.len); + warc->pool.str[fnam.len] = '\0'; + /* Hide the pool implementation behind the parsed string. */ + fnam.str = warc->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) { + /* Use a Last-Modified record header when present; otherwise fall back + * to WARC-Date. */ + if ((mtime = warc_read_last_modified(buf, eoh - buf)) == (time_t)-1) { mtime = rtime; } break; @@ -358,19 +360,19 @@ start_over: break; } - /* now eat some of those delicious buffer bits */ + /* Consume the record header. */ __archive_read_consume(a, eoh - buf); switch (ftyp) { case WT_RSRC: case WT_RSP: if (fnam.len > 0U) { - /* populate entry object */ + /* Populate the 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 */ + /* WARC-Date becomes ctime; mtime comes from Last-Modified or WARC-Date. */ archive_entry_set_ctime(entry, rtime, 0L); archive_entry_set_mtime(entry, mtime, 0L); break; @@ -385,8 +387,8 @@ start_over: case WT_CONT: case LAST_WT: default: - /* consume the content and start over */ - if (_warc_skip(a) < 0) + /* Skip this record body and look for the next one. */ + if (archive_read_format_warc_skip(a) < 0) return (ARCHIVE_FATAL); goto start_over; } @@ -394,62 +396,69 @@ start_over: } static int -_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off) +archive_read_format_warc_read_data(struct archive_read *a, const void **buf, + size_t *bsz, int64_t *off) { - struct warc_s *w = a->format->data; + struct warc *warc = 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; - w->unconsumed = 0U; - return (ARCHIVE_EOF); + if (warc->unconsumed) { + __archive_read_consume(a, warc->unconsumed); + warc->unconsumed = 0; } - if (w->unconsumed) { - __archive_read_consume(a, w->unconsumed); - w->unconsumed = 0U; + if (warc->cntoff >= warc->cntlen) { + /* No data is available to return for this entry. */ + *buf = NULL; + *bsz = 0U; + *off = warc->cntoff; + return (ARCHIVE_EOF); } rab = __archive_read_ahead(a, 1U, &nrd); if (nrd < 0) { *bsz = 0U; - /* big catastrophe */ + /* Propagate the read error. */ 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; + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated WARC file data"); + return (ARCHIVE_FATAL); + } else if ((int64_t)nrd > warc->cntlen - warc->cntoff) { + /* Clamp reads to Content-Length. */ + nrd = warc->cntlen - warc->cntoff; } - *off = w->cntoff; + *off = warc->cntoff; *bsz = nrd; *buf = rab; - w->cntoff += nrd; - w->unconsumed = (size_t)nrd; + warc->cntoff += nrd; + warc->unconsumed = nrd; return (ARCHIVE_OK); } static int -_warc_skip(struct archive_read *a) +archive_read_format_warc_skip(struct archive_read *a) { - struct warc_s *w = a->format->data; + struct warc *warc = a->format->data; - if (__archive_read_consume(a, w->cntlen) < 0 || + if (warc->cntoff > warc->cntlen) + return (ARCHIVE_FATAL); + if (warc->unconsumed) { + __archive_read_consume(a, warc->unconsumed); + warc->unconsumed = 0; + } + if (__archive_read_consume(a, warc->cntlen - warc->cntoff) < 0 || __archive_read_consume(a, 4U/*\r\n\r\n separator*/) < 0) return (ARCHIVE_FATAL); - w->cntlen = 0U; - w->cntoff = 0U; + warc->cntlen = 0; + warc->cntoff = 0; return (ARCHIVE_OK); } -/* private routines */ +/* Private routines */ static void* deconst(const void *c) { @@ -469,44 +478,41 @@ xmemmem(const char *hay, const size_t haysize, 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) */ + /* Handle trivial cases first. A zero-sized needle is defined to be + * found anywhere in the haystack; otherwise find the first candidate + * that begins with *NEEDLE. */ if (needlesize == 0UL) { return deconst(hay); } else if ((hay = memchr(hay, *needle, haysize)) == NULL) { - /* trivial */ + /* No candidate match remains. */ 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. */ + /* The first characters of haystack and needle already match, and both + * strings are at least one character long. Compute the rolling XOR + * values for the needle and the first NEEDLESIZE 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 */ + /* The haystack is smaller than the needle. */ return NULL; } else if (eqp) { - /* found a match */ + /* Found a match. */ return deconst(hay); } - /* now loop through the rest of haystack, - * updating the sum iteratively */ + /* Loop through the rest of the haystack and update the rolling XOR + * 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 */ + /* When the rolling XOR values match, it is enough to check + * NEEDLESIZE - 1 characters for equality. CAND is always before + * HP by design, so no range check is needed. */ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) { return deconst(cand); } @@ -519,7 +525,7 @@ 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 */ + /* Track the number of digits with rulim. */ int rulim; for (sp = str, rulim = ulim > 10 ? ulim : 10; @@ -546,11 +552,11 @@ time_from_tm(struct tm *t) /* Use platform timegm() if available. */ return (timegm(t)); #else - /* Else use direct calculation using POSIX assumptions. */ - /* First, fix up tm_yday based on the year/month/day. */ + /* Otherwise, calculate directly using POSIX assumptions. */ + /* First, fix up tm_yday based on the year, month, and day. */ if (mktime(t) == (time_t)-1) return ((time_t)-1); - /* Then we can compute timegm() from first principles. */ + /* Then compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 @@ -565,48 +571,48 @@ time_from_tm(struct tm *t) static time_t xstrpisotime(const char *s, char **endptr) { -/** like strptime() but strictly for ISO 8601 Zulu strings */ +/* Like strptime(), but only for ISO 8601 Zulu strings. */ struct tm tm; time_t res = (time_t)-1; - /* make sure tm is clean */ + /* Clear the tm structure. */ memset(&tm, 0, sizeof(tm)); - /* as a courtesy to our callers, and since this is a non-standard - * routine, we skip leading whitespace */ + /* This is a non-standard routine, so skip leading whitespace for + * caller convenience. */ while (*s == ' ' || *s == '\t') ++s; - /* read year */ + /* Read the year. */ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') { goto out; } - /* read month */ + /* Read the month. */ if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') { goto out; } - /* read day-of-month */ + /* Read the day of the month. */ if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') { goto out; } - /* read hour */ + /* Read the hour. */ if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') { goto out; } - /* read minute */ + /* Read the minute. */ if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') { goto out; } - /* read second */ + /* Read the second. */ if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') { goto out; } - /* massage TM to fulfill some of POSIX' constraints */ + /* Adjust tm fields to satisfy POSIX constraints. */ tm.tm_year -= 1900; tm.tm_mon--; - /* now convert our custom tm struct to a unix stamp using UTC */ + /* Convert the tm structure to a Unix timestamp in UTC. */ res = time_from_tm(&tm); out: @@ -616,8 +622,14 @@ out: return res; } +static int +warc_isdigit(const char c) +{ + return c >= '0' && c <= '9'; +} + static unsigned int -_warc_rdver(const char *buf, size_t bsz) +warc_read_version(const char *buf, size_t bsz) { static const char magic[] = "WARC/"; const char *c; @@ -625,35 +637,34 @@ _warc_rdver(const char *buf, size_t bsz) unsigned int end = 0U; if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) { - /* buffer too small or invalid magic */ + /* Buffer too small or invalid magic. */ return ver; } - /* looks good so far, read the version number for a laugh */ + /* Parse the version number. */ buf += sizeof(magic) - 1U; - if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') && - isdigit((unsigned char)buf[2U])) { - /* we support a maximum of 2 digits in the minor version */ - if (isdigit((unsigned char)buf[3U])) + if (warc_isdigit(buf[0]) && buf[1] == '.' && warc_isdigit(buf[2])) { + /* Support at most two digits in the minor version. */ + if (warc_isdigit(buf[3])) end = 1U; - /* set up major version */ + /* Set up the major version. */ ver = (buf[0U] - '0') * 10000U; - /* set up minor version */ + /* Set up the minor version. */ if (end == 1U) { ver += (buf[2U] - '0') * 1000U; ver += (buf[3U] - '0') * 100U; } else ver += (buf[2U] - '0') * 100U; /* - * WARC below version 0.12 has a space-separated header - * WARC 0.12 and above terminates the version with a CRLF + * WARC versions before 0.12 use a space-separated header. + * WARC 0.12 and later terminate the version with CRLF. */ c = buf + 3U + end; if (ver >= 1200U) { if (memcmp(c, "\r\n", 2U) != 0) ver = 0U; } else { - /* ver < 1200U */ + /* Version is below WARC 0.12. */ if (*c != ' ' && *c != '\t') ver = 0U; } @@ -662,22 +673,22 @@ _warc_rdver(const char *buf, size_t bsz) } static unsigned int -_warc_rdtyp(const char *buf, size_t bsz) +warc_read_type(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Type:"; const char *val, *eol; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { - /* no bother */ + /* Header field is absent. */ return WT_NONE; } val += sizeof(_key) - 1U; - if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { - /* no end of line */ + if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) { + /* Header field has no end of line. */ return WT_NONE; } - /* overread whitespace */ + /* Skip leading whitespace. */ while (val < eol && (*val == ' ' || *val == '\t')) ++val; @@ -691,55 +702,55 @@ _warc_rdtyp(const char *buf, size_t bsz) } static warc_string_t -_warc_rduri(const char *buf, size_t bsz) +warc_read_uri(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Target-URI:"; const char *val, *uri, *eol, *p; warc_string_t res = {0U, NULL}; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { - /* no bother */ + /* Header field is absent. */ return res; } - /* overread whitespace */ + /* Skip leading whitespace. */ val += sizeof(_key) - 1U; - if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { - /* no end of line */ + if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) { + /* Header field has no end of line. */ return res; } while (val < eol && (*val == ' ' || *val == '\t')) ++val; - /* overread URL designators */ + /* Locate the :// separator. */ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) { - /* not touching that! */ + /* Ignore values without a :// separator. */ return res; } - /* spaces inside uri are not allowed, CRLF should follow */ + /* Spaces inside a URI are not allowed; CRLF should follow. */ for (p = val; p < eol; p++) { if (isspace((unsigned char)*p)) return res; } - /* there must be at least space for ftp */ + /* Require enough room for the shortest supported scheme. */ if (uri < (val + 3U)) return res; - /* move uri to point to after :// */ + /* Move uri past the :// separator. */ uri += 3U; - /* now then, inspect the URI */ + /* Inspect the scheme prefix. */ if (memcmp(val, "file", 4U) == 0) { - /* perfect, nothing left to do here */ + /* Keep file:// paths as-is. */ } else if (memcmp(val, "http", 4U) == 0 || memcmp(val, "ftp", 3U) == 0) { - /* overread domain, and the first / */ + /* Skip the domain and the first slash. */ while (uri < eol && *uri++ != '/'); } else { - /* not sure what to do? best to bugger off */ + /* Unsupported URI scheme. */ return res; } res.str = uri; @@ -747,42 +758,49 @@ _warc_rduri(const char *buf, size_t bsz) return res; } -static ssize_t -_warc_rdlen(const char *buf, size_t bsz) +static int64_t +warc_read_length(const char *buf, size_t bsz) { static const char _key[] = "\r\nContent-Length:"; - const char *val, *eol; - char *on = NULL; - long int len; + const char *val, *eol, *p; + int64_t len; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { - /* no bother */ + /* Header field is absent. */ return -1; } val += sizeof(_key) - 1U; - if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) { - /* no end of line */ + + if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) { + /* Malformed field with no end of line. */ return -1; } - /* skip leading whitespace */ + /* Skip leading whitespace. */ while (val < eol && (*val == ' ' || *val == '\t')) val++; - /* there must be at least one digit */ - if (!isdigit((unsigned char)*val)) - return -1; - errno = 0; - len = strtol(val, &on, 10); - if (errno != 0 || on != eol) { - /* line must end here */ + + /* Require at least one digit. */ + if (val >= eol || *val < '0' || *val > '9') return -1; + + len = 0; + for (p = val; p < eol; p++) { + int64_t digit; + + if (*p < '0' || *p > '9') + return -1; + digit = *p - '0'; + if (archive_ckd_mul_i64(&len, len, 10) || + archive_ckd_add_i64(&len, len, digit)) + return -1; } - return (size_t)len; + return len; } static time_t -_warc_rdrtm(const char *buf, size_t bsz) +warc_read_date(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Date:"; const char *val, *eol; @@ -790,26 +808,26 @@ _warc_rdrtm(const char *buf, size_t bsz) time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { - /* no bother */ + /* Header field is absent. */ return (time_t)-1; } val += sizeof(_key) - 1U; - if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { - /* no end of line */ + if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) { + /* Header field has no end of line. */ return -1; } - /* xstrpisotime() kindly overreads whitespace for us, so use that */ + /* xstrpisotime() skips leading whitespace. */ res = xstrpisotime(val, &on); if (on != eol) { - /* line must end here */ + /* The field must end here. */ return -1; } return res; } static time_t -_warc_rdmtm(const char *buf, size_t bsz) +warc_read_last_modified(const char *buf, size_t bsz) { static const char _key[] = "\r\nLast-Modified:"; const char *val, *eol; @@ -817,26 +835,26 @@ _warc_rdmtm(const char *buf, size_t bsz) time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { - /* no bother */ + /* Header field is absent. */ return (time_t)-1; } val += sizeof(_key) - 1U; - if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) { - /* no end of line */ + if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) { + /* Header field has no end of line. */ return -1; } - /* xstrpisotime() kindly overreads whitespace for us, so use that */ + /* xstrpisotime() skips leading whitespace. */ res = xstrpisotime(val, &on); if (on != eol) { - /* line must end here */ + /* The field must end here. */ return -1; } return res; } -static const char* -_warc_find_eoh(const char *buf, size_t bsz) +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); @@ -847,12 +865,11 @@ _warc_find_eoh(const char *buf, size_t bsz) return hit; } -static const char* -_warc_find_eol(const char *buf, size_t bsz) +static const char * +warc_find_eol(const char *buf, size_t bsz) { static const char _marker[] = "\r\n"; const char *hit = xmemmem(buf, bsz, _marker, 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 36b5ab3ae0..dbe2c25d93 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -56,6 +56,7 @@ #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" @@ -107,10 +108,14 @@ archive_read_support_format_xar(struct archive *_a) #define CKSUM_NONE 0 #define CKSUM_SHA1 1 #define CKSUM_MD5 2 +#define CKSUM_SHA256 3 +#define CKSUM_SHA512 4 #define MD5_SIZE 16 #define SHA1_SIZE 20 -#define MAX_SUM_SIZE 20 +#define SHA256_SIZE 32 +#define SHA512_SIZE 64 +#define MAX_SUM_SIZE 64 enum enctype { NONE, @@ -134,6 +139,12 @@ struct chksumwork { #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif +#ifdef ARCHIVE_HAS_SHA256 + archive_sha256_ctx sha256ctx; +#endif +#ifdef ARCHIVE_HAS_SHA512 + archive_sha512_ctx sha512ctx; +#endif }; struct xattr { @@ -213,8 +224,8 @@ struct hdlink { struct heap_queue { struct xar_file **files; - int allocated; - int used; + size_t allocated; + size_t used; }; enum xmlstatus { @@ -387,8 +398,7 @@ static int rd_contents_init(struct archive_read *, enum enctype, int, int); static int rd_contents(struct archive_read *, const void **, size_t *, size_t *, uint64_t); -static uint64_t atol10(const char *, size_t); -static int64_t atol8(const char *, size_t); +static int atou64(const char *, size_t, int, uint64_t *); static size_t atohex(unsigned char *, size_t, const char *, size_t); static time_t parse_time(const char *p, size_t n); static int heap_add_entry(struct archive_read *a, @@ -396,7 +406,7 @@ static int heap_add_entry(struct archive_read *a, static struct xar_file *heap_get_entry(struct heap_queue *); static int add_link(struct archive_read *, struct xar *, struct xar_file *); -static void checksum_init(struct archive_read *, int, int); +static int checksum_init(struct archive_read *, int, int); static void checksum_update(struct archive_read *, const void *, size_t, const void *, size_t); static int checksum_final(struct archive_read *, const void *, @@ -421,7 +431,7 @@ static void unknowntag_end(struct xar *, const char *); static int xml_start(struct archive_read *, const char *, struct xmlattr_list *); static void xml_end(void *, const char *); -static void xml_data(void *, const char *, size_t); +static int xml_data(void *, const char *, size_t); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); #if defined(HAVE_LIBXML_XMLREADER_H) @@ -524,6 +534,8 @@ xar_bid(struct archive_read *a, int best_bid) case CKSUM_NONE: case CKSUM_SHA1: case CKSUM_MD5: + case CKSUM_SHA256: + case CKSUM_SHA512: bid += 32; break; default: @@ -536,7 +548,7 @@ xar_bid(struct archive_read *a, int best_bid) static int read_toc(struct archive_read *a) { - struct xar *xar; + struct xar *xar = a->format->data; struct xar_file *file; const unsigned char *b; uint64_t toc_compressed_size; @@ -545,8 +557,6 @@ read_toc(struct archive_read *a) ssize_t bytes; int r; - xar = (struct xar *)(a->format->data); - /* * Read xar header. */ @@ -674,12 +684,11 @@ read_toc(struct archive_read *a) static int xar_read_header(struct archive_read *a, struct archive_entry *entry) { - struct xar *xar; + struct xar *xar = a->format->data; struct xar_file *file; struct xattr *xattr; int r; - xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->offset == 0) { @@ -729,11 +738,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); + file_free(file); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Gname cannot be converted from %s to current locale.", + "Gname cannot be converted from %s to current locale", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } @@ -744,11 +754,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); + file_free(file); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Uname cannot be converted from %s to current locale.", + "Uname cannot be converted from %s to current locale", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } @@ -758,11 +769,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); + file_free(file); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Pathname cannot be converted from %s to current locale.", + "Pathname cannot be converted from %s to current locale", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } @@ -774,11 +786,12 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); + file_free(file); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Linkname cannot be converted from %s to current locale.", + "Linkname cannot be converted from %s to current locale", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } @@ -874,12 +887,10 @@ static int xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct xar *xar; + struct xar *xar = a->format->data; size_t used = 0; int r; - xar = (struct xar *)(a->format->data); - if (xar->entry_unconsumed) { __archive_read_consume(a, xar->entry_unconsumed); xar->entry_unconsumed = 0; @@ -937,10 +948,9 @@ abort_read_data: static int xar_read_data_skip(struct archive_read *a) { - struct xar *xar; + struct xar *xar = a->format->data; int64_t bytes_skipped; - xar = (struct xar *)(a->format->data); if (xar->end_of_file) return (ARCHIVE_EOF); bytes_skipped = __archive_read_consume(a, xar->entry_remaining + @@ -955,12 +965,11 @@ xar_read_data_skip(struct archive_read *a) static int xar_cleanup(struct archive_read *a) { - struct xar *xar; + struct xar *xar = a->format->data; struct hdlink *hdlink; - int i; + size_t i; int r; - xar = (struct xar *)(a->format->data); checksum_cleanup(a); r = decompression_cleanup(a); hdlink = xar->hdlink_list; @@ -990,9 +999,8 @@ xar_cleanup(struct archive_read *a) static int move_reading_point(struct archive_read *a, uint64_t offset) { - struct xar *xar; + struct xar *xar = a->format->data; - xar = (struct xar *)(a->format->data); if (xar->offset - xar->h_base != offset) { /* Seek forward to the start of file contents. */ int64_t step; @@ -1008,7 +1016,7 @@ move_reading_point(struct archive_read *a, uint64_t offset) if (pos == ARCHIVE_FAILED) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "Cannot seek."); + "Cannot seek"); return (ARCHIVE_FAILED); } xar->offset = pos; @@ -1027,8 +1035,7 @@ rd_contents_init(struct archive_read *a, enum enctype encoding, if ((r = decompression_init(a, encoding)) != ARCHIVE_OK) return (r); /* Init checksum library. */ - checksum_init(a, a_sum_alg, e_sum_alg); - return (ARCHIVE_OK); + return (checksum_init(a, a_sum_alg, e_sum_alg)); } static int @@ -1071,44 +1078,24 @@ rd_contents(struct archive_read *a, const void **buff, size_t *size, * it does obey locale. */ -static uint64_t -atol10(const char *p, size_t char_cnt) +static int +atou64(const char *p, size_t char_cnt, int base, uint64_t *val) { uint64_t l; - int digit; - - if (char_cnt == 0) - return (0); - - l = 0; - digit = *p - '0'; - while (digit >= 0 && digit < 10 && char_cnt-- > 0) { - l = (l * 10) + digit; - digit = *++p - '0'; - } - return (l); -} - -static int64_t -atol8(const char *p, size_t char_cnt) -{ - int64_t l; - int digit; - - if (char_cnt == 0) - return (0); l = 0; while (char_cnt-- > 0) { - if (*p >= '0' && *p <= '7') - digit = *p - '0'; - else + int digit = *p++ - '0'; + + if (digit < 0 || digit >= base) break; - p++; - l <<= 3; - l |= digit; + if (archive_ckd_mul_u64(&l, l, base) || + archive_ckd_add_u64(&l, l, digit)) + return (ARCHIVE_FATAL); } - return (l); + + *val = l; + return (ARCHIVE_OK); } static size_t @@ -1173,48 +1160,42 @@ parse_time(const char *p, size_t n) { struct tm tm; time_t t = 0; - int64_t data; + uint64_t data; memset(&tm, 0, sizeof(tm)); if (n != 20) return (t); - data = atol10(p, 4); - if (data < 1900) + if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1900) return (t); tm.tm_year = (int)data - 1900; p += 4; if (*p++ != '-') return (t); - data = atol10(p, 2); - if (data < 1 || data > 12) + if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 12) return (t); tm.tm_mon = (int)data -1; p += 2; if (*p++ != '-') return (t); - data = atol10(p, 2); - if (data < 1 || data > 31) + if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 31) return (t); tm.tm_mday = (int)data; p += 2; if (*p++ != 'T') return (t); - data = atol10(p, 2); - if (data < 0 || data > 23) + if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 23) return (t); tm.tm_hour = (int)data; p += 2; if (*p++ != ':') return (t); - data = atol10(p, 2); - if (data < 0 || data > 59) + if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 59) return (t); tm.tm_min = (int)data; p += 2; if (*p++ != ':') return (t); - data = atol10(p, 2); - if (data < 0 || data > 60) + if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 60) return (t); tm.tm_sec = (int)data; #if 0 @@ -1233,19 +1214,17 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct xar_file *file) { uint64_t file_id, parent_id; - int hole, parent; + size_t hole, parent; /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct xar_file **new_pending_files; - int new_size; + size_t new_size; if (heap->allocated < 1024) new_size = 1024; - else - new_size = heap->allocated * 2; - /* Overflow might keep us from growing the list. */ - if (new_size <= heap->allocated) { + else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) { + /* Overflow keeps us from growing the list. */ archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); @@ -1273,7 +1252,7 @@ heap_add_entry(struct archive_read *a, */ hole = heap->used++; while (hole > 0) { - parent = (hole - 1)/2; + parent = (hole - 1) / 2; parent_id = heap->files[parent]->id; if (file_id >= parent_id) { heap->files[hole] = file; @@ -1292,7 +1271,7 @@ static struct xar_file * heap_get_entry(struct heap_queue *heap) { uint64_t a_id, b_id, c_id; - int a, b, c; + size_t a, b, c; struct xar_file *r, *tmp; if (heap->used < 1) @@ -1362,7 +1341,7 @@ add_link(struct archive_read *a, struct xar *xar, struct xar_file *file) return (ARCHIVE_OK); } -static void +static int _checksum_init(struct chksumwork *sumwrk, int sum_alg) { sumwrk->alg = sum_alg; @@ -1375,7 +1354,20 @@ _checksum_init(struct chksumwork *sumwrk, int sum_alg) case CKSUM_MD5: archive_md5_init(&(sumwrk->md5ctx)); break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_init(&(sumwrk->sha256ctx)); + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_init(&(sumwrk->sha512ctx)); + break; +#endif + default: + return (ARCHIVE_FATAL); } + return (ARCHIVE_OK); } static void @@ -1391,6 +1383,16 @@ _checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size) case CKSUM_MD5: archive_md5_update(&(sumwrk->md5ctx), buff, size); break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_update(&(sumwrk->sha256ctx), buff, size); + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_update(&(sumwrk->sha512ctx), buff, size); + break; +#endif } } @@ -1415,27 +1417,48 @@ _checksum_final(struct chksumwork *sumwrk, const void *val, size_t len) memcmp(val, sum, MD5_SIZE) != 0) r = ARCHIVE_FAILED; break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_final(&(sumwrk->sha256ctx), sum); + if (len != SHA256_SIZE || + memcmp(val, sum, SHA256_SIZE) != 0) + r = ARCHIVE_FAILED; + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_final(&(sumwrk->sha512ctx), sum); + if (len != SHA512_SIZE || + memcmp(val, sum, SHA512_SIZE) != 0) + r = ARCHIVE_FAILED; + break; +#endif + default: + r = ARCHIVE_FAILED; } return (r); } -static void +static int checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg) { - struct xar *xar; + struct xar *xar = a->format->data; - xar = (struct xar *)(a->format->data); - _checksum_init(&(xar->a_sumwrk), a_sum_alg); - _checksum_init(&(xar->e_sumwrk), e_sum_alg); + if (_checksum_init(&(xar->a_sumwrk), a_sum_alg) != ARCHIVE_OK || + _checksum_init(&(xar->e_sumwrk), e_sum_alg) != ARCHIVE_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "Unsupported checksum"); + return (ARCHIVE_FATAL); + } + return (ARCHIVE_OK); } static void checksum_update(struct archive_read *a, const void *abuff, size_t asize, const void *ebuff, size_t esize) { - struct xar *xar; + struct xar *xar = a->format->data; - xar = (struct xar *)(a->format->data); _checksum_update(&(xar->a_sumwrk), abuff, asize); _checksum_update(&(xar->e_sumwrk), ebuff, esize); } @@ -1444,27 +1467,24 @@ static int checksum_final(struct archive_read *a, const void *a_sum_val, size_t a_sum_len, const void *e_sum_val, size_t e_sum_len) { - struct xar *xar; - int r; + struct xar *xar = a->format->data; - xar = (struct xar *)(a->format->data); - r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len); - if (r == ARCHIVE_OK) - r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len); - if (r != ARCHIVE_OK) + if (_checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len) != ARCHIVE_OK || + _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len) != ARCHIVE_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "Sumcheck error"); - return (r); + "Checksum error"); + return (ARCHIVE_FATAL); + } + return (ARCHIVE_OK); } static int decompression_init(struct archive_read *a, enum enctype encoding) { - struct xar *xar; + struct xar *xar = a->format->data; const char *detail; int r; - xar = (struct xar *)(a->format->data); xar->rd_encoding = encoding; switch (encoding) { case NONE: @@ -1476,7 +1496,7 @@ decompression_init(struct archive_read *a, enum enctype encoding) r = inflateInit(&(xar->stream)); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Couldn't initialize zlib stream."); + "Couldn't initialize zlib stream"); return (ARCHIVE_FATAL); } xar->stream_valid = 1; @@ -1600,12 +1620,11 @@ static int decompress(struct archive_read *a, const void **buff, size_t *outbytes, const void *b, size_t *used) { - struct xar *xar; + struct xar *xar = a->format->data; void *outbuff; size_t avail_in, avail_out; int r; - xar = (struct xar *)(a->format->data); avail_in = *used; outbuff = (void *)(uintptr_t)*buff; if (outbuff == NULL) { @@ -1691,7 +1710,7 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "%s decompression failed(%d)", + "%s decompression failed (%d)", (xar->entry_encoding == XZ)?"xz":"lzma", r); return (ARCHIVE_FATAL); @@ -1728,10 +1747,9 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, static int decompression_cleanup(struct archive_read *a) { - struct xar *xar; + struct xar *xar = a->format->data; int r; - xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->stream_valid) { if (inflateEnd(&(xar->stream)) != Z_OK) { @@ -1760,9 +1778,7 @@ decompression_cleanup(struct archive_read *a) static void checksum_cleanup(struct archive_read *a) { - struct xar *xar; - - xar = (struct xar *)(a->format->data); + struct xar *xar = a->format->data; _checksum_final(&(xar->a_sumwrk), NULL, 0); _checksum_final(&(xar->e_sumwrk), NULL, 0); @@ -1800,15 +1816,26 @@ file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) file->mode = 0777 | AE_IFREG; file->atime = 0; file->mtime = 0; - xar->file = file; xar->xattr = NULL; for (attr = list->first; attr != NULL; attr = attr->next) { - if (strcmp(attr->name, "id") == 0) - file->id = atol10(attr->value, strlen(attr->value)); + if (strcmp(attr->name, "id") == 0) { + int r; + + r = atou64(attr->value, strlen(attr->value), + 10, &file->id); + if (r != ARCHIVE_OK) { + free(file); + return (r); + } + } } + xar->file = file; file->nlink = 1; - if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) + if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) { + xar->file = file->parent; + file_free(file); return (ARCHIVE_FATAL); + } return (ARCHIVE_OK); } @@ -1822,6 +1849,7 @@ file_free(struct xar_file *file) archive_string_free(&(file->uname)); archive_string_free(&(file->gname)); archive_string_free(&(file->hardlink)); + archive_string_free(&(file->fflags_text)); xattr = file->xattr_list; while (xattr != NULL) { struct xattr *next; @@ -1845,11 +1873,19 @@ xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - xar->xattr = xattr; for (attr = list->first; attr != NULL; attr = attr->next) { - if (strcmp(attr->name, "id") == 0) - xattr->id = atol10(attr->value, strlen(attr->value)); + if (strcmp(attr->name, "id") == 0) { + int r; + + r = atou64(attr->value, strlen(attr->value), + 10, &xattr->id); + if (r != ARCHIVE_OK) { + free(xattr); + return (r); + } + } } + xar->xattr = xattr; /* Chain to xattr list. */ for (nx = &(xar->file->xattr_list); *nx != NULL; nx = &((*nx)->next)) { @@ -1866,6 +1902,7 @@ static void xattr_free(struct xattr *xattr) { archive_string_free(&(xattr->name)); + archive_string_free(&(xattr->fstype)); free(xattr); } @@ -1903,9 +1940,16 @@ getsumalgorithm(struct xmlattr_list *list) const char *v = attr->value; if ((v[0] == 'S' || v[0] == 's') && (v[1] == 'H' || v[1] == 'h') && - (v[2] == 'A' || v[2] == 'a') && - v[3] == '1' && v[4] == '\0') - alg = CKSUM_SHA1; + (v[2] == 'A' || v[2] == 'a')) { + if (v[3] == '1' && v[4] == '\0') + alg = CKSUM_SHA1; + else if (v[3] == '2' && v[4] == '5' && + v[5] == '6' && v[6] == '\0') + alg = CKSUM_SHA256; + else if (v[3] == '5' && v[4] == '1' && + v[5] == '2' && v[6] == '\0') + alg = CKSUM_SHA512; + } if ((v[0] == 'M' || v[0] == 'm') && (v[1] == 'D' || v[1] == 'd') && v[2] == '5' && v[3] == '\0') @@ -1963,11 +2007,9 @@ unknowntag_end(struct xar *xar, const char *name) static int xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) { - struct xar *xar; + struct xar *xar = a->format->data; struct xmlattr *attr; - xar = (struct xar *)(a->format->data); - #if DEBUG fprintf(stderr, "xml_sta:[%s]\n", name); for (attr = list->first; attr != NULL; attr = attr->next) @@ -2065,8 +2107,17 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) xar->file->hdnext = xar->hdlink_orgs; xar->hdlink_orgs = xar->file; } else { - xar->file->link = (unsigned)atol10(attr->value, - strlen(attr->value)); + uint64_t val; + int r; + r = atou64(attr->value, + strlen(attr->value), 10, &val); + if (r != ARCHIVE_OK) { + return (r); + } + if (val > UINT_MAX) { + return (ARCHIVE_FATAL); + } + xar->file->link = (unsigned)val; if (xar->file->link > 0) if (add_link(a, xar, xar->file) != ARCHIVE_OK) { return (ARCHIVE_FATAL); @@ -2250,11 +2301,8 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) static void xml_end(void *userData, const char *name) { - struct archive_read *a; - struct xar *xar; - - a = (struct archive_read *)userData; - xar = (struct xar *)(a->format->data); + struct archive_read *a = (struct archive_read *)userData; + struct xar *xar = a->format->data; #if DEBUG fprintf(stderr, "xml_end:[%s]\n", name); @@ -2628,7 +2676,7 @@ strappend_base64(struct xar *xar, len = 0; out = buff; b = (const unsigned char *)s; - while (l > 0) { + while (l > 1) { int n = 0; if (base64[b[0]] < 0 || base64[b[1]] < 0) @@ -2673,37 +2721,41 @@ is_string(const char *known, const char *data, size_t len) return memcmp(data, known, len); } -static void +static int xml_data(void *userData, const char *s, size_t len) { - struct archive_read *a; - struct xar *xar; - - a = (struct archive_read *)userData; - xar = (struct xar *)(a->format->data); + struct archive_read *a = (struct archive_read *)userData; + struct xar *xar = a->format->data; + uint64_t val; + int r; #if DEBUG { char buff[1024]; - 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); + size_t dlen = len; + if (dlen > sizeof(buff) - 1) + dlen = sizeof(buff) - 1; + strncpy(buff, s, dlen); + buff[dlen] = 0; + fprintf(stderr, "\tlen=%zu:\"%s\"\n", dlen, buff); } #endif switch (xar->xmlsts) { case TOC_CHECKSUM_OFFSET: - xar->toc_chksum_offset = atol10(s, len); + r = atou64(s, len, 10, &xar->toc_chksum_offset); + if (r != ARCHIVE_OK) + return (r); break; case TOC_CHECKSUM_SIZE: - xar->toc_chksum_size = atol10(s, len); + r = atou64(s, len, 10, &xar->toc_chksum_size); + if (r != ARCHIVE_OK) + return (r); break; default: break; } if (xar->file == NULL) - return; + return (ARCHIVE_OK); switch (xar->xmlsts) { case FILE_NAME: @@ -2752,42 +2804,76 @@ xml_data(void *userData, const char *s, size_t len) xar->file->has |= HAS_TYPE; break; case FILE_INODE: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_INO; - xar->file->ino64 = atol10(s, len); + xar->file->ino64 = (int64_t)val; break; case FILE_DEVICE_MAJOR: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEVMAJOR; - xar->file->devmajor = (dev_t)atol10(s, len); + xar->file->devmajor = (dev_t)val; break; case FILE_DEVICE_MINOR: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEVMINOR; - xar->file->devminor = (dev_t)atol10(s, len); + xar->file->devminor = (dev_t)val; break; case FILE_DEVICENO: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (dev_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_DEV; - xar->file->dev = (dev_t)atol10(s, len); + xar->file->dev = (dev_t)val; break; case FILE_MODE: + r = atou64(s, len, 8, &val); + if (r != ARCHIVE_OK) + return (r); + if (val != (mode_t)val) + return (ARCHIVE_FATAL); xar->file->has |= HAS_MODE; xar->file->mode = - (xar->file->mode & AE_IFMT) | - ((mode_t)(atol8(s, len)) & ~AE_IFMT); + (xar->file->mode & AE_IFMT) | ((mode_t)val & ~AE_IFMT); break; case FILE_GROUP: xar->file->has |= HAS_GID; archive_strncpy(&(xar->file->gname), s, len); break; case FILE_GID: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_GID; - xar->file->gid = atol10(s, len); + xar->file->gid = (int64_t)val; break; case FILE_USER: xar->file->has |= HAS_UID; archive_strncpy(&(xar->file->uname), s, len); break; case FILE_UID: + r = atou64(s, len, 10, &val); + if (r != ARCHIVE_OK) + return (r); + if (val > (uint64_t)INT64_MAX) + return (ARCHIVE_FATAL); xar->file->has |= HAS_UID; - xar->file->uid = atol10(s, len); + xar->file->uid = (int64_t)val; break; case FILE_CTIME: xar->file->has |= HAS_TIME | HAS_CTIME; @@ -2802,16 +2888,22 @@ xml_data(void *userData, const char *s, size_t len) xar->file->atime = parse_time(s, len); break; case FILE_DATA_LENGTH: + r = atou64(s, len, 10, &xar->file->length); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->length = atol10(s, len); break; case FILE_DATA_OFFSET: + r = atou64(s, len, 10, &xar->file->offset); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->offset = atol10(s, len); break; case FILE_DATA_SIZE: + r = atou64(s, len, 10, &xar->file->size); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_DATA; - xar->file->size = atol10(s, len); break; case FILE_DATA_A_CHECKSUM: xar->file->a_sum.len = atohex(xar->file->a_sum.val, @@ -2822,16 +2914,22 @@ xml_data(void *userData, const char *s, size_t len) sizeof(xar->file->e_sum.val), s, len); break; case FILE_EA_LENGTH: + r = atou64(s, len, 10, &xar->xattr->length); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->length = atol10(s, len); break; case FILE_EA_OFFSET: + r = atou64(s, len, 10, &xar->xattr->offset); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->offset = atol10(s, len); break; case FILE_EA_SIZE: + r = atou64(s, len, 10, &xar->xattr->size); + if (r != ARCHIVE_OK) + return (r); xar->file->has |= HAS_XATTR; - xar->xattr->size = atol10(s, len); break; case FILE_EA_A_CHECKSUM: xar->file->has |= HAS_XATTR; @@ -2907,6 +3005,8 @@ xml_data(void *userData, const char *s, size_t len) case UNKNOWN: break; } + + return (ARCHIVE_OK); } /* @@ -3108,16 +3208,13 @@ xml2_xmlattr_setup(struct archive_read *a, static int xml2_read_cb(void *context, char *buffer, int len) { - struct archive_read *a; - struct xar *xar; + struct archive_read *a = (struct archive_read *)context; + struct xar *xar = a->format->data; const void *d; size_t outbytes; size_t used = 0; int r; - a = (struct archive_read *)context; - xar = (struct xar *)(a->format->data); - if (xar->toc_remaining <= 0) return (0); d = buffer; @@ -3146,10 +3243,9 @@ static void xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator) { - struct archive_read *a; + struct archive_read *a = (struct archive_read *)arg; (void)locator; /* UNUSED */ - a = (struct archive_read *)arg; switch (severity) { case XML_PARSER_SEVERITY_VALIDITY_WARNING: case XML_PARSER_SEVERITY_WARNING: @@ -3205,7 +3301,12 @@ xml2_read_toc(struct archive_read *a) break; case XML_READER_TYPE_TEXT: value = (const char *)xmlTextReaderConstValue(reader); - xml_data(a, value, strlen(value)); + r = xml_data(a, value, strlen(value)); + if (r != ARCHIVE_OK) { + xmlFreeTextReader(reader); + xmlCleanupParser(); + return (r); + } break; case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: default: @@ -3285,21 +3386,22 @@ expat_data_cb(void *userData, const XML_Char *s, int len) { struct expat_userData *ud = (struct expat_userData *)userData; - xml_data(ud->archive, s, (size_t)len); + if (ud->state != ARCHIVE_OK) + return; + + ud->state = xml_data(ud->archive, s, (size_t)len); } static int expat_read_toc(struct archive_read *a) { - struct xar *xar; + struct xar *xar = a->format->data; XML_Parser parser; struct expat_userData ud; ud.state = ARCHIVE_OK; ud.archive = a; - xar = (struct xar *)(a->format->data); - /* Initialize XML Parser library. */ parser = XML_ParserCreate(NULL); if (parser == NULL) { @@ -3321,7 +3423,7 @@ expat_read_toc(struct archive_read *a) d = NULL; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); - if (r != ARCHIVE_OK) { + if (r != ARCHIVE_OK || outbytes > INT_MAX) { XML_ParserFree(parser); return (r); } @@ -3383,16 +3485,13 @@ static HRESULT STDMETHODCALLTYPE asaRead(ISequentialStream *this, void *pv, ULONG cb, ULONG *pcbRead) { struct ArchiveStreamAdapter *asa = (struct ArchiveStreamAdapter *)this; - struct archive_read *a; - struct xar *xar; + struct archive_read *a = asa->a; + struct xar *xar = a->format->data; const void *d = pv; size_t outbytes = cb; size_t used = 0; int r; - a = asa->a; - xar = (struct xar *)(a->format->data); - *pcbRead = 0; if (xar->toc_remaining <= 0) @@ -3638,7 +3737,11 @@ xmllite_read_toc(struct archive_read *a) goto out; } - xml_data(a, as.s, (int)archive_strlen(&as)); + r = xml_data(a, as.s, archive_strlen(&as)); + if (r != ARCHIVE_OK) { + /* xml_data sets an appropriate error */ + goto out; + } archive_string_free(&as); case XmlNodeType_None: diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 0c86ce935e..4148c4c904 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -152,8 +152,6 @@ struct zip { struct archive_string format_name; int64_t central_directory_offset; int64_t central_directory_offset_adjusted; - size_t central_directory_entries_total; - size_t central_directory_entries_on_this_disk; int has_encrypted_entries; /* List of entries (seekable Zip only) */ @@ -261,6 +259,15 @@ struct zip { /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) +/* True if this entry has declared a specific uncompressed size. */ +static int +zip_entry_size_is_set(const struct zip_entry *zip_entry) +{ + return (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) + || (zip_entry->uncompressed_size > 0 + && zip_entry->uncompressed_size != 0xffffffff)); +} + #ifdef HAVE_ZLIB_H static int zip_read_data_deflate(struct archive_read *a, const void **buff, @@ -272,6 +279,10 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif +static void +trad_enc_decrypt_update(struct trad_enc_ctx *, const uint8_t *, size_t, + uint8_t *, size_t); + /* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 * streams inside ZIP files. It has 2 purposes: one is to fetch the next * compressed byte from the stream, second one is to increase the counter how @@ -280,16 +291,33 @@ static Byte ppmd_read(void* p) { /* Get the handle to current decompression context. */ struct archive_read *a = ((IByteIn*)p)->a; - struct zip *zip = (struct zip*) a->format->data; - ssize_t bytes_avail = 0; + struct zip *zip = a->format->data; /* Fetch next byte. */ - const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 1) { + const uint8_t* data = __archive_read_ahead(a, 1, NULL); + if(data == NULL) { zip->ppmd8_stream_failed = 1; return 0; } + if (zip->tctx_valid || zip->cctx_valid) { + uint8_t val; + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + data, 1, &val, 1); + } else { + size_t dsize = 1; + archive_decrypto_aes_ctr_update(&zip->cctx, + data, 1, &val, &dsize); + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, data, 1); + + __archive_read_consume(a, 1); + ++zip->zipx_ppmd_read_compressed; + return val; + } + __archive_read_consume(a, 1); /* Increment the counter. */ @@ -401,6 +429,153 @@ crypt_derive_key_sha1(const void *p, int size, unsigned char *key, } #endif +/* Read and decrypt bytes for zipx init headers. + * Used by format-specific init functions (lzma, ppmd) that need to + * read a small header from the compressed stream. When encryption is + * active the bytes are decrypted in-place into the decryption buffer. */ +static int +zipx_read_header_and_decrypt(struct archive_read *a, const void **buf, size_t in_len, + size_t *out_len, size_t *consumed) +{ + struct zip *zip = a->format->data; + const void *raw; + ssize_t bytes_avail; + size_t to_decrypt; + + raw = __archive_read_ahead(a, in_len, &bytes_avail); + if (raw == NULL || bytes_avail < (ssize_t)in_len) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP file data"); + return (ARCHIVE_FATAL); + } + + if (zip->tctx_valid || zip->cctx_valid) { + to_decrypt = in_len; + if (to_decrypt > zip->decrypted_buffer_size) + to_decrypt = zip->decrypted_buffer_size; + + if (zip->tctx_valid) { + trad_enc_decrypt_update(&zip->tctx, + raw, to_decrypt, + zip->decrypted_buffer, to_decrypt); + } else { + size_t dsize = to_decrypt; + archive_decrypto_aes_ctr_update(&zip->cctx, + raw, to_decrypt, + zip->decrypted_buffer, &dsize); + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, + raw, to_decrypt); + + *buf = zip->decrypted_buffer; + *out_len = to_decrypt; + *consumed = to_decrypt; + } else { + *buf = raw; + *out_len = in_len; + *consumed = in_len; + } + return (ARCHIVE_OK); +} + +/* Decrypt bulk compressed data for zipx decompression. + * Manages the decryption buffer, handles partial fills, and returns decrypted + * data pointer + length. `sp` is set to the raw pointer for HMAC accounting. */ +static void +zip_read_decrypt(struct zip *zip, const void *compressed_buff, + ssize_t bytes_avail, const void **result_buff, ssize_t *result_avail, + const void **sp) +{ + *sp = compressed_buff; + + /* Safety check to prevent potential OOB reads if something went wrong + * previously. We should not have a negative bytes_avail count here. + * If we do, set them to zero so that reading the ZIP will fail later, + * safely as corrupted instead of crashing. */ + if (bytes_avail < 0) + bytes_avail = 0; + + if (zip->tctx_valid || zip->cctx_valid) { + if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { + size_t buff_remaining = + (zip->decrypted_buffer + + zip->decrypted_buffer_size) + - (zip->decrypted_ptr + + zip->decrypted_bytes_remaining); + /* The new bytes to decrypt start after decrypted_bytes_remaining + * in the raw stream: those leading bytes were already + * decrypted on a previous call but have not yet been consumed. */ + size_t new_bytes = + (size_t)bytes_avail + - zip->decrypted_bytes_remaining; + + if (buff_remaining > new_bytes) + buff_remaining = new_bytes; + + 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, + (const uint8_t *)compressed_buff + + zip->decrypted_bytes_remaining, + buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + buff_remaining); + } else { + size_t dsize = buff_remaining; + archive_decrypto_aes_ctr_update( + &zip->cctx, + (const uint8_t *)compressed_buff + + zip->decrypted_bytes_remaining, + buff_remaining, + zip->decrypted_ptr + + zip->decrypted_bytes_remaining, + &dsize); + } + zip->decrypted_bytes_remaining += + buff_remaining; + } + } + *result_avail = zip->decrypted_bytes_remaining; + *result_buff = (const char *)zip->decrypted_ptr; + } else { + *result_buff = compressed_buff; + *result_avail = bytes_avail; + } +} + +/* Post-decompression decrypt state update. + * Updates decrypt buffer pointers and HMAC after the decompressor + * has consumed `to_consume` bytes. */ +static void +zip_read_decrypt_update(struct zip *zip, ssize_t to_consume, const void *sp) +{ + if (zip->tctx_valid || zip->cctx_valid) { + zip->decrypted_bytes_remaining -= to_consume; + if (zip->decrypted_bytes_remaining == 0) + zip->decrypted_ptr = zip->decrypted_buffer; + else + zip->decrypted_ptr += to_consume; + } + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, sp, to_consume); +} + /* * Common code for streaming or seeking modes. * @@ -481,8 +656,8 @@ static int process_extra(struct archive_read *a, struct archive_entry *entry, const char *p, size_t extra_length, struct zip_entry* zip_entry) { + struct zip *zip = a->format->data; unsigned offset = 0; - struct zip *zip = (struct zip *)(a->format->data); if (extra_length == 0) { return ARCHIVE_OK; @@ -581,6 +756,22 @@ process_extra(struct archive_read *a, struct archive_entry *entry, * on which file starts, but we don't handle * multi-volume Zip files. */ break; + case 0x000d: + /* PKWARE Unix Extra Field fixed metadata. */ + if (datasize >= 12) { + zip_entry->atime = archive_le32dec(p + offset); + zip_entry->mtime = + archive_le32dec(p + offset + 4); + zip_entry->uid = + archive_le16dec(p + offset + 8); + zip_entry->gid = + archive_le16dec(p + offset + 10); + /* + * APPNOTE.TXT also defines additional data after + * this fixed metadata, depending on file type. + */ + } + break; #ifdef DEBUG case 0x0017: { @@ -1008,7 +1199,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } @@ -1256,7 +1447,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symlink cannot be converted " - "from %s to current locale.", + "from %s to current locale", archive_string_conversion_charset_name( sconv)); ret = ARCHIVE_WARN; @@ -1269,10 +1460,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, "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 - && zip_entry->uncompressed_size != 0xffffffff)) { - /* Set the size only if it's meaningful. */ + } else if (zip_entry_size_is_set(zip_entry)) { archive_entry_set_size(entry, zip_entry->uncompressed_size); } zip->entry_bytes_remaining = zip_entry->compressed_size; @@ -1295,7 +1483,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, static int check_authentication_code(struct archive_read *a, const void *_p) { - struct zip *zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; /* Check authentication code. */ if (zip->hctx_valid) { @@ -1522,6 +1710,26 @@ consume_end_of_file_marker(struct archive_read *a, struct zip *zip) return; } + /* None of the exact patterns matched. If entry size was unknown + * (ZIP_LENGTH_AT_END flag), before treating this as + * corruption, check whether the next ZIP record follows the data + * immediately: a length-at-end entry whose compression format has + * its own end-of-stream marker (e.g. PPMd) may be written with no + * data descriptor at all. In that case the byte counts we measured + * during decompression are authoritative, so trust them and leave + * the stream untouched. */ + if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + { + const uint32_t sig = archive_le32dec(p); + if (sig == 0x04034b50U /* Local file header */ + || sig == 0x02014b50U /* Central directory record */ + || sig == 0x06054b50U) /* End of central directory */ { + zip->entry->compressed_size = compressed_actual; + zip->entry->uncompressed_size = uncompressed_actual; + return; + } + } + /* If none of the above patterns gives us a full exact match, * then there's something definitely amiss. The fallback code * below will parse out some plausible values for error @@ -1599,7 +1807,7 @@ static int zip_read_data_none(struct archive_read *a, const void **_buff, size_t *size, int64_t *offset) { - struct zip *zip; + struct zip *zip = a->format->data; const char *buff; ssize_t bytes_avail; ssize_t trailing_extra; @@ -1607,7 +1815,6 @@ zip_read_data_none(struct archive_read *a, const void **_buff, (void)offset; /* UNUSED */ - zip = (struct zip *)(a->format->data); trailing_extra = zip->hctx_valid ? AUTH_CODE_SIZE : 0; if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { @@ -1726,7 +1933,7 @@ zipx_xz_init(struct archive_read *a, struct zip *zip) r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0); if (r != LZMA_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "xz initialization failed(%d)", + "xz initialization failed (%d)", r); return (ARCHIVE_FAILED); @@ -1775,10 +1982,10 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * "lzma alone" decoder from XZ Utils. */ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); - r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); + r = lzma_alone_decoder(&zip->zipx_lzma_stream, 576 * ((uint64_t)1 << 20)); if (r != LZMA_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "lzma initialization failed(%d)", r); + "lzma initialization failed (%d)", r); return (ARCHIVE_FAILED); } @@ -1825,12 +2032,38 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) */ /* Read magic1,magic2,lzma_params from the ZIPX stream. */ - if(zip->entry_bytes_remaining < 9 || (p = __archive_read_ahead(a, 9, NULL)) == NULL) { + /* When the compressed size is unknown (e.g. ZIP_LENGTH_AT_END read + * from a non-seekable source), entry_bytes_remaining is 0 or negative + * here. We can still attempt to read the 9-byte header; if the data + * is truly truncated, the __archive_read_ahead calls below will catch + * it. */ + if(zip->entry_bytes_remaining > 0 + && zip->entry_bytes_remaining < 9) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzma data"); return (ARCHIVE_FATAL); } + if (zip->tctx_valid || zip->cctx_valid) { + const void *decrypted; + size_t out_len; + size_t consumed; + int ret; + + ret = zipx_read_header_and_decrypt(a, &decrypted, 9, &out_len, &consumed); + if (ret != ARCHIVE_OK) + return ret; + p = decrypted; + } else { + p = __archive_read_ahead(a, 9, NULL); + if (p == NULL) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma data"); + return (ARCHIVE_FATAL); + } + } + if(p[2] != 0x05 || p[3] != 0x00) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid lzma data"); @@ -1868,15 +2101,21 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) * output bytes yet. */ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); if (r != LZMA_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "lzma stream initialization error"); + if (r == LZMA_MEMLIMIT_ERROR) + archive_set_error(&a->archive, ENOMEM, + "lzma stream requires too much memory"); + else + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "lzma stream initialization error"); return ARCHIVE_FATAL; } /* We've already consumed some bytes, so take this into account. */ __archive_read_consume(a, 9); - zip->entry_bytes_remaining -= 9; zip->entry_compressed_bytes_read += 9; + if (zip->entry_bytes_remaining > 0) { + zip->entry_bytes_remaining -= 9; + } zip->decompress_init = 1; return (ARCHIVE_OK); @@ -1886,11 +2125,12 @@ static int zip_read_data_zipx_xz(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip* zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; int ret; lzma_ret lz_ret; const void* compressed_buf; - ssize_t bytes_avail, in_bytes, to_consume = 0; + const void* sp; + ssize_t bytes_avail, to_consume = 0; (void) offset; /* UNUSED */ @@ -1901,16 +2141,22 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, return (ret); } - compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + compressed_buf = 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; + } if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated xz file body"); return (ARCHIVE_FATAL); } - in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); + zip_read_decrypt(zip, compressed_buf, bytes_avail, + &compressed_buf, &bytes_avail, &sp); + zip->zipx_lzma_stream.next_in = compressed_buf; - zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.avail_in = bytes_avail; zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; @@ -1921,7 +2167,7 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, switch(lz_ret) { case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "xz data error (error %d)", (int) lz_ret); + "xz data error (%d)", (int) lz_ret); return (ARCHIVE_FATAL); case LZMA_NO_CHECK: @@ -1930,15 +2176,17 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "xz unknown error %d", (int) lz_ret); + "xz unknown error (%d)", (int) lz_ret); return (ARCHIVE_FATAL); case LZMA_STREAM_END: lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; - if((int64_t) zip->zipx_lzma_stream.total_in != - zip->entry_bytes_remaining) + /* This assertion is only possible if the size of the compressed data + * stream is known -> !ZIP_LENGTH_AT_END */ + if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining + && !(zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -1957,6 +2205,16 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry) { + if (zip->hctx_valid) { + ret = check_authentication_code(a, NULL); + if (ret != ARCHIVE_OK) + return ret; + } + } + *size = (size_t)zip->zipx_lzma_stream.total_out; *buff = zip->uncompressed_buffer; @@ -1967,11 +2225,12 @@ static int zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip* zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; int ret; lzma_ret lz_ret; const void* compressed_buf; - ssize_t bytes_avail, in_bytes, to_consume; + const void* sp; + ssize_t bytes_avail, to_consume; (void) offset; /* UNUSED */ @@ -1991,26 +2250,38 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, * data. */ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (zip->entry_bytes_remaining > 0 + && bytes_avail > zip->entry_bytes_remaining) { + bytes_avail = (ssize_t)zip->entry_bytes_remaining; + } if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzma file body"); return (ARCHIVE_FATAL); } - /* Set decompressor parameters. */ - in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); + zip_read_decrypt(zip, compressed_buf, bytes_avail, + &compressed_buf, &bytes_avail, &sp); zip->zipx_lzma_stream.next_in = compressed_buf; - zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.avail_in = bytes_avail; zip->zipx_lzma_stream.total_in = 0; zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; - zip->zipx_lzma_stream.avail_out = - /* These lzma_alone streams lack end of stream marker, so let's - * make sure the unpacker won't try to unpack more than it's - * supposed to. */ - (size_t)zipmin((int64_t) zip->uncompressed_buffer_size, - zip->entry->uncompressed_size - - zip->entry_uncompressed_bytes_read); + /* These lzma_alone streams lack an end of stream marker in some + * cases, so when the uncompressed size is known we cap avail_out to + * make sure the unpacker won't try to unpack more than it's supposed + * to. When the compressed size is unknown (entry_bytes_remaining <= 0, + * e.g. ZIP_LENGTH_AT_END from a non-seekable source) we must use the + * full buffer and rely on the LZMA stream end marker to detect the end + * of the entry. */ + if (zip->entry_bytes_remaining <= 0) { + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + } else { + zip->zipx_lzma_stream.avail_out = + (size_t)zipmin((int64_t) zip->uncompressed_buffer_size, + zip->entry->uncompressed_size - + zip->entry_uncompressed_bytes_read); + } zip->zipx_lzma_stream.total_out = 0; /* Perform the decompression. */ @@ -2018,14 +2289,17 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, switch(lz_ret) { case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "lzma data error (error %d)", (int) lz_ret); + "lzma data error (%d)", (int) lz_ret); return (ARCHIVE_FATAL); /* This case is optional in lzma alone format. It can happen, * but most of the files don't have it. (GitHub #1257) */ case LZMA_STREAM_END: + /* This assertion is only possible if the size of the + * compressed data stream is known. */ if((int64_t) zip->zipx_lzma_stream.total_in != - zip->entry_bytes_remaining) + zip->entry_bytes_remaining + && zip->entry_bytes_remaining > 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2039,9 +2313,21 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, case LZMA_OK: break; + case LZMA_BUF_ERROR: + if (zip->zipx_lzma_stream.avail_out == 0) { + /* The output buffer was filled exactly. When + * the uncompressed size is known this means we + * have decompressed all expected bytes. When + * the size is unknown a full buffer just means + * we need another iteration. */ + if (zip->entry_bytes_remaining > 0) + zip->end_of_entry = 1; + break; + } + /* FALL THROUGH */ default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "lzma unknown error %d", (int) lz_ret); + "lzma unknown error (%d)", (int) lz_ret); return (ARCHIVE_FATAL); } @@ -2049,12 +2335,29 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, /* Update pointers. */ __archive_read_consume(a, to_consume); - zip->entry_bytes_remaining -= to_consume; zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; - if(zip->entry_bytes_remaining == 0) { - zip->end_of_entry = 1; + zip_read_decrypt_update(zip, to_consume, sp); + + if(zip->entry_bytes_remaining > 0) { + zip->entry_bytes_remaining -= to_consume; + if(zip->entry_bytes_remaining == 0) { + zip->end_of_entry = 1; + } + } + + if(zip->end_of_entry && zip->entry_bytes_remaining > 0) { + ssize_t remaining = (ssize_t)zip->entry_bytes_remaining; + const void *p = __archive_read_ahead(a, remaining, NULL); + if (p != NULL) { + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, + p, remaining); + __archive_read_consume(a, remaining); + zip->entry_compressed_bytes_read += remaining; + zip->entry_bytes_remaining = 0; + } } /* Free lzma decoder handle because we'll no longer need it. */ @@ -2063,6 +2366,12 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, if(zip->end_of_entry) { lzma_end(&zip->zipx_lzma_stream); zip->zipx_lzma_valid = 0; + + if (zip->hctx_valid) { + ret = check_authentication_code(a, NULL); + if (ret != ARCHIVE_OK) + return ret; + } } /* Return values. */ @@ -2104,11 +2413,22 @@ zipx_ppmd8_init(struct archive_read *a, struct zip *zip) zip->zipx_ppmd_read_compressed = 0; /* Read Ppmd8 header (2 bytes). */ - p = __archive_read_ahead(a, 2, NULL); - if(!p) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated file data in PPMd8 stream"); - return (ARCHIVE_FATAL); + if (zip->tctx_valid || zip->cctx_valid) { + size_t out_len; + size_t consumed; + int ret; + + ret = zipx_read_header_and_decrypt(a, &p, 2, &out_len, &consumed); + if (ret != ARCHIVE_OK) + return ret; + } else { + p = __archive_read_ahead(a, 2, NULL); + if(!p) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated file data in PPMd8 stream"); + return (ARCHIVE_FATAL); + } } __archive_read_consume(a, 2); @@ -2175,10 +2495,9 @@ static int zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip* zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; int ret; size_t consumed_bytes = 0; - ssize_t bytes_avail = 0; (void) offset; /* UNUSED */ @@ -2192,8 +2511,7 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, /* Fetch for more data. We're reading 1 byte here, but libarchive * should prefetch more bytes. */ - (void) __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 0) { + if(__archive_read_ahead(a, 1, NULL) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated PPMd8 file body"); return (ARCHIVE_FATAL); @@ -2234,6 +2552,12 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, if(zip->end_of_entry) { __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); zip->ppmd8_valid = 0; + + if (zip->hctx_valid) { + int r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return (r); + } } /* Update pointers for libarchive. */ @@ -2261,7 +2585,7 @@ zipx_bzip2_init(struct archive_read *a, struct zip *zip) r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1); if(r != BZ_OK) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "bzip2 initialization failed(%d)", + "bzip2 initialization failed (%d)", r); return ARCHIVE_FAILED; @@ -2290,9 +2614,10 @@ static int zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip *zip = (struct zip *)(a->format->data); - ssize_t bytes_avail = 0, in_bytes, to_consume; + struct zip *zip = a->format->data; + ssize_t bytes_avail = 0, to_consume; const void *compressed_buff; + const void *sp; int r; uint64_t total_out; @@ -2307,27 +2632,26 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, /* Fetch more compressed bytes. */ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated bzip2 file body"); - return (ARCHIVE_FATAL); + if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) + && bytes_avail > zip->entry_bytes_remaining) { + bytes_avail = (ssize_t)zip->entry_bytes_remaining; } - - in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); - if(in_bytes < 1) { + if(bytes_avail < 1) { /* libbz2 doesn't complain when caller feeds avail_in == 0. * It will actually return success in this case, which is * undesirable. This is why we need to make this check * manually. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated bzip2 file body"); return (ARCHIVE_FATAL); } + zip_read_decrypt(zip, compressed_buff, bytes_avail, + &compressed_buff, &bytes_avail, &sp); + /* Setup buffer boundaries. */ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; - zip->bzstream.avail_in = (uint32_t)in_bytes; + zip->bzstream.avail_in = (uint32_t)bytes_avail; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_in_lo32 = 0; zip->bzstream.next_out = (char*) zip->uncompressed_buffer; @@ -2375,6 +2699,14 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return r; + } + /* Give libarchive its due. */ *size = (size_t)total_out; *buff = zip->uncompressed_buffer; @@ -2433,9 +2765,10 @@ static int zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip *zip = (struct zip *)(a->format->data); - ssize_t bytes_avail = 0, in_bytes, to_consume; + struct zip *zip = a->format->data; + ssize_t bytes_avail = 0, to_consume; const void *compressed_buff; + const void *sp; int r; size_t ret; uint64_t total_out; @@ -2452,15 +2785,12 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, } /* Fetch more compressed bytes */ - compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); - if(bytes_avail < 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated zstd file body"); - return (ARCHIVE_FATAL); + 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; } - - in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail); - if(in_bytes < 1) { + if(bytes_avail < 1) { /* zstd doesn't complain when caller feeds avail_in == 0. * It will actually return success in this case, which is * undesirable. This is why we need to make this check @@ -2470,9 +2800,12 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } + zip_read_decrypt(zip, compressed_buff, bytes_avail, + &compressed_buff, &bytes_avail, &sp); + /* Setup buffer boundaries */ in.src = compressed_buff; - in.size = in_bytes; + in.size = bytes_avail; in.pos = 0; out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 }; @@ -2484,6 +2817,10 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, ZSTD_getErrorName(ret)); return (ARCHIVE_FATAL); } + /* End of stream handling for zips with ZIP_LENGTH_AT_END flag */ + if (ret == 0 && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + zip->end_of_entry = 1; + } /* Check end of the stream. */ if (ret == 0) { @@ -2504,6 +2841,14 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += total_out; + zip_read_decrypt_update(zip, to_consume, sp); + + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) + return r; + } + /* Give libarchive its due. */ *size = (size_t)total_out; *buff = zip->uncompressed_buffer; @@ -2527,7 +2872,7 @@ zip_deflate_init(struct archive_read *a, struct zip *zip) -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize ZIP decompression."); + "Can't initialize ZIP decompression"); return (ARCHIVE_FATAL); } /* Stream structure has been set up. */ @@ -2542,15 +2887,14 @@ static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { - struct zip *zip; + struct zip *zip = a->format->data; ssize_t bytes_avail, to_consume = 0; - const void *compressed_buff, *sp; + const void *compressed_buff; + const void *sp; int r; (void)offset; /* UNUSED */ - zip = (struct zip *)(a->format->data); - /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = 256 * 1024; @@ -2584,54 +2928,8 @@ 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 + - zip->decrypted_buffer_size) - - (zip->decrypted_ptr + - zip->decrypted_bytes_remaining); - - 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; - } + zip_read_decrypt(zip, compressed_buff, bytes_avail, + &compressed_buff, &bytes_avail, &sp); /* * A bug in zlib.h: stream.next_in should be marked 'const' @@ -2670,22 +2968,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip->entry_compressed_bytes_read += to_consume; zip->entry_uncompressed_bytes_read += zip->stream.total_out; - if (zip->tctx_valid || zip->cctx_valid) { - zip->decrypted_bytes_remaining -= to_consume; - if (zip->decrypted_bytes_remaining == 0) - zip->decrypted_ptr = zip->decrypted_buffer; - else - zip->decrypted_ptr += to_consume; - } - if (zip->hctx_valid) - archive_hmac_sha1_update(&zip->hctx, sp, to_consume); + zip_read_decrypt_update(zip, to_consume, sp); - if (zip->end_of_entry) { - if (zip->hctx_valid) { - r = check_authentication_code(a, NULL); - if (r != ARCHIVE_OK) { - return (r); - } + if (zip->end_of_entry && zip->hctx_valid) { + r = check_authentication_code(a, NULL); + if (r != ARCHIVE_OK) { + return r; } } @@ -2699,7 +2987,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, static int read_decryption_header(struct archive_read *a) { - struct zip *zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; const char *p; unsigned int remaining_size; unsigned int ts; @@ -2893,7 +3181,7 @@ nomem: static int zip_alloc_decryption_buffer(struct archive_read *a) { - struct zip *zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; size_t bs = 256 * 1024; if (zip->decrypted_buffer == NULL) { @@ -2912,7 +3200,7 @@ zip_alloc_decryption_buffer(struct archive_read *a) static int init_traditional_PKWARE_decryption(struct archive_read *a) { - struct zip *zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; const void *p; int retry; int r; @@ -2984,7 +3272,7 @@ init_traditional_PKWARE_decryption(struct archive_read *a) static int init_WinZip_AES_decryption(struct archive_read *a) { - struct zip *zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; const void *p; const uint8_t *pv; size_t key_len, salt_len; @@ -3078,8 +3366,8 @@ static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { + struct zip *zip = a->format->data; int r; - struct zip *zip = (struct zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { @@ -3157,6 +3445,25 @@ archive_read_format_zip_read_data(struct archive_read *a, } if (r != ARCHIVE_OK) return (r); + + /* + * FAIL if there are more uncompressed bytes than were + * initially advertised. The end-of-entry check below also + * compares these values, but only once decoding reaches its + * own natural end. Fail the entry so a caller relying on + * archive_entry_size() as a hard boundary is never misled; + * later entries in the archive can still be read normally. + */ + if (*size > 0 && zip_entry_size_is_set(zip->entry) && + zip->entry_uncompressed_bytes_read > zip->entry->uncompressed_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP uncompressed data is larger than the declared " + "entry size (read at least %jd, expected %jd)", + (intmax_t)zip->entry_uncompressed_bytes_read, + (intmax_t)zip->entry->uncompressed_size); + return (ARCHIVE_FAILED); + } + if (*size > 0) { zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff, (unsigned)*size); @@ -3187,13 +3494,11 @@ archive_read_format_zip_read_data(struct archive_read *a, (intmax_t)zip->entry->compressed_size); return (ARCHIVE_FAILED); } - /* 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)) { + if (zip->entry->uncompressed_size != + zip->entry_uncompressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP uncompressed data is wrong size " - "(read %jd, expected %jd)\n", + "(read %jd, expected %jd)", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); return (ARCHIVE_FAILED); @@ -3206,11 +3511,9 @@ archive_read_format_zip_read_data(struct archive_read *a, static int archive_read_format_zip_cleanup(struct archive_read *a) { - struct zip *zip; + struct zip *zip = a->format->data; 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); @@ -3258,7 +3561,7 @@ archive_read_format_zip_cleanup(struct archive_read *a) free(zip->v_data); archive_string_free(&zip->format_name); free(zip); - (a->format->data) = NULL; + a->format->data = NULL; return (ARCHIVE_OK); } @@ -3266,7 +3569,7 @@ 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; + struct zip *zip = _a->format->data; if (zip) { return zip->has_encrypted_entries; } @@ -3278,10 +3581,9 @@ static int archive_read_format_zip_options(struct archive_read *a, const char *key, const char *val) { - struct zip *zip; + struct zip *zip = a->format->data; 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; @@ -3388,14 +3690,12 @@ static int archive_read_format_zip_streamable_read_header(struct archive_read *a, struct archive_entry *entry) { - struct zip *zip; + struct zip *zip = a->format->data; 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 @@ -3481,10 +3781,9 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, static int archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) { - struct zip *zip; + struct zip *zip = a->format->data; 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) @@ -3726,7 +4025,7 @@ read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) static int archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { - struct zip *zip = (struct zip *)a->format->data; + struct zip *zip = a->format->data; int64_t file_size, current_offset; const char *p; int i, tail; @@ -3932,7 +4231,6 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, __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; @@ -3961,7 +4259,6 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* 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]; @@ -4041,12 +4338,14 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, r = rsrc_basename(name, filename_length); if (filename_length >= 9 && strncmp("__MACOSX/", name, 9) == 0) { + const char *name_end; + + name_end = name + filename_length; /* 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] != '_')) { + (name_end - r < 2 || r[0] != '.' || r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ @@ -4124,7 +4423,7 @@ 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; + struct zip *zip = a->format->data; unsigned char *metadata, *mp; int64_t offset = archive_filter_bytes(&a->archive, 0); size_t remaining_bytes, metadata_bytes; @@ -4275,7 +4574,7 @@ 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 *zip = a->format->data; struct zip_entry *rsrc; int64_t offset; int r, ret = ARCHIVE_OK; @@ -4355,8 +4654,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, static int archive_read_format_zip_read_data_skip_seekable(struct archive_read *a) { - struct zip *zip; - zip = (struct zip *)(a->format->data); + struct zip *zip = a->format->data; zip->unconsumed = 0; return (ARCHIVE_OK); diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index a4955d4cea..87e21c8d7f 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -450,6 +450,8 @@ default_iconv_charset(const char *charset) { return locale_charset(); #elif HAVE_NL_LANGINFO return nl_langinfo(CODESET); +#elif defined(__BIONIC__) + return "UTF-8"; #else return ""; #endif @@ -1314,7 +1316,17 @@ create_sconv_object(const char *fc, const char *tc, else if (strcmp(fc, "CP932") == 0) sc->cd = iconv_open(tc, "SJIS"); } -#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(__FreeBSD__) && !defined(HAVE_LIBICONV) + /* + * FreeBSD's native iconv() by default returns the number of + * invalid characters in the input string, as specified by + * POSIX, but iconv_strncat_in_locale() assumes GNU iconv + * semantics. + */ + int v = 1; + + (void)iconvctl(sc->cd, ICONV_SET_ILSEQ_INVALID, &v); +#elif defined(_WIN32) && !defined(__CYGWIN__) /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale @@ -1362,7 +1374,7 @@ free_sconv_object(struct archive_string_conv *sc) } #if defined(_WIN32) && !defined(__CYGWIN__) -# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define GetOEMCP() CP_OEMCP # endif @@ -1713,7 +1725,7 @@ get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag) if (a != NULL) { #if HAVE_ICONV archive_set_error(a, ARCHIVE_ERRNO_MISC, - "iconv_open failed : Cannot handle ``%s''", + "iconv_open failed: Cannot handle ``%s''", (flag & SCONV_TO_CHARSET)?tc:fc); #else archive_set_error(a, ARCHIVE_ERRNO_MISC, @@ -2315,7 +2327,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, remaining = length; itp = (const uint8_t *)_p; - while (*itp && remaining > 0) { + while (remaining > 0 && *itp) { if (*itp > 127) { // Non-ASCII: Substitute with suitable replacement if (sc->flag & SCONV_TO_UTF8) { @@ -2330,6 +2342,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, archive_strappend_char(as, *itp); } ++itp; + --remaining; } return (return_value); } diff --git a/libarchive/archive_time.c b/libarchive/archive_time.c index 3352c809bd..bc77082889 100644 --- a/libarchive/archive_time.c +++ b/libarchive/archive_time.c @@ -24,11 +24,20 @@ */ #include "archive_platform.h" + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#include "archive_integer.h" #include "archive_private.h" #include "archive_time_private.h" -#include -#include -#include #define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600) #define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000) @@ -149,15 +158,11 @@ unix_to_ntfs(int64_t secs, uint32_t nsecs) if (secs < -(int64_t)NTFS_EPOC_TIME) return 0; - ntfs = secs + NTFS_EPOC_TIME; - - if (ntfs > UINT64_MAX / NTFS_TICKS) + /* (secs + NTFS_EPOC_TIME) * NTFS_TICKS + nsecs / 100 */ + if (archive_ckd_add_u64(&ntfs, secs, NTFS_EPOC_TIME) || + archive_ckd_mul_u64(&ntfs, ntfs, NTFS_TICKS) || + archive_ckd_add_u64(&ntfs, ntfs, nsecs / 100)) return UINT64_MAX; - ntfs *= NTFS_TICKS; - - if (ntfs > UINT64_MAX - nsecs/100) - return UINT64_MAX; - - return ntfs + nsecs/100; + return ntfs; } diff --git a/libarchive/archive_util.3 b/libarchive/archive_util.3 index 3aa508f25a..26026d4323 100644 --- a/libarchive/archive_util.3 +++ b/libarchive/archive_util.3 @@ -33,12 +33,15 @@ .Nm archive_errno , .Nm archive_error_string , .Nm archive_file_count , +.Nm archive_filter_bytes , .Nm archive_filter_code , .Nm archive_filter_count , .Nm archive_filter_name , .Nm archive_format , .Nm archive_format_name , -.Nm archive_position , +.Nm archive_free , +.Nm archive_position_compressed , +.Nm archive_position_uncompressed , .Nm archive_set_error .Nd libarchive utility functions .Sh LIBRARY @@ -59,6 +62,8 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_error_string "struct archive *" .Ft int .Fn archive_file_count "struct archive *" +.Ft int64_t +.Fn archive_filter_bytes "struct archive *" "int" .Ft int .Fn archive_filter_code "struct archive *" "int" .Ft int @@ -69,8 +74,12 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_format "struct archive *" .Ft const char * .Fn archive_format_name "struct archive *" +.Ft int +.Fn archive_free "struct archive *" .Ft int64_t -.Fn archive_position "struct archive *" "int" +.Fn archive_position_compressed "struct archive *" +.Ft int64_t +.Fn archive_position_uncompressed "struct archive *" .Ft void .Fo archive_set_error .Fa "struct archive *" @@ -140,13 +149,13 @@ filter 0 is the gunzip filter, filter 1 is the uudecode filter, and filter 2 is the pseudo-filter that wraps the archive read functions. In this case, requesting -.Fn archive_position a -1 +.Fn archive_filter_bytes a -1 would be a synonym for -.Fn archive_position a 2 +.Fn archive_filter_bytes a 2 which would return the number of bytes currently read from the archive, while -.Fn archive_position a 1 +.Fn archive_filter_bytes a 1 would return the number of bytes after uudecoding, and -.Fn archive_position a 0 +.Fn archive_filter_bytes a 0 would return the number of bytes after decompression. .It Fn archive_filter_name Returns a textual name identifying the indicated filter. @@ -165,16 +174,24 @@ utilize GNU tar extensions and several entries that do not. These entries will have different format codes. .It Fn archive_format_name A textual description of the format of the current entry. -.It Fn archive_position +.It Fn archive_free +Closes the archive if necessary, then releases all resources. +.It Fn archive_filter_bytes Returns the number of bytes read from or written to the indicated filter. In particular, -.Fn archive_position a 0 +.Fn archive_filter_bytes a 0 returns the number of bytes read or written by the format handler, while -.Fn archive_position a -1 +.Fn archive_filter_bytes a -1 returns the number of bytes read or written to the archive. See .Fn archive_filter_count for details of the numbering here. +.It Fn archive_position_compressed +Synonym for +.Fn archive_filter_bytes a -1 . +.It Fn archive_position_uncompressed +Synonym for +.Fn archive_filter_bytes a 0 . .It Fn archive_set_error Sets the numeric error code and error description that will be returned by diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 0d1de1ef20..5e0b916de4 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -42,18 +42,12 @@ #include #endif #if defined(_WIN32) && !defined(__CYGWIN__) -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA -/* don't use bcrypt when XP needs to be supported */ #include /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) #endif - -#elif defined(HAVE_WINCRYPT_H) -#include -#endif #endif #ifdef HAVE_ZLIB_H #include @@ -110,8 +104,7 @@ archive_errno(struct archive *a) const char * archive_error_string(struct archive *a) { - - if (a->error != NULL && *a->error != '\0') + if (a->error != NULL && *a->error != '\0') return (a->error); else return (NULL); @@ -250,11 +243,7 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) DWORD attr; wchar_t *xp, *ep; int fd; -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA BCRYPT_ALG_HANDLE hAlg = NULL; -#else - HCRYPTPROV hProv = (HCRYPTPROV)NULL; -#endif fd = -1; ws = NULL; archive_string_init(&temp_name); @@ -262,22 +251,36 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) if (template == NULL) { /* Get a temporary directory. */ if (tmpdir == NULL) { - size_t l; - wchar_t *tmp; + wchar_t buf[MAX_PATH + 1]; + wchar_t *p = buf, *buf2 = NULL; + size_t l, s; - l = GetTempPathW(0, NULL); + s = MAX_PATH + 1; + l = GetTempPathW((DWORD)s, buf); if (l == 0) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } - tmp = malloc(l*sizeof(wchar_t)); - if (tmp == NULL) { - errno = ENOMEM; - goto exit_tmpfile; + while (l > s) { + wchar_t *tmp; + + s = l; + tmp = realloc(buf2, s * sizeof(wchar_t)); + if (tmp == NULL) { + free(buf2); + errno = ENOMEM; + goto exit_tmpfile; + } + p = buf2 = tmp; + l = GetTempPathW((DWORD)s, buf2); + if (l == 0) { + free(buf2); + la_dosmaperr(GetLastError()); + goto exit_tmpfile; + } } - GetTempPathW((DWORD)l, tmp); - archive_wstrcpy(&temp_name, tmp); - free(tmp); + archive_wstrcpy(&temp_name, p); + free(buf2); } else { if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) @@ -328,19 +331,11 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) abort(); } -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0))) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } -#else - if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } -#endif for (;;) { wchar_t *p; @@ -351,19 +346,11 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) /* Generate a random file name through CryptGenRandom(). */ p = xp; -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, (DWORD)(ep - p)*sizeof(wchar_t), 0))) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } -#else - if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), - (BYTE*)p)) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } -#endif for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; @@ -417,13 +404,8 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) break;/* success! */ } exit_tmpfile: -#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA if (hAlg != NULL) BCryptCloseAlgorithmProvider(hAlg, 0); -#else - if (hProv != (HCRYPTPROV)NULL) - CryptReleaseContext(hProv, 0); -#endif free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); diff --git a/libarchive/archive_version_details.c b/libarchive/archive_version_details.c index 9063faa794..be11aad67e 100644 --- a/libarchive/archive_version_details.c +++ b/libarchive/archive_version_details.c @@ -157,10 +157,6 @@ archive_crypto_version(struct archive_string* str) #if defined(ARCHIVE_CRYPTOR_USE_LIBMD) archive_strcat(str, " libmd/"); archive_strcat(str, archive_libmd_version()); -#endif -#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT) - archive_strcat(str, " WinCrypt/"); - archive_strcat(str, archive_wincrypt_version()); #endif // Just in case (void)str; /* UNUSED */ @@ -180,7 +176,7 @@ archive_version_details(void) const char *libiconv = archive_libiconv_version(); const char *libacl = archive_libacl_version(); const char *librichacl = archive_librichacl_version(); - const char *libattr = archive_libacl_version(); + const char *libattr = archive_libattr_version(); if (!init) { archive_string_init(&str); @@ -234,6 +230,7 @@ archive_version_details(void) archive_strcat(&str, " libiconv/"); archive_strcat(&str, libiconv); } + init = 1; } return str.s; } @@ -431,27 +428,7 @@ archive_cng_version(void) const char * archive_wincrypt_version(void) { -#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT) || defined(ARCHIVE_CRYPTO_WINCRYPT) - HCRYPTPROV prov; - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - if (GetLastError() != (DWORD)NTE_BAD_KEYSET) - return NULL; - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) - return NULL; - } - DWORD version, length = sizeof(version); - if (!CryptGetProvParam(prov, PP_VERSION, (BYTE *)&version, &length, 0)) { - return NULL; - } else { - char major = (version >> 8) & 0xFF; - char minor = version & 0xFF; - static char wincrypt_version[6]; - snprintf(wincrypt_version, 6, "%hhd.%hhd", major, minor); - return wincrypt_version; - } -#else return NULL; -#endif } const char * diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index 3fbea6c5b6..b787a0d376 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -63,23 +63,6 @@ #include #include -#if defined(__LA_LSEEK_NEEDED) -static BOOL SetFilePointerEx_perso(HANDLE hFile, - LARGE_INTEGER liDistanceToMove, - PLARGE_INTEGER lpNewFilePointer, - DWORD dwMoveMethod) -{ - LARGE_INTEGER li; - li.QuadPart = liDistanceToMove.QuadPart; - li.LowPart = SetFilePointer( - hFile, li.LowPart, &li.HighPart, dwMoveMethod); - if(lpNewFilePointer) { - lpNewFilePointer->QuadPart = li.QuadPart; - } - return li.LowPart != -1 || GetLastError() == NO_ERROR; -} -#endif - struct ustat { int64_t st_atime; uint32_t st_atime_nsec; @@ -236,7 +219,7 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, CREATEFILE2_EXTENDED_PARAMETERS createExParams; #endif -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) handle = CreateFileA(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); @@ -285,7 +268,7 @@ __la_lseek(int fd, __int64 offset, int whence) return (-1); } distance.QuadPart = offset; - if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) { + if (!SetFilePointerEx(handle, distance, &newpointer, whence)) { DWORD lasterr; lasterr = GetLastError(); @@ -324,7 +307,7 @@ __la_open(const char *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesA(path); -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) #endif { @@ -342,7 +325,7 @@ __la_open(const char *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (ws != NULL) handle = CreateFileW(ws, 0, 0, NULL, OPEN_EXISTING, @@ -437,7 +420,7 @@ __la_wopen(const wchar_t *path, int flags, ...) * "Permission denied" error. */ attr = GetFileAttributesW(path); -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) #endif { @@ -456,7 +439,7 @@ __la_wopen(const wchar_t *path, int flags, ...) } if (attr & FILE_ATTRIBUTE_DIRECTORY) { HANDLE handle; -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) if (fullpath != NULL) handle = CreateFileW(fullpath, 0, 0, NULL, OPEN_EXISTING, diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index ecb14e0108..fef7cf90df 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -299,6 +299,26 @@ typedef int mbstate_t; size_t wcrtomb(char *, wchar_t, mbstate_t *); #endif +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif + +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_PARTITION_SYSTEM +#define WINAPI_PARTITION_SYSTEM 1 +#endif + +#ifndef NTDDI_VERSION +#define NTDDI_VERSION 0x05020000 +#endif + +#ifndef NTDDI_WIN10_VB +#define NTDDI_WIN10_VB 0x0A000008 +#endif + #if !WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) && NTDDI_VERSION < NTDDI_WIN10_VB // not supported in UWP SDK before 20H1 #define GetVolumePathNameW(f, v, c) (0) @@ -309,17 +329,4 @@ WINBASEAPI BOOL WINAPI GetVolumePathNameW( DWORD cchBufferLength ); #endif -#if defined(_MSC_VER) && _MSC_VER < 1300 -# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */ -typedef struct _FILE_ALLOCATED_RANGE_BUFFER { - LARGE_INTEGER FileOffset; - LARGE_INTEGER Length; -} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER; -# define FSCTL_SET_SPARSE \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA) -# define FSCTL_QUERY_ALLOCATED_RANGES \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA) -# endif -#endif - #endif /* !LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */ diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index e1ce5d5728..e1a3ffebb1 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -69,7 +69,7 @@ static int _archive_write_header(struct archive *, struct archive_entry *); static int _archive_write_finish_entry(struct archive *); static ssize_t _archive_write_data(struct archive *, const void *, size_t); -struct archive_none { +struct client { size_t buffer_size; size_t avail; char *buffer; @@ -135,7 +135,7 @@ archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block) if (bytes_per_block < 0) { // Do nothing if the bytes_per_block is negative - return 0; + return ARCHIVE_OK; } a->bytes_per_block = bytes_per_block; return (ARCHIVE_OK); @@ -240,8 +240,10 @@ __archive_write_filter(struct archive_write_filter *f, /* If unset, a fatal error has already occurred, so this filter * didn't open. We cannot write anything. */ return(ARCHIVE_FATAL); + if (length > (uint64_t)(INT64_MAX - f->bytes_written)) + return(ARCHIVE_FATAL); r = (f->write)(f, buff, length); - f->bytes_written += length; + f->bytes_written += (int64_t)length; return (r); } @@ -338,7 +340,7 @@ __archive_write_filters_flush(struct archive_write *a) } int -__archive_write_nulls(struct archive_write *a, size_t length) +__archive_write_nulls(struct archive_write *a, uint64_t length) { if (length == 0) return (ARCHIVE_OK); @@ -357,7 +359,7 @@ static int archive_write_client_open(struct archive_write_filter *f) { struct archive_write *a = (struct archive_write *)f->archive; - struct archive_none *state; + struct client *client; void *buffer; size_t buffer_size; @@ -366,21 +368,21 @@ archive_write_client_open(struct archive_write_filter *f) archive_write_get_bytes_in_last_block(f->archive); buffer_size = f->bytes_per_block; - state = calloc(1, sizeof(*state)); + client = calloc(1, sizeof(*client)); buffer = malloc(buffer_size); - if (state == NULL || buffer == NULL) { - free(state); + if (client == NULL || buffer == NULL) { + free(client); free(buffer); archive_set_error(f->archive, ENOMEM, "Can't allocate data for output buffering"); return (ARCHIVE_FATAL); } - state->buffer_size = buffer_size; - state->buffer = buffer; - state->next = state->buffer; - state->avail = state->buffer_size; - f->data = state; + client->buffer_size = buffer_size; + client->buffer = buffer; + client->next = client->buffer; + client->avail = client->buffer_size; + f->data = client; if (a->client_opener == NULL) return (ARCHIVE_OK); @@ -392,7 +394,7 @@ archive_write_client_write(struct archive_write_filter *f, const void *_buff, size_t length) { struct archive_write *a = (struct archive_write *)f->archive; - struct archive_none *state = (struct archive_none *)f->data; + struct client *client = f->data; const char *buff = (const char *)_buff; ssize_t remaining, to_copy; ssize_t bytes_written; @@ -405,7 +407,7 @@ archive_write_client_write(struct archive_write_filter *f, * particular, this supports "no write delay" operation for * special applications. Just set the block size to zero. */ - if (state->buffer_size == 0) { + if (client->buffer_size == 0) { while (remaining > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, buff, remaining); @@ -418,20 +420,20 @@ archive_write_client_write(struct archive_write_filter *f, } /* If the copy buffer isn't empty, try to fill it. */ - if (state->avail < state->buffer_size) { + if (client->avail < client->buffer_size) { /* If buffer is not empty... */ /* ... copy data into buffer ... */ - to_copy = ((size_t)remaining > state->avail) ? - state->avail : (size_t)remaining; - memcpy(state->next, buff, to_copy); - state->next += to_copy; - state->avail -= to_copy; + to_copy = ((size_t)remaining > client->avail) ? + client->avail : (size_t)remaining; + memcpy(client->next, buff, to_copy); + client->next += to_copy; + client->avail -= to_copy; buff += to_copy; remaining -= to_copy; /* ... if it's full, write it out. */ - if (state->avail == 0) { - char *p = state->buffer; - size_t to_write = state->buffer_size; + if (client->avail == 0) { + char *p = client->buffer; + size_t to_write = client->buffer_size; while (to_write > 0) { bytes_written = (a->client_writer)(&a->archive, a->client_data, p, to_write); @@ -445,15 +447,15 @@ archive_write_client_write(struct archive_write_filter *f, p += bytes_written; to_write -= bytes_written; } - state->next = state->buffer; - state->avail = state->buffer_size; + client->next = client->buffer; + client->avail = client->buffer_size; } } - while ((size_t)remaining >= state->buffer_size) { + while ((size_t)remaining >= client->buffer_size) { /* Write out full blocks directly to client. */ bytes_written = (a->client_writer)(&a->archive, - a->client_data, buff, state->buffer_size); + a->client_data, buff, client->buffer_size); if (bytes_written <= 0) return (ARCHIVE_FATAL); buff += bytes_written; @@ -462,9 +464,9 @@ archive_write_client_write(struct archive_write_filter *f, if (remaining > 0) { /* Copy last bit into copy buffer. */ - memcpy(state->next, buff, remaining); - state->next += remaining; - state->avail -= remaining; + memcpy(client->next, buff, remaining); + client->next += remaining; + client->avail -= remaining; } return (ARCHIVE_OK); } @@ -473,7 +475,7 @@ static int archive_write_client_free(struct archive_write_filter *f) { struct archive_write *a = (struct archive_write *)f->archive; - struct archive_none *state = (struct archive_none *)f->data; + struct client *client = f->data; if (a->client_freer) (*a->client_freer)(&a->archive, a->client_data); @@ -487,9 +489,9 @@ archive_write_client_free(struct archive_write_filter *f) } /* Free state. */ - if (state != NULL) { - free(state->buffer); - free(state); + if (client != NULL) { + free(client->buffer); + free(client); f->data = NULL; } @@ -500,7 +502,7 @@ static int archive_write_client_close(struct archive_write_filter *f) { struct archive_write *a = (struct archive_write *)f->archive; - struct archive_none *state = (struct archive_none *)f->data; + struct client *client = f->data; ssize_t block_length; ssize_t target_block_length; ssize_t bytes_written; @@ -509,8 +511,8 @@ archive_write_client_close(struct archive_write_filter *f) int ret = ARCHIVE_OK; /* If there's pending data, pad and write the last block */ - if (state->next != state->buffer) { - block_length = state->buffer_size - state->avail; + if (client->next != client->buffer) { + block_length = client->buffer_size - client->avail; /* Tricky calculation to determine size of last block */ if (a->bytes_in_last_block <= 0) @@ -524,11 +526,11 @@ archive_write_client_close(struct archive_write_filter *f) if (target_block_length > a->bytes_per_block) target_block_length = a->bytes_per_block; if (block_length < target_block_length) { - memset(state->next, 0, + memset(client->next, 0, target_block_length - block_length); block_length = target_block_length; } - p = state->buffer; + p = client->buffer; to_write = block_length; while (to_write > 0) { bytes_written = (a->client_writer)(&a->archive, @@ -683,6 +685,29 @@ __archive_write_filters_free(struct archive *_a) a->filter_last = NULL; } +int +__archive_write_unregister_format(struct archive_write *a) +{ + int r = ARCHIVE_OK; + + if (a->format_free != NULL) + r = (a->format_free)(a); + + a->format_data = NULL; + a->format_name = NULL; + a->format_init = NULL; + a->format_options = NULL; + a->format_finish_entry = NULL; + a->format_write_header = NULL; + a->format_write_data = NULL; + a->format_close = NULL; + a->format_free = NULL; + a->archive.archive_format = 0; + a->archive.archive_format_name = NULL; + + return (r); +} + /* * Destroy the archive structure. * @@ -705,11 +730,9 @@ _archive_write_free(struct archive *_a) r = archive_write_close(&a->archive); /* Release format resources. */ - if (a->format_free != NULL) { - r1 = (a->format_free)(a); - if (r1 < r) - r = r1; - } + r1 = __archive_write_unregister_format(a); + if (r1 < r) + r = r1; __archive_write_filters_free(_a); @@ -742,7 +765,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) if (a->format_write_header == NULL) { archive_set_error(&(a->archive), -1, - "Format must be set before you can write to an archive."); + "Format must be set before you can write to an archive"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } @@ -761,7 +784,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) archive_entry_ino_is_set(entry) && archive_entry_dev(entry) == (dev_t)a->skip_file_dev && archive_entry_ino64(entry) == a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Can't add archive to itself"); return (ARCHIVE_FAILED); } diff --git a/libarchive/archive_write_add_filter.c b/libarchive/archive_write_add_filter.c index aa962515a0..ddcf282538 100644 --- a/libarchive/archive_write_add_filter.c +++ b/libarchive/archive_write_add_filter.c @@ -49,7 +49,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { 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 }, + { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzop }, { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode }, { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz }, { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd }, diff --git a/libarchive/archive_write_add_filter_b64encode.c b/libarchive/archive_write_add_filter_b64encode.c index dbedf9d305..a285a54d48 100644 --- a/libarchive/archive_write_add_filter_b64encode.c +++ b/libarchive/archive_write_add_filter_b64encode.c @@ -28,6 +28,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -36,13 +39,14 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" #define LBYTES 57 -struct private_b64encode { +struct b64encode { int mode; struct archive_string name; struct archive_string encoded_buff; @@ -60,6 +64,7 @@ static int archive_filter_b64encode_close(struct archive_write_filter *); static int archive_filter_b64encode_free(struct archive_write_filter *); static void la_b64_encode(struct archive_string *, const unsigned char *, size_t); static int64_t atol8(const char *, size_t); +static void free_data(struct b64encode *); static const char base64[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', @@ -76,34 +81,38 @@ static const char base64[] = { * Add a compress filter to this write handle. */ int -archive_write_add_filter_b64encode(struct archive *_a) +archive_write_add_filter_b64encode(struct archive *a) { - struct archive_write *a = (struct archive_write *)_a; - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct private_b64encode *state; + struct archive_write_filter *f; + struct b64encode *b64encode; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_b64encode"); - state = calloc(1, sizeof(*state)); - if (state == NULL) { - archive_set_error(f->archive, ENOMEM, - "Can't allocate data for b64encode filter"); - return (ARCHIVE_FATAL); - } - archive_strcpy(&state->name, "-"); - state->mode = 0644; + b64encode = calloc(1, sizeof(*b64encode)); + if (b64encode == NULL) + goto memerr; + archive_strcpy(&b64encode->name, "-"); + b64encode->mode = 0644; - f->data = state; + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; f->name = "b64encode"; f->code = ARCHIVE_FILTER_UU; - f->open = archive_filter_b64encode_open; + f->data = b64encode; f->options = archive_filter_b64encode_options; + f->open = archive_filter_b64encode_open; f->write = archive_filter_b64encode_write; f->close = archive_filter_b64encode_close; f->free = archive_filter_b64encode_free; return (ARCHIVE_OK); +memerr: + free_data(b64encode); + archive_set_error(a, ENOMEM, + "Can't allocate data for b64encode filter"); + return (ARCHIVE_FATAL); } /* @@ -113,15 +122,23 @@ static int archive_filter_b64encode_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_b64encode *state = (struct private_b64encode *)f->data; + struct b64encode *b64encode = f->data; if (strcmp(key, "mode") == 0) { + int64_t val; + if (value == NULL) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "mode option requires octal digits"); return (ARCHIVE_FAILED); } - state->mode = (int)atol8(value, strlen(value)) & 0777; + val = atol8(value, strlen(value)); + if (val < 0 || val > INT_MAX) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "invalid mode option"); + return (ARCHIVE_FAILED); + } + b64encode->mode = (int)val & 0777; return (ARCHIVE_OK); } else if (strcmp(key, "name") == 0) { if (value == NULL) { @@ -129,7 +146,7 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key "name option requires a string"); return (ARCHIVE_FAILED); } - archive_strcpy(&state->name, value); + archive_strcpy(&b64encode->name, value); return (ARCHIVE_OK); } @@ -145,7 +162,7 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key static int archive_filter_b64encode_open(struct archive_write_filter *f) { - struct private_b64encode *state = (struct private_b64encode *)f->data; + struct b64encode *b64encode = f->data; size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -158,18 +175,17 @@ archive_filter_b64encode_open(struct archive_write_filter *f) bs -= bs % bpb; } - state->bs = bs; - if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { + b64encode->bs = bs; + if (archive_string_ensure(&b64encode->encoded_buff, bs + 512) == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for b64encode buffer"); return (ARCHIVE_FATAL); } - archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", - (unsigned int)state->mode, state->name.s); + archive_string_sprintf(&b64encode->encoded_buff, "begin-base64 %o %s\n", + (unsigned int)b64encode->mode, b64encode->name.s); - f->data = state; - return (0); + return (ARCHIVE_OK); } static void @@ -213,39 +229,39 @@ static int archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_b64encode *state = (struct private_b64encode *)f->data; + struct b64encode *b64encode = f->data; const unsigned char *p = buff; int ret = ARCHIVE_OK; if (length == 0) return (ret); - if (state->hold_len) { - while (state->hold_len < LBYTES && length > 0) { - state->hold[state->hold_len++] = *p++; + if (b64encode->hold_len) { + while (b64encode->hold_len < LBYTES && length > 0) { + b64encode->hold[b64encode->hold_len++] = *p++; length--; } - if (state->hold_len < LBYTES) + if (b64encode->hold_len < LBYTES) return (ret); - la_b64_encode(&state->encoded_buff, state->hold, LBYTES); - state->hold_len = 0; + la_b64_encode(&b64encode->encoded_buff, b64encode->hold, LBYTES); + b64encode->hold_len = 0; } for (; length >= LBYTES; length -= LBYTES, p += LBYTES) - la_b64_encode(&state->encoded_buff, p, LBYTES); + la_b64_encode(&b64encode->encoded_buff, p, LBYTES); /* Save remaining bytes. */ if (length > 0) { - memcpy(state->hold, p, length); - state->hold_len = length; + memcpy(b64encode->hold, p, length); + b64encode->hold_len = length; } - while (archive_strlen(&state->encoded_buff) >= state->bs) { + while (archive_strlen(&b64encode->encoded_buff) >= b64encode->bs) { ret = __archive_write_filter(f->next_filter, - state->encoded_buff.s, state->bs); - memmove(state->encoded_buff.s, - state->encoded_buff.s + state->bs, - state->encoded_buff.length - state->bs); - state->encoded_buff.length -= state->bs; + b64encode->encoded_buff.s, b64encode->bs); + memmove(b64encode->encoded_buff.s, + b64encode->encoded_buff.s + b64encode->bs, + b64encode->encoded_buff.length - b64encode->bs); + b64encode->encoded_buff.length -= b64encode->bs; } return (ret); @@ -258,26 +274,24 @@ archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, static int archive_filter_b64encode_close(struct archive_write_filter *f) { - struct private_b64encode *state = (struct private_b64encode *)f->data; + struct b64encode *b64encode = f->data; /* Flush remaining bytes. */ - if (state->hold_len != 0) - la_b64_encode(&state->encoded_buff, state->hold, state->hold_len); - archive_string_sprintf(&state->encoded_buff, "====\n"); + if (b64encode->hold_len != 0) + la_b64_encode(&b64encode->encoded_buff, b64encode->hold, + b64encode->hold_len); + archive_string_sprintf(&b64encode->encoded_buff, "====\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); return __archive_write_filter(f->next_filter, - state->encoded_buff.s, archive_strlen(&state->encoded_buff)); + b64encode->encoded_buff.s, archive_strlen(&b64encode->encoded_buff)); } static int archive_filter_b64encode_free(struct archive_write_filter *f) { - struct private_b64encode *state = (struct private_b64encode *)f->data; - - archive_string_free(&state->name); - archive_string_free(&state->encoded_buff); - free(state); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } @@ -286,17 +300,30 @@ atol8(const char *p, size_t char_cnt) { int64_t l; int digit; - + + if (char_cnt == 0) + return (-1); + l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else - break; + return (-1); p++; - l <<= 3; - l |= digit; + if (archive_ckd_mul_i64(&l, l, 8) || + archive_ckd_add_i64(&l, l, digit)) + return (-1); } return (l); } +static void +free_data(struct b64encode *b64encode) +{ + if (b64encode != NULL) { + archive_string_free(&b64encode->name); + archive_string_free(&b64encode->encoded_buff); + free(b64encode); + } +} diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 2434528d51..53908783a3 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -53,11 +53,10 @@ archive_write_set_compression_bzip2(struct archive *a) } #endif -struct private_data { +struct bzip2 { int compression_level; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream stream; - int64_t total_in; char *compressed; size_t compressed_buffer_size; #else @@ -72,48 +71,64 @@ static int archive_compressor_bzip2_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_bzip2_write(struct archive_write_filter *, const void *, size_t); +static void free_data(struct bzip2 *); /* * Add a bzip2 compression filter to this write handle. */ int -archive_write_add_filter_bzip2(struct archive *_a) +archive_write_add_filter_bzip2(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; + struct archive_write_filter *f; + struct bzip2 *bzip2; + int r; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - data->compression_level = 9; /* default */ - - f->data = data; - f->options = &archive_compressor_bzip2_options; - f->close = &archive_compressor_bzip2_close; - f->free = &archive_compressor_bzip2_free; - f->open = &archive_compressor_bzip2_open; - f->code = ARCHIVE_FILTER_BZIP2; - f->name = "bzip2"; + bzip2 = calloc(1, sizeof(*bzip2)); + if (bzip2 == NULL) + goto memerr; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) - return (ARCHIVE_OK); + bzip2->compression_level = 9; + + r = ARCHIVE_OK; #else - data->pdata = __archive_write_program_allocate("bzip2"); - 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, + bzip2->pdata = __archive_write_program_allocate("bzip2"); + if (bzip2->pdata == NULL) + goto memerr; + bzip2->compression_level = 0; + + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external bzip2 program"); - return (ARCHIVE_WARN); + r = ARCHIVE_WARN; #endif + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = "bzip2"; + f->code = ARCHIVE_FILTER_BZIP2; + f->data = bzip2; + f->options = archive_compressor_bzip2_options; + f->open = archive_compressor_bzip2_open; + f->write = archive_compressor_bzip2_write; + f->close = archive_compressor_bzip2_close; + f->free = archive_compressor_bzip2_free; + + return (r); +memerr: + free_data(bzip2); + archive_set_error(a, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); +} + +static int +archive_compressor_bzip2_free(struct archive_write_filter *f) +{ + free_data(f->data); + f->data = NULL; + return (ARCHIVE_OK); } /* @@ -123,18 +138,21 @@ static int archive_compressor_bzip2_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } + bzip2->compression_level = value[0] - '0'; /* Make '0' be a synonym for '1'. */ /* This way, bzip2 compressor supports the same 0..9 * range of levels as gzip. */ - if (data->compression_level < 1) - data->compression_level = 1; + if (bzip2->compression_level < 1) + bzip2->compression_level = 1; return (ARCHIVE_OK); } @@ -154,7 +172,7 @@ archive_compressor_bzip2_options(struct archive_write_filter *f, #define SET_NEXT_IN(st,src) \ (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) static int drive_compressor(struct archive_write_filter *, - struct private_data *, int finishing); + struct bzip2 *, int finishing); /* * Setup callback. @@ -162,10 +180,10 @@ static int drive_compressor(struct archive_write_filter *, static int archive_compressor_bzip2_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; int ret; - if (data->compressed == NULL) { + if (bzip2->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the bytes @@ -176,25 +194,23 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) else if (bpb != 0) bs -= bs % bpb; } - data->compressed_buffer_size = bs; - data->compressed = malloc(data->compressed_buffer_size); - if (data->compressed == NULL) { + bzip2->compressed_buffer_size = bs; + bzip2->compressed = malloc(bzip2->compressed_buffer_size); + if (bzip2->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - memset(&data->stream, 0, sizeof(data->stream)); - data->stream.next_out = data->compressed; - data->stream.avail_out = (uint32_t)data->compressed_buffer_size; - f->write = archive_compressor_bzip2_write; + memset(&bzip2->stream, 0, sizeof(bzip2->stream)); + bzip2->stream.next_out = bzip2->compressed; + bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size; /* Initialize compression library */ - ret = BZ2_bzCompressInit(&(data->stream), - data->compression_level, 0, 30); + ret = BZ2_bzCompressInit(&(bzip2->stream), + bzip2->compression_level, 0, 30); if (ret == BZ_OK) { - f->data = data; return (ARCHIVE_OK); } @@ -234,15 +250,12 @@ static int archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; - - /* Update statistics */ - data->total_in += length; + struct bzip2 *bzip2 = f->data; /* Compress input data to output buffer */ - SET_NEXT_IN(data, buff); - data->stream.avail_in = (uint32_t)length; - if (drive_compressor(f, data, 0)) + SET_NEXT_IN(bzip2, buff); + bzip2->stream.avail_in = (uint32_t)length; + if (drive_compressor(f, bzip2, 0)) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } @@ -254,19 +267,19 @@ archive_compressor_bzip2_write(struct archive_write_filter *f, static int archive_compressor_bzip2_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; int ret; /* Finish compression cycle. */ - ret = drive_compressor(f, data, 1); + ret = drive_compressor(f, bzip2, 1); if (ret == ARCHIVE_OK) { /* Write the last block */ ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size - data->stream.avail_out); + bzip2->compressed, + bzip2->compressed_buffer_size - bzip2->stream.avail_out); } - switch (BZ2_bzCompressEnd(&(data->stream))) { + switch (BZ2_bzCompressEnd(&(bzip2->stream))) { case BZ_OK: break; default: @@ -277,20 +290,6 @@ archive_compressor_bzip2_close(struct archive_write_filter *f) return ret; } -static int -archive_compressor_bzip2_free(struct archive_write_filter *f) -{ - struct private_data *data = (struct private_data *)f->data; - - /* May already have been called, but not necessarily. */ - (void)BZ2_bzCompressEnd(&(data->stream)); - - free(data->compressed); - free(data); - f->data = NULL; - return (ARCHIVE_OK); -} - /* * Utility function to push input data through compressor, writing * full output blocks as necessary. @@ -300,35 +299,35 @@ archive_compressor_bzip2_free(struct archive_write_filter *f) */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int finishing) + struct bzip2 *bzip2, int finishing) { int ret; for (;;) { - if (data->stream.avail_out == 0) { + if (bzip2->stream.avail_out == 0) { ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size); + bzip2->compressed, + bzip2->compressed_buffer_size); if (ret != ARCHIVE_OK) { /* TODO: Handle this write failure */ return (ARCHIVE_FATAL); } - data->stream.next_out = data->compressed; - data->stream.avail_out = (uint32_t)data->compressed_buffer_size; + bzip2->stream.next_out = bzip2->compressed; + bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size; } /* If there's nothing to do, we're done. */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && bzip2->stream.avail_in == 0) return (ARCHIVE_OK); - ret = BZ2_bzCompress(&(data->stream), + ret = BZ2_bzCompress(&(bzip2->stream), finishing ? BZ_FINISH : BZ_RUN); switch (ret) { case BZ_RUN_OK: /* In non-finishing case, did compressor * consume everything? */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && bzip2->stream.avail_in == 0) return (ARCHIVE_OK); break; case BZ_FINISH_OK: /* Finishing: There's more work to do */ @@ -348,12 +347,24 @@ drive_compressor(struct archive_write_filter *f, } } +static void +free_data(struct bzip2 *bzip2) +{ + if (bzip2 != NULL) { + /* May already have been called, but not necessarily. */ + (void)BZ2_bzCompressEnd(&(bzip2->stream)); + + free(bzip2->compressed); + free(bzip2); + } +} + #else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ static int archive_compressor_bzip2_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; struct archive_string as; int r; @@ -361,13 +372,12 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) archive_strcpy(&as, "bzip2"); /* Specify compression level. */ - if (data->compression_level > 0) { + if (bzip2->compression_level > 0) { archive_strcat(&as, " -"); - archive_strappend_char(&as, '0' + data->compression_level); + archive_strappend_char(&as, '0' + bzip2->compression_level); } - f->write = archive_compressor_bzip2_write; - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, bzip2->pdata, as.s); archive_string_free(&as); return (r); } @@ -376,27 +386,26 @@ static int archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, bzip2->pdata, buff, length); } static int archive_compressor_bzip2_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct bzip2 *bzip2 = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, bzip2->pdata); } -static int -archive_compressor_bzip2_free(struct archive_write_filter *f) +static void +free_data(struct bzip2 *bzip2) { - struct private_data *data = (struct private_data *)f->data; - - __archive_write_program_free(data->pdata); - free(data); - return (ARCHIVE_OK); + if (bzip2 != NULL) { + __archive_write_program_free(bzip2->pdata); + free(bzip2); + } } #endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ diff --git a/libarchive/archive_write_add_filter_compress.c b/libarchive/archive_write_add_filter_compress.c index a54a857542..f882420493 100644 --- a/libarchive/archive_write_add_filter_compress.c +++ b/libarchive/archive_write_add_filter_compress.c @@ -85,7 +85,7 @@ #define FIRST 257 /* First free entry. */ #define CLEAR 256 /* Table clear output code. */ -struct private_data { +struct compress { int64_t in_count, out_count, checkpoint; int code_len; /* Number of bits/code. */ @@ -111,6 +111,7 @@ static int archive_compressor_compress_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_compress_close(struct archive_write_filter *); static int archive_compressor_compress_free(struct archive_write_filter *); +static void free_data(struct compress *); #if ARCHIVE_VERSION_NUMBER < 4000000 int @@ -125,17 +126,35 @@ archive_write_set_compression_compress(struct archive *a) * Add a compress filter to this write handle. */ int -archive_write_add_filter_compress(struct archive *_a) +archive_write_add_filter_compress(struct archive *a) { - struct archive_write *a = (struct archive_write *)_a; - struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct archive_write_filter *f; + struct compress *compress; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_compress"); - f->open = &archive_compressor_compress_open; - f->code = ARCHIVE_FILTER_COMPRESS; + + compress = calloc(1, sizeof(*compress)); + if (compress == NULL) + goto memerr; + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; f->name = "compress"; + f->code = ARCHIVE_FILTER_COMPRESS; + f->data = compress; + f->open = archive_compressor_compress_open; + f->write = archive_compressor_compress_write; + f->close = archive_compressor_compress_close; + f->free = archive_compressor_compress_free; + return (ARCHIVE_OK); +memerr: + free_data(compress); + archive_set_error(a, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); } /* @@ -144,19 +163,9 @@ archive_write_add_filter_compress(struct archive *_a) static int archive_compressor_compress_open(struct archive_write_filter *f) { - struct private_data *state; + struct compress *compress = f->data; size_t bs = 65536, bpb; - f->code = ARCHIVE_FILTER_COMPRESS; - f->name = "compress"; - - state = calloc(1, sizeof(*state)); - if (state == NULL) { - archive_set_error(f->archive, ENOMEM, - "Can't allocate data for compression"); - return (ARCHIVE_FATAL); - } - if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the bytes * per block for performance. */ @@ -166,41 +175,38 @@ archive_compressor_compress_open(struct archive_write_filter *f) else if (bpb != 0) bs -= bs % bpb; } - state->compressed_buffer_size = bs; - state->compressed = malloc(state->compressed_buffer_size); + compress->compressed_buffer_size = bs; + compress->compressed = malloc(compress->compressed_buffer_size); - if (state->compressed == NULL) { + if (compress->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); - free(state); return (ARCHIVE_FATAL); } - f->write = archive_compressor_compress_write; - f->close = archive_compressor_compress_close; - f->free = archive_compressor_compress_free; + /* Should NEVER generate this code. */ + compress->max_maxcode = 0x10000; + /* Length of input. */ + compress->in_count = 0; + compress->bit_buf = 0; + compress->bit_offset = 0; + /* Includes 3-byte header mojo. */ + compress->out_count = 3; + compress->compress_ratio = 0; + compress->checkpoint = CHECK_GAP; + compress->code_len = 9; + compress->cur_maxcode = MAXCODE(compress->code_len); + compress->first_free = FIRST; - state->max_maxcode = 0x10000; /* Should NEVER generate this code. */ - state->in_count = 0; /* Length of input. */ - state->bit_buf = 0; - state->bit_offset = 0; - state->out_count = 3; /* Includes 3-byte header mojo. */ - state->compress_ratio = 0; - state->checkpoint = CHECK_GAP; - state->code_len = 9; - state->cur_maxcode = MAXCODE(state->code_len); - state->first_free = FIRST; - - memset(state->hashtab, 0xff, sizeof(state->hashtab)); + memset(compress->hashtab, 0xff, sizeof(compress->hashtab)); /* Prime output buffer with a gzip header. */ - state->compressed[0] = 0x1f; /* Compress */ - state->compressed[1] = 0x9d; - state->compressed[2] = 0x90; /* Block mode, 16bit max */ - state->compressed_offset = 3; + compress->compressed[0] = 0x1f; /* Compress */ + compress->compressed[1] = 0x9d; + compress->compressed[2] = 0x90; /* Block mode, 16bit max */ + compress->compressed_offset = 3; - f->data = state; - return (0); + return (ARCHIVE_OK); } /*- @@ -224,17 +230,17 @@ static const unsigned char rmask[9] = static int output_byte(struct archive_write_filter *f, unsigned char c) { - struct private_data *state = f->data; + struct compress *compress = f->data; - state->compressed[state->compressed_offset++] = c; - ++state->out_count; + compress->compressed[compress->compressed_offset++] = c; + ++compress->out_count; - if (state->compressed_buffer_size == state->compressed_offset) { + if (compress->compressed_buffer_size == compress->compressed_offset) { int ret = __archive_write_filter(f->next_filter, - state->compressed, state->compressed_buffer_size); + compress->compressed, compress->compressed_buffer_size); if (ret != ARCHIVE_OK) return ARCHIVE_FATAL; - state->compressed_offset = 0; + compress->compressed_offset = 0; } return ARCHIVE_OK; @@ -243,7 +249,7 @@ output_byte(struct archive_write_filter *f, unsigned char c) static int output_code(struct archive_write_filter *f, int ocode) { - struct private_data *state = f->data; + struct compress *compress = f->data; int bits, ret, clear_flg, bit_offset; clear_flg = ocode == CLEAR; @@ -252,11 +258,11 @@ output_code(struct archive_write_filter *f, int ocode) * Since ocode is always >= 8 bits, only need to mask the first * hunk on the left. */ - bit_offset = state->bit_offset % 8; - state->bit_buf |= (ocode << bit_offset) & 0xff; - output_byte(f, state->bit_buf); + bit_offset = compress->bit_offset % 8; + compress->bit_buf |= (ocode << bit_offset) & 0xff; + output_byte(f, compress->bit_buf); - bits = state->code_len - (8 - bit_offset); + bits = compress->code_len - (8 - bit_offset); ocode >>= 8 - bit_offset; /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { @@ -265,41 +271,41 @@ output_code(struct archive_write_filter *f, int ocode) bits -= 8; } /* Last bits. */ - state->bit_offset += state->code_len; - state->bit_buf = ocode & rmask[bits]; - if (state->bit_offset == state->code_len * 8) - state->bit_offset = 0; + compress->bit_offset += compress->code_len; + compress->bit_buf = ocode & rmask[bits]; + if (compress->bit_offset == compress->code_len * 8) + compress->bit_offset = 0; /* * If the next entry is going to be too big for the ocode size, * then increase it, if possible. */ - if (clear_flg || state->first_free > state->cur_maxcode) { + if (clear_flg || compress->first_free > compress->cur_maxcode) { /* * Write the whole buffer, because the input side won't * discover the size increase until after it has read it. */ - if (state->bit_offset > 0) { - while (state->bit_offset < state->code_len * 8) { - ret = output_byte(f, state->bit_buf); + if (compress->bit_offset > 0) { + while (compress->bit_offset < compress->code_len * 8) { + ret = output_byte(f, compress->bit_buf); if (ret != ARCHIVE_OK) return ret; - state->bit_offset += 8; - state->bit_buf = 0; + compress->bit_offset += 8; + compress->bit_buf = 0; } } - state->bit_buf = 0; - state->bit_offset = 0; + compress->bit_buf = 0; + compress->bit_offset = 0; if (clear_flg) { - state->code_len = 9; - state->cur_maxcode = MAXCODE(state->code_len); + compress->code_len = 9; + compress->cur_maxcode = MAXCODE(compress->code_len); } else { - state->code_len++; - if (state->code_len == 16) - state->cur_maxcode = state->max_maxcode; + compress->code_len++; + if (compress->code_len == 16) + compress->cur_maxcode = compress->max_maxcode; else - state->cur_maxcode = MAXCODE(state->code_len); + compress->cur_maxcode = MAXCODE(compress->code_len); } } @@ -309,13 +315,13 @@ output_code(struct archive_write_filter *f, int ocode) static int output_flush(struct archive_write_filter *f) { - struct private_data *state = f->data; + struct compress *compress = f->data; int ret; /* At EOF, write the rest of the buffer. */ - if (state->bit_offset % 8) { - state->code_len = (state->bit_offset % 8 + 7) / 8; - ret = output_byte(f, state->bit_buf); + if (compress->bit_offset % 8) { + compress->code_len = (compress->bit_offset % 8 + 7) / 8; + ret = output_byte(f, compress->bit_buf); if (ret != ARCHIVE_OK) return ret; } @@ -330,7 +336,7 @@ static int archive_compressor_compress_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *state = (struct private_data *)f->data; + struct compress *compress = f->data; int i; int ratio; int c, disp, ret; @@ -341,23 +347,24 @@ archive_compressor_compress_write(struct archive_write_filter *f, bp = buff; - if (state->in_count == 0) { - state->cur_code = *bp++; - ++state->in_count; + if (compress->in_count == 0) { + compress->cur_code = *bp++; + ++compress->in_count; --length; } while (length--) { c = *bp++; - state->in_count++; - state->cur_fcode = (c << 16) | state->cur_code; - i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */ + compress->in_count++; + compress->cur_fcode = (c << 16) | compress->cur_code; + /* Xor hashing. */ + i = ((c << HSHIFT) ^ compress->cur_code); - if (state->hashtab[i] == state->cur_fcode) { - state->cur_code = state->codetab[i]; + if (compress->hashtab[i] == compress->cur_fcode) { + compress->cur_code = compress->codetab[i]; continue; } - if (state->hashtab[i] < 0) /* Empty slot. */ + if (compress->hashtab[i] < 0) /* Empty slot. */ goto nomatch; /* Secondary hash (after G. Knott). */ if (i == 0) @@ -368,40 +375,40 @@ archive_compressor_compress_write(struct archive_write_filter *f, if ((i -= disp) < 0) i += HSIZE; - if (state->hashtab[i] == state->cur_fcode) { - state->cur_code = state->codetab[i]; + if (compress->hashtab[i] == compress->cur_fcode) { + compress->cur_code = compress->codetab[i]; continue; } - if (state->hashtab[i] >= 0) + if (compress->hashtab[i] >= 0) goto probe; nomatch: - ret = output_code(f, state->cur_code); + ret = output_code(f, compress->cur_code); if (ret != ARCHIVE_OK) return ret; - state->cur_code = c; - if (state->first_free < state->max_maxcode) { - state->codetab[i] = state->first_free++; /* code -> hashtable */ - state->hashtab[i] = state->cur_fcode; + compress->cur_code = c; + if (compress->first_free < compress->max_maxcode) { + compress->codetab[i] = compress->first_free++; /* code -> hashtable */ + compress->hashtab[i] = compress->cur_fcode; continue; } - if (state->in_count < state->checkpoint) + if (compress->in_count < compress->checkpoint) continue; - state->checkpoint = state->in_count + CHECK_GAP; + compress->checkpoint = compress->in_count + CHECK_GAP; - if (state->in_count <= 0x007fffff && state->out_count != 0) - ratio = (int)(state->in_count * 256 / state->out_count); - else if ((ratio = (int)(state->out_count / 256)) == 0) + if (compress->in_count <= 0x007fffff && compress->out_count != 0) + ratio = (int)(compress->in_count * 256 / compress->out_count); + else if ((ratio = (int)(compress->out_count / 256)) == 0) ratio = 0x7fffffff; else - ratio = (int)(state->in_count / ratio); + ratio = (int)(compress->in_count / ratio); - if (ratio > state->compress_ratio) - state->compress_ratio = ratio; + if (ratio > compress->compress_ratio) + compress->compress_ratio = ratio; else { - state->compress_ratio = 0; - memset(state->hashtab, 0xff, sizeof(state->hashtab)); - state->first_free = FIRST; + compress->compress_ratio = 0; + memset(compress->hashtab, 0xff, sizeof(compress->hashtab)); + compress->first_free = FIRST; ret = output_code(f, CLEAR); if (ret != ARCHIVE_OK) return ret; @@ -418,10 +425,10 @@ archive_compressor_compress_write(struct archive_write_filter *f, static int archive_compressor_compress_close(struct archive_write_filter *f) { - struct private_data *state = (struct private_data *)f->data; + struct compress *compress = f->data; int ret; - ret = output_code(f, state->cur_code); + ret = output_code(f, compress->cur_code); if (ret != ARCHIVE_OK) return ret; ret = output_flush(f); @@ -430,16 +437,23 @@ archive_compressor_compress_close(struct archive_write_filter *f) /* Write the last block */ ret = __archive_write_filter(f->next_filter, - state->compressed, state->compressed_offset); + compress->compressed, compress->compressed_offset); return (ret); } static int archive_compressor_compress_free(struct archive_write_filter *f) { - struct private_data *state = (struct private_data *)f->data; - - free(state->compressed); - free(state); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } + +static void +free_data(struct compress *compress) +{ + if (compress != NULL) { + free(compress->compressed); + free(compress); + } +} diff --git a/libarchive/archive_write_add_filter_grzip.c b/libarchive/archive_write_add_filter_grzip.c index f8bb886061..be686c6176 100644 --- a/libarchive/archive_write_add_filter_grzip.c +++ b/libarchive/archive_write_add_filter_grzip.c @@ -35,7 +35,7 @@ #include "archive.h" #include "archive_write_private.h" -struct write_grzip { +struct grzip { struct archive_write_program_data *pdata; }; @@ -46,42 +46,45 @@ static int archive_write_grzip_write(struct archive_write_filter *, const void *, size_t); static int archive_write_grzip_close(struct archive_write_filter *); static int archive_write_grzip_free(struct archive_write_filter *); +static void free_data(struct grzip *); int -archive_write_add_filter_grzip(struct archive *_a) +archive_write_add_filter_grzip(struct archive *a) { - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct write_grzip *data; + struct archive_write_filter *f; + struct grzip *grzip; - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - data->pdata = __archive_write_program_allocate("grzip"); - if (data->pdata == NULL) { - free(data); - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } + grzip = calloc(1, sizeof(*grzip)); + if (grzip == NULL) + goto memerr; + grzip->pdata = __archive_write_program_allocate("grzip"); + if (grzip->pdata == NULL) + goto memerr; + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; f->name = "grzip"; f->code = ARCHIVE_FILTER_GRZIP; - f->data = data; - f->open = archive_write_grzip_open; + f->data = grzip; f->options = archive_write_grzip_options; + f->open = archive_write_grzip_open; f->write = archive_write_grzip_write; f->close = archive_write_grzip_close; f->free = archive_write_grzip_free; /* Note: This filter always uses an external program, so we * return "warn" to inform of the fact. */ - archive_set_error(_a, ARCHIVE_ERRNO_MISC, + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external grzip program for grzip compression"); return (ARCHIVE_WARN); +memerr: + free_data(grzip); + archive_set_error(a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int @@ -100,34 +103,41 @@ archive_write_grzip_options(struct archive_write_filter *f, const char *key, static int archive_write_grzip_open(struct archive_write_filter *f) { - struct write_grzip *data = (struct write_grzip *)f->data; + struct grzip *grzip = f->data; - return __archive_write_program_open(f, data->pdata, "grzip"); + return __archive_write_program_open(f, grzip->pdata, "grzip"); } static int archive_write_grzip_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct write_grzip *data = (struct write_grzip *)f->data; + struct grzip *grzip = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, grzip->pdata, buff, length); } static int archive_write_grzip_close(struct archive_write_filter *f) { - struct write_grzip *data = (struct write_grzip *)f->data; + struct grzip *grzip = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, grzip->pdata); } static int archive_write_grzip_free(struct archive_write_filter *f) { - struct write_grzip *data = (struct write_grzip *)f->data; - - __archive_write_program_free(data->pdata); - free(data); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } + +static void +free_data(struct grzip *grzip) +{ + if (grzip != NULL) { + __archive_write_program_free(grzip->pdata); + free(grzip); + } +} diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c index b09e669b75..a7679a728f 100644 --- a/libarchive/archive_write_add_filter_gzip.c +++ b/libarchive/archive_write_add_filter_gzip.c @@ -40,6 +40,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" @@ -55,13 +56,13 @@ archive_write_set_compression_gzip(struct archive *a) /* Don't compile this if we don't have zlib. */ -struct private_data { +struct gzip { int compression_level; int timestamp; char *original_filename; #ifdef HAVE_ZLIB_H z_stream stream; - int64_t total_in; + uint64_t total_in; unsigned char *compressed; size_t compressed_buffer_size; unsigned long crc; @@ -86,65 +87,66 @@ static int archive_compressor_gzip_close(struct archive_write_filter *); static int archive_compressor_gzip_free(struct archive_write_filter *); #ifdef HAVE_ZLIB_H static int drive_compressor(struct archive_write_filter *, - struct private_data *, int finishing); + struct gzip *, int finishing); #endif +static void free_data(struct gzip *); /* * Add a gzip compression filter to this write handle. */ int -archive_write_add_filter_gzip(struct archive *_a) +archive_write_add_filter_gzip(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, + struct archive_write_filter *f; + struct gzip *gzip; + int r; + + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - f->data = data; - f->open = &archive_compressor_gzip_open; - f->options = &archive_compressor_gzip_options; - f->close = &archive_compressor_gzip_close; - f->free = &archive_compressor_gzip_free; - f->code = ARCHIVE_FILTER_GZIP; - f->name = "gzip"; - - data->original_filename = NULL; + gzip = calloc(1, sizeof(*gzip)); + if (gzip == NULL) + goto memerr; + gzip->original_filename = NULL; #ifdef HAVE_ZLIB_H - data->compression_level = Z_DEFAULT_COMPRESSION; - return (ARCHIVE_OK); + gzip->compression_level = Z_DEFAULT_COMPRESSION; + + r = ARCHIVE_OK; #else - data->pdata = __archive_write_program_allocate("gzip"); - 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, + gzip->pdata = __archive_write_program_allocate("gzip"); + if (gzip->pdata == NULL) + goto memerr; + gzip->compression_level = 0; + + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external gzip program"); - return (ARCHIVE_WARN); + r = ARCHIVE_WARN; #endif + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = "gzip"; + f->code = ARCHIVE_FILTER_GZIP; + f->data = gzip; + f->options = archive_compressor_gzip_options; + f->open = archive_compressor_gzip_open; + f->write = archive_compressor_gzip_write; + f->close = archive_compressor_gzip_close; + f->free = archive_compressor_gzip_free; + + return (r); +memerr: + free_data(gzip); + archive_set_error(a, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); } static int archive_compressor_gzip_free(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; - -#ifdef HAVE_ZLIB_H - free(data->compressed); -#else - __archive_write_program_free(data->pdata); -#endif - free((void*)data->original_filename); - free(data); + free_data(f->data); f->data = NULL; return (ARCHIVE_OK); } @@ -156,24 +158,30 @@ static int archive_compressor_gzip_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } + gzip->compression_level = value[0] - '0'; return (ARCHIVE_OK); } if (strcmp(key, "timestamp") == 0) { - data->timestamp = (value == NULL)?-1:1; + gzip->timestamp = (value == NULL)?-1:1; return (ARCHIVE_OK); } if (strcmp(key, "original-filename") == 0) { - free((void*)data->original_filename); - data->original_filename = NULL; - if (value) - data->original_filename = strdup(value); + free((void*)gzip->original_filename); + gzip->original_filename = NULL; + if (value) { + gzip->original_filename = strdup(value); + if (gzip->original_filename == NULL) + return (ARCHIVE_FAILED); + } return (ARCHIVE_OK); } @@ -190,11 +198,11 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key, static int archive_compressor_gzip_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; int ret = ARCHIVE_OK; int init_success; - if (data->compressed == NULL) { + if (gzip->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of @@ -205,63 +213,60 @@ archive_compressor_gzip_open(struct archive_write_filter *f) else if (bpb != 0) bs -= bs % bpb; } - data->compressed_buffer_size = bs; - data->compressed = malloc(data->compressed_buffer_size); - if (data->compressed == NULL) { + gzip->compressed_buffer_size = bs; + gzip->compressed = malloc(gzip->compressed_buffer_size); + if (gzip->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - data->crc = crc32(0L, NULL, 0); - data->stream.next_out = data->compressed; - data->stream.avail_out = (uInt)data->compressed_buffer_size; + gzip->crc = crc32(0L, NULL, 0); + gzip->stream.next_out = gzip->compressed; + gzip->stream.avail_out = (uInt)gzip->compressed_buffer_size; /* Prime output buffer with a gzip header. */ - data->compressed[0] = 0x1f; /* GZip signature bytes */ - data->compressed[1] = 0x8b; - data->compressed[2] = 0x08; /* "Deflate" compression */ - data->compressed[3] = 0x00; /* Flags */ - if (data->timestamp >= 0) { - time_t t = time(NULL); - data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ - data->compressed[5] = (uint8_t)(t>>8)&0xff; - data->compressed[6] = (uint8_t)(t>>16)&0xff; - data->compressed[7] = (uint8_t)(t>>24)&0xff; + gzip->compressed[0] = 0x1f; /* GZip signature bytes */ + gzip->compressed[1] = 0x8b; + gzip->compressed[2] = 0x08; /* "Deflate" compression */ + gzip->compressed[3] = 0x00; /* Flags */ + if (gzip->timestamp >= 0) { + uint32_t t = (uint32_t)time(NULL); + archive_le32enc(gzip->compressed + 4, t); /* Timestamp */ } else { - memset(&data->compressed[4], 0, 4); + memset(&gzip->compressed[4], 0, 4); } - if (data->compression_level == 9) { - data->compressed[8] = 2; - } else if(data->compression_level == 1) { - data->compressed[8] = 4; + if (gzip->compression_level == 9) { + gzip->compressed[8] = 2; + } else if(gzip->compression_level == 1) { + gzip->compressed[8] = 4; } else { - data->compressed[8] = 0; + gzip->compressed[8] = 0; } - data->compressed[9] = 3; /* OS=Unix */ - data->stream.next_out += 10; - data->stream.avail_out -= 10; + gzip->compressed[9] = 3; /* OS=Unix */ + gzip->stream.next_out += 10; + gzip->stream.avail_out -= 10; - if (data->original_filename != NULL) { + if (gzip->original_filename != NULL) { /* Limit "original filename" to 32k or the * remaining space in the buffer, whichever is smaller. */ - int ofn_length = strlen(data->original_filename); - int ofn_max_length = 32768; - int ofn_space_available = data->compressed - + data->compressed_buffer_size - - data->stream.next_out + size_t ofn_length = strlen(gzip->original_filename); + size_t ofn_max_length = 32768; + size_t ofn_space_available = gzip->compressed + + gzip->compressed_buffer_size + - gzip->stream.next_out - 1; if (ofn_max_length > ofn_space_available) { ofn_max_length = ofn_space_available; } if (ofn_length < ofn_max_length) { - data->compressed[3] |= 0x8; - strcpy((char*)data->compressed + 10, - data->original_filename); - data->stream.next_out += ofn_length + 1; - data->stream.avail_out -= ofn_length + 1; + gzip->compressed[3] |= 0x8; + strcpy((char*)gzip->compressed + 10, + gzip->original_filename); + gzip->stream.next_out += ofn_length + 1; + gzip->stream.avail_out -= ofn_length + 1; } else { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Gzip 'Original Filename' ignored because it is too long"); @@ -269,18 +274,15 @@ archive_compressor_gzip_open(struct archive_write_filter *f) } } - f->write = archive_compressor_gzip_write; - /* Initialize compression library. */ - init_success = deflateInit2(&(data->stream), - data->compression_level, + init_success = deflateInit2(&(gzip->stream), + gzip->compression_level, Z_DEFLATED, -15 /* < 0 to suppress zlib header */, 8, Z_DEFAULT_STRATEGY); if (init_success == Z_OK) { - f->data = data; return (ret); } @@ -316,17 +318,17 @@ static int archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; int ret; /* Update statistics */ - data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length); - data->total_in += length; + gzip->crc = crc32(gzip->crc, (const Bytef *)buff, (uInt)length); + gzip->total_in += length; /* Compress input data to output buffer */ - SET_NEXT_IN(data, buff); - data->stream.avail_in = (uInt)length; - if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) + SET_NEXT_IN(gzip, buff); + gzip->stream.avail_in = (uInt)length; + if ((ret = drive_compressor(f, gzip, 0)) != ARCHIVE_OK) return (ret); return (ARCHIVE_OK); @@ -338,32 +340,26 @@ archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, static int archive_compressor_gzip_close(struct archive_write_filter *f) { + struct gzip *gzip = f->data; unsigned char trailer[8]; - struct private_data *data = (struct private_data *)f->data; int ret; /* Finish compression cycle */ - ret = drive_compressor(f, data, 1); + ret = drive_compressor(f, gzip, 1); if (ret == ARCHIVE_OK) { /* Write the last compressed data. */ ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size - data->stream.avail_out); + gzip->compressed, + gzip->compressed_buffer_size - gzip->stream.avail_out); } if (ret == ARCHIVE_OK) { /* Build and write out 8-byte trailer. */ - trailer[0] = (uint8_t)(data->crc)&0xff; - trailer[1] = (uint8_t)(data->crc >> 8)&0xff; - trailer[2] = (uint8_t)(data->crc >> 16)&0xff; - trailer[3] = (uint8_t)(data->crc >> 24)&0xff; - trailer[4] = (uint8_t)(data->total_in)&0xff; - trailer[5] = (uint8_t)(data->total_in >> 8)&0xff; - trailer[6] = (uint8_t)(data->total_in >> 16)&0xff; - trailer[7] = (uint8_t)(data->total_in >> 24)&0xff; + archive_le32enc(trailer, gzip->crc); + archive_le32enc(trailer + 4, gzip->total_in); ret = __archive_write_filter(f->next_filter, trailer, 8); } - switch (deflateEnd(&(data->stream))) { + switch (deflateEnd(&(gzip->stream))) { case Z_OK: break; default: @@ -383,34 +379,34 @@ archive_compressor_gzip_close(struct archive_write_filter *f) */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int finishing) + struct gzip *gzip, int finishing) { int ret; for (;;) { - if (data->stream.avail_out == 0) { + if (gzip->stream.avail_out == 0) { ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size); + gzip->compressed, + gzip->compressed_buffer_size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); - data->stream.next_out = data->compressed; - data->stream.avail_out = - (uInt)data->compressed_buffer_size; + gzip->stream.next_out = gzip->compressed; + gzip->stream.avail_out = + (uInt)gzip->compressed_buffer_size; } /* If there's nothing to do, we're done. */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && gzip->stream.avail_in == 0) return (ARCHIVE_OK); - ret = deflate(&(data->stream), + ret = deflate(&(gzip->stream), finishing ? Z_FINISH : Z_NO_FLUSH ); switch (ret) { case Z_OK: /* In non-finishing case, check if compressor * consumed everything */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && gzip->stream.avail_in == 0) return (ARCHIVE_OK); /* In finishing case, this return always means * there's more work */ @@ -429,12 +425,22 @@ drive_compressor(struct archive_write_filter *f, } } +static void +free_data(struct gzip *gzip) +{ + if (gzip != NULL) { + free(gzip->compressed); + free(gzip->original_filename); + free(gzip); + } +} + #else /* HAVE_ZLIB_H */ static int archive_compressor_gzip_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; struct archive_string as; int r; @@ -442,19 +448,18 @@ archive_compressor_gzip_open(struct archive_write_filter *f) archive_strcpy(&as, "gzip"); /* Specify compression level. */ - if (data->compression_level > 0) { + if (gzip->compression_level > 0) { archive_strcat(&as, " -"); - archive_strappend_char(&as, '0' + data->compression_level); + archive_strappend_char(&as, '0' + gzip->compression_level); } - if (data->timestamp < 0) + if (gzip->timestamp < 0) /* Do not save timestamp. */ archive_strcat(&as, " -n"); - else if (data->timestamp > 0) + else if (gzip->timestamp > 0) /* Save timestamp. */ archive_strcat(&as, " -N"); - f->write = archive_compressor_gzip_write; - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, gzip->pdata, as.s); archive_string_free(&as); return (r); } @@ -463,17 +468,26 @@ static int archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, gzip->pdata, buff, length); } static int archive_compressor_gzip_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct gzip *gzip = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, gzip->pdata); } +static void +free_data(struct gzip *gzip) +{ + if (gzip != NULL) { + __archive_write_program_free(gzip->pdata); + free(gzip->original_filename); + free(gzip); + } +} #endif /* HAVE_ZLIB_H */ diff --git a/libarchive/archive_write_add_filter_lrzip.c b/libarchive/archive_write_add_filter_lrzip.c index fe974c93d5..3bd5bc6425 100644 --- a/libarchive/archive_write_add_filter_lrzip.c +++ b/libarchive/archive_write_add_filter_lrzip.c @@ -39,7 +39,7 @@ #include "archive_string.h" #include "archive_write_private.h" -struct write_lrzip { +struct lrzip { struct archive_write_program_data *pdata; int compression_level; enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression; @@ -52,71 +52,83 @@ static int archive_write_lrzip_write(struct archive_write_filter *, const void *, size_t); static int archive_write_lrzip_close(struct archive_write_filter *); static int archive_write_lrzip_free(struct archive_write_filter *); +static void free_data(struct lrzip *); int -archive_write_add_filter_lrzip(struct archive *_a) +archive_write_add_filter_lrzip(struct archive *a) { - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct write_lrzip *data; + struct archive_write_filter *f; + struct lrzip *lrzip; - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - data->pdata = __archive_write_program_allocate("lrzip"); - if (data->pdata == NULL) { - free(data); - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } + lrzip = calloc(1, sizeof(*lrzip)); + if (lrzip == NULL) + goto memerr; + lrzip->pdata = __archive_write_program_allocate("lrzip"); + if (lrzip->pdata == NULL) + goto memerr; + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; f->name = "lrzip"; f->code = ARCHIVE_FILTER_LRZIP; - f->data = data; - f->open = archive_write_lrzip_open; + f->data = lrzip; f->options = archive_write_lrzip_options; + f->open = archive_write_lrzip_open; f->write = archive_write_lrzip_write; f->close = archive_write_lrzip_close; f->free = archive_write_lrzip_free; /* Note: This filter always uses an external program, so we * return "warn" to inform of the fact. */ - archive_set_error(_a, ARCHIVE_ERRNO_MISC, + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external lrzip program for lrzip compression"); return (ARCHIVE_WARN); +memerr: + free_data(lrzip); + archive_set_error(a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int archive_write_lrzip_options(struct archive_write_filter *f, const char *key, const char *value) { - struct write_lrzip *data = (struct write_lrzip *)f->data; + struct lrzip *lrzip = f->data; if (strcmp(key, "compression") == 0) { - if (value == NULL) - return (ARCHIVE_WARN); + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression option requires an argument"); + return (ARCHIVE_FAILED); + } else if (strcmp(value, "bzip2") == 0) - data->compression = bzip2; + lrzip->compression = bzip2; else if (strcmp(value, "gzip") == 0) - data->compression = gzip; + lrzip->compression = gzip; else if (strcmp(value, "lzo") == 0) - data->compression = lzo; + lrzip->compression = lzo; else if (strcmp(value, "none") == 0) - data->compression = none; + lrzip->compression = none; else if (strcmp(value, "zpaq") == 0) - data->compression = zpaq; - else - return (ARCHIVE_WARN); + lrzip->compression = zpaq; + else { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression invalid"); + return (ARCHIVE_FAILED); + } return (ARCHIVE_OK); } else if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '1' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } + lrzip->compression_level = value[0] - '0'; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options @@ -128,7 +140,7 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key, static int archive_write_lrzip_open(struct archive_write_filter *f) { - struct write_lrzip *data = (struct write_lrzip *)f->data; + struct lrzip *lrzip = f->data; struct archive_string as; int r; @@ -136,7 +148,7 @@ archive_write_lrzip_open(struct archive_write_filter *f) archive_strcpy(&as, "lrzip -q"); /* Specify compression type. */ - switch (data->compression) { + switch (lrzip->compression) { case lzma:/* default compression */ break; case bzip2: @@ -157,12 +169,12 @@ archive_write_lrzip_open(struct archive_write_filter *f) } /* Specify compression level. */ - if (data->compression_level > 0) { + if (lrzip->compression_level > 0) { archive_strcat(&as, " -L "); - archive_strappend_char(&as, '0' + data->compression_level); + archive_strappend_char(&as, '0' + lrzip->compression_level); } - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, lrzip->pdata, as.s); archive_string_free(&as); return (r); } @@ -171,25 +183,32 @@ static int archive_write_lrzip_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct write_lrzip *data = (struct write_lrzip *)f->data; + struct lrzip *lrzip = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, lrzip->pdata, buff, length); } static int archive_write_lrzip_close(struct archive_write_filter *f) { - struct write_lrzip *data = (struct write_lrzip *)f->data; + struct lrzip *lrzip = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, lrzip->pdata); } static int archive_write_lrzip_free(struct archive_write_filter *f) { - struct write_lrzip *data = (struct write_lrzip *)f->data; - - __archive_write_program_free(data->pdata); - free(data); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } + +static void +free_data(struct lrzip *lrzip) +{ + if (lrzip != NULL) { + __archive_write_program_free(lrzip->pdata); + free(lrzip); + } +} diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c index 24061a1695..8b3b575fae 100644 --- a/libarchive/archive_write_add_filter_lz4.c +++ b/libarchive/archive_write_add_filter_lz4.c @@ -50,7 +50,7 @@ #define LZ4_MAGICNUMBER 0x184d2204 -struct private_data { +struct lz4 { int compression_level; unsigned header_written:1; unsigned version_number:1; @@ -61,7 +61,6 @@ struct private_data { 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; @@ -86,66 +85,81 @@ 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); +static void free_data(struct lz4 *); /* - * Add a lz4 compression filter to this write handle. + * Add an lz4 compression filter to this write handle. */ int -archive_write_add_filter_lz4(struct archive *_a) +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; + struct archive_write_filter *f; + struct lz4 *lz4; + int r; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, 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); - } - + lz4 = calloc(1, sizeof(*lz4)); + if (lz4 == NULL) + goto memerr; /* * 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"; + lz4->version_number = 0x01; + lz4->block_independence = 1; + lz4->block_checksum = 0; + lz4->stream_size = 0; + lz4->stream_checksum = 1; + lz4->preset_dictionary = 0; + lz4->block_maximum_size = 7; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 - return (ARCHIVE_OK); + lz4->compression_level = 1; + + r = 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, + lz4->pdata = __archive_write_program_allocate("lz4"); + if (lz4->pdata == NULL) + goto memerr; + lz4->compression_level = 0; + + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external lz4 program"); - return (ARCHIVE_WARN); + r = ARCHIVE_WARN; #endif + + /* + * Setup a filter setting. + */ + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = "lz4"; + f->code = ARCHIVE_FILTER_LZ4; + f->data = lz4; + f->options = archive_filter_lz4_options; + f->open = archive_filter_lz4_open; + f->write = archive_filter_lz4_write; + f->close = archive_filter_lz4_close; + f->free = archive_filter_lz4_free; + + return (r); +memerr: + free_data(lz4); + archive_set_error(a, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); +} + +static int +archive_filter_lz4_free(struct archive_write_filter *f) +{ + free_data(f->data); + f->data = NULL; + return (ARCHIVE_OK); } /* @@ -155,13 +169,16 @@ 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; + struct lz4 *lz4 = 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); + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } #ifndef HAVE_LZ4HC_H if(val >= 3) @@ -171,26 +188,29 @@ archive_filter_lz4_options(struct archive_write_filter *f, return (ARCHIVE_FATAL); } #endif - data->compression_level = val; + lz4->compression_level = val; return (ARCHIVE_OK); } if (strcmp(key, "stream-checksum") == 0) { - data->stream_checksum = value != NULL; + lz4->stream_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-checksum") == 0) { - data->block_checksum = value != NULL; + lz4->block_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-size") == 0) { if (value == NULL || !(value[0] >= '4' && value[0] <= '7') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->block_maximum_size = value[0] - '0'; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "block-size invalid"); + return (ARCHIVE_FAILED); + } + lz4->block_maximum_size = value[0] - '0'; return (ARCHIVE_OK); } if (strcmp(key, "block-dependence") == 0) { - data->block_independence = value == NULL; + lz4->block_independence = value == NULL; return (ARCHIVE_OK); } @@ -220,24 +240,24 @@ static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *, static int archive_filter_lz4_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; size_t required_size; - static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, + static const size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; - if (data->block_maximum_size < 4) - data->block_size = bkmap[0]; + if (lz4->block_maximum_size < 4) + lz4->block_size = bkmap[0]; else - data->block_size = bkmap[data->block_maximum_size - 4]; + lz4->block_size = bkmap[lz4->block_maximum_size - 4]; - required_size = 4 + 15 + 4 + data->block_size + 4 + 4; - if (data->out_buffer_size < required_size) { + required_size = 4 + 15 + 4 + lz4->block_size + 4 + 4; + if (lz4->out_buffer_size < required_size) { size_t bs = required_size, bpb; - free(data->out_buffer); + free(lz4->out_buffer); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of - * the of bytes per block for performance. */ + * the bytes per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) bs = bpb; @@ -246,34 +266,32 @@ archive_filter_lz4_open(struct archive_write_filter *f) bs -= bs % bpb; } } - data->out_block_size = bs; + lz4->out_block_size = bs; bs += required_size; - data->out_buffer = malloc(bs); - data->out = data->out_buffer; - data->out_buffer_size = bs; + lz4->out_buffer = malloc(bs); + lz4->out = lz4->out_buffer; + lz4->out_buffer_size = bs; } - pre_block_size = (data->block_independence)? 0: 64 * 1024; - if (data->in_buffer_size < data->block_size + pre_block_size) { - free(data->in_buffer_allocated); - data->in_buffer_size = data->block_size; - data->in_buffer_allocated = - malloc(data->in_buffer_size + pre_block_size); - data->in_buffer = data->in_buffer_allocated + pre_block_size; - if (!data->block_independence && data->compression_level >= 3) - data->in_buffer = data->in_buffer_allocated; - data->in = data->in_buffer; - data->in_buffer_size = data->block_size; + pre_block_size = (lz4->block_independence)? 0: 64 * 1024; + if (lz4->in_buffer_size < lz4->block_size + pre_block_size) { + free(lz4->in_buffer_allocated); + lz4->in_buffer_size = lz4->block_size; + lz4->in_buffer_allocated = + malloc(lz4->in_buffer_size + pre_block_size); + lz4->in_buffer = lz4->in_buffer_allocated + pre_block_size; + if (!lz4->block_independence && lz4->compression_level >= 3) + lz4->in_buffer = lz4->in_buffer_allocated; + lz4->in = lz4->in_buffer; + lz4->in_buffer_size = lz4->block_size; } - if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { + if (lz4->out_buffer == NULL || lz4->in_buffer_allocated == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } - f->write = archive_filter_lz4_write; - return (ARCHIVE_OK); } @@ -286,23 +304,20 @@ static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; int ret = ARCHIVE_OK; const char *p; size_t remaining; ssize_t size; /* If we haven't written a stream descriptor, we have to do it first. */ - if (!data->header_written) { + if (!lz4->header_written) { ret = lz4_write_stream_descriptor(f); if (ret != ARCHIVE_OK) return (ret); - data->header_written = 1; + lz4->header_written = 1; } - /* Update statistics */ - data->total_in += length; - p = (const char *)buff; remaining = length; while (remaining) { @@ -311,14 +326,14 @@ archive_filter_lz4_write(struct archive_write_filter *f, size = lz4_write_one_block(f, p, remaining); if (size < ARCHIVE_OK) return (ARCHIVE_FATAL); - l = data->out - data->out_buffer; - if (l >= data->out_block_size) { + l = lz4->out - lz4->out_buffer; + if (l >= lz4->out_block_size) { ret = __archive_write_filter(f->next_filter, - data->out_buffer, data->out_block_size); - l -= data->out_block_size; - memcpy(data->out_buffer, - data->out_buffer + data->out_block_size, l); - data->out = data->out_buffer + l; + lz4->out_buffer, lz4->out_block_size); + l -= lz4->out_block_size; + memcpy(lz4->out_buffer, + lz4->out_buffer + lz4->out_block_size, l); + lz4->out = lz4->out_buffer + l; if (ret < ARCHIVE_WARN) break; } @@ -335,7 +350,7 @@ archive_filter_lz4_write(struct archive_write_filter *f, static int archive_filter_lz4_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; int ret; /* Finish compression cycle. */ @@ -346,75 +361,48 @@ archive_filter_lz4_close(struct archive_write_filter *f) */ /* Write End Of Stream. */ - memset(data->out, 0, 4); data->out += 4; + memset(lz4->out, 0, 4); lz4->out += 4; /* Write Stream checksum if needed. */ - if (data->stream_checksum) { + if (lz4->stream_checksum) { unsigned int checksum; checksum = __archive_xxhash.XXH32_digest( - data->xxh32_state); - data->xxh32_state = NULL; - archive_le32enc(data->out, checksum); - data->out += 4; + lz4->xxh32_state); + lz4->xxh32_state = NULL; + archive_le32enc(lz4->out, checksum); + lz4->out += 4; } ret = __archive_write_filter(f->next_filter, - data->out_buffer, data->out - data->out_buffer); + lz4->out_buffer, lz4->out - lz4->out_buffer); } return ret; } -static int -archive_filter_lz4_free(struct archive_write_filter *f) -{ - struct private_data *data = (struct private_data *)f->data; - - if (data->lz4_stream != NULL) { -#ifdef HAVE_LZ4HC_H - if (data->compression_level >= 3) -#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 - LZ4_freeStreamHC(data->lz4_stream); -#else - LZ4_freeHC(data->lz4_stream); -#endif - else -#endif -#if LZ4_VERSION_MINOR >= 3 - LZ4_freeStream(data->lz4_stream); -#else - LZ4_free(data->lz4_stream); -#endif - } - free(data->out_buffer); - free(data->in_buffer_allocated); - free(data->xxh32_state); - free(data); - f->data = NULL; - return (ARCHIVE_OK); -} - static int lz4_write_stream_descriptor(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; uint8_t *sd; - sd = (uint8_t *)data->out; + sd = (uint8_t *)lz4->out; /* Write Magic Number. */ archive_le32enc(&sd[0], LZ4_MAGICNUMBER); /* FLG */ - sd[4] = (data->version_number << 6) - | (data->block_independence << 5) - | (data->block_checksum << 4) - | (data->stream_size << 3) - | (data->stream_checksum << 2) - | (data->preset_dictionary << 0); + sd[4] = (lz4->version_number << 6) + | (lz4->block_independence << 5) + | (lz4->block_checksum << 4) + | (lz4->stream_size << 3) + | (lz4->stream_checksum << 2) + | (lz4->preset_dictionary << 0); /* BD */ - sd[5] = (data->block_maximum_size << 4); + sd[5] = (lz4->block_maximum_size << 4); sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; - data->out += 7; - if (data->stream_checksum) - data->xxh32_state = __archive_xxhash.XXH32_init(0); - else - data->xxh32_state = NULL; + lz4->out += 7; + if (lz4->stream_checksum) { + lz4->xxh32_state = __archive_xxhash.XXH32_init(0); + if (lz4->xxh32_state == NULL) + return (ARCHIVE_FATAL); + } else + lz4->xxh32_state = NULL; return (ARCHIVE_OK); } @@ -422,36 +410,36 @@ static ssize_t lz4_write_one_block(struct archive_write_filter *f, const char *p, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; ssize_t r; if (p == NULL) { /* Compress remaining uncompressed data. */ - if (data->in_buffer == data->in) + if (lz4->in_buffer == lz4->in) return 0; else { - size_t l = data->in - data->in_buffer; - r = drive_compressor(f, data->in_buffer, l); + size_t l = lz4->in - lz4->in_buffer; + r = drive_compressor(f, lz4->in_buffer, l); if (r == ARCHIVE_OK) r = (ssize_t)l; } - } else if ((data->block_independence || data->compression_level < 3) && - data->in_buffer == data->in && length >= data->block_size) { - r = drive_compressor(f, p, data->block_size); + } else if ((lz4->block_independence || lz4->compression_level < 3) && + lz4->in_buffer == lz4->in && length >= lz4->block_size) { + r = drive_compressor(f, p, lz4->block_size); if (r == ARCHIVE_OK) - r = (ssize_t)data->block_size; + r = (ssize_t)lz4->block_size; } else { - size_t remaining_size = data->in_buffer_size - - (data->in - data->in_buffer); + size_t remaining_size = lz4->in_buffer_size - + (lz4->in - lz4->in_buffer); size_t l = (remaining_size > length)? length: remaining_size; - memcpy(data->in, p, l); - data->in += l; + memcpy(lz4->in, p, l); + lz4->in += l; if (l == remaining_size) { - r = drive_compressor(f, data->in_buffer, - data->block_size); + r = drive_compressor(f, lz4->in_buffer, + lz4->block_size); if (r == ARCHIVE_OK) r = (ssize_t)l; - data->in = data->in_buffer; + lz4->in = lz4->in_buffer; } else r = (ssize_t)l; } @@ -470,12 +458,12 @@ lz4_write_one_block(struct archive_write_filter *f, const char *p, static int drive_compressor(struct archive_write_filter *f, const char *p, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; - if (data->stream_checksum) - __archive_xxhash.XXH32_update(data->xxh32_state, + if (lz4->stream_checksum) + __archive_xxhash.XXH32_update(lz4->xxh32_state, p, (int)length); - if (data->block_independence) + if (lz4->block_independence) return drive_compressor_independence(f, p, length); else return drive_compressor_dependence(f, p, length); @@ -485,48 +473,48 @@ static int drive_compressor_independence(struct archive_write_filter *f, const char *p, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; unsigned int outsize; #ifdef HAVE_LZ4HC_H - if (data->compression_level >= 3) + if (lz4->compression_level >= 3) #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 - outsize = LZ4_compress_HC(p, data->out + 4, - (int)length, (int)data->block_size, - data->compression_level); + outsize = LZ4_compress_HC(p, lz4->out + 4, + (int)length, (int)lz4->block_size, + lz4->compression_level); #else - outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, - (int)length, (int)data->block_size, - data->compression_level); + outsize = LZ4_compressHC2_limitedOutput(p, lz4->out + 4, + (int)length, (int)lz4->block_size, + lz4->compression_level); #endif else #endif #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 - outsize = LZ4_compress_default(p, data->out + 4, - (int)length, (int)data->block_size); + outsize = LZ4_compress_default(p, lz4->out + 4, + (int)length, (int)lz4->block_size); #else - outsize = LZ4_compress_limitedOutput(p, data->out + 4, - (int)length, (int)data->block_size); + outsize = LZ4_compress_limitedOutput(p, lz4->out + 4, + (int)length, (int)lz4->block_size); #endif if (outsize) { /* The buffer is compressed. */ - archive_le32enc(data->out, outsize); - data->out += 4; + archive_le32enc(lz4->out, outsize); + lz4->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); - data->out += 4; - memcpy(data->out, p, length); + archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000)); + lz4->out += 4; + memcpy(lz4->out, p, length); outsize = (uint32_t)length; } - data->out += outsize; - if (data->block_checksum) { + lz4->out += outsize; + if (lz4->block_checksum) { unsigned int checksum = - __archive_xxhash.XXH32(data->out - outsize, outsize, 0); - archive_le32enc(data->out, checksum); - data->out += 4; + __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0); + archive_le32enc(lz4->out, checksum); + lz4->out += 4; } return (ARCHIVE_OK); } @@ -535,21 +523,21 @@ static int drive_compressor_dependence(struct archive_write_filter *f, const char *p, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; int outsize; #define DICT_SIZE (64 * 1024) #ifdef HAVE_LZ4HC_H - if (data->compression_level >= 3) { - if (data->lz4_stream == NULL) { + if (lz4->compression_level >= 3) { + if (lz4->lz4_stream == NULL) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 - data->lz4_stream = LZ4_createStreamHC(); - LZ4_resetStreamHC(data->lz4_stream, data->compression_level); + lz4->lz4_stream = LZ4_createStreamHC(); + LZ4_resetStreamHC(lz4->lz4_stream, lz4->compression_level); #else - data->lz4_stream = - LZ4_createHC(data->in_buffer_allocated); + lz4->lz4_stream = + LZ4_createHC(lz4->in_buffer_allocated); #endif - if (data->lz4_stream == NULL) { + if (lz4->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); @@ -557,23 +545,23 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p, } } else - LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); + LZ4_loadDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_HC_continue( - data->lz4_stream, p, data->out + 4, (int)length, - (int)data->block_size); + lz4->lz4_stream, p, lz4->out + 4, (int)length, + (int)lz4->block_size); #else outsize = LZ4_compressHC2_limitedOutput_continue( - data->lz4_stream, p, data->out + 4, (int)length, - (int)data->block_size, data->compression_level); + lz4->lz4_stream, p, lz4->out + 4, (int)length, + (int)lz4->block_size, lz4->compression_level); #endif } else #endif { - if (data->lz4_stream == NULL) { - data->lz4_stream = LZ4_createStream(); - if (data->lz4_stream == NULL) { + if (lz4->lz4_stream == NULL) { + lz4->lz4_stream = LZ4_createStream(); + if (lz4->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); @@ -581,64 +569,91 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p, } } else - LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); + LZ4_loadDict(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_fast_continue( - data->lz4_stream, p, data->out + 4, (int)length, - (int)data->block_size, 1); + lz4->lz4_stream, p, lz4->out + 4, (int)length, + (int)lz4->block_size, 1); #else outsize = LZ4_compress_limitedOutput_continue( - data->lz4_stream, p, data->out + 4, (int)length, - (int)data->block_size); + lz4->lz4_stream, p, lz4->out + 4, (int)length, + (int)lz4->block_size); #endif } if (outsize) { /* The buffer is compressed. */ - archive_le32enc(data->out, outsize); - data->out += 4; + archive_le32enc(lz4->out, outsize); + lz4->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ - archive_le32enc(data->out, (uint32_t)(length | 0x80000000)); - data->out += 4; - memcpy(data->out, p, length); + archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000)); + lz4->out += 4; + memcpy(lz4->out, p, length); outsize = (uint32_t)length; } - data->out += outsize; - if (data->block_checksum) { + lz4->out += outsize; + if (lz4->block_checksum) { unsigned int checksum = - __archive_xxhash.XXH32(data->out - outsize, outsize, 0); - archive_le32enc(data->out, checksum); - data->out += 4; + __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0); + archive_le32enc(lz4->out, checksum); + lz4->out += 4; } - if (length == data->block_size) { + if (length == lz4->block_size) { #ifdef HAVE_LZ4HC_H - if (data->compression_level >= 3) { + if (lz4->compression_level >= 3) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 - LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); + LZ4_saveDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE); #else - LZ4_slideInputBufferHC(data->lz4_stream); + LZ4_slideInputBufferHC(lz4->lz4_stream); #endif - data->in_buffer = data->in_buffer_allocated + DICT_SIZE; + lz4->in_buffer = lz4->in_buffer_allocated + DICT_SIZE; } else #endif - LZ4_saveDict(data->lz4_stream, - data->in_buffer_allocated, DICT_SIZE); + LZ4_saveDict(lz4->lz4_stream, + lz4->in_buffer_allocated, DICT_SIZE); #undef DICT_SIZE } return (ARCHIVE_OK); } +static void +free_data(struct lz4 *lz4) +{ + if (lz4 != NULL) { + if (lz4->lz4_stream != NULL) { +#ifdef HAVE_LZ4HC_H + if (lz4->compression_level >= 3) +#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 + LZ4_freeStreamHC(lz4->lz4_stream); +#else + LZ4_freeHC(lz4->lz4_stream); +#endif + else +#endif +#if LZ4_VERSION_MINOR >= 3 + LZ4_freeStream(lz4->lz4_stream); +#else + LZ4_free(lz4->lz4_stream); +#endif + } + free(lz4->out_buffer); + free(lz4->in_buffer_allocated); + free(lz4->xxh32_state); + free(lz4); + } +} + #else /* HAVE_LIBLZ4 */ static int archive_filter_lz4_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; struct archive_string as; int r; @@ -646,24 +661,22 @@ archive_filter_lz4_open(struct archive_write_filter *f) archive_strcpy(&as, "lz4 -z -q -q"); /* Specify a compression level. */ - if (data->compression_level > 0) { + if (lz4->compression_level > 0) { archive_strcat(&as, " -"); - archive_strappend_char(&as, '0' + data->compression_level); + archive_strappend_char(&as, '0' + lz4->compression_level); } /* Specify a block size. */ archive_strcat(&as, " -B"); - archive_strappend_char(&as, '0' + data->block_maximum_size); + archive_strappend_char(&as, '0' + lz4->block_maximum_size); - if (data->block_checksum) + if (lz4->block_checksum) archive_strcat(&as, " -BX"); - if (data->stream_checksum == 0) + if (lz4->stream_checksum == 0) archive_strcat(&as, " --no-frame-crc"); - if (data->block_independence == 0) + if (lz4->block_independence == 0) archive_strcat(&as, " -BD"); - f->write = archive_filter_lz4_write; - - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, lz4->pdata, as.s); archive_string_free(&as); return (r); } @@ -672,27 +685,26 @@ static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, lz4->pdata, buff, length); } static int archive_filter_lz4_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct lz4 *lz4 = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, lz4->pdata); } -static int -archive_filter_lz4_free(struct archive_write_filter *f) +static void +free_data(struct lz4 *lz4) { - struct private_data *data = (struct private_data *)f->data; - - __archive_write_program_free(data->pdata); - free(data); - return (ARCHIVE_OK); + if (lz4 != NULL) { + __archive_write_program_free(lz4->pdata); + free(lz4); + } } #endif /* HAVE_LIBLZ4 */ diff --git a/libarchive/archive_write_add_filter_lzop.c b/libarchive/archive_write_add_filter_lzop.c index 8580e58844..3ed721e1ea 100644 --- a/libarchive/archive_write_add_filter_lzop.c +++ b/libarchive/archive_write_add_filter_lzop.c @@ -55,7 +55,7 @@ enum lzo_method { METHOD_LZO1X_1_15 = 2, METHOD_LZO1X_999 = 3 }; -struct write_lzop { +struct lzop { int compression_level; #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) unsigned char *uncompressed; @@ -80,6 +80,7 @@ static int archive_write_lzop_write(struct archive_write_filter *, const void *, size_t); static int archive_write_lzop_close(struct archive_write_filter *); static int archive_write_lzop_free(struct archive_write_filter *); +static void free_data(struct lzop *); #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) /* Maximum block size. */ @@ -133,73 +134,72 @@ static const unsigned char header[] = { #endif int -archive_write_add_filter_lzop(struct archive *_a) +archive_write_add_filter_lzop(struct archive *a) { - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct write_lzop *data; + struct archive_write_filter *f; + struct lzop *lzop; + int r; - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - - f->name = "lzop"; - f->code = ARCHIVE_FILTER_LZOP; - f->data = data; - f->open = archive_write_lzop_open; - f->options = archive_write_lzop_options; - f->write = archive_write_lzop_write; - f->close = archive_write_lzop_close; - f->free = archive_write_lzop_free; + lzop = calloc(1, sizeof(*lzop)); + if (lzop == NULL) + goto memerr; #if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) if (lzo_init() != LZO_E_OK) { - free(data); - archive_set_error(_a, ARCHIVE_ERRNO_MISC, + free_data(lzop); + archive_set_error(a, ARCHIVE_ERRNO_MISC, "lzo_init(type check) failed"); return (ARCHIVE_FATAL); } if (lzo_version() < 0x940) { - free(data); - archive_set_error(_a, ARCHIVE_ERRNO_MISC, + free_data(lzop); + archive_set_error(a, ARCHIVE_ERRNO_MISC, "liblzo library is too old(%s < 0.940)", lzo_version_string()); return (ARCHIVE_FATAL); } - data->compression_level = 5; - return (ARCHIVE_OK); + lzop->compression_level = 5; + + r = ARCHIVE_OK; #else - data->pdata = __archive_write_program_allocate("lzop"); - if (data->pdata == NULL) { - free(data); - archive_set_error(_a, ENOMEM, "Can't allocate memory"); - return (ARCHIVE_FATAL); - } - data->compression_level = 0; + lzop->pdata = __archive_write_program_allocate("lzop"); + if (lzop->pdata == NULL) + goto memerr; + lzop->compression_level = 0; + /* Note: We return "warn" to inform of using an external lzop * program. */ - archive_set_error(_a, ARCHIVE_ERRNO_MISC, + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external lzop program for lzop compression"); - return (ARCHIVE_WARN); + r = ARCHIVE_WARN; #endif + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = "lzop"; + f->code = ARCHIVE_FILTER_LZOP; + f->data = lzop; + f->options = archive_write_lzop_options; + f->open = archive_write_lzop_open; + f->write = archive_write_lzop_write; + f->close = archive_write_lzop_close; + f->free = archive_write_lzop_free; + + return (r); +memerr: + free_data(lzop); + archive_set_error(a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int archive_write_lzop_free(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; - -#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) - free(data->uncompressed); - free(data->compressed); - free(data->work_buffer); -#else - __archive_write_program_free(data->pdata); -#endif - free(data); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } @@ -207,13 +207,16 @@ static int archive_write_lzop_options(struct archive_write_filter *f, const char *key, const char *value) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '1' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } + lzop->compression_level = value[0] - '0'; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options @@ -226,58 +229,58 @@ archive_write_lzop_options(struct archive_write_filter *f, const char *key, static int archive_write_lzop_open(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; - switch (data->compression_level) { + switch (lzop->compression_level) { case 1: - data->method = METHOD_LZO1X_1_15; data->level = 1; break; + lzop->method = METHOD_LZO1X_1_15; lzop->level = 1; break; default: case 2: case 3: case 4: case 5: case 6: - data->method = METHOD_LZO1X_1; data->level = 5; break; + lzop->method = METHOD_LZO1X_1; lzop->level = 5; break; case 7: - data->method = METHOD_LZO1X_999; data->level = 7; break; + lzop->method = METHOD_LZO1X_999; lzop->level = 7; break; case 8: - data->method = METHOD_LZO1X_999; data->level = 8; break; + lzop->method = METHOD_LZO1X_999; lzop->level = 8; break; case 9: - data->method = METHOD_LZO1X_999; data->level = 9; break; + lzop->method = METHOD_LZO1X_999; lzop->level = 9; break; } - switch (data->method) { + switch (lzop->method) { case METHOD_LZO1X_1: - data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break; + lzop->work_buffer_size = LZO1X_1_MEM_COMPRESS; break; case METHOD_LZO1X_1_15: - data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break; + lzop->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break; case METHOD_LZO1X_999: - data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break; + lzop->work_buffer_size = LZO1X_999_MEM_COMPRESS; break; } - if (data->work_buffer == NULL) { - data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size); - if (data->work_buffer == NULL) { + if (lzop->work_buffer == NULL) { + lzop->work_buffer = (lzo_voidp)malloc(lzop->work_buffer_size); + if (lzop->work_buffer == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - if (data->compressed == NULL) { - data->compressed_buffer_size = sizeof(header) + + if (lzop->compressed == NULL) { + lzop->compressed_buffer_size = sizeof(header) + BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3; - data->compressed = (unsigned char *) - malloc(data->compressed_buffer_size); - if (data->compressed == NULL) { + lzop->compressed = (unsigned char *) + malloc(lzop->compressed_buffer_size); + if (lzop->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - if (data->uncompressed == NULL) { - data->uncompressed_buffer_size = BLOCK_SIZE; - data->uncompressed = (unsigned char *) - malloc(data->uncompressed_buffer_size); - if (data->uncompressed == NULL) { + if (lzop->uncompressed == NULL) { + lzop->uncompressed_buffer_size = BLOCK_SIZE; + lzop->uncompressed = (unsigned char *) + malloc(lzop->uncompressed_buffer_size); + if (lzop->uncompressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } - data->uncompressed_avail_bytes = BLOCK_SIZE; + lzop->uncompressed_avail_bytes = BLOCK_SIZE; } return (ARCHIVE_OK); } @@ -285,68 +288,68 @@ archive_write_lzop_open(struct archive_write_filter *f) static int make_header(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; int64_t t; uint32_t checksum; - memcpy(data->compressed, header, sizeof(header)); + memcpy(lzop->compressed, header, sizeof(header)); /* Overwrite library version. */ - data->compressed[HEADER_LIBVERSION] = (unsigned char ) + lzop->compressed[HEADER_LIBVERSION] = (unsigned char ) (lzo_version() >> 8) & 0xff; - data->compressed[HEADER_LIBVERSION + 1] = (unsigned char ) + lzop->compressed[HEADER_LIBVERSION + 1] = (unsigned char ) lzo_version() & 0xff; /* Overwrite method and level. */ - data->compressed[HEADER_METHOD] = (unsigned char)data->method; - data->compressed[HEADER_LEVEL] = data->level; + lzop->compressed[HEADER_METHOD] = (unsigned char)lzop->method; + lzop->compressed[HEADER_LEVEL] = lzop->level; /* Overwrite mtime with current time. */ t = (int64_t)time(NULL); - archive_be32enc(&data->compressed[HEADER_MTIME_LOW], + archive_be32enc(&lzop->compressed[HEADER_MTIME_LOW], (uint32_t)(t & 0xffffffff)); - archive_be32enc(&data->compressed[HEADER_MTIME_HIGH], + archive_be32enc(&lzop->compressed[HEADER_MTIME_HIGH], (uint32_t)((t >> 32) & 0xffffffff)); /* Overwrite header checksum with calculated value. */ - checksum = lzo_adler32(1, data->compressed + HEADER_VERSION, + checksum = lzo_adler32(1, lzop->compressed + HEADER_VERSION, (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION)); - archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum); + archive_be32enc(&lzop->compressed[HEADER_H_CHECKSUM], checksum); return (sizeof(header)); } static int drive_compressor(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; unsigned char *p; const int block_info_bytes = 12; int header_bytes, r; lzo_uint usize, csize; uint32_t checksum; - if (!data->header_written) { + if (!lzop->header_written) { header_bytes = make_header(f); - data->header_written = 1; + lzop->header_written = 1; } else header_bytes = 0; - p = data->compressed; + p = lzop->compressed; usize = (lzo_uint) - (data->uncompressed_buffer_size - data->uncompressed_avail_bytes); + (lzop->uncompressed_buffer_size - lzop->uncompressed_avail_bytes); csize = 0; - switch (data->method) { + switch (lzop->method) { default: case METHOD_LZO1X_1: - r = lzo1x_1_compress(data->uncompressed, usize, + r = lzo1x_1_compress(lzop->uncompressed, usize, p + header_bytes + block_info_bytes, &csize, - data->work_buffer); + lzop->work_buffer); break; case METHOD_LZO1X_1_15: - r = lzo1x_1_15_compress(data->uncompressed, usize, + r = lzo1x_1_15_compress(lzop->uncompressed, usize, p + header_bytes + block_info_bytes, &csize, - data->work_buffer); + lzop->work_buffer); break; case METHOD_LZO1X_999: - r = lzo1x_999_compress_level(data->uncompressed, usize, + r = lzo1x_999_compress_level(lzop->uncompressed, usize, p + header_bytes + block_info_bytes, &csize, - data->work_buffer, NULL, 0, 0, data->level); + lzop->work_buffer, NULL, 0, 0, lzop->level); break; } if (r != LZO_E_OK) { @@ -358,13 +361,13 @@ drive_compressor(struct archive_write_filter *f) /* Store uncompressed size. */ archive_be32enc(p + header_bytes, (uint32_t)usize); /* Store the checksum of the uncompressed data. */ - checksum = lzo_adler32(1, data->uncompressed, usize); + checksum = lzo_adler32(1, lzop->uncompressed, usize); archive_be32enc(p + header_bytes + 8, checksum); if (csize < usize) { /* Store compressed size. */ archive_be32enc(p + header_bytes + 4, (uint32_t)csize); - r = __archive_write_filter(f->next_filter, data->compressed, + r = __archive_write_filter(f->next_filter, lzop->compressed, header_bytes + block_info_bytes + csize); } else { /* @@ -372,11 +375,11 @@ drive_compressor(struct archive_write_filter *f) */ /* Store uncompressed size as compressed size. */ archive_be32enc(p + header_bytes + 4, (uint32_t)usize); - r = __archive_write_filter(f->next_filter, data->compressed, + r = __archive_write_filter(f->next_filter, lzop->compressed, header_bytes + block_info_bytes); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); - r = __archive_write_filter(f->next_filter, data->uncompressed, + r = __archive_write_filter(f->next_filter, lzop->uncompressed, usize); } @@ -389,30 +392,30 @@ static int archive_write_lzop_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; const char *p = buff; int r; do { - if (data->uncompressed_avail_bytes > length) { - memcpy(data->uncompressed - + data->uncompressed_buffer_size - - data->uncompressed_avail_bytes, + if (lzop->uncompressed_avail_bytes > length) { + memcpy(lzop->uncompressed + + lzop->uncompressed_buffer_size + - lzop->uncompressed_avail_bytes, p, length); - data->uncompressed_avail_bytes -= length; + lzop->uncompressed_avail_bytes -= length; return (ARCHIVE_OK); } - memcpy(data->uncompressed + data->uncompressed_buffer_size - - data->uncompressed_avail_bytes, - p, data->uncompressed_avail_bytes); - length -= data->uncompressed_avail_bytes; - p += data->uncompressed_avail_bytes; - data->uncompressed_avail_bytes = 0; + memcpy(lzop->uncompressed + lzop->uncompressed_buffer_size + - lzop->uncompressed_avail_bytes, + p, lzop->uncompressed_avail_bytes); + length -= lzop->uncompressed_avail_bytes; + p += lzop->uncompressed_avail_bytes; + lzop->uncompressed_avail_bytes = 0; r = drive_compressor(f); if (r != ARCHIVE_OK) return (r); - data->uncompressed_avail_bytes = BLOCK_SIZE; + lzop->uncompressed_avail_bytes = BLOCK_SIZE; } while (length); return (ARCHIVE_OK); @@ -421,11 +424,11 @@ archive_write_lzop_write(struct archive_write_filter *f, static int archive_write_lzop_close(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; const uint32_t endmark = 0; int r; - if (data->uncompressed_avail_bytes < BLOCK_SIZE) { + if (lzop->uncompressed_avail_bytes < BLOCK_SIZE) { /* Compress and output remaining data. */ r = drive_compressor(f); if (r != ARCHIVE_OK) @@ -436,24 +439,35 @@ archive_write_lzop_close(struct archive_write_filter *f) return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); } +static void +free_data(struct lzop *lzop) +{ + if (lzop != NULL) { + free(lzop->uncompressed); + free(lzop->compressed); + free(lzop->work_buffer); + free(lzop); + } +} + #else static int archive_write_lzop_open(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; struct archive_string as; int r; archive_string_init(&as); archive_strcpy(&as, "lzop"); /* Specify compression level. */ - if (data->compression_level > 0) { + if (lzop->compression_level > 0) { archive_strappend_char(&as, ' '); archive_strappend_char(&as, '-'); - archive_strappend_char(&as, '0' + data->compression_level); + archive_strappend_char(&as, '0' + lzop->compression_level); } - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, lzop->pdata, as.s); archive_string_free(&as); return (r); } @@ -462,16 +476,25 @@ static int archive_write_lzop_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, lzop->pdata, buff, length); } static int archive_write_lzop_close(struct archive_write_filter *f) { - struct write_lzop *data = (struct write_lzop *)f->data; + struct lzop *lzop = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, lzop->pdata); +} + +static void +free_data(struct lzop *lzop) +{ + if (lzop != NULL) { + __archive_write_program_free(lzop->pdata); + free(lzop); + } } #endif diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c index f12db33738..ee0869717e 100644 --- a/libarchive/archive_write_add_filter_program.c +++ b/libarchive/archive_write_add_filter_program.c @@ -70,7 +70,7 @@ struct archive_write_program_data { char *program_name; }; -struct private_data { +struct program { struct archive_write_program_data *pdata; struct archive_string description; char *cmd; @@ -81,51 +81,53 @@ static int archive_compressor_program_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_program_close(struct archive_write_filter *); static int archive_compressor_program_free(struct archive_write_filter *); +static void free_data(struct program *); /* * Add a filter to this write handle that passes all data through an * external program. */ int -archive_write_add_filter_program(struct archive *_a, const char *cmd) +archive_write_add_filter_program(struct archive *a, const char *cmd) { - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct private_data *data; + struct archive_write_filter *f; + struct program *program; static const char prefix[] = "Program: "; - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); - f->data = calloc(1, sizeof(*data)); - if (f->data == NULL) + program = calloc(1, sizeof(*program)); + if (program == NULL) goto memerr; - data = (struct private_data *)f->data; - - data->cmd = strdup(cmd); - if (data->cmd == NULL) + program->cmd = strdup(cmd); + if (program->cmd == NULL) goto memerr; - - data->pdata = __archive_write_program_allocate(cmd); - if (data->pdata == NULL) + program->pdata = __archive_write_program_allocate(cmd); + if (program->pdata == NULL) goto memerr; - /* Make up a description string. */ - if (archive_string_ensure(&data->description, + if (archive_string_ensure(&program->description, strlen(prefix) + strlen(cmd) + 1) == NULL) goto memerr; - archive_strcpy(&data->description, prefix); - archive_strcat(&data->description, cmd); + archive_strcpy(&program->description, prefix); + archive_strcat(&program->description, cmd); - f->name = data->description.s; + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = program->description.s; f->code = ARCHIVE_FILTER_PROGRAM; + f->data = program; f->open = archive_compressor_program_open; f->write = archive_compressor_program_write; f->close = archive_compressor_program_close; f->free = archive_compressor_program_free; + return (ARCHIVE_OK); memerr: - archive_compressor_program_free(f); - archive_set_error(_a, ENOMEM, + free_data(program); + archive_set_error(a, ENOMEM, "Can't allocate memory for filter program"); return (ARCHIVE_FATAL); } @@ -133,40 +135,33 @@ memerr: static int archive_compressor_program_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct program *program = f->data; - return __archive_write_program_open(f, data->pdata, data->cmd); + return __archive_write_program_open(f, program->pdata, program->cmd); } static int archive_compressor_program_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct program *program = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, program->pdata, buff, length); } static int archive_compressor_program_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct program *program = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, program->pdata); } static int archive_compressor_program_free(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; - - if (data) { - free(data->cmd); - archive_string_free(&data->description); - __archive_write_program_free(data->pdata); - free(data); - f->data = NULL; - } + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } @@ -184,6 +179,10 @@ __archive_write_program_allocate(const char *program) data->child_stdin = -1; data->child_stdout = -1; data->program_name = strdup(program); + if (data->program_name == NULL) { + free(data); + return (NULL); + } return (data); } @@ -387,3 +386,13 @@ cleanup: return ret; } +static void +free_data(struct program *program) +{ + if (program) { + free(program->cmd); + archive_string_free(&program->description); + __archive_write_program_free(program->pdata); + free(program); + } +} diff --git a/libarchive/archive_write_add_filter_uuencode.c b/libarchive/archive_write_add_filter_uuencode.c index 99c7a2cb7a..83ec2ebffb 100644 --- a/libarchive/archive_write_add_filter_uuencode.c +++ b/libarchive/archive_write_add_filter_uuencode.c @@ -28,6 +28,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -36,13 +39,14 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" #define LBYTES 45 -struct private_uuencode { +struct uuencode { int mode; struct archive_string name; struct archive_string encoded_buff; @@ -60,39 +64,44 @@ static int archive_filter_uuencode_close(struct archive_write_filter *); static int archive_filter_uuencode_free(struct archive_write_filter *); static void uu_encode(struct archive_string *, const unsigned char *, size_t); static int64_t atol8(const char *, size_t); +static void free_data(struct uuencode *); /* * Add a compress filter to this write handle. */ int -archive_write_add_filter_uuencode(struct archive *_a) +archive_write_add_filter_uuencode(struct archive *a) { - struct archive_write *a = (struct archive_write *)_a; - struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct private_uuencode *state; + struct archive_write_filter *f; + struct uuencode *uuencode; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_uuencode"); - state = calloc(1, sizeof(*state)); - if (state == NULL) { - archive_set_error(f->archive, ENOMEM, - "Can't allocate data for uuencode filter"); - return (ARCHIVE_FATAL); - } - archive_strcpy(&state->name, "-"); - state->mode = 0644; + uuencode = calloc(1, sizeof(*uuencode)); + if (uuencode == NULL) + goto memerr; + archive_strcpy(&uuencode->name, "-"); + uuencode->mode = 0644; - f->data = state; + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; f->name = "uuencode"; f->code = ARCHIVE_FILTER_UU; - f->open = archive_filter_uuencode_open; + f->data = uuencode; f->options = archive_filter_uuencode_options; + f->open = archive_filter_uuencode_open; f->write = archive_filter_uuencode_write; f->close = archive_filter_uuencode_close; f->free = archive_filter_uuencode_free; return (ARCHIVE_OK); +memerr: + free_data(uuencode); + archive_set_error(a, ENOMEM, + "Can't allocate data for uuencode filter"); + return (ARCHIVE_FATAL); } /* @@ -102,15 +111,23 @@ static int archive_filter_uuencode_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_uuencode *state = (struct private_uuencode *)f->data; + struct uuencode *uuencode = f->data; if (strcmp(key, "mode") == 0) { + int64_t val; + if (value == NULL) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "mode option requires octal digits"); return (ARCHIVE_FAILED); } - state->mode = (int)atol8(value, strlen(value)) & 0777; + val = atol8(value, strlen(value)); + if (val < 0 || val > INT_MAX) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "invalid mode option"); + return (ARCHIVE_FAILED); + } + uuencode->mode = (int)val & 0777; return (ARCHIVE_OK); } else if (strcmp(key, "name") == 0) { if (value == NULL) { @@ -118,7 +135,7 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key, "name option requires a string"); return (ARCHIVE_FAILED); } - archive_strcpy(&state->name, value); + archive_strcpy(&uuencode->name, value); return (ARCHIVE_OK); } @@ -134,7 +151,7 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key, static int archive_filter_uuencode_open(struct archive_write_filter *f) { - struct private_uuencode *state = (struct private_uuencode *)f->data; + struct uuencode *uuencode = f->data; size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -147,18 +164,17 @@ archive_filter_uuencode_open(struct archive_write_filter *f) bs -= bs % bpb; } - state->bs = bs; - if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { + uuencode->bs = bs; + if (archive_string_ensure(&uuencode->encoded_buff, bs + 512) == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for uuencode buffer"); return (ARCHIVE_FATAL); } - archive_string_sprintf(&state->encoded_buff, "begin %o %s\n", - (unsigned int)state->mode, state->name.s); + archive_string_sprintf(&uuencode->encoded_buff, "begin %o %s\n", + (unsigned int)uuencode->mode, uuencode->name.s); - f->data = state; - return (0); + return (ARCHIVE_OK); } static void @@ -204,39 +220,39 @@ static int archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_uuencode *state = (struct private_uuencode *)f->data; + struct uuencode *uuencode = f->data; const unsigned char *p = buff; int ret = ARCHIVE_OK; if (length == 0) return (ret); - if (state->hold_len) { - while (state->hold_len < LBYTES && length > 0) { - state->hold[state->hold_len++] = *p++; + if (uuencode->hold_len) { + while (uuencode->hold_len < LBYTES && length > 0) { + uuencode->hold[uuencode->hold_len++] = *p++; length--; } - if (state->hold_len < LBYTES) + if (uuencode->hold_len < LBYTES) return (ret); - uu_encode(&state->encoded_buff, state->hold, LBYTES); - state->hold_len = 0; + uu_encode(&uuencode->encoded_buff, uuencode->hold, LBYTES); + uuencode->hold_len = 0; } for (; length >= LBYTES; length -= LBYTES, p += LBYTES) - uu_encode(&state->encoded_buff, p, LBYTES); + uu_encode(&uuencode->encoded_buff, p, LBYTES); /* Save remaining bytes. */ if (length > 0) { - memcpy(state->hold, p, length); - state->hold_len = length; + memcpy(uuencode->hold, p, length); + uuencode->hold_len = length; } - while (archive_strlen(&state->encoded_buff) >= state->bs) { + while (archive_strlen(&uuencode->encoded_buff) >= uuencode->bs) { ret = __archive_write_filter(f->next_filter, - state->encoded_buff.s, state->bs); - memmove(state->encoded_buff.s, - state->encoded_buff.s + state->bs, - state->encoded_buff.length - state->bs); - state->encoded_buff.length -= state->bs; + uuencode->encoded_buff.s, uuencode->bs); + memmove(uuencode->encoded_buff.s, + uuencode->encoded_buff.s + uuencode->bs, + uuencode->encoded_buff.length - uuencode->bs); + uuencode->encoded_buff.length -= uuencode->bs; } return (ret); @@ -249,26 +265,24 @@ archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff, static int archive_filter_uuencode_close(struct archive_write_filter *f) { - struct private_uuencode *state = (struct private_uuencode *)f->data; + struct uuencode *uuencode = f->data; /* Flush remaining bytes. */ - if (state->hold_len != 0) - uu_encode(&state->encoded_buff, state->hold, state->hold_len); - archive_string_sprintf(&state->encoded_buff, "`\nend\n"); + if (uuencode->hold_len != 0) + uu_encode(&uuencode->encoded_buff, uuencode->hold, + uuencode->hold_len); + archive_string_sprintf(&uuencode->encoded_buff, "`\nend\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); return __archive_write_filter(f->next_filter, - state->encoded_buff.s, archive_strlen(&state->encoded_buff)); + uuencode->encoded_buff.s, archive_strlen(&uuencode->encoded_buff)); } static int archive_filter_uuencode_free(struct archive_write_filter *f) { - struct private_uuencode *state = (struct private_uuencode *)f->data; - - archive_string_free(&state->name); - archive_string_free(&state->encoded_buff); - free(state); + free_data(f->data); + f->data = NULL; return (ARCHIVE_OK); } @@ -277,17 +291,30 @@ atol8(const char *p, size_t char_cnt) { int64_t l; int digit; - + + if (char_cnt == 0) + return (-1); + l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else - break; + return (-1); p++; - l <<= 3; - l |= digit; + if (archive_ckd_mul_i64(&l, l, 8) || + archive_ckd_add_i64(&l, l, digit)) + return (-1); } return (l); } +static void +free_data(struct uuencode *uuencode) +{ + if (uuencode != NULL) { + archive_string_free(&uuencode->name); + archive_string_free(&uuencode->encoded_buff); + free(uuencode); + } +} diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c index 098f0c9557..599ee413b0 100644 --- a/libarchive/archive_write_add_filter_xz.c +++ b/libarchive/archive_write_add_filter_xz.c @@ -29,6 +29,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -96,7 +99,7 @@ archive_write_add_filter_lzip(struct archive *a) #else /* Don't compile this if we don't have liblzma. */ -struct private_data { +struct xz { int compression_level; uint32_t threads; lzma_stream stream; @@ -118,7 +121,8 @@ static int archive_compressor_xz_write(struct archive_write_filter *, static int archive_compressor_xz_close(struct archive_write_filter *); static int archive_compressor_xz_free(struct archive_write_filter *); static int drive_compressor(struct archive_write_filter *, - struct private_data *, int finishing); + struct xz *, int finishing); +static void free_data(struct xz *); struct option_value { uint32_t dict_size; @@ -139,85 +143,72 @@ static const struct option_value option_values[] = { }; static int -common_setup(struct archive_write_filter *f) +common_setup(struct archive *a, const char *name, int code) { - struct private_data *data; - struct archive_write *a = (struct archive_write *)f->archive; - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - f->data = data; - data->compression_level = LZMA_PRESET_DEFAULT; - data->threads = 1; - f->open = &archive_compressor_xz_open; + struct xz *xz; + struct archive_write_filter *f; + + xz = calloc(1, sizeof(*xz)); + if (xz == NULL) + goto memerr; + xz->compression_level = LZMA_PRESET_DEFAULT; + xz->threads = 1; + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = name; + f->code = code; + f->data = xz; + f->options = archive_compressor_xz_options; + f->open = archive_compressor_xz_open; + f->write = archive_compressor_xz_write; f->close = archive_compressor_xz_close; f->free = archive_compressor_xz_free; - f->options = &archive_compressor_xz_options; + return (ARCHIVE_OK); +memerr: + free_data(xz); + archive_set_error(a, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); } /* * Add an xz compression filter to this write handle. */ int -archive_write_add_filter_xz(struct archive *_a) +archive_write_add_filter_xz(struct archive *a) { - struct archive_write_filter *f; - int r; - - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_xz"); - f = __archive_write_allocate_filter(_a); - r = common_setup(f); - if (r == ARCHIVE_OK) { - f->code = ARCHIVE_FILTER_XZ; - f->name = "xz"; - } - return (r); + + return common_setup(a, "xz", ARCHIVE_FILTER_XZ); } /* LZMA is handled identically, we just need a different compression * code set. (The liblzma setup looks at the code to determine * the one place that XZ and LZMA require different handling.) */ int -archive_write_add_filter_lzma(struct archive *_a) +archive_write_add_filter_lzma(struct archive *a) { - struct archive_write_filter *f; - int r; - - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma"); - f = __archive_write_allocate_filter(_a); - r = common_setup(f); - if (r == ARCHIVE_OK) { - f->code = ARCHIVE_FILTER_LZMA; - f->name = "lzma"; - } - return (r); + + return common_setup(a, "lzma", ARCHIVE_FILTER_LZMA); } int -archive_write_add_filter_lzip(struct archive *_a) +archive_write_add_filter_lzip(struct archive *a) { - struct archive_write_filter *f; - int r; - - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip"); - f = __archive_write_allocate_filter(_a); - r = common_setup(f); - if (r == ARCHIVE_OK) { - f->code = ARCHIVE_FILTER_LZIP; - f->name = "lzip"; - } - return (r); + + return common_setup(a, "lzip", ARCHIVE_FILTER_LZIP); } static int archive_compressor_xz_init_stream(struct archive_write_filter *f, - struct private_data *data) + struct xz *xz) { static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT; int ret; @@ -225,27 +216,27 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f, lzma_mt mt_options; #endif - data->stream = lzma_stream_init_data; - data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + xz->stream = lzma_stream_init_data; + xz->stream.next_out = xz->compressed; + xz->stream.avail_out = xz->compressed_buffer_size; if (f->code == ARCHIVE_FILTER_XZ) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT - if (data->threads != 1) { + if (xz->threads != 1) { memset(&mt_options, 0, sizeof(mt_options)); - mt_options.threads = data->threads; + mt_options.threads = xz->threads; mt_options.timeout = 300; - mt_options.filters = data->lzmafilters; + mt_options.filters = xz->lzmafilters; mt_options.check = LZMA_CHECK_CRC64; - ret = lzma_stream_encoder_mt(&(data->stream), + ret = lzma_stream_encoder_mt(&(xz->stream), &mt_options); } else #endif - ret = lzma_stream_encoder(&(data->stream), - data->lzmafilters, LZMA_CHECK_CRC64); + ret = lzma_stream_encoder(&(xz->stream), + xz->lzmafilters, LZMA_CHECK_CRC64); } else if (f->code == ARCHIVE_FILTER_LZMA) { - ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt); + ret = lzma_alone_encoder(&(xz->stream), &xz->lzma_opt); } else { /* ARCHIVE_FILTER_LZIP */ - int dict_size = data->lzma_opt.dict_size; + int dict_size = xz->lzma_opt.dict_size; int ds, log2dic, wedges; /* Calculate a coded dictionary size */ @@ -267,18 +258,18 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f, wedges = 0; ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f); - data->crc32 = 0; + xz->crc32 = 0; /* Make a header */ - data->compressed[0] = 0x4C; - data->compressed[1] = 0x5A; - data->compressed[2] = 0x49; - data->compressed[3] = 0x50; - data->compressed[4] = 1;/* Version */ - data->compressed[5] = (unsigned char)ds; - data->stream.next_out += 6; - data->stream.avail_out -= 6; + xz->compressed[0] = 0x4C; + xz->compressed[1] = 0x5A; + xz->compressed[2] = 0x49; + xz->compressed[3] = 0x50; + xz->compressed[4] = 1;/* Version */ + xz->compressed[5] = (unsigned char)ds; + xz->stream.next_out += 6; + xz->stream.avail_out -= 6; - ret = lzma_raw_encoder(&(data->stream), data->lzmafilters); + ret = lzma_raw_encoder(&(xz->stream), xz->lzmafilters); } if (ret == LZMA_OK) return (ARCHIVE_OK); @@ -304,10 +295,10 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f, static int archive_compressor_xz_open(struct archive_write_filter *f) { - struct private_data *data = f->data; + struct xz *xz = f->data; int ret; - if (data->compressed == NULL) { + if (xz->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the bytes @@ -318,49 +309,46 @@ archive_compressor_xz_open(struct archive_write_filter *f) else if (bpb != 0) bs -= bs % bpb; } - data->compressed_buffer_size = bs; - data->compressed = malloc(data->compressed_buffer_size); - if (data->compressed == NULL) { + xz->compressed_buffer_size = bs; + xz->compressed = malloc(xz->compressed_buffer_size); + if (xz->compressed == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - f->write = archive_compressor_xz_write; - /* Initialize compression library. */ if (f->code == ARCHIVE_FILTER_LZIP) { const struct option_value *val = - &option_values[data->compression_level]; + &option_values[xz->compression_level]; - data->lzma_opt.dict_size = val->dict_size; - data->lzma_opt.preset_dict = NULL; - data->lzma_opt.preset_dict_size = 0; - data->lzma_opt.lc = LZMA_LC_DEFAULT; - data->lzma_opt.lp = LZMA_LP_DEFAULT; - data->lzma_opt.pb = LZMA_PB_DEFAULT; - data->lzma_opt.mode = - data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL; - data->lzma_opt.nice_len = val->nice_len; - data->lzma_opt.mf = val->mf; - data->lzma_opt.depth = 0; - data->lzmafilters[0].id = LZMA_FILTER_LZMA1; - data->lzmafilters[0].options = &data->lzma_opt; - data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ + xz->lzma_opt.dict_size = val->dict_size; + xz->lzma_opt.preset_dict = NULL; + xz->lzma_opt.preset_dict_size = 0; + xz->lzma_opt.lc = LZMA_LC_DEFAULT; + xz->lzma_opt.lp = LZMA_LP_DEFAULT; + xz->lzma_opt.pb = LZMA_PB_DEFAULT; + xz->lzma_opt.mode = + xz->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL; + xz->lzma_opt.nice_len = val->nice_len; + xz->lzma_opt.mf = val->mf; + xz->lzma_opt.depth = 0; + xz->lzmafilters[0].id = LZMA_FILTER_LZMA1; + xz->lzmafilters[0].options = &xz->lzma_opt; + xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ } else { - if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) { + if (lzma_lzma_preset(&xz->lzma_opt, xz->compression_level)) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing compression library"); } - data->lzmafilters[0].id = LZMA_FILTER_LZMA2; - data->lzmafilters[0].options = &data->lzma_opt; - data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ + xz->lzmafilters[0].id = LZMA_FILTER_LZMA2; + xz->lzmafilters[0].options = &xz->lzma_opt; + xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ } - ret = archive_compressor_xz_init_stream(f, data); + ret = archive_compressor_xz_init_stream(f, xz); if (ret == LZMA_OK) { - f->data = data; - return (0); + return (ARCHIVE_OK); } return (ARCHIVE_FATAL); } @@ -372,32 +360,42 @@ static int archive_compressor_xz_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_data *data = (struct private_data *)f->data; + struct xz *xz = f->data; if (strcmp(key, "compression-level") == 0) { if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; - if (data->compression_level > 9) - data->compression_level = 9; + value[1] != '\0') { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); + } + xz->compression_level = value[0] - '0'; + if (xz->compression_level > 9) + xz->compression_level = 9; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { char *endptr; + unsigned long val; - if (value == NULL) - return (ARCHIVE_WARN); - errno = 0; - data->threads = (int)strtoul(value, &endptr, 10); - if (errno != 0 || *endptr != '\0') { - data->threads = 1; - return (ARCHIVE_WARN); + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "threads option requires an argument"); + return (ARCHIVE_FAILED); } - if (data->threads == 0) { + errno = 0; + val = strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) { + xz->threads = 1; + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "threads invalid"); + return (ARCHIVE_FAILED); + } + xz->threads = (int)val; + if (xz->threads == 0) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT - data->threads = lzma_cputhreads(); + xz->threads = lzma_cputhreads(); #else - data->threads = 1; + xz->threads = 1; #endif } return (ARCHIVE_OK); @@ -416,18 +414,18 @@ static int archive_compressor_xz_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct xz *xz = f->data; int ret; /* Update statistics */ - data->total_in += length; + xz->total_in += length; if (f->code == ARCHIVE_FILTER_LZIP) - data->crc32 = lzma_crc32(buff, length, data->crc32); + xz->crc32 = lzma_crc32(buff, length, xz->crc32); /* Compress input data to output buffer */ - data->stream.next_in = buff; - data->stream.avail_in = length; - if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) + xz->stream.next_in = buff; + xz->stream.avail_in = length; + if ((ret = drive_compressor(f, xz, 0)) != ARCHIVE_OK) return (ret); return (ARCHIVE_OK); @@ -440,34 +438,32 @@ archive_compressor_xz_write(struct archive_write_filter *f, static int archive_compressor_xz_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct xz *xz = f->data; int ret; - ret = drive_compressor(f, data, 1); + ret = drive_compressor(f, xz, 1); if (ret == ARCHIVE_OK) { - data->total_out += - data->compressed_buffer_size - data->stream.avail_out; + xz->total_out += + xz->compressed_buffer_size - xz->stream.avail_out; ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size - data->stream.avail_out); + xz->compressed, + xz->compressed_buffer_size - xz->stream.avail_out); if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) { - archive_le32enc(data->compressed, data->crc32); - archive_le64enc(data->compressed+4, data->total_in); - archive_le64enc(data->compressed+12, data->total_out + 20); + archive_le32enc(xz->compressed, xz->crc32); + archive_le64enc(xz->compressed+4, xz->total_in); + archive_le64enc(xz->compressed+12, xz->total_out + 20); ret = __archive_write_filter(f->next_filter, - data->compressed, 20); + xz->compressed, 20); } } - lzma_end(&(data->stream)); + lzma_end(&(xz->stream)); return ret; } static int archive_compressor_xz_free(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; - free(data->compressed); - free(data); + free_data(f->data); f->data = NULL; return (ARCHIVE_OK); } @@ -481,34 +477,34 @@ archive_compressor_xz_free(struct archive_write_filter *f) */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int finishing) + struct xz *xz, int finishing) { int ret; for (;;) { - if (data->stream.avail_out == 0) { - data->total_out += data->compressed_buffer_size; + if (xz->stream.avail_out == 0) { + xz->total_out += xz->compressed_buffer_size; ret = __archive_write_filter(f->next_filter, - data->compressed, - data->compressed_buffer_size); + xz->compressed, + xz->compressed_buffer_size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); - data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + xz->stream.next_out = xz->compressed; + xz->stream.avail_out = xz->compressed_buffer_size; } /* If there's nothing to do, we're done. */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && xz->stream.avail_in == 0) return (ARCHIVE_OK); - ret = lzma_code(&(data->stream), + ret = lzma_code(&(xz->stream), finishing ? LZMA_FINISH : LZMA_RUN ); switch (ret) { case LZMA_OK: /* In non-finishing case, check if compressor * consumed everything */ - if (!finishing && data->stream.avail_in == 0) + if (!finishing && xz->stream.avail_in == 0) return (ARCHIVE_OK); /* In finishing case, this return always means * there's more work */ @@ -524,7 +520,7 @@ drive_compressor(struct archive_write_filter *f, archive_set_error(f->archive, ENOMEM, "lzma compression error: " "%ju MiB would have been needed", - (uintmax_t)((lzma_memusage(&(data->stream)) + (uintmax_t)((lzma_memusage(&(xz->stream)) + 1024 * 1024 -1) / (1024 * 1024))); return (ARCHIVE_FATAL); @@ -539,4 +535,13 @@ drive_compressor(struct archive_write_filter *f, } } +static void +free_data(struct xz *xz) +{ + if (xz != NULL) { + free(xz->compressed); + free(xz); + } +} + #endif /* HAVE_LZMA_H */ diff --git a/libarchive/archive_write_add_filter_zstd.c b/libarchive/archive_write_add_filter_zstd.c index d4752c2471..826227fd47 100644 --- a/libarchive/archive_write_add_filter_zstd.c +++ b/libarchive/archive_write_add_filter_zstd.c @@ -55,7 +55,7 @@ /* Don't compile this if we don't have zstd.h */ -struct private_data { +struct zstd { int compression_level; int threads; int long_distance; @@ -73,7 +73,6 @@ struct private_data { size_t cur_frame; size_t cur_frame_in; size_t cur_frame_out; - size_t total_in; ZSTD_CStream *cstream; ZSTD_outBuffer out; #else @@ -104,79 +103,77 @@ static int archive_compressor_zstd_close(struct archive_write_filter *); static int archive_compressor_zstd_free(struct archive_write_filter *); #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream static int drive_compressor(struct archive_write_filter *, - struct private_data *, int, const void *, size_t); + struct zstd *, int, const void *, size_t); #endif +static void free_data(struct zstd *); /* * Add a zstd compression filter to this write handle. */ int -archive_write_add_filter_zstd(struct archive *_a) +archive_write_add_filter_zstd(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, + struct archive_write_filter *f; + struct zstd *zstd; + int r; + + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - f->data = data; - f->open = &archive_compressor_zstd_open; - f->options = &archive_compressor_zstd_options; - f->flush = &archive_compressor_zstd_flush; - f->close = &archive_compressor_zstd_close; - f->free = &archive_compressor_zstd_free; - f->code = ARCHIVE_FILTER_ZSTD; - f->name = "zstd"; - data->compression_level = CLEVEL_DEFAULT; - data->threads = 0; - data->long_distance = 0; + zstd = calloc(1, sizeof(*zstd)); + if (zstd == NULL) + goto memerr; + zstd->compression_level = CLEVEL_DEFAULT; + zstd->threads = 0; + zstd->long_distance = 0; #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream - data->frame_per_file = 0; - data->min_frame_in = 0; - data->max_frame_in = SIZE_MAX; - data->min_frame_out = 0; - data->max_frame_out = SIZE_MAX; - data->cur_frame_in = 0; - data->cur_frame_out = 0; - data->cstream = ZSTD_createCStream(); - if (data->cstream == NULL) { - free(data); - archive_set_error(&a->archive, ENOMEM, - "Failed to allocate zstd compressor object"); - return (ARCHIVE_FATAL); - } + zstd->frame_per_file = 0; + zstd->min_frame_in = 0; + zstd->max_frame_in = SIZE_MAX; + zstd->min_frame_out = 0; + zstd->max_frame_out = SIZE_MAX; + zstd->cur_frame_in = 0; + zstd->cur_frame_out = 0; + zstd->cstream = ZSTD_createCStream(); + if (zstd->cstream == NULL) + goto memerr; - return (ARCHIVE_OK); + r = ARCHIVE_OK; #else - data->pdata = __archive_write_program_allocate("zstd"); - if (data->pdata == NULL) { - free(data); - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + zstd->pdata = __archive_write_program_allocate("zstd"); + if (zstd->pdata == NULL) + goto memerr; + + archive_set_error(a, ARCHIVE_ERRNO_MISC, "Using external zstd program"); - return (ARCHIVE_WARN); + r = ARCHIVE_WARN; #endif + + f = __archive_write_allocate_filter(a); + if (f == NULL) + goto memerr; + f->name = "zstd"; + f->code = ARCHIVE_FILTER_ZSTD; + f->data = zstd; + f->options = archive_compressor_zstd_options; + f->open = archive_compressor_zstd_open; + f->write = archive_compressor_zstd_write; + f->flush = archive_compressor_zstd_flush; + f->close = archive_compressor_zstd_close; + f->free = archive_compressor_zstd_free; + + return (r); +memerr: + free_data(zstd); + archive_set_error(a, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); } static int archive_compressor_zstd_free(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; -#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream - ZSTD_freeCStream(data->cstream); - free(data->out.dst); -#else - __archive_write_program_free(data->pdata); -#endif - free(data); + free_data(f->data); f->data = NULL; return (ARCHIVE_OK); } @@ -240,12 +237,14 @@ static int archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, const char *value) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; if (strcmp(key, "compression-level") == 0) { intmax_t level; if (string_to_number(value, &level) != ARCHIVE_OK) { - return (ARCHIVE_WARN); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level invalid"); + return (ARCHIVE_FAILED); } /* If we don't have the library, hard-code the max level */ int minimum = CLEVEL_MIN; @@ -263,14 +262,18 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, } #endif if (level < minimum || level > maximum) { - return (ARCHIVE_WARN); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "compression-level out of range"); + return (ARCHIVE_FAILED); } - data->compression_level = (int)level; + zstd->compression_level = (int)level; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { intmax_t threads; if (string_to_number(value, &threads) != ARCHIVE_OK) { - return (ARCHIVE_WARN); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "threads invalid"); + return (ARCHIVE_FAILED); } #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) @@ -286,36 +289,46 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, } #endif if (threads < 0 || threads > INT_MAX) { - return (ARCHIVE_WARN); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "threads out of rnage"); + return (ARCHIVE_FAILED); } - data->threads = (int)threads; + zstd->threads = (int)threads; return (ARCHIVE_OK); #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream } else if (strcmp(key, "frame-per-file") == 0) { - data->frame_per_file = 1; + zstd->frame_per_file = 1; return (ARCHIVE_OK); } else if (strcmp(key, "min-frame-in") == 0) { - if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) { - return (ARCHIVE_WARN); + if (string_to_size(value, &zstd->min_frame_in) != ARCHIVE_OK) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "min-frame-in invalid"); + return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } else if (strcmp(key, "min-frame-out") == 0 || strcmp(key, "min-frame-size") == 0) { - if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) { - return (ARCHIVE_WARN); + if (string_to_size(value, &zstd->min_frame_out) != ARCHIVE_OK) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "min-frame-out invalid"); + return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } else if (strcmp(key, "max-frame-in") == 0 || strcmp(key, "max-frame-size") == 0) { - if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK || - data->max_frame_in < 1024) { - return (ARCHIVE_WARN); + if (string_to_size(value, &zstd->max_frame_in) != ARCHIVE_OK || + zstd->max_frame_in < 1024) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "max-frame-size invalid"); + return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } else if (strcmp(key, "max-frame-out") == 0) { - if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK || - data->max_frame_out < 1024) { - return (ARCHIVE_WARN); + if (string_to_size(value, &zstd->max_frame_out) != ARCHIVE_OK || + zstd->max_frame_out < 1024) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "max-frame-out invalid"); + return (ARCHIVE_FAILED); } return (ARCHIVE_OK); #endif @@ -323,24 +336,32 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, else if (strcmp(key, "long") == 0) { intmax_t long_distance; if (string_to_number(value, &long_distance) != ARCHIVE_OK) { - return (ARCHIVE_WARN); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "long invalid"); + return (ARCHIVE_FAILED); } #if HAVE_ZSTD_H && HAVE_ZSTD_compressStream && ZSTD_VERSION_NUMBER >= MINVER_LONG ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog); if (ZSTD_isError(bounds.error)) { int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31)); - if (((int)long_distance) < 10 || (int)long_distance > max_distance) - return (ARCHIVE_WARN); + if (((int)long_distance) < 10 || (int)long_distance > max_distance) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "long out of range"); + return (ARCHIVE_FAILED); + } } else { - if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound) - return (ARCHIVE_WARN); + if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "long out of range"); + return (ARCHIVE_FAILED); + } } #else int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31)); if (((int)long_distance) < 10 || (int)long_distance > max_distance) - return (ARCHIVE_WARN); + return (ARCHIVE_FAILED); #endif - data->long_distance = (int)long_distance; + zstd->long_distance = (int)long_distance; return (ARCHIVE_OK); } @@ -357,9 +378,9 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, static int archive_compressor_zstd_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - if (data->out.dst == NULL) { + if (zstd->out.dst == NULL) { size_t bs = ZSTD_CStreamOutSize(), bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of @@ -370,31 +391,29 @@ archive_compressor_zstd_open(struct archive_write_filter *f) else if (bpb != 0) bs -= bs % bpb; } - data->out.size = bs; - data->out.pos = 0; - data->out.dst = malloc(data->out.size); - if (data->out.dst == NULL) { + zstd->out.size = bs; + zstd->out.pos = 0; + zstd->out.dst = malloc(zstd->out.size); + if (zstd->out.dst == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } } - f->write = archive_compressor_zstd_write; - - if (ZSTD_isError(ZSTD_initCStream(data->cstream, - data->compression_level))) { + if (ZSTD_isError(ZSTD_initCStream(zstd->cstream, + zstd->compression_level))) { archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing zstd compressor object"); return (ARCHIVE_FATAL); } - ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads); + ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_nbWorkers, zstd->threads); - ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_checksumFlag, 1); + ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_checksumFlag, 1); #if ZSTD_VERSION_NUMBER >= MINVER_LONG - ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance); + ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_windowLog, zstd->long_distance); #endif return (ARCHIVE_OK); @@ -407,9 +426,9 @@ static int archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - return (drive_compressor(f, data, 0, buff, length)); + return (drive_compressor(f, zstd, 0, buff, length)); } /* @@ -418,15 +437,15 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, static int archive_compressor_zstd_flush(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - if (data->frame_per_file && data->state == running) { - if (data->cur_frame_in > data->min_frame_in && - data->cur_frame_out > data->min_frame_out) { - data->state = finishing; + if (zstd->frame_per_file && zstd->state == running) { + if (zstd->cur_frame_in > zstd->min_frame_in && + zstd->cur_frame_out > zstd->min_frame_out) { + zstd->state = finishing; } } - return (drive_compressor(f, data, 1, NULL, 0)); + return (drive_compressor(f, zstd, 1, NULL, 0)); } /* @@ -435,11 +454,11 @@ archive_compressor_zstd_flush(struct archive_write_filter *f) static int archive_compressor_zstd_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - if (data->state == running) - data->state = finishing; - return (drive_compressor(f, data, 1, NULL, 0)); + if (zstd->state == running) + zstd->state = finishing; + return (drive_compressor(f, zstd, 1, NULL, 0)); } /* @@ -448,7 +467,7 @@ archive_compressor_zstd_close(struct archive_write_filter *f) */ static int drive_compressor(struct archive_write_filter *f, - struct private_data *data, int flush, const void *src, size_t length) + struct zstd *zstd, int flush, const void *src, size_t length) { ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 }; size_t ipos, opos, zstdret = 0; @@ -456,47 +475,46 @@ drive_compressor(struct archive_write_filter *f, for (;;) { ipos = in.pos; - opos = data->out.pos; - switch (data->state) { + opos = zstd->out.pos; + switch (zstd->state) { case running: if (in.pos == in.size) return (ARCHIVE_OK); - zstdret = ZSTD_compressStream(data->cstream, - &data->out, &in); + zstdret = ZSTD_compressStream(zstd->cstream, + &zstd->out, &in); if (ZSTD_isError(zstdret)) goto zstd_fatal; break; case finishing: - zstdret = ZSTD_endStream(data->cstream, &data->out); + zstdret = ZSTD_endStream(zstd->cstream, &zstd->out); if (ZSTD_isError(zstdret)) goto zstd_fatal; if (zstdret == 0) - data->state = resetting; + zstd->state = resetting; break; case resetting: - ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only); - data->cur_frame++; - data->cur_frame_in = 0; - data->cur_frame_out = 0; - data->state = running; + ZSTD_CCtx_reset(zstd->cstream, ZSTD_reset_session_only); + zstd->cur_frame++; + zstd->cur_frame_in = 0; + zstd->cur_frame_out = 0; + zstd->state = running; break; } - data->total_in += in.pos - ipos; - data->cur_frame_in += in.pos - ipos; - data->cur_frame_out += data->out.pos - opos; - if (data->state == running) { - if (data->cur_frame_in >= data->max_frame_in || - data->cur_frame_out >= data->max_frame_out) { - data->state = finishing; + zstd->cur_frame_in += in.pos - ipos; + zstd->cur_frame_out += zstd->out.pos - opos; + if (zstd->state == running) { + if (zstd->cur_frame_in >= zstd->max_frame_in || + zstd->cur_frame_out >= zstd->max_frame_out) { + zstd->state = finishing; } } - if (data->out.pos == data->out.size || - (flush && data->out.pos > 0)) { + if (zstd->out.pos == zstd->out.size || + (flush && zstd->out.pos > 0)) { ret = __archive_write_filter(f->next_filter, - data->out.dst, data->out.pos); + zstd->out.dst, zstd->out.pos); if (ret != ARCHIVE_OK) goto fatal; - data->out.pos = 0; + zstd->out.pos = 0; } } zstd_fatal: @@ -507,12 +525,22 @@ fatal: return (ARCHIVE_FATAL); } +static void +free_data(struct zstd *zstd) +{ + if (zstd != NULL) { + ZSTD_freeCStream(zstd->cstream); + free(zstd->out.dst); + free(zstd); + } +} + #else /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */ static int archive_compressor_zstd_open(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; struct archive_string as; int r; @@ -520,26 +548,25 @@ archive_compressor_zstd_open(struct archive_write_filter *f) /* --no-check matches library default */ archive_strcpy(&as, "zstd --no-check"); - if (data->compression_level < CLEVEL_STD_MIN) { - archive_string_sprintf(&as, " --fast=%d", -data->compression_level); + if (zstd->compression_level < CLEVEL_STD_MIN) { + archive_string_sprintf(&as, " --fast=%d", -zstd->compression_level); } else { - archive_string_sprintf(&as, " -%d", data->compression_level); + archive_string_sprintf(&as, " -%d", zstd->compression_level); } - if (data->compression_level > CLEVEL_STD_MAX) { + if (zstd->compression_level > CLEVEL_STD_MAX) { archive_strcat(&as, " --ultra"); } - if (data->threads != 0) { - archive_string_sprintf(&as, " --threads=%d", data->threads); + if (zstd->threads != 0) { + archive_string_sprintf(&as, " --threads=%d", zstd->threads); } - if (data->long_distance != 0) { - archive_string_sprintf(&as, " --long=%d", data->long_distance); + if (zstd->long_distance != 0) { + archive_string_sprintf(&as, " --long=%d", zstd->long_distance); } - f->write = archive_compressor_zstd_write; - r = __archive_write_program_open(f, data->pdata, as.s); + r = __archive_write_program_open(f, zstd->pdata, as.s); archive_string_free(&as); return (r); } @@ -548,9 +575,9 @@ static int archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, size_t length) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - return __archive_write_program_write(f, data->pdata, buff, length); + return __archive_write_program_write(f, zstd->pdata, buff, length); } static int @@ -564,9 +591,18 @@ archive_compressor_zstd_flush(struct archive_write_filter *f) static int archive_compressor_zstd_close(struct archive_write_filter *f) { - struct private_data *data = (struct private_data *)f->data; + struct zstd *zstd = f->data; - return __archive_write_program_close(f, data->pdata); + return __archive_write_program_close(f, zstd->pdata); +} + +static void +free_data(struct zstd *zstd) +{ + if (zstd != NULL) { + __archive_write_program_free(zstd->pdata); + free(zstd); + } } #endif /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */ diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 7e4dba3c43..a27e73188b 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -300,7 +300,7 @@ struct archive_write_disk { uint32_t compressed_rsrc_position; uint32_t compressed_rsrc_position_v; /* Buffer for uncompressed data. */ - char *uncompressed_buffer; + unsigned char *uncompressed_buffer; size_t block_remaining_bytes; size_t file_remaining_bytes; #ifdef HAVE_ZLIB_H @@ -308,6 +308,10 @@ struct archive_write_disk { int stream_valid; int decmpfs_compression_level; #endif +#if !(ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX ||\ + ARCHIVE_XATTR_FREEBSD) + int warning_done; +#endif }; /* @@ -596,11 +600,12 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); a->entry = archive_entry_clone(entry); + if (a->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } a->fd = -1; a->fd_offset = 0; a->offset = 0; @@ -971,7 +976,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1249,7 +1254,7 @@ static int hfs_decompress(struct archive_write_disk *a) { uint32_t *block_info; - unsigned int block_count; + uint32_t block_count; uint32_t data_pos, data_size; ssize_t r; ssize_t bytes_written, bytes_to_write; @@ -1272,10 +1277,10 @@ hfs_decompress(struct archive_write_disk *a) bytes_to_write = data_size -1; b = a->compressed_buffer + 1; } else { - uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; + size_t dest_len = MAX_DECMPFS_BLOCK_SIZE; int zr; - zr = uncompress((Bytef *)a->uncompressed_buffer, + zr = uncompress(a->uncompressed_buffer, &dest_len, a->compressed_buffer, data_size); if (zr != Z_OK) { archive_set_error(&a->archive, @@ -1284,7 +1289,7 @@ hfs_decompress(struct archive_write_disk *a) return (ARCHIVE_WARN); } bytes_to_write = dest_len; - b = (unsigned char *)a->uncompressed_buffer; + b = a->uncompressed_buffer; } do { bytes_written = write(a->fd, b, bytes_to_write); @@ -1467,8 +1472,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, if (a->decmpfs_block_count == (unsigned)-1) { void *new_block; - size_t new_size; - unsigned int block_count; + size_t block_count, new_size; if (a->decmpfs_header_p == NULL) { new_block = malloc(MAX_DECMPFS_XATTR_SIZE @@ -1488,13 +1492,20 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], a->filesize); - /* Calculate a block count of the file. */ - block_count = - (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / - MAX_DECMPFS_BLOCK_SIZE; + /* + * Calculate block count for the file. + */ + block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE - 1) / + MAX_DECMPFS_BLOCK_SIZE; + if (block_count > (SIZE_MAX - RSRC_H_SIZE - 4 - RSRC_F_SIZE) / + (sizeof(uint32_t) * 2) || block_count > UINT32_MAX) { + archive_set_error(&a->archive, EFBIG, "File too big"); + return (ARCHIVE_FATAL); + } + /* * Allocate buffer for resource fork. - * Set up related pointers; + * Set up related pointers. */ new_size = RSRC_H_SIZE + /* header */ @@ -1535,7 +1546,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, a->decmpfs_block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); /* Set the block count to the resource fork. */ - archive_le32enc(a->decmpfs_block_info++, block_count); + archive_le32enc(a->decmpfs_block_info++, (uint32_t)block_count); /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = @@ -1595,7 +1606,7 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1674,7 +1685,7 @@ _archive_write_disk_data_block(struct archive *_a, if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EOVERFLOW, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); @@ -2119,7 +2130,7 @@ restore_entry(struct archive_write_disk *a) if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, - "Hard-link target '%s' does not exist.", + "Hard-link target '%s' does not exist", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } @@ -2196,7 +2207,7 @@ restore_entry(struct archive_write_disk *a) if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } @@ -2364,7 +2375,7 @@ create_filesystem_object(struct archive_write_disk *a) #endif if (r != 0) r = errno; - else if ((st.st_mode & AE_IFMT) == AE_IFREG) { + else if (S_ISREG(st.st_mode)) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); __archive_ensure_cloexec_flag(a->fd); @@ -2996,7 +3007,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, */ /* if (!S_ISLNK(path)) { - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, -1, "Removing symlink ", path); } */ @@ -3012,7 +3023,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, #endif if (r != 0) { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, EIO, "Cannot remove intervening " "symlink ", path); res = ARCHIVE_FAILED; @@ -3072,7 +3083,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, head = tail + 1; } else { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, ELOOP, "Cannot extract through " "symlink ", path); res = ARCHIVE_FAILED; @@ -3080,7 +3091,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, } } else { tail[0] = c; - fsobj_error(a_eno, a_estr, 0, + fsobj_error(a_eno, a_estr, ELOOP, "Cannot extract through symlink ", path); res = ARCHIVE_FAILED; break; @@ -4019,7 +4030,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, - "Can't set file flags on symlink."); + "Can't set file flags on symlink"); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) @@ -4339,7 +4350,7 @@ copy_metadata(struct archive_write_disk *a, const char *metadata, * with at least a writing mode(O_RDWR or O_WRONLY). it * makes the data fork uncompressed. */ - dffd = open(datafork, 0); + dffd = open(datafork, O_SYMLINK); if (dffd == -1) { archive_set_error(&a->archive, errno, "Failed to open the data fork for metadata"); @@ -4441,14 +4452,16 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname) */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); - if ( #ifdef HAVE_LSTAT - lstat(datafork.s, &st) == -1 || + if (lstat(datafork.s, &st) == -1) + goto skip_appledouble; #else - la_stat(datafork.s, &st) == -1 || + if (la_stat(datafork.s, &st) == -1) + goto skip_appledouble; #endif - (((st.st_mode & AE_IFMT) != AE_IFREG) && - ((st.st_mode & AE_IFMT) != AE_IFDIR))) + if (!S_ISREG(st.st_mode) && + !S_ISDIR(st.st_mode) && + !S_ISLNK(st.st_mode)) goto skip_appledouble; /* @@ -4578,7 +4591,7 @@ set_xattrs(struct archive_write_disk *a) } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended " - "attributes on this file system."); + "attributes on this file system"); } archive_string_free(&errlist); @@ -4680,7 +4693,7 @@ set_xattrs(struct archive_write_disk *a) } else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore extended " - "attributes on this file system."); + "attributes on this file system"); } archive_string_free(&errlist); @@ -4693,12 +4706,10 @@ set_xattrs(struct archive_write_disk *a) static int set_xattrs(struct archive_write_disk *a) { - static int warning_done = 0; - /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ - if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { - warning_done = 1; + if (archive_entry_xattr_count(a->entry) != 0 && !a->warning_done) { + a->warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); diff --git a/libarchive/archive_write_disk_set_standard_lookup.c b/libarchive/archive_write_disk_set_standard_lookup.c index 964169898e..5705a978f9 100644 --- a/libarchive/archive_write_disk_set_standard_lookup.c +++ b/libarchive/archive_write_disk_set_standard_lookup.c @@ -45,6 +45,7 @@ #endif #include "archive.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_write_disk_private.h" @@ -134,7 +135,8 @@ lookup_gid(void *private_data, const char *gname, int64_t gid) break; if (r != ERANGE) break; - bufsize *= 2; + if (archive_ckd_mul_size(&bufsize, bufsize, 2)) + break; free(allocated); allocated = malloc(bufsize); if (allocated == NULL) @@ -203,7 +205,8 @@ lookup_uid(void *private_data, const char *uname, int64_t uid) break; if (r != ERANGE) break; - bufsize *= 2; + if (archive_ckd_mul_size(&bufsize, bufsize, 2)) + break; free(allocated); allocated = malloc(bufsize); if (allocated == NULL) diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index d4f6f1820e..a78c0d2d34 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -70,21 +70,6 @@ #define IO_REPARSE_TAG_SYMLINK 0xA000000CL #endif -static BOOL SetFilePointerEx_perso(HANDLE hFile, - LARGE_INTEGER liDistanceToMove, - PLARGE_INTEGER lpNewFilePointer, - DWORD dwMoveMethod) -{ - LARGE_INTEGER li; - li.QuadPart = liDistanceToMove.QuadPart; - li.LowPart = SetFilePointer( - hFile, li.LowPart, &li.HighPart, dwMoveMethod); - if(lpNewFilePointer) { - lpNewFilePointer->QuadPart = li.QuadPart; - } - return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR; -} - struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; @@ -209,6 +194,8 @@ struct archive_write_disk { static int disk_unlink(const wchar_t *); static int disk_rmdir(const wchar_t *); +static int check_symlinks_by_path(struct archive_write_disk *, + wchar_t *, size_t *, int); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, @@ -259,8 +246,8 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *, ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow) static int -file_information(struct archive_write_disk *a, wchar_t *path, - BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat) +file_information(wchar_t *path, BY_HANDLE_FILE_INFORMATION *st, + mode_t *mode, int sim_lstat) { HANDLE h; int r; @@ -297,10 +284,10 @@ file_information(struct archive_write_disk *a, wchar_t *path, ZeroMemory(&createExParams, sizeof(createExParams)); createExParams.dwSize = sizeof(createExParams); createExParams.dwFileFlags = flag; - h = CreateFile2(a->name, 0, 0, + h = CreateFile2(path, 0, 0, OPEN_EXISTING, &createExParams); #else - h = CreateFileW(a->name, 0, 0, NULL, + h = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, flag, NULL); #endif if (h == INVALID_HANDLE_VALUE && @@ -595,45 +582,11 @@ la_mktemp(struct archive_write_disk *a) return (fd); } -#if _WIN32_WINNT < _WIN32_WINNT_VISTA -static void * -la_GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary(TEXT("kernel32.dll")); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} -#endif - static int la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) { - static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); BOOL ret; - -#if _WIN32_WINNT < _WIN32_WINNT_XP - static int set; -/* CreateHardLinkW is available since XP and always loaded */ - if (!set) { - set = 1; - f = la_GetFunctionKernel32("CreateHardLinkW"); - } -#else - f = CreateHardLinkW; -#endif - if (!f) { - errno = ENOTSUP; - return (0); - } - ret = (*f)(linkname, target, NULL); + ret = CreateHardLinkW(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove * the "\\?\" prefix. */ @@ -652,7 +605,7 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) target += 4; } #undef IS_UNC - ret = (*f)(linkname, target, NULL); + ret = CreateHardLinkW(linkname, target, NULL); } return (ret); } @@ -666,30 +619,19 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) static int la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, int linktype) { - static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); + BOOL ret = 0; +#if _WIN32_WINNT < _WIN32_WINNT_VISTA ||\ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + (void)linkname; /* UNUSED */ + (void)target; /* UNUSED */ + (void)linktype; /* UNUSED */ +#else wchar_t *ttarget, *p; size_t len; DWORD attrs = 0; DWORD flags = 0; DWORD newflags = 0; - BOOL ret = 0; - -#if _WIN32_WINNT < _WIN32_WINNT_VISTA -/* CreateSymbolicLinkW is available since Vista and always loaded */ - static int set; - if (!set) { - set = 1; - f = la_GetFunctionKernel32("CreateSymbolicLinkW"); - } -#else -# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - f = CreateSymbolicLinkW; -# else - f = NULL; -# endif -#endif - if (!f) - return (0); + DWORD lasterr = 0; len = wcslen(target); if (len == 0) { @@ -751,15 +693,20 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, disk_unlink(linkname); } - ret = (*f)(linkname, ttarget, newflags); + ret = CreateSymbolicLinkW(linkname, ttarget, newflags); /* * Prior to Windows 10 calling CreateSymbolicLinkW() will fail - * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set + * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set; however, + * trying this fallback if we fail due to a bad path will replace + * ENOENT with EPERM and confuse later error recovery efforts. */ if (!ret) { - ret = (*f)(linkname, ttarget, flags); + lasterr = GetLastError(); + if (lasterr != ERROR_PATH_NOT_FOUND) + ret = CreateSymbolicLinkW(linkname, ttarget, flags); } free(ttarget); +#endif return (ret); } @@ -773,7 +720,7 @@ la_ftruncate(HANDLE handle, int64_t length) return (-1); } distance.QuadPart = length; - if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) { + if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN)) { la_dosmaperr(GetLastError()); return (-1); } @@ -801,7 +748,7 @@ lazy_stat(struct archive_write_disk *a) * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ - if (file_information(a, a->name, &a->st, NULL, 1) == 0) { + if (file_information(a->name, &a->st, NULL, 1) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } @@ -874,8 +821,11 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->current_fixup = NULL; a->deferred = 0; archive_entry_free(a->entry); - a->entry = NULL; a->entry = archive_entry_clone(entry); + if (a->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; a->offset = 0; @@ -1106,7 +1056,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (ARCHIVE_OK); if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } @@ -1190,7 +1140,7 @@ _archive_write_disk_data_block(struct archive *_a, if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EOVERFLOW, "Write request too large"); return (ARCHIVE_WARN); } @@ -1516,7 +1466,7 @@ restore_entry(struct archive_write_disk *a) if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, - "Hard-link target '%s' does not exist.", + "Hard-link target '%s' does not exist", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } @@ -1573,7 +1523,7 @@ restore_entry(struct archive_write_disk *a) * such symlinks. We always need both source and target * information. */ - r = file_information(a, a->name, &lst, &lst_mode, 1); + r = file_information(a->name, &lst, &lst_mode, 1); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); @@ -1582,7 +1532,7 @@ restore_entry(struct archive_write_disk *a) if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) dirlnk = 1; /* In case of a symlink we need target information */ - r = file_information(a, a->name, &a->st, &st_mode, 0); + r = file_information(a->name, &a->st, &st_mode, 0); if (r != 0) { a->st = lst; st_mode = lst_mode; @@ -1607,7 +1557,7 @@ restore_entry(struct archive_write_disk *a) if (a->skip_file_set && bhfi_dev(&a->st) == a->skip_file_dev && bhfi_ino(&a->st) == a->skip_file_ino) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } @@ -1710,6 +1660,7 @@ create_filesystem_object(struct archive_write_disk *a) mode_t final_mode, mode; int r; DWORD attrs = 0; + DWORD file_flags = 0; # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ CREATEFILE2_EXTENDED_PARAMETERS createExParams; #endif @@ -1732,6 +1683,22 @@ create_filesystem_object(struct archive_write_disk *a) free(linksanitized); return (r); } + + if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { + r = check_symlinks_by_path(a, linksanitized, NULL, 1); + if (r != ARCHIVE_OK) { + /* + * Prevent extracting a hardlink to a symlink. + * libarchive supports the POSIX feature of + * hardlinks with data payloads, which would + * provide a way to write through an otherwise + * disallowed symlink. + */ + free(linksanitized); + return (r); + } + } + linkfull = __la_win_permissive_name_w(linksanitized); free(linksanitized); namefull = __la_win_permissive_name_w(a->name); @@ -1809,6 +1776,13 @@ create_filesystem_object(struct archive_write_disk *a) else disk_unlink(a->name); } + + /* + * Since the symlink-safe path cache is just an optimization, and we + * are about to create a new symlink, invalidating the cache prevents + * incidentally writing through the entry we are about to create. + */ + a->path_safe.s[0] = 0; #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else @@ -1847,16 +1821,31 @@ create_filesystem_object(struct archive_write_disk *a) case AE_IFREG: a->tmpname = NULL; fullname = a->name; + if ((a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) { + /* + * Creating a regular file is expected to fail with EEXIST if *any + * object* exists at the target location. + * SECURE_SYMLINKS deletes any symbolic link that would have been + * written through. When it's off, though, we risk writing through + * extant but broken symlinks unless we allow CreateFile to fail + * on the link rather than creating its target. + * + * This is effectively O_EXCL. + */ + file_flags |= FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS; + } + /* O_WRONLY | O_CREAT | O_EXCL */ # if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */ ZeroMemory(&createExParams, sizeof(createExParams)); createExParams.dwSize = sizeof(createExParams); createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + createExParams.dwFileFlags = file_flags; a->fh = CreateFile2(fullname, GENERIC_WRITE, 0, CREATE_NEW, &createExParams); #else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, - CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL); #endif if (a->fh == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME && @@ -1867,7 +1856,7 @@ create_filesystem_object(struct archive_write_disk *a) CREATE_NEW, &createExParams); #else a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL, - CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL); #endif } if (a->fh == INVALID_HANDLE_VALUE) { @@ -2132,9 +2121,10 @@ current_fixup(struct archive_write_disk *a, const wchar_t *pathname) * recent paths. */ static int -check_symlinks(struct archive_write_disk *a) +check_symlinks_by_path(struct archive_write_disk *a, + wchar_t *path, size_t *safe_len, int checking_linkname) { - wchar_t *pn, *p; + wchar_t *pn, *p, *su; wchar_t c; int r; BY_HANDLE_FILE_INFORMATION st; @@ -2145,13 +2135,17 @@ check_symlinks(struct archive_write_disk *a) * destination would be altered by a symlink. */ /* Whatever we checked last time doesn't need to be re-checked. */ - pn = a->name; + pn = path; p = a->path_safe.s; while ((*pn != '\0') && (*p == *pn)) ++p, ++pn; /* Skip leading backslashes */ while (*pn == '\\') ++pn; + su = pn; + /* The end-of-safe region, however, should not cover a partial leaf name */ + while (su > path && *su != '\\') + --su; c = pn[0]; /* Keep going until we've checked the entire name. */ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) { @@ -2161,13 +2155,20 @@ check_symlinks(struct archive_write_disk *a) c = pn[0]; pn[0] = '\0'; /* Check that we haven't hit a symlink. */ - r = file_information(a, a->name, &st, &st_mode, 1); + r = file_information(path, &st, &st_mode, 1); if (r != 0) { /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) break; } else if (S_ISLNK(st_mode)) { if (c == '\0') { + if (checking_linkname) { + archive_set_error(&a->archive, ELOOP, + "Cannot write hardlink to symlink %ls", + path); + pn[0] = c; + return (ARCHIVE_FAILED); + } /* * Last element is a file or directory symlink. * Remove it so we can overwrite it with the @@ -2179,14 +2180,14 @@ check_symlinks(struct archive_write_disk *a) } if (st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - r = disk_rmdir(a->name); + r = disk_rmdir(path); } else { - r = disk_unlink(a->name); + r = disk_unlink(path); } if (r) { archive_set_error(&a->archive, errno, "Could not remove symlink %ls", - a->name); + path); pn[0] = c; return (ARCHIVE_FAILED); } @@ -2198,13 +2199,18 @@ check_symlinks(struct archive_write_disk *a) * symlink with another symlink. */ if (!S_ISLNK(a->mode)) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, -1, "Removing symlink %ls", - a->name); + path); + /* + * We can safely return the entire path as a safe range, + * having deleted the symlink at the leaf with no intent + * to create another symlink. + */ + su = pn; } /* Symlink gone. No more problem! */ - pn[0] = c; - return (0); + break; } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ if (a->flags & @@ -2213,37 +2219,62 @@ check_symlinks(struct archive_write_disk *a) } if (st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - r = disk_rmdir(a->name); + r = disk_rmdir(path); } else { - r = disk_unlink(a->name); + r = disk_unlink(path); } if (r != 0) { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, EIO, "Cannot remove intervening " - "symlink %ls", a->name); + "symlink %ls", path); pn[0] = c; return (ARCHIVE_FAILED); } a->pst = NULL; } else { - archive_set_error(&a->archive, 0, + archive_set_error(&a->archive, ELOOP, "Cannot extract through symlink %ls", - a->name); + path); pn[0] = c; return (ARCHIVE_FAILED); } } + su = pn; if (!c) break; pn[0] = c; pn++; } pn[0] = c; - /* We've checked and/or cleaned the whole path, so remember it. */ - archive_wstrcpy(&a->path_safe, a->name); + if (safe_len) + *safe_len = su - path; return (ARCHIVE_OK); } +static int +check_symlinks(struct archive_write_disk *a) +{ + int r; + size_t safe_len; + r = check_symlinks_by_path(a, a->name, &safe_len, 0); + + if (r == ARCHIVE_OK) { + /* + * We've checked and/or cleaned the whole path, so remember the + * portion check_symlinks_by_path returned as "safe" (typically + * the parent of the entry we are about to create, unless that + * entry was just unlinked.) + * + * We only cache path safety info for entry paths (which we + * operate on in a->name). We do not cache safety info for + * any other paths. + */ + archive_wstrncpy(&a->path_safe, a->name, safe_len); + } + + return (r); +} + static int guidword(wchar_t *p, int n) { @@ -2513,7 +2544,7 @@ create_dir(struct archive_write_disk *a, wchar_t *path) * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ - if (file_information(a, path, &st, &st_mode, 0) == 0) { + if (file_information(path, &st, &st_mode, 0) == 0) { if (S_ISDIR(st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { @@ -2580,7 +2611,7 @@ create_dir(struct archive_write_disk *a, wchar_t *path) * don't add it to the fixup list here, as it's already been * added. */ - if (file_information(a, path, &st, &st_mode, 0) == 0 && + if (file_information(path, &st, &st_mode, 0) == 0 && S_ISDIR(st_mode)) return (ARCHIVE_OK); diff --git a/libarchive/archive_write_filter.3 b/libarchive/archive_write_filter.3 index b39cabe047..9939de414b 100644 --- a/libarchive/archive_write_filter.3 +++ b/libarchive/archive_write_filter.3 @@ -50,6 +50,8 @@ Streaming Archive Library (libarchive, -larchive) .Ft int .Fn archive_write_add_filter_b64encode "struct archive *" .Ft int +.Fn archive_write_add_filter_by_name "struct archive *" "const char *name" +.Ft int .Fn archive_write_add_filter_bzip2 "struct archive *" .Ft int .Fn archive_write_add_filter_compress "struct archive *" @@ -100,6 +102,8 @@ Note that the compressed output is always properly blocked. .Xc The output will be encoded as specified. The encoded output is always properly blocked. +.It Fn archive_write_add_filter_by_name +Sets the corresponding filter based on the common name. .It Fn archive_write_add_filter_none This is never necessary. It is provided only for backwards compatibility. @@ -111,6 +115,10 @@ write callbacks. .Sh RETURN VALUES These functions return .Cm ARCHIVE_OK +.Po or +.Cm ARCHIVE_WARN +if an external program had to be executed to handle the filter +.Pc on success, or .Cm ARCHIVE_FATAL . .\" diff --git a/libarchive/archive_write_format.3 b/libarchive/archive_write_format.3 index 9e331368ae..1b26c1c464 100644 --- a/libarchive/archive_write_format.3 +++ b/libarchive/archive_write_format.3 @@ -129,7 +129,9 @@ Sets the corresponding format based on the common name. .Fn archive_write_set_format_filter_by_ext .Fn archive_write_set_format_filter_by_ext_def .Xc -Sets both filters and format based on the output filename. +Sets both filters and format based on the output filename, optionally +referring to a default extension if the supplied filename does not have a +known one. Supported extensions: .7z, .zip, .jar, .cpio, .iso, .a, .ar, .tar, .tgz, .tar.gz, .tar.bz2, .tar.xz .It Xo .Fn archive_write_set_format_7zip diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h index f259ccb165..ca9393fb8e 100644 --- a/libarchive/archive_write_private.h +++ b/libarchive/archive_write_private.h @@ -69,7 +69,7 @@ void __archive_write_filters_free(struct archive *); struct archive_write_filter *__archive_write_allocate_filter(struct archive *); int __archive_write_output(struct archive_write *, const void *, size_t); -int __archive_write_nulls(struct archive_write *, size_t); +int __archive_write_nulls(struct archive_write *, uint64_t); int __archive_write_filter(struct archive_write_filter *, const void *, size_t); struct archive_write { @@ -132,6 +132,8 @@ struct archive_write { void *passphrase_client_data; }; +int __archive_write_unregister_format(struct archive_write *); + /* * Utility function to format a USTAR header into a buffer. If * "strict" is set, this tries to create the absolutely most portable diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c index ebdb16e902..b5cc8e5001 100644 --- a/libarchive/archive_write_set_format_7zip.c +++ b/libarchive/archive_write_set_format_7zip.c @@ -28,6 +28,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -212,8 +215,6 @@ struct _7zip { size_t total_number_dir_entry; size_t total_bytes_entry_name; size_t total_number_time_defined[3]; - uint64_t total_bytes_compressed; - uint64_t total_bytes_uncompressed; uint64_t entry_bytes_remaining; uint32_t entry_crc32; uint32_t precode_crc32; @@ -311,7 +312,7 @@ static int _7z_compression_init_encoder(struct archive_write *, unsigned, int); static int compression_init_encoder_zstd(struct archive *, struct la_zstream *, int, int); -#if defined(HAVE_ZSTD_H) +#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream static int compression_code_zstd(struct archive *, struct la_zstream *, enum la_zaction); static int compression_end_zstd(struct archive *, struct la_zstream *); @@ -333,6 +334,7 @@ string_to_number(const char *string, intmax_t *numberp) if (string == NULL || *string == '\0') return (ARCHIVE_WARN); + errno = 0; *numberp = strtoimax(string, &end, 10); if (end == string || *end != '\0' || errno == EOVERFLOW) { *numberp = 0; @@ -354,8 +356,7 @@ archive_write_set_format_7zip(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_7zip"); /* If another format was already registered, unregister it. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { @@ -375,7 +376,7 @@ archive_write_set_format_7zip(struct archive *_a) zip->opt_compression = _7Z_BZIP2; #elif defined(HAVE_ZLIB_H) zip->opt_compression = _7Z_DEFLATE; -#elif HAVE_ZSTD_H +#elif HAVE_ZSTD_H && HAVE_ZSTD_compressStream zip->opt_compression = _7Z_ZSTD; #else zip->opt_compression = _7Z_COPY; @@ -411,9 +412,7 @@ archive_write_set_format_7zip(struct archive *_a) static int _7z_options(struct archive_write *a, const char *key, const char *value) { - struct _7zip *zip; - - zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; if (strcmp(key, "compression") == 0) { const char *name = NULL; @@ -487,8 +486,9 @@ _7z_options(struct archive_write *a, const char *key, const char *value) } char *end = NULL; + errno = 0; long lvl = strtol(value, &end, 10); - if (end == NULL || *end != '\0') { + if (errno != 0 || end == NULL || *end != '\0') { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "parsing compression-level option value failed `%s'", value); return (ARCHIVE_FAILED); @@ -525,7 +525,7 @@ _7z_options(struct archive_write *a, const char *key, const char *value) if (string_to_number(value, &threads) != ARCHIVE_OK) { return (ARCHIVE_WARN); } - if (threads < 0) { + if (threads < 0 || threads > INT_MAX) { return (ARCHIVE_WARN); } if (threads == 0) { @@ -554,11 +554,10 @@ _7z_options(struct archive_write *a, const char *key, const char *value) static int _7z_write_header(struct archive_write *a, struct archive_entry *entry) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; struct file *file; int r; - zip = (struct _7zip *)a->format_data; zip->cur_file = NULL; zip->entry_bytes_remaining = 0; @@ -662,12 +661,10 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; const unsigned char *p; ssize_t ws; - zip = (struct _7zip *)a->format_data; - /* * Open a temporary file. */ @@ -700,7 +697,7 @@ static ssize_t compress_out(struct archive_write *a, const void *buff, size_t s, enum la_zaction run) { - struct _7zip *zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; int r; if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0) @@ -746,11 +743,9 @@ compress_out(struct archive_write *a, const void *buff, size_t s, static ssize_t _7z_write_data(struct archive_write *a, const void *buff, size_t s) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; ssize_t bytes; - zip = (struct _7zip *)a->format_data; - if (s > zip->entry_bytes_remaining) s = (size_t)zip->entry_bytes_remaining; if (s == 0 || zip->cur_file == NULL) @@ -766,11 +761,10 @@ _7z_write_data(struct archive_write *a, const void *buff, size_t s) static int _7z_finish_entry(struct archive_write *a) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; size_t s; ssize_t r; - zip = (struct _7zip *)a->format_data; if (zip->cur_file == NULL) return (ARCHIVE_OK); @@ -782,8 +776,6 @@ _7z_finish_entry(struct archive_write *a) if (r < 0) return ((int)r); } - zip->total_bytes_compressed += zip->stream.total_in; - zip->total_bytes_uncompressed += zip->stream.total_out; zip->cur_file->crc32 = zip->entry_crc32; zip->cur_file = NULL; @@ -793,11 +785,10 @@ _7z_finish_entry(struct archive_write *a) static int flush_wbuff(struct archive_write *a) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; int r; size_t s; - zip = (struct _7zip *)a->format_data; s = sizeof(zip->wbuff) - zip->wbuff_remaining; r = __archive_write_output(a, zip->wbuff, s); if (r != ARCHIVE_OK) @@ -809,10 +800,9 @@ flush_wbuff(struct archive_write *a) static int copy_out(struct archive_write *a, uint64_t offset, uint64_t length) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; int r; - zip = (struct _7zip *)a->format_data; if (zip->temp_offset > 0 && lseek(zip->temp_fd, offset, SEEK_SET) < 0) { archive_set_error(&(a->archive), errno, "lseek failed"); @@ -836,7 +826,8 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) return (ARCHIVE_FATAL); } if (rs == 0) { - archive_set_error(&(a->archive), 0, + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip archive"); return (ARCHIVE_FATAL); } @@ -854,15 +845,13 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) static int _7z_close(struct archive_write *a) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; unsigned char *wb; uint64_t header_offset, header_size, header_unpacksize; uint64_t length; uint32_t header_crc32; int r; - zip = (struct _7zip *)a->format_data; - if (zip->total_number_entry > 0) { struct archive_rb_node *n; uint64_t data_offset, data_size, data_unpacksize; @@ -1027,7 +1016,7 @@ enc_uint64(struct archive_write *a, uint64_t val) static int make_substreamsInfo(struct archive_write *a, struct coder *coders) { - struct _7zip *zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; struct file *file; int r; @@ -1103,7 +1092,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, uint64_t unpack_size, int num_coder, struct coder *coders, int substrm, uint32_t header_crc) { - struct _7zip *zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; uint8_t codec_buff[8]; int numFolders, fi; int codec_size; @@ -1295,8 +1284,8 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, static int make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) { + struct _7zip *zip = a->format_data; uint8_t filetime[8]; - struct _7zip *zip = (struct _7zip *)a->format_data; struct file *file; int r; uint8_t b, mask; @@ -1384,7 +1373,7 @@ static int make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, uint64_t unpack_size, int codernum, struct coder *coders) { - struct _7zip *zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; struct file *file; int r; uint8_t b, mask; @@ -1592,7 +1581,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, static int _7z_free(struct archive_write *a) { - struct _7zip *zip = (struct _7zip *)a->format_data; + struct _7zip *zip = a->format_data; /* Close the temporary file. */ if (zip->temp_fd >= 0) @@ -1630,13 +1619,12 @@ static int file_new(struct archive_write *a, struct archive_entry *entry, struct file **newfile) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; struct file *file; const char *u16; size_t u16len; int ret = ARCHIVE_OK; - zip = (struct _7zip *)a->format_data; *newfile = NULL; file = calloc(1, sizeof(*file)); @@ -1680,7 +1668,7 @@ file_new(struct archive_write *a, struct archive_entry *entry, const char* linkpath; linkpath = archive_entry_symlink_utf8(entry); if (linkpath == NULL) { - free(file); + file_free(file); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "symlink path could not be converted to UTF-8"); return (ARCHIVE_FAILED); @@ -2254,7 +2242,7 @@ static void ppmd_write(void *p, Byte b) { struct archive_write *a = ((IByteOut *)p)->a; - struct _7zip *zip = (struct _7zip *)(a->format_data); + struct _7zip *zip = a->format_data; struct la_zstream *lastrm = &(zip->stream); struct ppmd_stream *strm; @@ -2511,10 +2499,9 @@ static int _7z_compression_init_encoder(struct archive_write *a, unsigned compression, int compression_level) { - struct _7zip *zip; + struct _7zip *zip = a->format_data; int r; - zip = (struct _7zip *)a->format_data; switch (compression) { case _7Z_DEFLATE: r = compression_init_encoder_deflate( diff --git a/libarchive/archive_write_set_format_ar.c b/libarchive/archive_write_set_format_ar.c index e5e9c806b6..57572ea38b 100644 --- a/libarchive/archive_write_set_format_ar.c +++ b/libarchive/archive_write_set_format_ar.c @@ -40,10 +40,11 @@ #include "archive.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_string.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" -struct ar_w { +struct ar { uint64_t entry_bytes_remaining; uint64_t entry_padding; int is_strtab; @@ -78,7 +79,6 @@ static ssize_t archive_write_ar_data(struct archive_write *, static int archive_write_ar_free(struct archive_write *); static int archive_write_ar_close(struct archive_write *); static int archive_write_ar_finish_entry(struct archive_write *); -static const char *ar_basename(const char *path); static int format_octal(int64_t v, char *p, int s); static int format_decimal(int64_t v, char *p, int s); @@ -120,11 +120,10 @@ archive_write_set_format_ar_svr4(struct archive *_a) static int archive_write_set_format_ar(struct archive_write *a) { - struct ar_w *ar; + struct ar *ar; /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); ar = calloc(1, sizeof(*ar)); if (ar == NULL) { @@ -147,16 +146,18 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) { int ret, append_fn; char buff[60]; - char *ss, *se; - struct ar_w *ar; + char *ss; + struct ar *ar = a->format_data; + struct archive_string se; const char *pathname; const char *filename; + size_t filename_length; int64_t size; append_fn = 0; - ar = (struct ar_w *)a->format_data; ar->is_strtab = 0; filename = NULL; + filename_length = 0; size = archive_entry_size(entry); @@ -172,7 +173,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) /* * If we are now at the beginning of the archive, - * we need first write the ar global header. + * we have to write the ar global header first. */ if (!ar->wrote_global_header) { __archive_write_output(a, "!\n", 8); @@ -215,12 +216,17 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * Otherwise, entry is a normal archive member. * Strip leading paths from filenames, if any. */ - if ((filename = ar_basename(pathname)) == NULL) { + filename = strrchr(pathname, '/'); + if (filename == NULL) + filename = pathname; + else if (filename[1] == '\0') { /* Reject filenames with trailing "/" */ archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); - } + } else + filename++; + filename_length = strlen(filename); if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { /* @@ -229,10 +235,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * So, the longest filename here (without extension) is * actually 15 bytes. */ - if (strlen(filename) <= 15) { + if (filename_length <= 15) { memcpy(&buff[AR_name_offset], - filename, strlen(filename)); - buff[AR_name_offset + strlen(filename)] = '/'; + filename, filename_length); + buff[AR_name_offset + filename_length] = '/'; } else { /* * For filename longer than 15 bytes, GNU variant @@ -246,18 +252,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_WARN); } - se = malloc(strlen(filename) + 3); - if (se == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate filename buffer"); - return (ARCHIVE_FATAL); - } - - memcpy(se, filename, strlen(filename)); - strcpy(se + strlen(filename), "/\n"); - - ss = strstr(ar->strtab, se); - free(se); + archive_string_init(&se); + archive_string_sprintf(&se, "%s/\n", filename); + ss = strstr(ar->strtab, se.s); + archive_string_free(&se); if (ss == NULL) { archive_set_error(&a->archive, EINVAL, @@ -289,13 +287,13 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * The name is then written immediately following the * archive header. */ - if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { - memcpy(&buff[AR_name_offset], filename, strlen(filename)); - buff[AR_name_offset + strlen(filename)] = ' '; + if (filename_length <= 16 && strchr(filename, ' ') == NULL) { + memcpy(&buff[AR_name_offset], filename, filename_length); + buff[AR_name_offset + filename_length] = ' '; } else { memcpy(buff + AR_name_offset, "#1/", 3); - if (format_decimal(strlen(filename), + if (format_decimal(filename_length, buff + AR_name_offset + 3, AR_name_size - 3)) { archive_set_error(&a->archive, ERANGE, @@ -303,7 +301,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_WARN); } append_fn = 1; - size += strlen(filename); + size += filename_length; } } @@ -353,10 +351,10 @@ size: ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { - ret = __archive_write_output(a, filename, strlen(filename)); + ret = __archive_write_output(a, filename, filename_length); if (ret != ARCHIVE_OK) return (ret); - ar->entry_bytes_remaining -= strlen(filename); + ar->entry_bytes_remaining -= filename_length; } return (ARCHIVE_OK); @@ -365,10 +363,9 @@ size: static ssize_t archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) { - struct ar_w *ar; + struct ar *ar = a->format_data; int ret; - ar = (struct ar_w *)a->format_data; if (s > ar->entry_bytes_remaining) s = (size_t)ar->entry_bytes_remaining; @@ -401,9 +398,7 @@ archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) static int archive_write_ar_free(struct archive_write *a) { - struct ar_w *ar; - - ar = (struct ar_w *)a->format_data; + struct ar *ar = a->format_data; if (ar == NULL) return (ARCHIVE_OK); @@ -421,14 +416,13 @@ archive_write_ar_free(struct archive_write *a) static int archive_write_ar_close(struct archive_write *a) { - struct ar_w *ar; + struct ar *ar = a->format_data; int ret; /* * If we haven't written anything yet, we need to write * the ar global header now to make it a valid ar archive. */ - ar = (struct ar_w *)a->format_data; if (!ar->wrote_global_header) { ar->wrote_global_header = 1; ret = __archive_write_output(a, "!\n", 8); @@ -441,11 +435,9 @@ archive_write_ar_close(struct archive_write *a) static int archive_write_ar_finish_entry(struct archive_write *a) { - struct ar_w *ar; + struct ar *ar = a->format_data; int ret; - ar = (struct ar_w *)a->format_data; - if (ar->entry_bytes_remaining != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Entry remaining bytes larger than 0"); @@ -546,24 +538,3 @@ format_decimal(int64_t v, char *p, int s) return (-1); } - -static const char * -ar_basename(const char *path) -{ - const char *endp, *startp; - - endp = path + strlen(path) - 1; - /* - * For filename with trailing slash(es), we return - * NULL indicating an error. - */ - if (*endp == '/') - return (NULL); - - /* Find the start of the base */ - startp = endp; - while (startp > path && *(startp - 1) != '/') - startp--; - - return (startp); -} diff --git a/libarchive/archive_write_set_format_cpio_binary.c b/libarchive/archive_write_set_format_cpio_binary.c index aefb2ca6f5..6648b18d90 100644 --- a/libarchive/archive_write_set_format_cpio_binary.c +++ b/libarchive/archive_write_set_format_cpio_binary.c @@ -40,6 +40,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" @@ -182,8 +183,7 @@ archive_write_set_format_cpio_binary(struct archive *_a, int format) ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); cpio = calloc(1, sizeof(*cpio)); if (cpio == NULL) { @@ -235,7 +235,7 @@ static int archive_write_binary_options(struct archive_write *a, const char *key, const char *val) { - struct cpio *cpio = (struct cpio *)a->format_data; + struct cpio *cpio = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -307,10 +307,16 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) /* Ensure space for the new mapping. */ if (cpio->ino_list_size <= cpio->ino_list_next) { - size_t newsize = cpio->ino_list_size < 512 - ? 512 : cpio->ino_list_size * 2; - void *newlist = realloc(cpio->ino_list, - sizeof(cpio->ino_list[0]) * newsize); + size_t newsize, size; + if (cpio->ino_list_size < 512) + newsize = 512; + else if (archive_ckd_mul_size(&newsize, + cpio->ino_list_size, 2)) + return (-1); + if (archive_ckd_mul_size(&size, + newsize, sizeof(cpio->ino_list[0]))) + return (-1); + void *newlist = realloc(cpio->ino_list, size); if (newlist == NULL) return (-1); @@ -329,10 +335,9 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) static struct archive_string_conv * get_sconv(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; struct archive_string_conv *sconv; - cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { @@ -378,16 +383,15 @@ archive_write_binary_header(struct archive_write *a, struct archive_entry *entry static int write_header(struct archive_write *a, struct archive_entry *entry) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; int64_t ino; struct cpio_binary_header h; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; - cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); @@ -423,7 +427,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) ret_final = ARCHIVE_WARN; } /* Include trailing null */ - pathlength = (int)len + 1; + pathlength = len + 1; h.h_magic = la_swap16(070707); h.h_dev = la_swap16(archive_entry_dev(entry)); @@ -472,7 +476,13 @@ write_header(struct archive_write *a, struct archive_entry *entry) h.h_majmin = 0; h.h_mtime = la_swap32((uint32_t)archive_entry_mtime(entry)); - h.h_namesize = la_swap16(pathlength); + if (pathlength > 0xffff) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } + h.h_namesize = la_swap16((uint16_t)pathlength); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) @@ -506,12 +516,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) && (archive_entry_size(entry) > 256*256*256-1)) { archive_set_error(&a->archive, ERANGE, - "File is too large for PWB binary cpio format."); + "File is too large for PWB binary cpio format"); ret_final = ARCHIVE_FAILED; goto exit_write_header; } else if (archive_entry_size(entry) > INT32_MAX) { archive_set_error(&a->archive, ERANGE, - "File is too large for binary cpio format."); + "File is too large for binary cpio format"); ret_final = ARCHIVE_FAILED; goto exit_write_header; } @@ -555,10 +565,9 @@ exit_write_header: static ssize_t archive_write_binary_data(struct archive_write *a, const void *buff, size_t s) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; int ret; - cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; @@ -592,9 +601,8 @@ archive_write_binary_close(struct archive_write *a) static int archive_write_binary_free(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; free(cpio->ino_list); free(cpio); a->format_data = NULL; @@ -604,9 +612,7 @@ archive_write_binary_free(struct archive_write *a) static int archive_write_binary_finish_entry(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; - return (__archive_write_nulls(a, - (size_t)cpio->entry_bytes_remaining)); + return (__archive_write_nulls(a, cpio->entry_bytes_remaining)); } diff --git a/libarchive/archive_write_set_format_cpio_newc.c b/libarchive/archive_write_set_format_cpio_newc.c index 254d5a9901..b9de7d362e 100644 --- a/libarchive/archive_write_set_format_cpio_newc.c +++ b/libarchive/archive_write_set_format_cpio_newc.c @@ -113,8 +113,7 @@ archive_write_set_format_cpio_newc(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); cpio = calloc(1, sizeof(*cpio)); if (cpio == NULL) { @@ -138,7 +137,7 @@ static int archive_write_newc_options(struct archive_write *a, const char *key, const char *val) { - struct cpio *cpio = (struct cpio *)a->format_data; + struct cpio *cpio = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -166,10 +165,9 @@ archive_write_newc_options(struct archive_write *a, const char *key, static struct archive_string_conv * get_sconv(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; struct archive_string_conv *sconv; - cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { @@ -216,17 +214,16 @@ archive_write_newc_header(struct archive_write *a, struct archive_entry *entry) static int write_header(struct archive_write *a, struct archive_entry *entry) { + struct cpio *cpio = a->format_data; int64_t ino; - struct cpio *cpio; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; char h[c_header_size]; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; int pad; - cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); @@ -261,7 +258,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } - pathlength = (int)len + 1; /* Include trailing null. */ + pathlength = len + 1; /* Include trailing null. */ memset(h, 0, c_header_size); format_hex(0x070701, h + c_magic_offset, c_magic_size); @@ -292,7 +289,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); } format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); - format_hex(pathlength, h + c_namesize_offset, c_namesize_size); + if (format_hex(pathlength, h + c_namesize_offset, c_namesize_size)) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } format_hex(0, h + c_checksum_offset, c_checksum_size); /* Non-regular files don't store bodies. */ @@ -322,7 +324,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, - "File is too large for this format."); + "File is too large for this format"); ret_final = ARCHIVE_FAILED; goto exit_write_header; } @@ -373,10 +375,9 @@ exit_write_header: static ssize_t archive_write_newc_data(struct archive_write *a, const void *buff, size_t s) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; int ret; - cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; @@ -425,6 +426,8 @@ archive_write_newc_close(struct archive_write *a) struct archive_entry *trailer; trailer = archive_entry_new(); + if (trailer == NULL) + return ARCHIVE_FATAL; archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); archive_entry_set_pathname(trailer, "TRAILER!!!"); @@ -437,9 +440,8 @@ archive_write_newc_close(struct archive_write *a) static int archive_write_newc_free(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; free(cpio); a->format_data = NULL; return (ARCHIVE_OK); @@ -448,9 +450,8 @@ archive_write_newc_free(struct archive_write *a) static int archive_write_newc_finish_entry(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; return (__archive_write_nulls(a, - (size_t)cpio->entry_bytes_remaining + cpio->padding)); + cpio->entry_bytes_remaining + cpio->padding)); } diff --git a/libarchive/archive_write_set_format_cpio_odc.c b/libarchive/archive_write_set_format_cpio_odc.c index c72c6b2796..c384d48272 100644 --- a/libarchive/archive_write_set_format_cpio_odc.c +++ b/libarchive/archive_write_set_format_cpio_odc.c @@ -40,6 +40,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" @@ -107,8 +108,7 @@ archive_write_set_format_cpio_odc(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); cpio = calloc(1, sizeof(*cpio)); if (cpio == NULL) { @@ -132,7 +132,7 @@ static int archive_write_odc_options(struct archive_write *a, const char *key, const char *val) { - struct cpio *cpio = (struct cpio *)a->format_data; + struct cpio *cpio = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -204,10 +204,16 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) /* Ensure space for the new mapping. */ if (cpio->ino_list_size <= cpio->ino_list_next) { - size_t newsize = cpio->ino_list_size < 512 - ? 512 : cpio->ino_list_size * 2; - void *newlist = realloc(cpio->ino_list, - sizeof(cpio->ino_list[0]) * newsize); + size_t newsize, size; + if (cpio->ino_list_size < 512) + newsize = 512; + else if (archive_ckd_mul_size(&newsize, + cpio->ino_list_size, 2)) + return (-1); + if (archive_ckd_mul_size(&size, + newsize, sizeof(cpio->ino_list[0]))) + return (-1); + void *newlist = realloc(cpio->ino_list, size); if (newlist == NULL) return (-1); @@ -226,10 +232,9 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) static struct archive_string_conv * get_sconv(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; struct archive_string_conv *sconv; - cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { @@ -275,16 +280,15 @@ archive_write_odc_header(struct archive_write *a, struct archive_entry *entry) static int write_header(struct archive_write *a, struct archive_entry *entry) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; const char *p, *path; - int pathlength, ret, ret_final; + int ret, ret_final; int64_t ino; char h[76]; struct archive_string_conv *sconv; struct archive_entry *entry_main; - size_t len; + size_t len, pathlength; - cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); @@ -320,7 +324,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) ret_final = ARCHIVE_WARN; } /* Include trailing null. */ - pathlength = (int)len + 1; + pathlength = len + 1; memset(h, 0, sizeof(h)); format_octal(070707, h + c_magic_offset, c_magic_size); @@ -351,7 +355,12 @@ write_header(struct archive_write *a, struct archive_entry *entry) else format_octal(0, h + c_rdev_offset, c_rdev_size); format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); - format_octal(pathlength, h + c_namesize_offset, c_namesize_size); + if (format_octal((int64_t)pathlength, h + c_namesize_offset, c_namesize_size)) { + archive_set_error(&a->archive, ERANGE, + "Filename is too long for cpio format"); + ret_final = ARCHIVE_FAILED; + goto exit_write_header; + } /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) @@ -380,7 +389,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, - "File is too large for cpio format."); + "File is too large for cpio format"); ret_final = ARCHIVE_FAILED; goto exit_write_header; } @@ -415,10 +424,9 @@ exit_write_header: static ssize_t archive_write_odc_data(struct archive_write *a, const void *buff, size_t s) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; int ret; - cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; @@ -482,9 +490,8 @@ archive_write_odc_close(struct archive_write *a) static int archive_write_odc_free(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; free(cpio->ino_list); free(cpio); a->format_data = NULL; @@ -494,9 +501,8 @@ archive_write_odc_free(struct archive_write *a) static int archive_write_odc_finish_entry(struct archive_write *a) { - struct cpio *cpio; + struct cpio *cpio = a->format_data; - cpio = (struct cpio *)a->format_data; - return (__archive_write_nulls(a, - (size_t)cpio->entry_bytes_remaining)); + return (__archive_write_nulls(a, + cpio->entry_bytes_remaining)); } diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c index b67007a631..5a767e1bd7 100644 --- a/libarchive/archive_write_set_format_gnutar.c +++ b/libarchive/archive_write_set_format_gnutar.c @@ -174,6 +174,11 @@ archive_write_set_format_gnutar(struct archive *_a) struct archive_write *a = (struct archive_write *)_a; struct gnutar *gnutar; + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_format_gnutar"); + + (void)__archive_write_unregister_format(a); + gnutar = calloc(1, sizeof(*gnutar)); if (gnutar == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -197,7 +202,7 @@ static int archive_write_gnutar_options(struct archive_write *a, const char *key, const char *val) { - struct gnutar *gnutar = (struct gnutar *)a->format_data; + struct gnutar *gnutar = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -231,9 +236,8 @@ archive_write_gnutar_close(struct archive_write *a) static int archive_write_gnutar_free(struct archive_write *a) { - struct gnutar *gnutar; + struct gnutar *gnutar = a->format_data; - gnutar = (struct gnutar *)a->format_data; free(gnutar); a->format_data = NULL; return (ARCHIVE_OK); @@ -242,12 +246,11 @@ archive_write_gnutar_free(struct archive_write *a) static int archive_write_gnutar_finish_entry(struct archive_write *a) { - struct gnutar *gnutar; + struct gnutar *gnutar = a->format_data; int ret; - gnutar = (struct gnutar *)a->format_data; - ret = __archive_write_nulls(a, (size_t) - (gnutar->entry_bytes_remaining + gnutar->entry_padding)); + ret = __archive_write_nulls(a, + gnutar->entry_bytes_remaining + gnutar->entry_padding); gnutar->entry_bytes_remaining = gnutar->entry_padding = 0; return (ret); } @@ -255,10 +258,9 @@ archive_write_gnutar_finish_entry(struct archive_write *a) static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s) { - struct gnutar *gnutar; + struct gnutar *gnutar = a->format_data; int ret; - gnutar = (struct gnutar *)a->format_data; if (s > gnutar->entry_bytes_remaining) s = (size_t)gnutar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); @@ -272,15 +274,13 @@ static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { + struct gnutar *gnutar = a->format_data; char buff[512]; int r, ret, ret2 = ARCHIVE_OK; char tartype; - struct gnutar *gnutar; struct archive_string_conv *sconv; struct archive_entry *entry_main; - gnutar = (struct gnutar *)a->format_data; - /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { @@ -321,7 +321,8 @@ archive_write_gnutar_header(struct archive_write *a, const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); @@ -493,6 +494,13 @@ archive_write_gnutar_header(struct archive_write *a, size_t length = gnutar->linkname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); + if (temp == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Linkname"); + ret = ARCHIVE_FATAL; + goto exit_write_header; + } + /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); @@ -523,6 +531,13 @@ archive_write_gnutar_header(struct archive_write *a, size_t length = gnutar->pathname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); + if (temp == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Linkname"); + ret = ARCHIVE_FATAL; + goto exit_write_header; + } + /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); @@ -588,13 +603,11 @@ static int archive_format_gnutar_header(struct archive_write *a, char h[512], struct archive_entry *entry, char tartype) { + struct gnutar *gnutar = a->format_data; unsigned int checksum; int i, ret; size_t copy_length; const char *p; - struct gnutar *gnutar; - - gnutar = (struct gnutar *)a->format_data; ret = 0; @@ -651,7 +664,7 @@ archive_format_gnutar_header(struct archive_write *a, char h[512], copy_length = gnutar->gname_length; } if (copy_length > 0) { - if (strlen(p) > GNUTAR_gname_size) + if (copy_length > GNUTAR_gname_size) copy_length = GNUTAR_gname_size; memcpy(h + GNUTAR_gname_offset, p, copy_length); } diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c index c275c1ec92..ee50f7c084 100644 --- a/libarchive/archive_write_set_format_iso9660.c +++ b/libarchive/archive_write_set_format_iso9660.c @@ -119,6 +119,8 @@ static const unsigned char zisofs_magic[8] = { #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) +#define MAX_JOLIET_ID_NUM 46656 /* 3 base 36 digits */ + /* * Manage extra records. */ @@ -764,7 +766,7 @@ struct iso9660 { #ifdef HAVE_ZLIB_H /* * Copy a compressed file to iso9660.zisofs.temp_fd - * and also copy a uncompressed file(original file) to + * and also copy an uncompressed file(original file) to * iso9660.temp_fd . If the number of logical block * of the compressed file is less than the number of * logical block of the uncompressed file, use it and @@ -1008,7 +1010,7 @@ static int idr_start(struct archive_write *, struct idr *, static void idr_register(struct idr *, struct isoent *, int, int); static void idr_extend_identifier(struct idrent *, int, int); -static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); +static int idr_resolve(struct idr *, void (*)(unsigned char *, int)); static void idr_set_num(unsigned char *, int); static void idr_set_num_beutf16(unsigned char *, int); static int isoent_gen_iso9660_identifier(struct archive_write *, @@ -1057,8 +1059,7 @@ archive_write_set_format_iso9660(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660"); /* If another format was already registered, unregister it. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); iso9660 = calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { @@ -1168,6 +1169,7 @@ archive_write_set_format_iso9660(struct archive *_a) iso9660->cur_dirent = iso9660->primary.rootent; archive_string_init(&(iso9660->cur_dirstr)); if (archive_string_ensure(&(iso9660->cur_dirstr), 1) == NULL) { + free(iso9660->cur_dirent); free(iso9660); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -1381,7 +1383,7 @@ iso9660_options(struct archive_write *a, const char *key, const char *value) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Option ``%s'' " - "is not supported on this platform.", key); + "is not supported on this platform", key); return (ARCHIVE_FATAL); #endif } @@ -1424,8 +1426,8 @@ iso9660_options(struct archive_write *a, const char *key, const char *value) break; case 'i': if (strcmp(key, "iso-level") == 0) { - if (value != NULL && value[1] == '\0' && - (value[0] >= '1' && value[0] <= '4')) { + if (value != NULL && value[0] >= '1' && + value[0] <= '4' && value[1] == '\0') { iso9660->opt.iso_level = value[0]-'0'; return (ARCHIVE_OK); } @@ -1503,7 +1505,7 @@ iso9660_options(struct archive_write *a, const char *key, const char *value) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "``zisofs'' " - "is not supported on this platform."); + "is not supported on this platform"); return (ARCHIVE_FATAL); #endif } @@ -1526,20 +1528,18 @@ invalid_value: static int iso9660_write_header(struct archive_write *a, struct archive_entry *entry) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; struct isofile *file; struct isoent *isoent; int r, ret = ARCHIVE_OK; - iso9660 = a->format_data; - iso9660->cur_file = NULL; iso9660->bytes_remaining = 0; iso9660->need_multi_extent = 0; if (archive_entry_filetype(entry) == AE_IFLNK && iso9660->opt.rr == OPT_RR_DISABLED) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignore symlink file."); + "Ignore symlink file"); iso9660->cur_file = NULL; return (ARCHIVE_WARN); } @@ -1549,7 +1549,7 @@ iso9660_write_header(struct archive_write *a, struct archive_entry *entry) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignore over %lld bytes file. " - "This file too large.", + "This file too large", MULTI_EXTENT_SIZE); iso9660->cur_file = NULL; return (ARCHIVE_WARN); @@ -1674,7 +1674,7 @@ wb_write_to_temp(struct archive_write *a, const void *buff, size_t s) * order to reduce a extra memory copy. */ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) { - struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; + struct iso9660 *iso9660 = a->format_data; xs = s % LOGICAL_BLOCK_SIZE; iso9660->wbuff_offset += s - xs; if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK) @@ -1856,11 +1856,9 @@ iso9660_finish_entry(struct archive_write *a) static int iso9660_close(struct archive_write *a) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; int ret, blocks; - iso9660 = a->format_data; - /* * Write remaining data out to the temporary file. */ @@ -2103,7 +2101,7 @@ iso9660_close(struct archive_write *a) if (iso9660->directories_too_deep != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: Directories too deep.", + "%s: Directories too deep", archive_entry_pathname( iso9660->directories_too_deep->file->entry)); return (ARCHIVE_WARN); @@ -2118,11 +2116,9 @@ iso9660_close(struct archive_write *a) static int iso9660_free(struct archive_write *a) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; int i, ret; - iso9660 = a->format_data; - /* Close the temporary file. */ if (iso9660->temp_fd >= 0) close(iso9660->temp_fd); @@ -2918,10 +2914,10 @@ set_directory_record_rr(unsigned char *bp, int dr_len, extra_tell_used_size(&ctl, length); if (extra_space(&ctl) < 6) { bp = extra_next_record(&ctl, 6); - nmmax = extra_space(&ctl); - if (nmmax > 0xff) - nmmax = 0xff; } + nmmax = extra_space(&ctl); + if (nmmax > 0xff) + nmmax = 0xff; if (bp != NULL) { bp[1] = 'N'; bp[2] = 'M'; @@ -3036,8 +3032,8 @@ set_directory_record_rr(unsigned char *bp, int dr_len, const char *sl; char sl_last; - if (extra_space(&ctl) < 7) - bp = extra_next_record(&ctl, 7); + if (extra_space(&ctl) < 12) + bp = extra_next_record(&ctl, 12); sl = file->symlink.s; sl_last = '\0'; if (bp != NULL) { @@ -3616,7 +3612,7 @@ get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent, static inline unsigned char * wb_buffptr(struct archive_write *a) { - struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; + struct iso9660 *iso9660 = a->format_data; return (&(iso9660->wbuff[sizeof(iso9660->wbuff) - iso9660->wbuff_remaining])); @@ -3625,7 +3621,7 @@ wb_buffptr(struct archive_write *a) static int wb_write_out(struct archive_write *a) { - struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; + struct iso9660 *iso9660 = a->format_data; size_t wsize, nw; int r; @@ -3650,7 +3646,7 @@ wb_write_out(struct archive_write *a) static int wb_consume(struct archive_write *a, size_t size) { - struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; + struct iso9660 *iso9660 = a->format_data; if (size > iso9660->wbuff_remaining || iso9660->wbuff_remaining == 0) { @@ -3671,7 +3667,7 @@ wb_consume(struct archive_write *a, size_t size) static int wb_set_offset(struct archive_write *a, int64_t off) { - struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; + struct iso9660 *iso9660 = a->format_data; int64_t used, ext_bytes; if (iso9660->wbuff_type != WB_TO_TEMP) { @@ -3799,7 +3795,7 @@ set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, isoent = isoent_find_entry(vdd->rootent, ids); if (isoent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Not Found %s `%s'.", + "Not Found %s `%s'", label, ids); return (ARCHIVE_FATAL); } @@ -3838,7 +3834,7 @@ set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, static int write_VD(struct archive_write *a, struct vdd *vdd) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; unsigned char *bp; uint16_t volume_set_size = 1; char identifier[256]; @@ -3847,7 +3843,6 @@ write_VD(struct archive_write *a, struct vdd *vdd) unsigned char vd_ver, fst_ver; int r; - iso9660 = a->format_data; switch (vdd->vdd_type) { case VDD_JOLIET: vdt = VDT_SUPPLEMENTARY; @@ -3985,10 +3980,9 @@ write_VD(struct archive_write *a, struct vdd *vdd) static int write_VD_boot_record(struct archive_write *a) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; unsigned char *bp; - iso9660 = a->format_data; bp = wb_buffptr(a) -1; /* Volume Descriptor Type */ set_VD_bp(bp, VDT_BOOT_RECORD, 1); @@ -4056,7 +4050,7 @@ set_option_info(struct archive_string *info, int *opt, const char *key, static int write_information_block(struct archive_write *a) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; char buf[128]; const char *v; int opt, r; @@ -4064,7 +4058,6 @@ write_information_block(struct archive_write *a) size_t info_size = LOGICAL_BLOCK_SIZE * NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; - iso9660 = (struct iso9660 *)a->format_data; if (info_size > wb_remaining(a)) { r = wb_write_out(a); if (r != ARCHIVE_OK) @@ -4281,9 +4274,11 @@ _write_path_table(struct archive_write *a, int type_m, int depth, set_num_731(bp+3, np->dir_location); /* Parent Directory Number */ if (type_m) - set_num_722(bp+7, np->parent->dir_number); + set_num_722(bp+7, + np->parent != NULL ? np->parent->dir_number : 0); else - set_num_721(bp+7, np->parent->dir_number); + set_num_721(bp+7, + np->parent != NULL ? np->parent->dir_number : 0); /* Directory Identifier */ if (np->identifier == NULL) bp[9] = 0; @@ -4712,14 +4707,12 @@ cleanup_backslash_2(wchar_t *p) static int isofile_gen_utility_names(struct archive_write *a, struct isofile *file) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; const char *pathname; char *p, *dirname, *slash; size_t len; int ret = ARCHIVE_OK; - iso9660 = a->format_data; - archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->basename_utf16)); @@ -4910,20 +4903,29 @@ isofile_gen_utility_names(struct archive_write *a, struct isofile *file) * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -5526,7 +5528,7 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) static int get_path_component(char *name, size_t n, const char *fn) { - char *p; + const char *p; size_t l; p = strchr(fn, '/'); @@ -5549,6 +5551,7 @@ get_path_component(char *name, size_t n, const char *fn) static int isoent_tree(struct archive_write *a, struct isoent **isoentpp) { + struct iso9660 *iso9660 = a->format_data; #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 @@ -5556,7 +5559,6 @@ isoent_tree(struct archive_write *a, struct isoent **isoentpp) #else char name[256]; #endif - struct iso9660 *iso9660 = a->format_data; struct isoent *dent, *isoent, *np; struct isofile *f1, *f2; const char *fn, *p; @@ -5903,21 +5905,22 @@ idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) static void idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) { - unsigned char *p; - int wnp_ext_off; - - wnp_ext_off = wnp->isoent->ext_off; - if (wnp->noff + numsize != wnp_ext_off) { - p = (unsigned char *)wnp->isoent->identifier; - /* Extend the filename; foo.c --> foo___.c */ - memmove(p + wnp->noff + numsize, p + wnp_ext_off, + if (wnp->noff + numsize != wnp->isoent->ext_off) { + /* + * Extend the filename; foo.c --> foo___.c + * + * The caller must verify that enough memory is available. + */ + memmove(wnp->isoent->identifier + wnp->noff + numsize, + wnp->isoent->identifier + wnp->isoent->ext_off, wnp->isoent->ext_len + nullsize); - wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; - wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; + wnp->isoent->ext_off = wnp->noff + numsize; + wnp->isoent->id_len = + wnp->isoent->ext_off + wnp->isoent->ext_len; } } -static void +static int idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) { struct idrent *n; @@ -5927,10 +5930,13 @@ idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) idr_extend_identifier(n, idr->num_size, idr->null_size); p = (unsigned char *)n->isoent->identifier + n->noff; do { + if (n->avail->rename_num >= MAX_JOLIET_ID_NUM) + return (-ERANGE); fsetnum(p, n->avail->rename_num++); } while (!__archive_rb_tree_insert_node( &(idr->rbtree), &(n->rbnode))); } + return (0); } static void @@ -5980,7 +5986,7 @@ static int isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; struct isoent *np; char *p; int l, r; @@ -5990,11 +5996,14 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 }; + const int num_size = 3; + const int dot_size = 1; + const int version_size = 2; + const int null_size = 1; if (isoent->children.cnt == 0) - return (0); + return (ARCHIVE_OK); - iso9660 = a->format_data; char_map = idr->char_map; if (iso9660->opt.iso_level <= 3) { allow_ldots = 0; @@ -6030,7 +6039,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, fnmax = ffmax = dnmax = 207; } - r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); + r = idr_start(a, idr, isoent->children.cnt, ffmax, num_size, null_size, &rb_ops); if (r < 0) return (r); @@ -6039,7 +6048,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, int ext_off, noff, weight; l = (int)np->file->basename.length; - p = malloc(l+31+2+1); + p = malloc(l + num_size + dot_size + version_size + null_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -6189,13 +6198,19 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, noff = ext_off - 1; else noff = ext_off; + if (noff < 0) + noff = 0; } /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier. */ - idr_resolve(idr, idr_set_num); + r = idr_resolve(idr, idr_set_num); + if (r < 0) { + archive_set_error(&a->archive, -r, "Too many duplicated identifiers"); + return (ARCHIVE_FATAL); + } /* Add a period and a version number to identifiers. */ for (np = isoent->children.first; np != NULL; np = np->chnext) { @@ -6230,7 +6245,7 @@ static int isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, struct idr *idr) { - struct iso9660 *iso9660; + struct iso9660 *iso9660 = a->format_data; struct isoent *np; unsigned char *p; size_t l; @@ -6239,17 +6254,18 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, static const struct archive_rb_tree_ops rb_ops = { isoent_cmp_node_joliet, isoent_cmp_key_joliet }; + const int num_size = 6; + const int null_size = 2; if (isoent->children.cnt == 0) - return (0); + return (ARCHIVE_OK); - iso9660 = a->format_data; if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) ffmax = 206; else ffmax = 128; - r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); + r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, num_size, null_size, &rb_ops); if (r < 0) return (r); @@ -6265,7 +6281,7 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, if ((l = np->file->basename_utf16.length) > ffmax) l = ffmax; - p = malloc((l+1)*2); + p = malloc(l + num_size + null_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); @@ -6334,12 +6350,18 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, noff = ext_off - 2; else noff = ext_off; + if (noff < 0) + noff = 0; /* Register entry to the identifier resolver. */ idr_register(idr, np, weight, noff); } /* Resolve duplicate identifier with Joliet Volume. */ - idr_resolve(idr, idr_set_num_beutf16); + r = idr_resolve(idr, idr_set_num_beutf16); + if (r < 0) { + archive_set_error(&a->archive, -r, "Too many duplicated identifiers"); + return (ARCHIVE_FATAL); + } return (ARCHIVE_OK); } @@ -6727,7 +6749,12 @@ isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, /* * The mvent becomes a child of the rr_moved entry. */ - isoent_add_child_tail(rrmoved, mvent); + if (!isoent_add_child_tail(rrmoved, mvent)) { + _isoent_free(mvent); + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unable to insert rr_moved entry"); + return (ARCHIVE_FATAL); + } archive_entry_set_nlink(rrmoved->file->entry, archive_entry_nlink(rrmoved->file->entry) + 1); /* @@ -6820,7 +6847,15 @@ _compare_path_table(const void *v1, const void *v2) p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ - cmp = p1->parent->dir_number - p2->parent->dir_number; + if (p1->parent == NULL || p2->parent == NULL) { + if (p1->parent == p2->parent) + cmp = 0; + else if (p1->parent == NULL) + return (-1); + else + return (1); + } else + cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); @@ -6863,7 +6898,15 @@ _compare_path_table_joliet(const void *v1, const void *v2) p2 = *((const struct isoent **)(uintptr_t)v2); /* Compare parent directory number */ - cmp = p1->parent->dir_number - p2->parent->dir_number; + if (p1->parent == NULL || p2->parent == NULL) { + if (p1->parent == p2->parent) + cmp = 0; + else if (p1->parent == NULL) + return (-1); + else + return (1); + } else + cmp = p1->parent->dir_number - p2->parent->dir_number; if (cmp != 0) return (cmp); @@ -7080,7 +7123,7 @@ isoent_make_path_table(struct archive_write *a) * See also ISO9660 Standard 9.4. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Too many directories(%d) over 65535.", dir_number); + "Too many directories(%d) over 65535", dir_number); return (ARCHIVE_FATAL); } @@ -7203,7 +7246,7 @@ isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Boot image file(``%s'') size is too big " - "for fd type.", + "for fd type", iso9660->el_torito.boot_filename.s); return (ARCHIVE_FATAL); } @@ -7465,8 +7508,9 @@ zisofs_init(struct archive_write *a, struct isofile *file) (uint32_t)archive_entry_size(file->entry); /* Calculate a size of Block Pointers of zisofs. */ - _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) - >> file->zisofs.log2_bs; + _ceil = (size_t)(((uint64_t)file->zisofs.uncompressed_size + + (ZF_BLOCK_SIZE - 1)) + >> file->zisofs.log2_bs); iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; iso9660->zisofs.block_pointers_idx = 0; @@ -7964,7 +8008,7 @@ zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't initialize zisofs decompression."); + "Can't initialize zisofs decompression"); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; diff --git a/libarchive/archive_write_set_format_mtree.c b/libarchive/archive_write_set_format_mtree.c index 8131574c8d..b3433507e9 100644 --- a/libarchive/archive_write_set_format_mtree.c +++ b/libarchive/archive_write_set_format_mtree.c @@ -117,7 +117,7 @@ struct mtree_entry { int64_t ino; }; -struct mtree_writer { +struct mtree { struct mtree_entry *mtree_entry; struct mtree_entry *root; struct mtree_entry *cur_dirent; @@ -220,10 +220,10 @@ static int attr_counter_inc(struct attr_counter **, struct attr_counter *, struct attr_counter *, struct mtree_entry *); static struct attr_counter * attr_counter_new(struct mtree_entry *, struct attr_counter *); -static int attr_counter_set_collect(struct mtree_writer *, +static int attr_counter_set_collect(struct mtree *, struct mtree_entry *); -static void attr_counter_set_free(struct mtree_writer *); -static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *); +static void attr_counter_set_free(struct mtree *); +static int get_global_set_keys(struct mtree *, struct mtree_entry *); static int mtree_entry_add_child_tail(struct mtree_entry *, struct mtree_entry *); static int mtree_entry_create_virtual_dir(struct archive_write *, const char *, @@ -236,14 +236,14 @@ static int mtree_entry_exchange_same_entry(struct archive_write *, static void mtree_entry_free(struct mtree_entry *); static int mtree_entry_new(struct archive_write *, struct archive_entry *, struct mtree_entry **); -static void mtree_entry_register_free(struct mtree_writer *); -static void mtree_entry_register_init(struct mtree_writer *); +static void mtree_entry_register_free(struct mtree *); +static void mtree_entry_register_init(struct mtree *); static int mtree_entry_setup_filenames(struct archive_write *, struct mtree_entry *, struct archive_entry *); static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **); -static void sum_init(struct mtree_writer *); -static void sum_update(struct mtree_writer *, const void *, size_t); -static void sum_final(struct mtree_writer *, struct reg_info *); +static void sum_init(struct mtree *); +static void sum_update(struct mtree *, const void *, size_t); +static void sum_final(struct mtree *, struct reg_info *); static void sum_write(struct archive_string *, struct reg_info *); static int write_mtree_entry(struct archive_write *, struct mtree_entry *); static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *); @@ -307,14 +307,14 @@ static const uint32_t crctab[] = { static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ - /* !"$%&'()*+,-./ EXCLUSION:0x20( ) 0x23(#) */ - 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ - /* 0123456789:;<>? EXCLUSION:0x3d(=) */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ + /* !"$%&'()+,-./ EXCLUSION:0x20( ) 0x23(#) 0x2a(*) */ + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 20 - 2F */ + /* 0123456789:;<> EXCLUSION:0x3d(=) 0x3f(?) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ - /* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\) */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */ + /* PQRSTUVWXYZ]^_ EXCLUSION:0x5b([) 0x5c(\) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ @@ -358,7 +358,7 @@ mtree_quote(struct archive_string *s, const char *str) * Indent a line as the mtree utility does so it is readable for people. */ static void -mtree_indent(struct mtree_writer *mtree) +mtree_indent(struct mtree *mtree) { int i, fn, nd, pd; const char *r, *s, *x; @@ -434,7 +434,7 @@ mtree_indent(struct mtree_writer *mtree) * collected by the attr_counter_set_collect() function. */ static void -write_global(struct mtree_writer *mtree) +write_global(struct mtree *mtree) { struct archive_string setstr; struct archive_string unsetstr; @@ -636,7 +636,7 @@ attr_counter_inc(struct attr_counter **top, struct attr_counter *ac, * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set. */ static int -attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me) +attr_counter_set_collect(struct mtree *mtree, struct mtree_entry *me) { struct attr_counter *ac, *last; struct attr_counter_set *acs = &mtree->acs; @@ -713,7 +713,7 @@ attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me) } static void -attr_counter_set_free(struct mtree_writer *mtree) +attr_counter_set_free(struct mtree *mtree) { struct attr_counter_set *acs = &mtree->acs; @@ -724,7 +724,7 @@ attr_counter_set_free(struct mtree_writer *mtree) } static int -get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me) +get_global_set_keys(struct mtree *mtree, struct mtree_entry *me) { int keys; @@ -801,6 +801,8 @@ mtree_entry_new(struct archive_write *a, struct archive_entry *entry, archive_strcpy(&me->symlink, s); me->nlink = archive_entry_nlink(entry); me->filetype = archive_entry_filetype(entry); + if (me->filetype == AE_IFLNK && me->symlink.s == NULL) + archive_strcpy(&me->symlink, ""); me->mode = archive_entry_mode(entry) & 07777; me->uid = archive_entry_uid(entry); me->gid = archive_entry_gid(entry); @@ -911,7 +913,7 @@ static int archive_write_mtree_header(struct archive_write *a, struct archive_entry *entry) { - struct mtree_writer *mtree= a->format_data; + struct mtree *mtree = a->format_data; struct mtree_entry *mtree_entry; int r, r2; @@ -954,7 +956,7 @@ archive_write_mtree_header(struct archive_write *a, static int write_mtree_entry(struct archive_write *a, struct mtree_entry *me) { - struct mtree_writer *mtree = a->format_data; + struct mtree *mtree = a->format_data; struct archive_string *str; int keys, ret; @@ -988,7 +990,8 @@ write_mtree_entry(struct archive_write *a, struct mtree_entry *me) * a full pathname. */ mtree_quote(str, me->parentdir.s); - archive_strappend_char(str, '/'); + if (strcmp(me->basename.s, ".") != 0) + archive_strappend_char(str, '/'); } mtree_quote(str, me->basename.s); @@ -1104,7 +1107,7 @@ write_mtree_entry(struct archive_write *a, struct mtree_entry *me) static int write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n) { - struct mtree_writer *mtree = a->format_data; + struct mtree *mtree = a->format_data; int ret; if (n->parentdir.s) { @@ -1139,12 +1142,14 @@ write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n) static int write_mtree_entry_tree(struct archive_write *a) { - struct mtree_writer *mtree = a->format_data; + struct mtree *mtree = a->format_data; struct mtree_entry *np = mtree->root; struct archive_rb_node *n; int ret; do { + if (np->dir_info == NULL) + break; if (mtree->output_global_set) { /* * Collect attribute information to know which value @@ -1240,7 +1245,7 @@ write_mtree_entry_tree(struct archive_write *a) static int archive_write_mtree_finish_entry(struct archive_write *a) { - struct mtree_writer *mtree = a->format_data; + struct mtree *mtree = a->format_data; struct mtree_entry *me; if ((me = mtree->mtree_entry) == NULL) @@ -1256,7 +1261,7 @@ archive_write_mtree_finish_entry(struct archive_write *a) static int archive_write_mtree_close(struct archive_write *a) { - struct mtree_writer *mtree= a->format_data; + struct mtree *mtree = a->format_data; int ret; if (mtree->root != NULL) { @@ -1273,7 +1278,7 @@ archive_write_mtree_close(struct archive_write *a) static ssize_t archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n) { - struct mtree_writer *mtree= a->format_data; + struct mtree *mtree = a->format_data; if (n > mtree->entry_bytes_remaining) n = (size_t)mtree->entry_bytes_remaining; @@ -1292,7 +1297,7 @@ archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n) static int archive_write_mtree_free(struct archive_write *a) { - struct mtree_writer *mtree= a->format_data; + struct mtree *mtree = a->format_data; if (mtree == NULL) return (ARCHIVE_OK); @@ -1312,7 +1317,7 @@ static int archive_write_mtree_options(struct archive_write *a, const char *key, const char *value) { - struct mtree_writer *mtree= a->format_data; + struct mtree *mtree = a->format_data; int keybit = 0; switch (key[0]) { @@ -1424,12 +1429,11 @@ static int archive_write_set_format_mtree_default(struct archive *_a, const char *fn) { struct archive_write *a = (struct archive_write *)_a; - struct mtree_writer *mtree; + struct mtree *mtree; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn); - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); if ((mtree = calloc(1, sizeof(*mtree))) == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -1476,9 +1480,7 @@ archive_write_set_format_mtree_classic(struct archive *_a) "archive_write_set_format_mtree_classic"); if (r == ARCHIVE_OK) { struct archive_write *a = (struct archive_write *)_a; - struct mtree_writer *mtree; - - mtree = (struct mtree_writer *)a->format_data; + struct mtree *mtree = a->format_data; /* Set to output a mtree archive in classic format. */ mtree->classic = 1; @@ -1490,7 +1492,7 @@ archive_write_set_format_mtree_classic(struct archive *_a) } static void -sum_init(struct mtree_writer *mtree) +sum_init(struct mtree *mtree) { mtree->compute_sum = 0; @@ -1551,7 +1553,7 @@ sum_init(struct mtree_writer *mtree) } static void -sum_update(struct mtree_writer *mtree, const void *buff, size_t n) +sum_update(struct mtree *mtree, const void *buff, size_t n) { if (mtree->compute_sum & F_CKSUM) { /* @@ -1609,8 +1611,9 @@ sum_update(struct mtree_writer *mtree, const void *buff, size_t n) } static void -sum_final(struct mtree_writer *mtree, struct reg_info *reg) +sum_final(struct mtree *mtree, struct reg_info *reg) { + struct ae_digest digest; if (mtree->compute_sum & F_CKSUM) { uint64_t len; @@ -1620,40 +1623,34 @@ sum_final(struct mtree_writer *mtree, struct reg_info *reg) reg->crc = ~mtree->crc; } #ifdef ARCHIVE_HAS_MD5 - if ((mtree->compute_sum & F_MD5) - && !(reg->mset_digest & AE_MSET_DIGEST_MD5)) + if (mtree->compute_sum & F_MD5) - archive_md5_final(&mtree->md5ctx, reg->digest.md5); + archive_md5_final(&mtree->md5ctx, (reg->mset_digest & AE_MSET_DIGEST_MD5) ? digest.md5 : reg->digest.md5); #endif #ifdef ARCHIVE_HAS_RMD160 - if ((mtree->compute_sum & F_RMD160) - && !(reg->mset_digest & AE_MSET_DIGEST_RMD160)) + if (mtree->compute_sum & F_RMD160) - archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160); + archive_rmd160_final(&mtree->rmd160ctx, (reg->mset_digest & AE_MSET_DIGEST_RMD160) ? digest.rmd160 : reg->digest.rmd160); #endif #ifdef ARCHIVE_HAS_SHA1 - if ((mtree->compute_sum & F_SHA1) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA1)) + if (mtree->compute_sum & F_SHA1) - archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1); + archive_sha1_final(&mtree->sha1ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA1) ? digest.sha1 : reg->digest.sha1); #endif #ifdef ARCHIVE_HAS_SHA256 - if ((mtree->compute_sum & F_SHA256) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA256)) + if (mtree->compute_sum & F_SHA256) - archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256); + archive_sha256_final(&mtree->sha256ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA256) ? digest.sha256 : reg->digest.sha256); #endif #ifdef ARCHIVE_HAS_SHA384 - if ((mtree->compute_sum & F_SHA384) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA384)) + if (mtree->compute_sum & F_SHA384) - archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384); + archive_sha384_final(&mtree->sha384ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA384) ? digest.sha384 : reg->digest.sha384); #endif #ifdef ARCHIVE_HAS_SHA512 - if ((mtree->compute_sum & F_SHA512) - && !(reg->mset_digest & AE_MSET_DIGEST_SHA512)) + if (mtree->compute_sum & F_SHA512) - archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512); + archive_sha512_final(&mtree->sha512ctx, (reg->mset_digest & AE_MSET_DIGEST_SHA512) ? digest.sha512 : reg->digest.sha512); #endif /* Save what types of sum are computed. */ reg->compute_sum = mtree->compute_sum; @@ -1886,20 +1883,29 @@ mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -1986,7 +1992,7 @@ mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname, } static void -mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file) +mtree_entry_register_add(struct mtree *mtree, struct mtree_entry *file) { file->next = NULL; *mtree->file_list.last = file; @@ -1994,14 +2000,14 @@ mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file) } static void -mtree_entry_register_init(struct mtree_writer *mtree) +mtree_entry_register_init(struct mtree *mtree) { mtree->file_list.first = NULL; mtree->file_list.last = &(mtree->file_list.first); } static void -mtree_entry_register_free(struct mtree_writer *mtree) +mtree_entry_register_free(struct mtree *mtree) { struct mtree_entry *file, *file_next; @@ -2041,7 +2047,7 @@ mtree_entry_find_child(struct mtree_entry *parent, const char *child_name) static int get_path_component(char *name, size_t n, const char *fn) { - char *p; + const char *p; size_t l; p = strchr(fn, '/'); @@ -2064,6 +2070,7 @@ get_path_component(char *name, size_t n, const char *fn) static int mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) { + struct mtree *mtree = a->format_data; #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 @@ -2071,7 +2078,6 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) #else char name[256]; #endif - struct mtree_writer *mtree = (struct mtree_writer *)a->format_data; struct mtree_entry *dent, *file, *np; const char *fn, *p; int l, r; @@ -2079,6 +2085,11 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) file = *filep; if (file->parentdir.length == 0 && file->basename.length == 1 && file->basename.s[0] == '.') { + if (file->filetype != AE_IFDIR) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Root entry '.' must be a directory"); + return (ARCHIVE_FAILED); + } file->parent = file; if (mtree->root != NULL) { np = mtree->root; @@ -2262,7 +2273,7 @@ mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np, if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found duplicate entries for `%s' with " - "differing file types.", + "differing file types", np->pathname.s); return (ARCHIVE_FAILED); } diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index 66e6d75196..1e27260501 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -40,10 +40,13 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" +#define PAX_MAX_ALIGN (1U << 20) + struct sparse_block { struct sparse_block *next; int is_hole; @@ -63,6 +66,10 @@ struct pax { struct archive_string_conv *sconv_utf8; int opt_binary; + /* If non-zero, align regular file data to this many bytes in the + * uncompressed stream (power of two, multiple of 512). */ + size_t align; + unsigned flags; #define WRITE_SCHILY_XATTR (1 << 0) #define WRITE_LIBARCHIVE_XATTR (1 << 1) @@ -78,6 +85,8 @@ static void add_pax_attr_int(struct archive_string *, static void add_pax_attr_time(struct archive_string *, const char *key, int64_t sec, unsigned long nanos); +static size_t add_pax_padding_prefix(struct archive_string *, + size_t cur_len, size_t target_len); static int add_pax_acl(struct archive_write *, struct archive_entry *, struct pax *, int); static ssize_t archive_write_pax_data(struct archive_write *, @@ -89,6 +98,7 @@ static int archive_write_pax_header(struct archive_write *, struct archive_entry *); static int archive_write_pax_options(struct archive_write *, const char *, const char *); +static int write_pax_padding(struct archive_write *, size_t); static char *base64_encode(const char *src, size_t len); static char *build_gnu_sparse_name(char *dest, const char *src); static char *build_pax_attribute_name(char *dest, const char *src); @@ -135,8 +145,7 @@ archive_write_set_format_pax(struct archive *_a) archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_pax"); - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); pax = calloc(1, sizeof(*pax)); if (pax == NULL) { @@ -163,7 +172,7 @@ static int archive_write_pax_options(struct archive_write *a, const char *key, const char *val) { - struct pax *pax = (struct pax *)a->format_data; + struct pax *pax = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -222,6 +231,25 @@ archive_write_pax_options(struct archive_write *a, const char *key, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: invalid xattr header name"); return (ret); + } else if (strcmp(key, "align") == 0) { + unsigned long v; + char *end; + + if (val == NULL || val[0] == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: align option needs a value"); + return (ARCHIVE_FAILED); + } + v = strtoul(val, &end, 10); + if (*end != '\0' || v == 0 || (v % 512) != 0 || + (v & (v - 1)) != 0 || v > PAX_MAX_ALIGN) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: align must be a power of two, a multiple " + "of 512, and no larger than 1 MiB"); + return (ARCHIVE_FAILED); + } + pax->align = (size_t)v; + return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options @@ -360,6 +388,81 @@ add_pax_attr_binary(struct archive_string *as, const char *key, archive_strappend_char(as, '\n'); } +/* + * Append the length/key prefix of an ignorable "LIBARCHIVE.pad" record whose + * full length rounds the pax body up to 'target_len'. Return the number of + * value/newline bytes that must be emitted after the in-memory header. + */ +static size_t +add_pax_padding_prefix(struct archive_string *as, size_t cur_len, + size_t target_len) +{ + static const char key[] = "LIBARCHIVE.pad"; + char tmp[1 + 3 * sizeof(int64_t)]; + char *length; + size_t prefix_len, record_len; + + if (target_len <= cur_len) + return (0); + record_len = target_len - cur_len; + tmp[sizeof(tmp) - 1] = 0; + length = format_int(tmp + sizeof(tmp) - 1, (int64_t)record_len); + prefix_len = strlen(length) + 1 + sizeof(key) - 1 + 1; + if (record_len <= prefix_len) + return (0); /* not enough room; leave unaligned */ + + archive_strcat(as, length); + archive_strappend_char(as, ' '); + archive_strcat(as, key); + archive_strappend_char(as, '='); + return (record_len - prefix_len); +} + +/* Emit a deferred pax padding value followed by its record newline. */ +static int +write_pax_padding(struct archive_write *a, size_t length) +{ + char padding[1024]; + + if (length == 0) + return (ARCHIVE_OK); + memset(padding, 'X', sizeof(padding)); + while (length > 1) { + size_t to_write = length - 1; + int r; + + if (to_write > sizeof(padding)) + to_write = sizeof(padding); + r = __archive_write_output(a, padding, to_write); + if (r != ARCHIVE_OK) + return (r); + length -= to_write; + } + return (__archive_write_output(a, "\n", 1)); +} + +/* + * For the pax "align" option: given entry offset 'off' (a multiple of 512) and + * current extended-header body length 'l0', return the body length needed so + * the file data (after the x header) lands on 'align', or 'l0' if already + * aligned. + */ +static size_t +pax_align_body_len(uint64_t off, size_t l0, size_t align) +{ + uint64_t amask = (uint64_t)align - 1; + /* Data offset with no extra padding (no x header when l0 == 0). */ + uint64_t natural_off = (l0 == 0) ? off + 512 + : off + 1024 + (((uint64_t)l0 + 511) & ~(uint64_t)511); + + if ((natural_off & amask) == 0) + return (l0); + + /* Grow the body (past l0 plus pad-record room) to land data on align. */ + uint64_t base = off + 1024; + return ((size_t)(((base + l0 + 64 + amask) & ~amask) - base)); +} + static void archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name, const void *value, size_t value_len) @@ -577,6 +680,7 @@ static int archive_write_pax_header(struct archive_write *a, struct archive_entry *entry_original) { + struct pax *pax = a->format_data; struct archive_entry *entry_main; const char *p; const char *suffix; @@ -584,7 +688,6 @@ archive_write_pax_header(struct archive_write *a, int acl_types; int sparse_count; uint64_t sparse_total, real_size; - struct pax *pax; const char *hardlink; const char *path = NULL, *linkpath = NULL; const char *uname = NULL, *gname = NULL; @@ -592,6 +695,7 @@ archive_write_pax_header(struct archive_write *a, size_t mac_metadata_size; struct archive_string_conv *sconv; size_t hardlink_length, path_length, linkpath_length; + size_t pax_header_padding = 0; size_t uname_length, gname_length; char paxbuff[512]; @@ -603,7 +707,6 @@ archive_write_pax_header(struct archive_write *a, ret = ARCHIVE_OK; need_extension = 0; - pax = (struct pax *)a->format_data; const time_t ustar_max_mtime = get_ustar_max_mtime(); @@ -676,7 +779,8 @@ archive_write_pax_header(struct archive_write *a, const wchar_t *wp; wp = archive_entry_pathname_w(entry_original); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); @@ -1062,7 +1166,7 @@ archive_write_pax_header(struct archive_write *a, } /* If numeric GID is too large, add 'gid' to pax extended attrs. */ - if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) { + if (archive_entry_gid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "gid", archive_entry_gid(entry_main)); need_extension = 1; @@ -1078,7 +1182,7 @@ archive_write_pax_header(struct archive_write *a, } /* If numeric UID is too large, add 'uid' to pax extended attrs. */ - if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) { + if (archive_entry_uid(entry_main) >= (1 << 18)) { add_pax_attr_int(&(pax->pax_header), "uid", archive_entry_uid(entry_main)); need_extension = 1; @@ -1335,14 +1439,6 @@ archive_write_pax_header(struct archive_write *a, if (archive_entry_filetype(entry_main) != AE_IFREG) archive_entry_set_size(entry_main, 0); - /* - * Pax-restricted does not store data for hardlinks, in order - * to improve compatibility with ustar. - */ - if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && - hardlink != NULL) - archive_entry_set_size(entry_main, 0); - /* * XXX Full pax interchange format does permit a hardlink * entry to have data associated with it. I'm not supporting @@ -1409,6 +1505,21 @@ archive_write_pax_header(struct archive_write *a, return (ARCHIVE_FATAL); } + /* Pad the extended header so contiguous file data starts on a pax->align + * boundary (for reflinking); short and sparse files are left alone. */ + if (pax->align > 0 + && archive_entry_filetype(entry_main) == AE_IFREG + && hardlink == NULL + && sparse_count == 0 + && real_size >= (uint64_t)pax->align) { + size_t l0 = archive_strlen(&(pax->pax_header)); + size_t needed = pax_align_body_len( + (uint64_t)a->filter_first->bytes_written, l0, pax->align); + if (needed != l0) + pax_header_padding = add_pax_padding_prefix( + &(pax->pax_header), l0, needed); + } + /* If we built any extended attributes, write that entry first. */ if (archive_strlen(&(pax->pax_header)) > 0) { struct archive_entry *pax_attr_entry; @@ -1417,11 +1528,20 @@ archive_write_pax_header(struct archive_write *a, __LA_MODE_T mode; pax_attr_entry = archive_entry_new2(&a->archive); + if (pax_attr_entry == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Out of memory"); + archive_entry_free(entry_main); + archive_string_free(&entry_name); + return (ARCHIVE_FATAL); + } + p = entry_name.s; archive_entry_set_pathname(pax_attr_entry, build_pax_attribute_name(pax_entry_name, p)); archive_entry_set_size(pax_attr_entry, - archive_strlen(&(pax->pax_header))); + archive_strlen(&(pax->pax_header)) + + pax_header_padding); /* Copy uid/gid (but clip to ustar limits). */ uid = archive_entry_uid(entry_main); if (uid >= 1 << 18) @@ -1471,7 +1591,7 @@ archive_write_pax_header(struct archive_write *a, if (r < ARCHIVE_WARN) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "archive_write_pax_header: " - "'x' header failed?! This can't happen.\n"); + "'x' header failed?! This can't happen"); archive_entry_free(entry_main); archive_string_free(&entry_name); return (ARCHIVE_FATAL); @@ -1487,7 +1607,8 @@ archive_write_pax_header(struct archive_write *a, return (ARCHIVE_FATAL); } - pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)); + pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)) + + pax_header_padding; pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); @@ -1499,8 +1620,14 @@ archive_write_pax_header(struct archive_write *a, archive_string_free(&entry_name); return (ARCHIVE_FATAL); } + r = write_pax_padding(a, pax_header_padding); + if (r != ARCHIVE_OK) { + archive_entry_free(entry_main); + archive_string_free(&entry_name); + return (ARCHIVE_FATAL); + } /* Pad out the end of the entry. */ - r = __archive_write_nulls(a, (size_t)pax->entry_padding); + r = __archive_write_nulls(a, pax->entry_padding); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ archive_entry_free(entry_main); @@ -1572,18 +1699,21 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length, char *p; int need_slash = 0; /* Was there a trailing slash? */ size_t suffix_length = 98; /* 99 - 1 for trailing slash */ - size_t insert_length; + size_t insert_length, insert_name_length; /* Length of additional dir element to be added. */ - if (insert == NULL) + if (insert == NULL) { + insert_name_length = 0; insert_length = 0; - else + } else { + insert_name_length = strlen(insert); /* +2 here allows for '/' before and after the insert. */ - insert_length = strlen(insert) + 2; + insert_length = insert_name_length + 2; + } /* Step 0: Quick bailout in a common case. */ if (src_length < 100 && insert == NULL) { - strncpy(dest, src, src_length); + memcpy(dest, src, src_length); dest[src_length] = '\0'; return (dest); } @@ -1605,6 +1735,26 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length, } break; } + + /* + * Pathological case: after trimming trailing '/' characters and + * '/.' path elements, there is no filename component left. This + * happens for pathnames made entirely of '/' characters. Do not + * attempt to compute filename_end - 1 in that case, which would + * move the filename pointer in front of the input buffer and read + * one byte out of bounds. Emit a root-like ustar name instead. + */ + if (filename_end == src) { + p = dest; + if (insert != NULL) { + strcpy(p, insert); + p += strlen(insert); + } + *p++ = '/'; + *p = '\0'; + return (dest); + } + if (need_slash) suffix_length--; /* Find start of filename. */ @@ -1645,24 +1795,22 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length, suffix_end++; /* Step 4: Build the new name. */ - /* The OpenBSD strlcpy function is safer, but less portable. */ - /* Rather than maintain two versions, just use the strncpy version. */ p = dest; if (prefix_end > prefix) { - strncpy(p, prefix, prefix_end - prefix); + memcpy(p, prefix, (size_t)(prefix_end - prefix)); p += prefix_end - prefix; } if (suffix_end > suffix) { - strncpy(p, suffix, suffix_end - suffix); + memcpy(p, suffix, (size_t)(suffix_end - suffix)); p += suffix_end - suffix; } if (insert != NULL) { /* Note: assume insert does not have leading or trailing '/' */ - strcpy(p, insert); - p += strlen(insert); + memcpy(p, insert, insert_name_length); + p += insert_name_length; *p++ = '/'; } - strncpy(p, filename, filename_end - filename); + memcpy(p, filename, (size_t)(filename_end - filename)); p += filename_end - filename; if (need_slash) *p++ = '/'; @@ -1792,7 +1940,12 @@ build_gnu_sparse_name(char *dest, const char *src) } /* General case: build a ustar-compatible name adding - * "/GNUSparseFile/". */ + * "/GNUSparseFile.0/". */ + + if (p == src) { + strcpy(dest, "/GNUSparseFile.0/rootdir"); + return (dest); + } build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0"); return (dest); @@ -1808,9 +1961,8 @@ archive_write_pax_close(struct archive_write *a) static int archive_write_pax_free(struct archive_write *a) { - struct pax *pax; + struct pax *pax = a->format_data; - pax = (struct pax *)a->format_data; if (pax == NULL) return (ARCHIVE_OK); @@ -1826,11 +1978,10 @@ archive_write_pax_free(struct archive_write *a) static int archive_write_pax_finish_entry(struct archive_write *a) { - struct pax *pax; + struct pax *pax = a->format_data; uint64_t remaining; int ret; - pax = (struct pax *)a->format_data; remaining = pax->entry_bytes_remaining; if (remaining == 0) { while (pax->sparse_list) { @@ -1842,7 +1993,7 @@ archive_write_pax_finish_entry(struct archive_write *a) pax->sparse_list = sb; } } - ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding)); + ret = __archive_write_nulls(a, remaining + pax->entry_padding); pax->entry_bytes_remaining = pax->entry_padding = 0; return (ret); } @@ -1850,13 +2001,11 @@ archive_write_pax_finish_entry(struct archive_write *a) static ssize_t archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) { - struct pax *pax; + struct pax *pax = a->format_data; size_t ws; size_t total; int ret; - pax = (struct pax *)a->format_data; - /* * According to GNU PAX format 1.0, write a sparse map * before the body. @@ -1934,17 +2083,17 @@ url_encode(const char *in) for (s = in; *s != '\0'; s++) { if (*s < 33 || *s > 126 || *s == '%' || *s == '=') { - if (SIZE_MAX - out_len < 4) + if (archive_ckd_add_size(&out_len, out_len, 3)) return (NULL); - out_len += 3; } else { - if (SIZE_MAX - out_len < 2) + if (archive_ckd_add_size(&out_len, out_len, 1)) return (NULL); - out_len++; } } - out = malloc(out_len + 1); + if (archive_ckd_add_size(&out_len, out_len, 1)) + return (NULL); + out = malloc(out_len); if (out == NULL) return (NULL); @@ -1978,20 +2127,27 @@ base64_encode(const char *s, size_t len) 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s', 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', '8','9','+','/' }; - int v; + uint32_t v; char *d, *out; + size_t out_len; /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */ - out = malloc((len * 4 + 2) / 3 + 1); + if (archive_ckd_mul_size(&out_len, len, 4) || + archive_ckd_add_size(&out_len, out_len, 2)) + return (NULL); + out_len = out_len / 3; + if (archive_ckd_add_size(&out_len, out_len, 1)) + return (NULL); + out = malloc(out_len); if (out == NULL) return (NULL); d = out; /* Convert each group of 3 bytes into 4 characters. */ while (len >= 3) { - v = (((int)s[0] << 16) & 0xff0000) - | (((int)s[1] << 8) & 0xff00) - | (((int)s[2]) & 0x00ff); + v = ((uint32_t)(unsigned char)s[0] << 16) + | ((uint32_t)(unsigned char)s[1] << 8) + | (uint32_t)(unsigned char)s[2]; s += 3; len -= 3; *d++ = digits[(v >> 18) & 0x3f]; @@ -2003,13 +2159,13 @@ base64_encode(const char *s, size_t len) switch (len) { case 0: break; case 1: - v = (((int)s[0] << 16) & 0xff0000); + v = ((uint32_t)(unsigned char)s[0] << 16); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; break; case 2: - v = (((int)s[0] << 16) & 0xff0000) - | (((int)s[1] << 8) & 0xff00); + v = ((uint32_t)(unsigned char)s[0] << 16) + | ((uint32_t)(unsigned char)s[1] << 8); *d++ = digits[(v >> 18) & 0x3f]; *d++ = digits[(v >> 12) & 0x3f]; *d++ = digits[(v >> 6) & 0x3f]; diff --git a/libarchive/archive_write_set_format_raw.c b/libarchive/archive_write_set_format_raw.c index ff3e9ae0eb..20c42e3d30 100644 --- a/libarchive/archive_write_set_format_raw.c +++ b/libarchive/archive_write_set_format_raw.c @@ -55,8 +55,7 @@ archive_write_set_format_raw(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_raw"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); raw = calloc(1, sizeof(*raw)); if (raw == NULL) { @@ -82,7 +81,7 @@ archive_write_set_format_raw(struct archive *_a) static int archive_write_raw_header(struct archive_write *a, struct archive_entry *entry) { - struct raw *raw = (struct raw *)a->format_data; + struct raw *raw = a->format_data; if (archive_entry_filetype(entry) != AE_IFREG) { archive_set_error(&a->archive, ERANGE, @@ -116,9 +115,8 @@ archive_write_raw_data(struct archive_write *a, const void *buff, size_t s) static int archive_write_raw_free(struct archive_write *a) { - struct raw *raw; + struct raw *raw = a->format_data; - raw = (struct raw *)a->format_data; free(raw); a->format_data = NULL; return (ARCHIVE_OK); diff --git a/libarchive/archive_write_set_format_shar.c b/libarchive/archive_write_set_format_shar.c index f6f28debdf..ef4b3d907c 100644 --- a/libarchive/archive_write_set_format_shar.c +++ b/libarchive/archive_write_set_format_shar.c @@ -38,6 +38,7 @@ #endif #include "archive.h" +#include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" @@ -97,11 +98,8 @@ shar_quote(struct archive_string *buf, const char *str, int in_shell) } } -/* - * Set output format to 'shar' format. - */ -int -archive_write_set_format_shar(struct archive *_a) +static int +shar_set_format(struct archive *_a, int dump) { struct archive_write *a = (struct archive_write *)_a; struct shar *shar; @@ -110,8 +108,7 @@ archive_write_set_format_shar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_shar"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); shar = calloc(1, sizeof(*shar)); if (shar == NULL) { @@ -120,18 +117,34 @@ archive_write_set_format_shar(struct archive *_a) } archive_string_init(&shar->work); archive_string_init(&shar->quoted_name); + if (dump) { + shar->dump = 1; + a->format_write_data = archive_write_shar_data_uuencode; + a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP; + a->archive.archive_format_name = "shar dump"; + } else { + a->format_write_data = archive_write_shar_data_sed; + a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE; + a->archive.archive_format_name = "shar"; + } a->format_data = shar; a->format_name = "shar"; a->format_write_header = archive_write_shar_header; a->format_close = archive_write_shar_close; a->format_free = archive_write_shar_free; - a->format_write_data = archive_write_shar_data_sed; a->format_finish_entry = archive_write_shar_finish_entry; - a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE; - a->archive.archive_format_name = "shar"; return (ARCHIVE_OK); } +/* + * Set output format to 'shar' format. + */ +int +archive_write_set_format_shar(struct archive *a) +{ + return shar_set_format(a, 0); +} + /* * An alternate 'shar' that uses uudecode instead of 'sed' to encode * file contents and can therefore be used to archive binary files. @@ -139,31 +152,19 @@ archive_write_set_format_shar(struct archive *_a) * and other extended file information. */ int -archive_write_set_format_shar_dump(struct archive *_a) +archive_write_set_format_shar_dump(struct archive *a) { - struct archive_write *a = (struct archive_write *)_a; - struct shar *shar; - - int ret = archive_write_set_format_shar(&a->archive); - if (ret != ARCHIVE_OK) - return ret; - shar = (struct shar *)a->format_data; - shar->dump = 1; - a->format_write_data = archive_write_shar_data_uuencode; - a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP; - a->archive.archive_format_name = "shar dump"; - return (ARCHIVE_OK); + return shar_set_format(a, 1); } static int archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) { + struct shar *shar = a->format_data; const char *linkname; const char *name; char *p, *pp; - struct shar *shar; - shar = (struct shar *)a->format_data; if (!shar->wrote_header) { archive_strcat(&shar->work, "#!/bin/sh\n"); archive_strcat(&shar->work, "# This is a shell archive\n"); @@ -173,6 +174,10 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) /* Save the entry for the closing. */ archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); + if (shar->entry == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } name = archive_entry_pathname(entry); /* Handle some preparatory issues. */ @@ -338,14 +343,13 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) static ssize_t archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) { + struct shar *shar = a->format_data; static const size_t ensured = 65533; - struct shar *shar; const char *src; char *buf, *buf_end; int ret; size_t written = n; - shar = (struct shar *)a->format_data; if (!shar->has_data || n == 0) return (0); @@ -408,9 +412,9 @@ static void uuencode_group(const char _in[3], char out[4]) { const unsigned char *in = (const unsigned char *)_in; - int t; + uint32_t t; - t = (in[0] << 16) | (in[1] << 8) | in[2]; + t = archive_be24dec(in); out[0] = UUENC( 0x3f & (t >> 18) ); out[1] = UUENC( 0x3f & (t >> 12) ); out[2] = UUENC( 0x3f & (t >> 6) ); @@ -470,12 +474,11 @@ static ssize_t archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, size_t length) { - struct shar *shar; + struct shar *shar = a->format_data; const char *src; size_t n; int ret; - shar = (struct shar *)a->format_data; if (!shar->has_data) return (ARCHIVE_OK); src = (const char *)buff; @@ -519,13 +522,12 @@ archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, static int archive_write_shar_finish_entry(struct archive_write *a) { + struct shar *shar = a->format_data; const char *g, *p, *u; - struct shar *shar; int ret; - shar = (struct shar *)a->format_data; if (shar->entry == NULL) - return (0); + return (ARCHIVE_OK); if (shar->dump) { /* Finish uuencoded data. */ @@ -597,7 +599,7 @@ archive_write_shar_finish_entry(struct archive_write *a) static int archive_write_shar_close(struct archive_write *a) { - struct shar *shar; + struct shar *shar = a->format_data; int ret; /* @@ -605,8 +607,6 @@ archive_write_shar_close(struct archive_write *a) * fix them all up at end-of-archive. */ - shar = (struct shar *)a->format_data; - /* * Only write the end-of-archive markers if the archive was * actually started. This avoids problems if someone sets @@ -635,9 +635,8 @@ archive_write_shar_close(struct archive_write *a) static int archive_write_shar_free(struct archive_write *a) { - struct shar *shar; + struct shar *shar = a->format_data; - shar = (struct shar *)a->format_data; if (shar == NULL) return (ARCHIVE_OK); diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c index 97724c1588..a55cbb75b0 100644 --- a/libarchive/archive_write_set_format_ustar.c +++ b/libarchive/archive_write_set_format_ustar.c @@ -172,8 +172,7 @@ archive_write_set_format_ustar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_ustar"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { @@ -206,7 +205,7 @@ static int archive_write_ustar_options(struct archive_write *a, const char *key, const char *val) { - struct ustar *ustar = (struct ustar *)a->format_data; + struct ustar *ustar = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -234,14 +233,12 @@ archive_write_ustar_options(struct archive_write *a, const char *key, static int archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) { + struct ustar *ustar = a->format_data; char buff[512]; int ret, ret2; - struct ustar *ustar; struct archive_entry *entry_main; struct archive_string_conv *sconv; - ustar = (struct ustar *)a->format_data; - /* Setup default string conversion. */ if (ustar->opt_sconv == NULL) { if (!ustar->init_default_conversion) { @@ -281,7 +278,8 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); @@ -735,9 +733,8 @@ archive_write_ustar_close(struct archive_write *a) static int archive_write_ustar_free(struct archive_write *a) { - struct ustar *ustar; + struct ustar *ustar = a->format_data; - ustar = (struct ustar *)a->format_data; free(ustar); a->format_data = NULL; return (ARCHIVE_OK); @@ -746,12 +743,11 @@ archive_write_ustar_free(struct archive_write *a) static int archive_write_ustar_finish_entry(struct archive_write *a) { - struct ustar *ustar; + struct ustar *ustar = a->format_data; int ret; - ustar = (struct ustar *)a->format_data; ret = __archive_write_nulls(a, - (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding)); + ustar->entry_bytes_remaining + ustar->entry_padding); ustar->entry_bytes_remaining = ustar->entry_padding = 0; return (ret); } @@ -759,10 +755,9 @@ archive_write_ustar_finish_entry(struct archive_write *a) static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s) { - struct ustar *ustar; + struct ustar *ustar = a->format_data; int ret; - ustar = (struct ustar *)a->format_data; if (s > ustar->entry_bytes_remaining) s = (size_t)ustar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); diff --git a/libarchive/archive_write_set_format_v7tar.c b/libarchive/archive_write_set_format_v7tar.c index 37ba73a132..74875547b0 100644 --- a/libarchive/archive_write_set_format_v7tar.c +++ b/libarchive/archive_write_set_format_v7tar.c @@ -149,8 +149,7 @@ archive_write_set_format_v7tar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar"); /* If someone else was already registered, unregister them. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { @@ -183,7 +182,7 @@ static int archive_write_v7tar_options(struct archive_write *a, const char *key, const char *val) { - struct v7tar *v7tar = (struct v7tar *)a->format_data; + struct v7tar *v7tar = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { @@ -211,14 +210,12 @@ archive_write_v7tar_options(struct archive_write *a, const char *key, static int archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) { + struct v7tar *v7tar = a->format_data; char buff[512]; int ret, ret2; - struct v7tar *v7tar; struct archive_entry *entry_main; struct archive_string_conv *sconv; - v7tar = (struct v7tar *)a->format_data; - /* Setup default string conversion. */ if (v7tar->opt_sconv == NULL) { if (!v7tar->init_default_conversion) { @@ -259,7 +256,8 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) const wchar_t *wp; wp = archive_entry_pathname_w(entry); - if (wp != NULL && wp[wcslen(wp) -1] != L'/') { + if (wp != NULL && wp[0] != L'\0' && + wp[wcslen(wp) - 1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); @@ -615,9 +613,8 @@ archive_write_v7tar_close(struct archive_write *a) static int archive_write_v7tar_free(struct archive_write *a) { - struct v7tar *v7tar; + struct v7tar *v7tar = a->format_data; - v7tar = (struct v7tar *)a->format_data; free(v7tar); a->format_data = NULL; return (ARCHIVE_OK); @@ -626,12 +623,11 @@ archive_write_v7tar_free(struct archive_write *a) static int archive_write_v7tar_finish_entry(struct archive_write *a) { - struct v7tar *v7tar; + struct v7tar *v7tar = a->format_data; int ret; - v7tar = (struct v7tar *)a->format_data; ret = __archive_write_nulls(a, - (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding)); + v7tar->entry_bytes_remaining + v7tar->entry_padding); v7tar->entry_bytes_remaining = v7tar->entry_padding = 0; return (ret); } @@ -639,10 +635,9 @@ archive_write_v7tar_finish_entry(struct archive_write *a) static ssize_t archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s) { - struct v7tar *v7tar; + struct v7tar *v7tar = a->format_data; int ret; - v7tar = (struct v7tar *)a->format_data; if (s > v7tar->entry_bytes_remaining) s = (size_t)v7tar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); diff --git a/libarchive/archive_write_set_format_warc.c b/libarchive/archive_write_set_format_warc.c index 3d22e1f4ba..d06abdb372 100644 --- a/libarchive/archive_write_set_format_warc.c +++ b/libarchive/archive_write_set_format_warc.c @@ -49,68 +49,81 @@ #include "archive_write_private.h" #include "archive_write_set_format_private.h" -struct warc_s { +/* + * Overview of the WARC writer: + * + * This writer emits WARC/1.0 resource records for regular files. It + * writes a warcinfo record once by default, unless the omit-warcinfo + * option is used. Each resource record gets a WARC-Target-URI from + * the entry pathname, a generated WARC-Record-ID, and a mandatory + * Content-Length header. + */ + +struct warc { unsigned int omit_warcinfo:1; time_t now; - mode_t typ; - unsigned int rng; - /* populated size */ - uint64_t populz; + mode_t filetype; + /* Remaining bytes to write for the current entry */ + uint64_t entry_bytes_remaining; }; -static const char warcinfo[] = +#define WARC_HEADER_MAX_SIZE 512 + +static const char warcinfo_payload[] = "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n" "format: WARC file version 1.0\r\n"; -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, - /* continuation, unsupported at the moment */ - WT_CONT, - /* invalid type */ - LAST_WT -} warc_type_t; +enum warc_type { + WARC_TYPE_NONE, + /* WARC info */ + WARC_TYPE_INFO, + /* Metadata */ + WARC_TYPE_METADATA, + /* Resource */ + WARC_TYPE_RESOURCE, + /* Request, unsupported */ + WARC_TYPE_REQUEST, + /* Response, unsupported by this writer */ + WARC_TYPE_RESPONSE, + /* Revisit, unsupported */ + WARC_TYPE_REVISIT, + /* Conversion, unsupported */ + WARC_TYPE_CONVERSION, + /* Continuation, currently unsupported */ + WARC_TYPE_CONTINUATION, + WARC_TYPE_LAST +}; -typedef struct { - warc_type_t type; - const char *tgturi; - const char *recid; - time_t rtime; - time_t mtime; - const char *cnttyp; - uint64_t cntlen; -} warc_essential_hdr_t; +struct warc_header { + enum warc_type type; + const char *target_uri; + const char *record_id; + time_t record_time; + time_t modification_time; + const char *content_type; + uint64_t content_length; +}; -typedef struct { - unsigned int u[4U]; -} warc_uuid_t; +struct warc_uuid { + unsigned int value[4U]; +}; -static int _warc_options(struct archive_write*, const char *key, const char *v); -static int _warc_header(struct archive_write *a, struct archive_entry *entry); -static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz); -static int _warc_finish_entry(struct archive_write *a); -static int _warc_close(struct archive_write *a); -static int _warc_free(struct archive_write *a); +static int archive_write_warc_options(struct archive_write *, + const char *, const char *); +static int archive_write_warc_header(struct archive_write *, + struct archive_entry *); +static ssize_t archive_write_warc_data(struct archive_write *, const void *, + size_t); +static int archive_write_warc_finish_entry(struct archive_write *); +static int archive_write_warc_close(struct archive_write *); +static int archive_write_warc_free(struct archive_write *); -/* private routines */ -static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t); -static int _gen_uuid(warc_uuid_t *tgt); +static void warc_format_time(struct archive_string *, const char *, time_t); +static ssize_t warc_populate_header(struct archive_string *, size_t, + struct warc_header); +static void warc_generate_uuid(struct warc_uuid *); - /* * Set output format to ISO 28500 (aka WARC) format. */ @@ -118,103 +131,110 @@ int archive_write_set_format_warc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; - struct warc_s *w; + struct warc *warc; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_warc"); /* If another format was already registered, unregister it. */ - if (a->format_free != NULL) { - (a->format_free)(a); - } + (void)__archive_write_unregister_format(a); - w = malloc(sizeof(*w)); - if (w == NULL) { + warc = malloc(sizeof(*warc)); + if (warc == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } - /* by default we're emitting a file wide header */ - w->omit_warcinfo = 0U; - /* obtain current time for date fields */ - w->now = time(NULL); - /* reset file type info */ - w->typ = 0; - /* also initialise our rng */ - w->rng = (unsigned int)w->now; + /* Emit a warcinfo record by default. */ + warc->omit_warcinfo = 0U; + /* Use the current time for WARC-Date values. */ + warc->now = time(NULL); + /* Reset file type information. */ + warc->filetype = 0; - a->format_data = w; + a->format_data = warc; a->format_name = "WARC/1.0"; - a->format_options = _warc_options; - a->format_write_header = _warc_header; - a->format_write_data = _warc_data; - a->format_close = _warc_close; - a->format_free = _warc_free; - a->format_finish_entry = _warc_finish_entry; + a->format_options = archive_write_warc_options; + a->format_write_header = archive_write_warc_header; + a->format_write_data = archive_write_warc_data; + a->format_close = archive_write_warc_close; + a->format_free = archive_write_warc_free; + a->format_finish_entry = archive_write_warc_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_WARC; a->archive.archive_format_name = "WARC/1.0"; return (ARCHIVE_OK); } - -/* archive methods */ static int -_warc_options(struct archive_write *a, const char *key, const char *val) +archive_write_warc_options(struct archive_write *a, const char *key, + const char *val) { - struct warc_s *w = a->format_data; + struct warc *warc = a->format_data; if (strcmp(key, "omit-warcinfo") == 0) { if (val == NULL || strcmp(val, "true") == 0) { - /* great */ - w->omit_warcinfo = 1U; + /* Option accepted. */ + warc->omit_warcinfo = 1U; 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. */ + /* ARCHIVE_WARN tells the options supervisor that this option was not + * handled here. It will report an error if no module uses it. */ return (ARCHIVE_WARN); } static int -_warc_header(struct archive_write *a, struct archive_entry *entry) +archive_write_warc_header(struct archive_write *a, struct archive_entry *entry) { - struct warc_s *w = a->format_data; - struct archive_string hdr; -#define MAX_HDR_SIZE 512 + struct warc *warc = a->format_data; + struct archive_string header; - /* check whether warcinfo record needs outputting */ - if (!w->omit_warcinfo) { - ssize_t r; - warc_essential_hdr_t wi = { - WT_INFO, - /*uri*/NULL, - /*urn*/NULL, - /*rtm*/0, - /*mtm*/0, - /*cty*/"application/warc-fields", - /*len*/sizeof(warcinfo) - 1U, + /* Emit the warcinfo record if needed. */ + if (!warc->omit_warcinfo) { + ssize_t header_size; + int ret; + struct warc_header warcinfo_header = { + WARC_TYPE_INFO, + /* URI */NULL, + /* Record ID */NULL, + /* Record time */0, + /* Modified time */0, + /* Content type */"application/warc-fields", + /* Content length */sizeof(warcinfo_payload) - 1U, }; - wi.rtime = w->now; - wi.mtime = w->now; + warcinfo_header.record_time = warc->now; + warcinfo_header.modification_time = warc->now; - archive_string_init(&hdr); - r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi); - if (r >= 0) { - /* jackpot! */ - /* now also use HDR buffer for the actual warcinfo */ - archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1); - - /* append end-of-record indicator */ - archive_strncat(&hdr, "\r\n\r\n", 4); - - /* write to output stream */ - __archive_write_output(a, hdr.s, archive_strlen(&hdr)); + archive_string_init(&header); + header_size = warc_populate_header(&header, + WARC_HEADER_MAX_SIZE, warcinfo_header); + if (header_size < 0) { + archive_string_free(&header); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Cannot archive warcinfo record"); + return (ARCHIVE_FAILED); } - /* indicate we're done with file header writing */ - w->omit_warcinfo = 1U; - archive_string_free(&hdr); + + /* Reuse the header buffer for the warcinfo payload. */ + archive_strncat(&header, warcinfo_payload, + sizeof(warcinfo_payload) - 1); + + /* Append the end-of-record indicator. */ + archive_strncat(&header, "\r\n\r\n", 4); + + /* Write the warcinfo record to the output stream. */ + ret = __archive_write_output(a, header.s, + archive_strlen(&header)); + if (ret != ARCHIVE_OK) { + archive_string_free(&header); + return (ret); + } + + /* Mark the file header as written. */ + warc->omit_warcinfo = 1U; + archive_string_free(&header); } if (archive_entry_pathname(entry) == NULL) { @@ -223,221 +243,249 @@ _warc_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_WARN); } - w->typ = archive_entry_filetype(entry); - w->populz = 0U; - if (w->typ == AE_IFREG) { - warc_essential_hdr_t rh = { - WT_RSRC, - /*uri*/NULL, - /*urn*/NULL, - /*rtm*/0, - /*mtm*/0, - /*cty*/NULL, - /*len*/0, + warc->filetype = archive_entry_filetype(entry); + warc->entry_bytes_remaining = 0U; + if (warc->filetype == AE_IFREG) { + struct warc_header resource_header = { + WARC_TYPE_RESOURCE, + /* URI */NULL, + /* Record ID */NULL, + /* Record time */0, + /* Modified time */0, + /* Content type */NULL, + /* Content length */0, }; - ssize_t r; - rh.tgturi = archive_entry_pathname(entry); - rh.rtime = w->now; - rh.mtime = archive_entry_mtime(entry); - rh.cntlen = (size_t)archive_entry_size(entry); + ssize_t header_size; + int ret; + int64_t size; - archive_string_init(&hdr); - r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); - if (r < 0) { - /* don't bother */ - archive_set_error( - &a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "cannot archive file"); - return (ARCHIVE_WARN); + resource_header.target_uri = archive_entry_pathname(entry); + resource_header.record_time = warc->now; + resource_header.modification_time = archive_entry_mtime(entry); + if (!archive_entry_size_is_set(entry)) { + archive_set_error(&a->archive, -1, + "Size required"); + return (ARCHIVE_FAILED); } - /* otherwise append to output stream */ - __archive_write_output(a, hdr.s, r); - /* and let subsequent calls to _data() know about the size */ - w->populz = rh.cntlen; - archive_string_free(&hdr); + size = archive_entry_size(entry); + if (size < 0) { + archive_set_error(&a->archive, -1, + "Size required"); + return (ARCHIVE_FAILED); + } + resource_header.content_length = (uint64_t)size; + + archive_string_init(&header); + header_size = warc_populate_header(&header, + WARC_HEADER_MAX_SIZE, resource_header); + if (header_size < 0) { + /* Header generation failed. */ + archive_string_free(&header); + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "WARC resource header is too large"); + return (ARCHIVE_FATAL); + } + /* Append the header to the output stream. */ + ret = __archive_write_output(a, header.s, header_size); + if (ret != ARCHIVE_OK) { + archive_string_free(&header); + return (ret); + } + /* Save the remaining size for subsequent data callbacks. */ + warc->entry_bytes_remaining = resource_header.content_length; + archive_string_free(&header); return (ARCHIVE_OK); } - /* just resort to erroring as per Tim's advice */ + /* Report unsupported file types through the common helper. */ __archive_write_entry_filetype_unsupported( &a->archive, entry, "WARC"); return (ARCHIVE_FAILED); } static ssize_t -_warc_data(struct archive_write *a, const void *buf, size_t len) +archive_write_warc_data(struct archive_write *a, const void *buf, size_t len) { - struct warc_s *w = a->format_data; + struct warc *warc = a->format_data; - if (w->typ == AE_IFREG) { - int rc; + if (warc->filetype == AE_IFREG) { + int ret; - /* never write more bytes than announced */ - if (len > w->populz) { - len = (size_t)w->populz; + /* Never write more bytes than announced. */ + if ((uint64_t)len > warc->entry_bytes_remaining) { + len = (size_t)warc->entry_bytes_remaining; } - /* now then, out we put the whole shebang */ - rc = __archive_write_output(a, buf, len); - if (rc != ARCHIVE_OK) { - return rc; + /* Write the entry data. */ + ret = __archive_write_output(a, buf, len); + if (ret != ARCHIVE_OK) { + return (ret); } + warc->entry_bytes_remaining -= len; } - return len; + return (len); } static int -_warc_finish_entry(struct archive_write *a) +archive_write_warc_finish_entry(struct archive_write *a) { - static const char _eor[] = "\r\n\r\n"; - struct warc_s *w = a->format_data; + static const char end_of_record[] = "\r\n\r\n"; + struct warc *warc = a->format_data; - if (w->typ == AE_IFREG) { - int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U); + if (warc->filetype == AE_IFREG) { + int ret; - if (rc != ARCHIVE_OK) { - return rc; + if (warc->entry_bytes_remaining != 0U) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "WARC entry is shorter than Content-Length"); + return (ARCHIVE_FATAL); + } + + ret = __archive_write_output(a, end_of_record, + sizeof(end_of_record) - 1U); + if (ret != ARCHIVE_OK) { + return (ret); } } - /* reset type info */ - w->typ = 0; + /* Reset file type information. */ + warc->filetype = 0; return (ARCHIVE_OK); } static int -_warc_close(struct archive_write *a) +archive_write_warc_close(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int -_warc_free(struct archive_write *a) +archive_write_warc_free(struct archive_write *a) { - struct warc_s *w = a->format_data; + struct warc *warc = a->format_data; - free(w); + free(warc); a->format_data = NULL; return (ARCHIVE_OK); } - -/* private routines */ +/* Like strftime(3), but for time_t objects. */ static void -xstrftime(struct archive_string *as, const char *fmt, time_t t) +warc_format_time(struct archive_string *str, const char *format, time_t t) { -/** like strftime(3) but for time_t objects */ - struct tm *rt; + struct tm *tm; #if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) - struct tm timeHere; + struct tm tm_storage; #endif - char strtime[100]; + char time_string[100]; size_t len; #if defined(HAVE_GMTIME_S) - rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere; + tm = gmtime_s(&tm_storage, &t) ? NULL : &tm_storage; #elif defined(HAVE_GMTIME_R) - rt = gmtime_r(&t, &timeHere); + tm = gmtime_r(&t, &tm_storage); #else - rt = gmtime(&t); + tm = gmtime(&t); #endif - if (!rt) + if (tm == NULL) return; - /* leave the hard yacker to our role model strftime() */ - len = strftime(strtime, sizeof(strtime)-1, fmt, rt); - archive_strncat(as, strtime, len); + /* Let strftime() handle the actual formatting. */ + len = strftime(time_string, sizeof(time_string) - 1, format, tm); + archive_strncat(str, time_string, len); } static ssize_t -_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr) +warc_populate_header(struct archive_string *header_string, size_t max_size, + struct warc_header header) { - static const char _ver[] = "WARC/1.0\r\n"; - static const char * const _typ[LAST_WT] = { + static const char version[] = "WARC/1.0\r\n"; + static const char * const record_types[WARC_TYPE_LAST] = { NULL, "warcinfo", "metadata", "resource", NULL }; - char std_uuid[48U]; + char generated_record_id[48U]; - if (hdr.type == WT_NONE || hdr.type > WT_RSRC) { - /* brilliant, how exactly did we get here? */ - return -1; + if (header.type == WARC_TYPE_NONE || header.type > WARC_TYPE_RESOURCE) { + /* Invalid record type for this writer. */ + return (-1); } - archive_strcpy(tgt, _ver); + archive_strcpy(header_string, version); - archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]); + archive_string_sprintf(header_string, "WARC-Type: %s\r\n", + record_types[header.type]); - if (hdr.tgturi != NULL) { - /* check if there's a xyz:// */ - static const char _uri[] = ""; - static const char _fil[] = "file://"; - const char *u; - char *chk = strchr(hdr.tgturi, ':'); + if (header.target_uri != NULL) { + const char *uri_prefix; + const char *scheme = strchr(header.target_uri, ':'); - if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') { - /* yep, it's definitely a URI */ - u = _uri; + /* Check whether the value already contains ://. */ + if (scheme != NULL && scheme[1U] == '/' && scheme[2U] == '/') { + /* Already has a scheme-style :// prefix. */ + uri_prefix = ""; } else { - /* hm, best to prepend file:// then */ - u = _fil; + /* Prepend file:// for local paths. */ + uri_prefix = "file://"; } - archive_string_sprintf(tgt, - "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi); + archive_string_sprintf(header_string, + "WARC-Target-URI: %s%s\r\n", uri_prefix, + header.target_uri); } - /* record time is usually when the http is sent off, - * just treat the archive writing as such for a moment */ - xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime); + /* Write WARC-Date from header.record_time. */ + warc_format_time(header_string, + "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", header.record_time); - /* while we're at it, record the mtime */ - xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime); + /* Also write Last-Modified from header.modification_time. */ + warc_format_time(header_string, + "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", + header.modification_time); - if (hdr.recid == NULL) { - /* generate one, grrrr */ - warc_uuid_t u; + if (header.record_id == NULL) { + /* Generate a record ID when one was not provided. */ + struct warc_uuid uuid; - _gen_uuid(&u); - /* Unfortunately, archive_string_sprintf does not - * handle the minimum number following '%'. - * So we have to use snprintf function here instead - * of archive_string_snprintf function. */ -#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900) + warc_generate_uuid(&uuid); + /* archive_string_sprintf() does not support minimum field widths, so + * use snprintf() for UUID formatting. */ +#if defined(_WIN32) && !defined(__CYGWIN__) && \ + !(defined(_MSC_VER) && _MSC_VER >= 1900) #define snprintf _snprintf #endif - snprintf( - std_uuid, sizeof(std_uuid), - "", - u.u[0U], - u.u[1U] >> 16U, u.u[1U] & 0xffffU, - u.u[2U] >> 16U, u.u[2U] & 0xffffU, - u.u[3U]); - hdr.recid = std_uuid; + snprintf(generated_record_id, sizeof(generated_record_id), + "", + uuid.value[0U], + uuid.value[1U] >> 16U, uuid.value[1U] & 0xffffU, + uuid.value[2U] >> 16U, uuid.value[2U] & 0xffffU, + uuid.value[3U]); + header.record_id = generated_record_id; } - /* record-id is mandatory, fingers crossed we won't fail */ - archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid); + /* WARC-Record-ID is mandatory. */ + archive_string_sprintf(header_string, "WARC-Record-ID: %s\r\n", + header.record_id); - if (hdr.cnttyp != NULL) { - archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp); + if (header.content_type != NULL) { + archive_string_sprintf(header_string, "Content-Type: %s\r\n", + header.content_type); } - /* next one is mandatory */ - archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen); - /**/ - archive_strncat(tgt, "\r\n", 2); + /* Content-Length is mandatory. */ + archive_string_sprintf(header_string, "Content-Length: %ju\r\n", + (uintmax_t)header.content_length); + /* End of header. */ + archive_strncat(header_string, "\r\n", 2); - return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt); + return (archive_strlen(header_string) >= max_size) ? + -1 : (ssize_t)archive_strlen(header_string); } -static int -_gen_uuid(warc_uuid_t *tgt) +static void +warc_generate_uuid(struct warc_uuid *uuid) { - archive_random(tgt->u, sizeof(tgt->u)); - /* obey uuid version 4 rules */ - tgt->u[1U] &= 0xffff0fffU; - tgt->u[1U] |= 0x4000U; - tgt->u[2U] &= 0x3fffffffU; - tgt->u[2U] |= 0x80000000U; - return 0; + archive_random(uuid->value, sizeof(uuid->value)); + /* Apply UUID version 4 rules. */ + uuid->value[1U] &= 0xffff0fffU; + uuid->value[1U] |= 0x4000U; + uuid->value[2U] &= 0x3fffffffU; + uuid->value[2U] |= 0x80000000U; } - -/* archive_write_set_format_warc.c ends here */ diff --git a/libarchive/archive_write_set_format_xar.c b/libarchive/archive_write_set_format_xar.c index ec3219057e..0058693792 100644 --- a/libarchive/archive_write_set_format_xar.c +++ b/libarchive/archive_write_set_format_xar.c @@ -111,7 +111,7 @@ static int xml_writer_start_element(struct xml_writer *ctx, static int xml_writer_write_attribute(struct xml_writer *ctx, const char *key, const char *value); static int xml_writer_write_attributef(struct xml_writer *ctx, const char *key, - const char *format, ...); + const char *format, ...) __LA_PRINTF(3, 4); static int xml_writer_write_string(struct xml_writer *ctx, const char *string); static int xml_writer_write_base64(struct xml_writer* ctx, const char *data, size_t start, size_t len); @@ -129,14 +129,20 @@ static int xml_writer_destroy(struct xml_writer *ctx); enum sumalg { CKSUM_NONE = 0, CKSUM_SHA1 = 1, - CKSUM_MD5 = 2 + CKSUM_MD5 = 2, + CKSUM_SHA256 = 3, + CKSUM_SHA512 = 4 }; #define MD5_SIZE 16 #define SHA1_SIZE 20 -#define MAX_SUM_SIZE 20 +#define SHA256_SIZE 32 +#define SHA512_SIZE 64 +#define MAX_SUM_SIZE 64 #define MD5_NAME "md5" #define SHA1_NAME "sha1" +#define SHA256_NAME "sha256" +#define SHA512_NAME "sha512" enum enctype { NONE, @@ -154,6 +160,12 @@ struct chksumwork { #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif +#ifdef ARCHIVE_HAS_SHA256 + archive_sha256_ctx sha256ctx; +#endif +#ifdef ARCHIVE_HAS_SHA512 + archive_sha512_ctx sha512ctx; +#endif }; enum la_zaction { @@ -366,8 +378,7 @@ archive_write_set_format_xar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_xar"); /* If another format was already registered, unregister it. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); xar = calloc(1, sizeof(*xar)); if (xar == NULL) { @@ -380,22 +391,19 @@ archive_write_set_format_xar(struct archive *_a) file_init_hardlinks(xar); archive_string_init(&(xar->tstr)); archive_string_init(&(xar->vstr)); + archive_string_init(&(xar->cur_dirstr)); /* * Create the root directory. */ xar->root = file_create_virtual_dir(a, xar, ""); - if (xar->root == NULL) { - free(xar); - archive_set_error(&a->archive, ENOMEM, - "Can't allocate xar data"); - return (ARCHIVE_FATAL); - } + if (xar->root == NULL) + goto memerr; xar->root->parent = xar->root; file_register(xar, xar->root); xar->cur_dirent = xar->root; - archive_string_init(&(xar->cur_dirstr)); - archive_string_ensure(&(xar->cur_dirstr), 1); + if (archive_string_ensure(&(xar->cur_dirstr), 1) == NULL) + goto memerr; xar->cur_dirstr.s[0] = 0; /* @@ -422,14 +430,23 @@ archive_write_set_format_xar(struct archive *_a) a->archive.archive_format_name = "xar"; return (ARCHIVE_OK); + +memerr: + archive_string_free(&(xar->cur_dirstr)); + archive_string_free(&(xar->tstr)); + archive_string_free(&(xar->vstr)); + file_free_hardlinks(xar); + file_free_register(xar); + free(xar); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate xar data"); + return (ARCHIVE_FATAL); } static int xar_options(struct archive_write *a, const char *key, const char *value) { - struct xar *xar; - - xar = (struct xar *)a->format_data; + struct xar *xar = a->format_data; if (strcmp(key, "checksum") == 0) { if (value == NULL) @@ -440,6 +457,14 @@ xar_options(struct archive_write *a, const char *key, const char *value) xar->opt_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) xar->opt_sumalg = CKSUM_MD5; +#ifdef ARCHIVE_HAS_SHA256 + else if (strcmp(value, "sha256") == 0) + xar->opt_sumalg = CKSUM_SHA256; +#endif +#ifdef ARCHIVE_HAS_SHA512 + else if (strcmp(value, "sha512") == 0) + xar->opt_sumalg = CKSUM_SHA512; +#endif else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, @@ -515,6 +540,14 @@ xar_options(struct archive_write *a, const char *key, const char *value) xar->opt_toc_sumalg = CKSUM_SHA1; else if (strcmp(value, "md5") == 0) xar->opt_toc_sumalg = CKSUM_MD5; +#ifdef ARCHIVE_HAS_SHA256 + else if (strcmp(value, "sha256") == 0) + xar->opt_toc_sumalg = CKSUM_SHA256; +#endif +#ifdef ARCHIVE_HAS_SHA512 + else if (strcmp(value, "sha512") == 0) + xar->opt_toc_sumalg = CKSUM_SHA512; +#endif else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, @@ -526,12 +559,13 @@ xar_options(struct archive_write *a, const char *key, const char *value) } if (strcmp(key, "threads") == 0) { char *endptr; + unsigned long val; if (value == NULL) return (ARCHIVE_FAILED); errno = 0; - xar->opt_threads = (int)strtoul(value, &endptr, 10); - if (errno != 0 || *endptr != '\0') { + val = strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) { xar->opt_threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, @@ -539,6 +573,7 @@ xar_options(struct archive_write *a, const char *key, const char *value) value); return (ARCHIVE_FAILED); } + xar->opt_threads = (int)val; if (xar->opt_threads == 0) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT xar->opt_threads = lzma_cputhreads(); @@ -557,12 +592,11 @@ xar_options(struct archive_write *a, const char *key, const char *value) static int xar_write_header(struct archive_write *a, struct archive_entry *entry) { - struct xar *xar; + struct xar *xar = a->format_data; struct file *file; struct archive_entry *file_entry; int r, r2; - xar = (struct xar *)a->format_data; xar->cur_file = NULL; xar->bytes_remaining = 0; @@ -580,8 +614,10 @@ xar_write_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_FATAL); } r2 = file_gen_utility_names(a, file); - if (r2 < ARCHIVE_WARN) + if (r2 < ARCHIVE_WARN) { + file_free(file); return (r2); + } /* * Ignore a path which looks like the top of directory name @@ -670,20 +706,25 @@ xar_write_header(struct archive_write *a, struct archive_entry *entry) checksum_init(&(xar->e_sumwrk), xar->opt_sumalg); r = xar_compression_init_encoder(a); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + struct chksumval sumval; + + checksum_final(&(xar->a_sumwrk), &sumval); + checksum_final(&(xar->e_sumwrk), &sumval); + xar->a_sumwrk.alg = CKSUM_NONE; + xar->e_sumwrk.alg = CKSUM_NONE; return (r); - else + } else return (r2); } static int write_to_temp(struct archive_write *a, const void *buff, size_t s) { - struct xar *xar; + struct xar *xar = a->format_data; const unsigned char *p; ssize_t ws; - xar = (struct xar *)a->format_data; p = (const unsigned char *)buff; while (s) { ws = write(xar->temp_fd, p, s); @@ -702,14 +743,12 @@ write_to_temp(struct archive_write *a, const void *buff, size_t s) static ssize_t xar_write_data(struct archive_write *a, const void *buff, size_t s) { - struct xar *xar; + struct xar *xar = a->format_data; enum la_zaction run; size_t size = 0; size_t rsize; int r; - xar = (struct xar *)a->format_data; - if (s > xar->bytes_remaining) s = (size_t)xar->bytes_remaining; if (s == 0 || xar->cur_file == NULL) @@ -803,12 +842,11 @@ xar_write_data(struct archive_write *a, const void *buff, size_t s) static int xar_finish_entry(struct archive_write *a) { - struct xar *xar; + struct xar *xar = a->format_data; struct file *file; size_t s; ssize_t w; - xar = (struct xar *)a->format_data; if (xar->cur_file == NULL) return (ARCHIVE_OK); @@ -817,9 +855,7 @@ xar_finish_entry(struct archive_write *a) if (s > a->null_length) s = a->null_length; w = xar_write_data(a, a->nulls, s); - if (w > 0) - xar->bytes_remaining -= w; - else + if (w <= 0) return ((int)w); } file = xar->cur_file; @@ -907,14 +943,13 @@ xmlwrite_string(struct archive_write *a, struct xml_writer *writer, return (ARCHIVE_OK); } -static int +static int __LA_PRINTF(4, 5) xmlwrite_fstring(struct archive_write *a, struct xml_writer *writer, const char *key, const char *fmt, ...) { - struct xar *xar; + struct xar *xar = a->format_data; va_list ap; - xar = (struct xar *)a->format_data; va_start(ap, fmt); archive_string_empty(&xar->vstr); archive_string_vsprintf(&xar->vstr, fmt, ap); @@ -1108,30 +1143,27 @@ make_fflags_entry(struct archive_write *a, struct xml_writer *writer, { NULL, NULL} }; const struct flagentry *fe, *flagentry; -#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd)) - const struct flagentry *avail[FLAGENTRY_MAXSIZE]; const char *p; - int i, n, r; + int r, started; if (strcmp(element, "ext2") == 0) flagentry = flagext2; else flagentry = flagbsd; - n = 0; p = fflags_text; + started = 0; do { - const char *cp; + const char *cp, *name = NULL; cp = strchr(p, ','); if (cp == NULL) cp = p + strlen(p); for (fe = flagentry; fe->name != NULL; fe++) { - if (fe->name[cp - p] != '\0' - || p[0] != fe->name[0]) - continue; if (strncmp(p, fe->name, cp - p) == 0) { - avail[n++] = fe; + if (fe->name[cp - p] != '\0') + continue; + name = fe->xarname; break; } } @@ -1139,23 +1171,26 @@ make_fflags_entry(struct archive_write *a, struct xml_writer *writer, p = cp + 1; else p = NULL; - } while (p != NULL); - if (n > 0) { - r = xml_writer_start_element(writer, element); - if (r < 0) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "xml_writer_start_element() failed: %d", r); - return (ARCHIVE_FATAL); - } - for (i = 0; i < n; i++) { - r = xmlwrite_string(a, writer, - avail[i]->xarname, NULL); + if (name != NULL) { + if (!started) { + r = xml_writer_start_element(writer, element); + if (r < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "xml_writer_start_element()" + " failed: %d", r); + return (ARCHIVE_FATAL); + } + started = 1; + } + r = xmlwrite_string(a, writer, name, NULL); if (r != ARCHIVE_OK) return (r); } + } while (p != NULL); + if (started) { r = xml_writer_end_element(writer); if (r < 0) { archive_set_error(&a->archive, @@ -1202,7 +1237,7 @@ static int make_file_entry(struct archive_write *a, struct xml_writer *writer, struct file *file) { - struct xar *xar; + struct xar *xar = a->format_data; const char *filetype, *filelink, *fflags; struct archive_string linkto; struct heap_data *heap; @@ -1210,7 +1245,6 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, size_t len; int r, r2; - xar = (struct xar *)a->format_data; r2 = ARCHIVE_OK; /* @@ -1333,11 +1367,11 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, return (ARCHIVE_FATAL); } r = xmlwrite_fstring(a, writer, "major", - "%d", archive_entry_rdevmajor(file->entry)); + "%ld", (long)archive_entry_rdevmajor(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = xmlwrite_fstring(a, writer, "minor", - "%d", archive_entry_rdevminor(file->entry)); + "%ld", (long)archive_entry_rdevminor(file->entry)); if (r < 0) return (ARCHIVE_FATAL); r = xml_writer_end_element(writer); @@ -1361,7 +1395,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, return (ARCHIVE_FATAL); if (archive_entry_dev(file->entry) != 0) { r = xmlwrite_fstring(a, writer, "deviceno", - "%d", archive_entry_dev(file->entry)); + "%ld", (long)archive_entry_dev(file->entry)); if (r < 0) return (ARCHIVE_FATAL); } @@ -1437,7 +1471,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, } /* - * Make a mtime entry, "". + * Make an mtime entry, "". */ if (archive_entry_mtime_is_set(file->entry)) { r = xmlwrite_time(a, writer, "mtime", @@ -1572,7 +1606,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer, static int make_toc(struct archive_write *a) { - struct xar *xar; + struct xar *xar = a->format_data; struct file *np; struct xml_writer *writer; const char* content; @@ -1580,8 +1614,6 @@ make_toc(struct archive_write *a) int algsize; int r, ret; - xar = (struct xar *)a->format_data; - ret = ARCHIVE_FATAL; /* @@ -1822,11 +1854,10 @@ exit_toc: static int flush_wbuff(struct archive_write *a) { - struct xar *xar; + struct xar *xar = a->format_data; int r; size_t s; - xar = (struct xar *)a->format_data; s = sizeof(xar->wbuff) - xar->wbuff_remaining; r = __archive_write_output(a, xar->wbuff, s); if (r != ARCHIVE_OK) @@ -1838,10 +1869,9 @@ flush_wbuff(struct archive_write *a) static int copy_out(struct archive_write *a, uint64_t offset, uint64_t length) { - struct xar *xar; + struct xar *xar = a->format_data; int r; - xar = (struct xar *)a->format_data; if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) { archive_set_error(&(a->archive), errno, "lseek failed"); return (ARCHIVE_FATAL); @@ -1864,7 +1894,8 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) return (ARCHIVE_FATAL); } if (rs == 0) { - archive_set_error(&(a->archive), 0, + archive_set_error(&(a->archive), + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated xar archive"); return (ARCHIVE_FATAL); } @@ -1882,13 +1913,11 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length) static int xar_close(struct archive_write *a) { - struct xar *xar; + struct xar *xar = a->format_data; unsigned char *wb; uint64_t length; int r; - xar = (struct xar *)a->format_data; - /* Empty! */ if (xar->root->children.first == NULL) return (ARCHIVE_OK); @@ -1948,9 +1977,7 @@ xar_close(struct archive_write *a) static int xar_free(struct archive_write *a) { - struct xar *xar; - - xar = (struct xar *)a->format_data; + struct xar *xar = a->format_data; /* Close the temporary file. */ if (xar->temp_fd >= 0) @@ -2108,13 +2135,12 @@ cleanup_backslash(char *utf8, size_t len) static int file_gen_utility_names(struct archive_write *a, struct file *file) { - struct xar *xar; + struct xar *xar = a->format_data; const char *pp; char *p, *dirname, *slash; size_t len; int r = ARCHIVE_OK; - xar = (struct xar *)a->format_data; archive_string_empty(&(file->parentdir)); archive_string_empty(&(file->basename)); archive_string_empty(&(file->symlink)); @@ -2203,20 +2229,29 @@ file_gen_utility_names(struct archive_write *a, struct file *file) * --> 'dir/dir2/' */ char *rp = p -1; + size_t off; + for (off = 4; p[off] == '/'; off++) + ; while (rp >= dirname) { if (*rp == '/') break; --rp; } if (rp > dirname) { - strcpy(rp, p+3); + memmove(rp + 1, p + off, strlen(p + off) + 1); p = rp; } else { - strcpy(dirname, p+4); + memmove(dirname, p + off, strlen(p + off) + 1); p = dirname; } } else p++; + } else if (p == dirname && p[0] == '.' && p[1] == '.' && p[2] == '/') { + size_t off; + for (off = 3; p[off] == '/'; off++) + ; + memmove(dirname, p + off, strlen(p + off) + 1); + p = dirname; } else p++; } @@ -2270,7 +2305,7 @@ file_gen_utility_names(struct archive_write *a, struct file *file) static int get_path_component(char *name, int n, const char *fn) { - char *p; + const char *p; int l; p = strchr(fn, '/'); @@ -2293,6 +2328,7 @@ get_path_component(char *name, int n, const char *fn) static int file_tree(struct archive_write *a, struct file **filepp) { + struct xar *xar = a->format_data; #if defined(_WIN32) && !defined(__CYGWIN__) char name[_MAX_FNAME];/* Included null terminator size. */ #elif defined(NAME_MAX) && NAME_MAX >= 255 @@ -2300,7 +2336,6 @@ file_tree(struct archive_write *a, struct file **filepp) #else char name[256]; #endif - struct xar *xar = (struct xar *)a->format_data; struct file *dent, *file, *np; struct archive_entry *ent; const char *fn, *p; @@ -2376,10 +2411,24 @@ file_tree(struct archive_write *a, struct file **filepp) archive_string_init(&as); archive_strncat(&as, p, fn - p + l); - if (as.s[as.length-1] == '/') { + if (as.length > 0 && as.s[as.length-1] == '/') { as.s[as.length-1] = '\0'; as.length--; } + if (as.length == 0) { + archive_string_free(&as); + fn += strspn(fn, "/"); + l = get_path_component(name, sizeof(name), fn); + if (l < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "A name buffer is too small"); + file_free(file); + *filepp = NULL; + return (ARCHIVE_FATAL); + } + continue; + } vp = file_create_virtual_dir(a, xar, as.s); if (vp == NULL) { archive_string_free(&as); @@ -2390,8 +2439,12 @@ file_tree(struct archive_write *a, struct file **filepp) return (ARCHIVE_FATAL); } archive_string_free(&as); - if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED) + if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED) { + file_free(vp); + file_free(file); + *filepp = NULL; return (ARCHIVE_FATAL); + } file_add_child_tail(dent, vp); file_register(xar, vp); np = vp; @@ -2502,7 +2555,7 @@ file_free_register(struct xar *xar) static int file_register_hardlink(struct archive_write *a, struct file *file) { - struct xar *xar = (struct xar *)a->format_data; + struct xar *xar = a->format_data; struct hardlink *hl; const char *pathname; @@ -2624,6 +2677,16 @@ checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg) case CKSUM_MD5: archive_md5_init(&(sumwrk->md5ctx)); break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_init(&(sumwrk->sha256ctx)); + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_init(&(sumwrk->sha512ctx)); + break; +#endif } } @@ -2640,6 +2703,16 @@ checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size) case CKSUM_MD5: archive_md5_update(&(sumwrk->md5ctx), buff, size); break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_update(&(sumwrk->sha256ctx), buff, size); + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_update(&(sumwrk->sha512ctx), buff, size); + break; +#endif } } @@ -2659,6 +2732,18 @@ checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval) archive_md5_final(&(sumwrk->md5ctx), sumval->val); sumval->len = MD5_SIZE; break; +#ifdef ARCHIVE_HAS_SHA256 + case CKSUM_SHA256: + archive_sha256_final(&(sumwrk->sha256ctx), sumval->val); + sumval->len = SHA256_SIZE; + break; +#endif +#ifdef ARCHIVE_HAS_SHA512 + case CKSUM_SHA512: + archive_sha512_final(&(sumwrk->sha512ctx), sumval->val); + sumval->len = SHA512_SIZE; + break; +#endif } sumval->alg = sumwrk->alg; } @@ -3108,10 +3193,9 @@ compression_init_encoder_xz(struct archive *a, static int xar_compression_init_encoder(struct archive_write *a) { - struct xar *xar; + struct xar *xar = a->format_data; int r; - xar = (struct xar *)a->format_data; switch (xar->opt_compression) { case GZIP: r = compression_init_encoder_gzip( @@ -3168,14 +3252,13 @@ compression_end(struct archive *a, struct la_zstream *lastrm) static int save_xattrs(struct archive_write *a, struct file *file) { - struct xar *xar; + struct xar *xar = a->format_data; const char *name; const void *value; struct heap_data *heap; size_t size; int count, r; - xar = (struct xar *)a->format_data; count = archive_entry_xattr_reset(file->entry); if (count == 0) return (ARCHIVE_OK); @@ -3187,6 +3270,12 @@ save_xattrs(struct archive_write *a, struct file *file) heap = calloc(1, sizeof(*heap)); if (heap == NULL) { + struct chksumval sumval; + + checksum_final(&(xar->a_sumwrk), &sumval); + checksum_final(&(xar->e_sumwrk), &sumval); + xar->a_sumwrk.alg = CKSUM_NONE; + xar->e_sumwrk.alg = CKSUM_NONE; archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for xattr"); return (ARCHIVE_FATAL); @@ -3224,6 +3313,10 @@ save_xattrs(struct archive_write *a, struct file *file) */ r = xar_compression_init_encoder(a); if (r != ARCHIVE_OK) { + struct chksumval sumval; + + checksum_final(&(xar->a_sumwrk), &sumval); + xar->a_sumwrk.alg = CKSUM_NONE; free(heap); return (ARCHIVE_FATAL); } @@ -3278,6 +3371,10 @@ getalgsize(enum sumalg sumalg) return (SHA1_SIZE); case CKSUM_MD5: return (MD5_SIZE); + case CKSUM_SHA256: + return (SHA256_SIZE); + case CKSUM_SHA512: + return (SHA512_SIZE); } } @@ -3292,6 +3389,10 @@ getalgname(enum sumalg sumalg) return (SHA1_NAME); case CKSUM_MD5: return (MD5_NAME); + case CKSUM_SHA256: + return (SHA256_NAME); + case CKSUM_SHA512: + return (SHA512_NAME); } } diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index f74922ac1b..9c7073fb76 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -68,6 +68,7 @@ #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_time_private.h" @@ -154,7 +155,6 @@ struct zip { enum compression entry_compression; enum encryption entry_encryption; int entry_flags; - int experiments; struct trad_enc_ctx tctx; char tctx_valid; unsigned char trad_chkdat; @@ -235,7 +235,6 @@ static int archive_write_zip_header(struct archive_write *, struct archive_entry *); static int archive_write_zip_options(struct archive_write *, const char *, const char *); -static size_t path_length(struct archive_entry *); static int write_path(struct archive_entry *, struct archive_write *); static void copy_path(struct archive_entry *, unsigned char *); static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *); @@ -398,16 +397,17 @@ archive_write_zip_options(struct archive_write *a, const char *key, return (ret); } else if (strcmp(key, "compression-level") == 0) { char *endptr; + unsigned long v; if (val == NULL) return (ARCHIVE_WARN); errno = 0; - zip->compression_level = (short)strtoul(val, &endptr, 10); - if (errno != 0 || *endptr != '\0' || zip->compression_level < 0 || - zip->compression_level > 9) { + v = strtoul(val, &endptr, 10); + if (errno != 0 || *endptr != '\0' || v > 9) { zip->compression_level = 6; // set to default return (ARCHIVE_WARN); } + zip->compression_level = (short)v; if (zip->compression_level == 0) { zip->requested_compression = COMPRESSION_STORE; @@ -435,17 +435,19 @@ archive_write_zip_options(struct archive_write *a, const char *key, } } else if (strcmp(key, "threads") == 0) { char *endptr; + unsigned long v; if (val == NULL) return (ARCHIVE_FAILED); errno = 0; - zip->threads = (short)strtoul(val, &endptr, 10); - if (errno != 0 || *endptr != '\0') { + v = strtoul(val, &endptr, 10); + if (errno != 0 || *endptr != '\0' || v > SHRT_MAX) { zip->threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Illegal value `%s'", val); return (ARCHIVE_FAILED); } + zip->threads = (short)v; if (zip->threads == 0) { #ifdef HAVE_LZMA_STREAM_ENCODER_MT zip->threads = lzma_cputhreads(); @@ -727,8 +729,7 @@ archive_write_set_format_zip(struct archive *_a) ARCHIVE_STATE_NEW, "archive_write_set_format_zip"); /* If another format was already registered, unregister it. */ - if (a->format_free != NULL) - (a->format_free)(a); + (void)__archive_write_unregister_format(a); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { @@ -788,12 +789,13 @@ is_all_ascii(const char *p) static int archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) { + struct zip *zip = a->format_data; unsigned char local_header[32]; unsigned char local_extra[144]; - struct zip *zip = a->format_data; unsigned char *e; unsigned char *cd_extra; size_t filename_length; + const char *path; const char *slink = NULL; size_t slink_size = 0; struct archive_string_conv *sconv = get_sconv(a, zip); @@ -955,7 +957,22 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) #endif } } - filename_length = path_length(zip->entry); + /* Reject empty or overlong pathnames */ + path = archive_entry_pathname(zip->entry); + if (path == NULL || path[0] == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZIP format requires a non-empty pathname"); + return (ARCHIVE_FAILED); + } + filename_length = strlen(path); + /* Include the trailing slash added to directories. */ + if (type == AE_IFDIR && path[filename_length - 1] != '/') + filename_length++; + if (filename_length > 0xffff) { + archive_set_error(&a->archive, ENAMETOOLONG, + "Pathname too long for ZIP format"); + return (ARCHIVE_FAILED); + } /* Determine appropriate compression and size for this entry. */ if (type == AE_IFLNK) { @@ -1049,8 +1066,13 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) default: break; } - if (zip->entry_compression == COMPRESSION_STORE) - zip->entry_compressed_size += additional_size; + if (zip->entry_compression == COMPRESSION_STORE && + archive_ckd_add_i64(&zip->entry_compressed_size, + zip->entry_compressed_size, additional_size)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "File size too large for encrypted ZIP entry"); + return (ARCHIVE_FAILED); + } } /* @@ -1064,7 +1086,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) * (compression might make file larger) */ if ((zip->flags & ZIP_FLAG_FORCE_ZIP64) - || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX) + || (zip->entry_uncompressed_size > ZIP_4GB_MAX - additional_size) || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED && zip->entry_compression != COMPRESSION_STORE)) { MIN_VERSION_NEEDED(45); @@ -1498,8 +1520,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) static ssize_t archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) { - int ret; struct zip *zip = a->format_data; + int ret; if ((int64_t)s > zip->entry_uncompressed_limit) s = (size_t)zip->entry_uncompressed_limit; @@ -2167,9 +2189,9 @@ archive_write_zip_finish_entry(struct archive_write *a) static int archive_write_zip_close(struct archive_write *a) { + struct zip *zip = a->format_data; uint8_t buff[64]; int64_t offset_start, offset_end; - struct zip *zip = a->format_data; struct cd_segment *segment; int ret; @@ -2239,10 +2261,9 @@ archive_write_zip_close(struct archive_write *a) static int archive_write_zip_free(struct archive_write *a) { - struct zip *zip; + struct zip *zip = a->format_data; struct cd_segment *segment; - zip = a->format_data; while (zip->central_directory != NULL) { segment = zip->central_directory; zip->central_directory = segment->next; @@ -2262,24 +2283,6 @@ archive_write_zip_free(struct archive_write *a) return (ARCHIVE_OK); } -static size_t -path_length(struct archive_entry *entry) -{ - mode_t type; - const char *path; - size_t len; - - type = archive_entry_filetype(entry); - path = archive_entry_pathname(entry); - - if (path == NULL) - return (0); - len = strlen(path); - if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/')) - ++len; /* Space for the trailing / */ - return len; -} - static int write_path(struct archive_entry *entry, struct archive_write *archive) { @@ -2301,7 +2304,7 @@ write_path(struct archive_entry *entry, struct archive_write *archive) written_bytes += strlen(path); /* Folders are recognized by a trailing slash. */ - if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) { + if ((type == AE_IFDIR) && (path[strlen(path) - 1] != '/')) { ret = __archive_write_output(archive, "/", 1); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/archive_write_set_options.3 b/libarchive/archive_write_set_options.3 index c3de50c8fe..e03b517577 100644 --- a/libarchive/archive_write_set_options.3 +++ b/libarchive/archive_write_set_options.3 @@ -555,6 +555,20 @@ used when translating file names. .El .It Format pax .Bl -tag -compact -width indent +.It Cm align +Pad the extended header of each regular file so that the file data that +follows begins on a multiple of the given number of bytes within the +uncompressed archive stream. +The value must be a power of two and a multiple of 512. +Because the alignment is applied to the uncompressed stream, it is +independent of any compression filter: the decompressed archive is +aligned regardless of whether the archive is compressed. +This allows tools to share file contents out of an +uncompressed archive (or an uncompressed copy of one) using +reflinks or +.Xr copy_file_range 2 , +at the cost of some extra padding. +Files shorter than the alignment are not padded. .It Cm hdrcharset The value is used as a character set name that will be used when translating file, group and user names. @@ -608,9 +622,11 @@ as file checksum method. Supported values are .Dq none , .Dq md5 , -and .Dq sha1 -.Pq default . +.Pq default , +.Dq sha256 , +and +.Dq sha512 . .It Cm compression Ns = Ns Ar type Use .Ar type @@ -631,10 +647,12 @@ Use as table of contents checksum method. Supported values are .Dq none , -.Dq md5 -and +.Dq md5 , .Dq sha1 -.Pq default . +.Pq default , +.Dq sha256 , +and +.Dq sha512 . .El .It Format zip .Bl -tag -compact -width indent diff --git a/libarchive/filter_fork_windows.c b/libarchive/filter_fork_windows.c index 9e49c5655f..a0fd638940 100644 --- a/libarchive/filter_fork_windows.c +++ b/libarchive/filter_fork_windows.c @@ -31,43 +31,68 @@ #include "filter_fork.h" -#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) /* There are some editions of Windows ("nano server," for example) that * do not host user32.dll. If we want to keep running on those editions, * we need to delay-load WaitForInputIdle. */ -static void * -la_GetFunctionUser32(const char *name) + +static int +failing_wait(HANDLE hProcess, DWORD dwMilliseconds) { + /* An inability to wait for input idle is + * not _good_, but it is not catastrophic. */ + (void)hProcess; /* UNUSED */ + (void)dwMilliseconds; /* UNUSED */ + return WAIT_FAILED; +} + +# if _WIN32_WINNT < _WIN32_WINNT_VISTA +static int +la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds) { - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; + static DWORD (WINAPI * volatile f)(HANDLE, DWORD); + + if (f == NULL) { + HINSTANCE lib; + void *old; + DWORD (WINAPI *tmp)(HANDLE, DWORD); + lib = LoadLibrary(TEXT("user32.dll")); + tmp = (lib != NULL) ? + (PVOID)GetProcAddress(lib, "WaitForInputIdle") : + failing_wait; + old = InterlockedCompareExchangePointer((volatile PVOID *)&f, + tmp, NULL); + if (old != NULL && lib != NULL) + FreeLibrary(lib); } - if (lib == NULL) { - return NULL; - } - return (void *)GetProcAddress(lib, name); + + return (*f)(hProcess, dwMilliseconds); +} +# else +static BOOL CALLBACK +load_WaitForInputIdle(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) { + HMODULE user32 = LoadLibrary(TEXT("user32.dll")); + + (void)InitOnce; /* UNUSED */ + (void)Parameter; /* UNUSED */ + + *Context = (user32 != NULL) ? + (PVOID)GetProcAddress(user32, "WaitForInputIdle") : failing_wait; + + return TRUE; } static int la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds) { static DWORD (WINAPI *f)(HANDLE, DWORD); - static int set; + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; - if (!set) { - set = 1; - f = la_GetFunctionUser32("WaitForInputIdle"); - } + InitOnceExecuteOnce(&once, load_WaitForInputIdle, NULL, (PVOID)&f); - if (!f) { - /* An inability to wait for input idle is - * not _good_, but it is not catastrophic. */ - return WAIT_FAILED; - } return (*f)(hProcess, dwMilliseconds); } +# endif int __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, diff --git a/libarchive/libarchive-formats.5 b/libarchive/libarchive-formats.5 index 1b057b4da0..eb720eb740 100644 --- a/libarchive/libarchive-formats.5 +++ b/libarchive/libarchive-formats.5 @@ -210,7 +210,7 @@ values of varying byte order and length. .Bl -tag -width indent .It Cm binary The libarchive library transparently reads both big-endian and -little-endian variants of the the two binary cpio formats; the +little-endian variants of the two binary cpio formats; the original one from PWB/UNIX, and the later, more widely used, variant. This format used 32-bit binary values for file size and mtime, and 16-bit binary values for the other fields. The formats support only diff --git a/libarchive/xxhash.c b/libarchive/xxhash.c index beacd23912..37dc6ffd67 100644 --- a/libarchive/xxhash.c +++ b/libarchive/xxhash.c @@ -361,6 +361,8 @@ static void* XXH32_init (U32 seed) { void* state = XXH_malloc (sizeof(struct XXH_state32_t)); + if (state == NULL) + return NULL; XXH32_resetState(state, seed); return state; }