Merge topic 'update-libarchive'

e420259ecc libarchive: Restore compilation of archive_parse_date within CMakeLib
5b530ba347 libarchive: Set build options the way we need for CMake
dbcd6402c3 Utilities: Update hard-coded try_compile results for libarchive 3.8.9
357710687f Merge branch 'upstream-LibArchive' into update-libarchive
94ae161ab5 LibArchive 2026-07-28 (27cbc782)
46ccf27ae3 libarchive: Update script to get 3.8.9
16984b0124 ctest: Fix STOP_TIME for vendored archive_parse_date from libarchive 3.8.8+

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !12398
This commit is contained in:
Brad King
2026-08-12 11:38:33 -04:00
committed by Kitware Robot
128 changed files with 9588 additions and 7722 deletions
+2 -1
View File
@@ -288,7 +288,8 @@ else()
set(ENABLE_PCREPOSIX OFF)
set(ENABLE_PCRE2POSIX OFF)
set(ENABLE_LIBGCC OFF)
set(ENABLE_CNG OFF)
set(ENABLE_CLANG_TIDY OFF)
set(ENABLE_CNG ON)
set(ENABLE_TAR OFF)
set(ENABLE_TAR_SHARED OFF)
set(ENABLE_CPIO OFF)
+1 -1
View File
@@ -2864,7 +2864,7 @@ void cmCTest::SetStopTime(std::string const& time_str)
tzone_offset *= 100;
char buf[1024];
snprintf(buf, sizeof(buf), "%d%02d%02d %s %+05i", lctime->tm_year + 1900,
snprintf(buf, sizeof(buf), "%d-%02d-%02d %s %+05i", lctime->tm_year + 1900,
lctime->tm_mon + 1, lctime->tm_mday, time_str.c_str(),
tzone_offset);
+2 -2
View File
@@ -12,11 +12,11 @@ bool parse_date()
std::time_t now;
std::time(&now);
{
std::time_t t = cm_parse_date(now, "20000101 00:01:02 -0000");
std::time_t t = cm_parse_date(now, "2000-01-01 00:01:02 -0000");
ASSERT_EQUAL(t, 946684862);
}
{
std::time_t t = cm_parse_date(now, "20380601 00:01:02 -0000");
std::time_t t = cm_parse_date(now, "2038-06-01 00:01:02 -0000");
ASSERT_EQUAL(t, sizeof(time_t) <= 4 ? -1 : 2158963262);
}
return true;
+1 -1
View File
@@ -8,7 +8,7 @@ readonly name="LibArchive"
readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>"
readonly subtree="Utilities/cmlibarchive"
readonly repo="https://github.com/libarchive/libarchive.git"
readonly tag="v3.8.6"
readonly tag="v3.8.9"
readonly shortlog=false
readonly exact_tree_match=false
readonly paths="
+5 -1
View File
@@ -33,6 +33,7 @@ if(WIN32)
set(HAVE_ARC4RANDOM 0)
set(HAVE_ARC4RANDOM_BUF 0)
set(HAVE_ARPA_TFTP_H 0)
set(HAVE_BCRYPT_H 1)
set(HAVE_BSWAP_16 0)
set(HAVE_BSWAP_32 0)
set(HAVE_BSWAP_64 0)
@@ -259,7 +260,10 @@ if(WIN32)
set(HAVE_SIZEOF_CURL_SOCKET_T 1)
set(SIZEOF_CURL_SOCKET_T 8)
if(NOT MINGW)
if(MINGW)
set(HAVE_GETTIMEOFDAY 1)
else()
set(HAVE_GETTIMEOFDAY 0)
set(HAVE_STRTOK_R 0)
endif()
+113 -118
View File
@@ -277,19 +277,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)
@@ -372,27 +368,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.
#
@@ -690,7 +728,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)
@@ -767,6 +805,7 @@ CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
#include <linux/fs.h>
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)
@@ -781,6 +820,8 @@ 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("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)
@@ -814,12 +855,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)
#
@@ -847,11 +898,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)
@@ -867,6 +921,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)
@@ -875,6 +930,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)
@@ -890,11 +947,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)
CMAKE_PUSH_CHECK_STATE()
set(CMAKE_REQUIRED_LIBRARIES OpenSSL::Crypto)
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
@@ -915,9 +976,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")
#
@@ -1029,85 +1094,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 <windows.h>
#include <wincrypt.h>
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 **
@@ -1551,6 +1537,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)
@@ -2155,18 +2142,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()
IF(0) # CMake does not build libarchive's shared library.
# Check visibility annotations
@@ -2231,6 +2219,13 @@ IF(APPLE)
LIST(APPEND ADDITIONAL_LIBS "-framework CoreServices")
ENDIF(APPLE)
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)
IF(0) # CMake does not build libarchive's command-line tools.
add_subdirectory(cat)
@@ -453,6 +453,9 @@
/* 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
@@ -471,6 +474,9 @@
/* Define to 1 if you have the <iconv.h> header file. */
#cmakedefine HAVE_ICONV_H 1
/* Define to 1 if you have the <intsafe.h> header file. */
#cmakedefine HAVE_INTSAFE_H 1
/* Define to 1 if you have the <io.h> header file. */
#cmakedefine HAVE_IO_H 1
@@ -809,6 +815,12 @@
/* Define to 1 if you have the <stdarg.h> header file. */
#cmakedefine HAVE_STDARG_H 1
/* Define to 1 if you have the <stdckdint.h> header file. */
#cmakedefine HAVE_STDCKDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#cmakedefine HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#cmakedefine HAVE_STDLIB_H 1
@@ -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()
+1 -1
View File
@@ -1 +1 @@
3008006
3008009
@@ -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
+5 -4
View File
@@ -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 <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -174,7 +174,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);
@@ -207,7 +207,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);
@@ -650,7 +652,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 *);
@@ -1231,7 +1232,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 *,
+171 -286
View File
@@ -60,9 +60,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,
@@ -71,9 +69,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,
@@ -82,9 +80,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;
@@ -543,20 +541,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;
@@ -569,107 +559,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);
}
/*
@@ -680,15 +574,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);
@@ -699,9 +590,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)
@@ -709,28 +598,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) {
@@ -752,108 +633,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;
}
@@ -861,51 +729,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);
}
}
@@ -917,15 +783,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);
@@ -936,9 +800,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)
@@ -946,28 +808,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) {
@@ -990,109 +844,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;
}
@@ -1100,51 +940,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);
}
}
@@ -1255,11 +1093,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;
@@ -1374,7 +1219,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;
@@ -1409,7 +1257,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. */
@@ -1443,8 +1295,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';
@@ -1671,7 +1523,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;
@@ -1694,6 +1546,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') {
/*
@@ -1720,6 +1573,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;
@@ -1757,15 +1622,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) {
@@ -1773,6 +1644,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
@@ -1878,7 +1751,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;
@@ -1913,8 +1789,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. */
@@ -1948,8 +1827,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';
@@ -2131,7 +2010,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 == '#') {
@@ -2139,7 +2021,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. */
@@ -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"
@@ -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;
@@ -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)
@@ -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. */
@@ -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)
@@ -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 <bcrypt.h>
#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;
@@ -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
@@ -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 <bcrypt.h>
#define ARCHIVE_CRYPTO_CNG 1
typedef struct {
@@ -174,22 +173,6 @@ typedef struct {
BCRYPT_ALG_HANDLE hAlg;
BCRYPT_HASH_HANDLE hHash;
} Digest_CTX;
#else
#include <windows.h>
#include <wincrypt.h>
/* Undefine indirect <wincrypt.h> symbols conflicting with BoringSSL/AWS-LC. */
#undef X509_NAME
#undef X509_EXTENSIONS
#undef PKCS7_ISSUER_AND_SERIAL
#undef PKCS7_SIGNER_INFO
#undef OCSP_REQUEST
#undef OCSP_RESPONSE
#define ARCHIVE_CRYPTO_WINCRYPT 1
typedef struct {
int valid;
HCRYPTPROV cryptProv;
HCRYPTHASH hash;
} Digest_CTX;
#endif
#endif
@@ -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)
{
@@ -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 */
@@ -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
@@ -662,7 +662,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.
*/
@@ -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 *));
@@ -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
@@ -28,9 +28,6 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#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);
}
@@ -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;
@@ -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
@@ -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 <bcrypt.h>
typedef struct {
@@ -0,0 +1,272 @@
/*-
* 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
#ifdef CM_PARSE_DATE
#define HAVE_STDINT_H 1
#define USE_STDCKDINT 0
#else
#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
#endif
#ifdef HAVE_INTSAFE_H
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
#include <intsafe.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_STDCKDINT_H
#include <stdckdint.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#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
@@ -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);
}
/*
@@ -605,7 +620,7 @@ add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
int64_t offset;
int r;
ar = archive_read_new();
ar = archive_read_new();
if (ar == NULL) {
archive_set_error(&(a->archive), ENOMEM, "No memory");
return (ARCHIVE_FATAL);
@@ -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. */
@@ -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)) {
@@ -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]),
@@ -38,6 +38,7 @@
#include <time.h>
#include "archive.h"
#include "archive_integer.h"
/* Basic time units. */
#define EPOCH 1970
@@ -51,7 +52,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; };
/*
@@ -820,20 +821,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;
}
/*
@@ -908,12 +922,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)++;
@@ -951,6 +972,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 + */
@@ -959,12 +981,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;
}
@@ -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));
}
@@ -65,7 +65,7 @@ typedef struct CPpmd8_Node_
#define EMPTY_NODE 0xFFFFFFFF
void Ppmd8_Construct(CPpmd8 *p)
static void Ppmd8_Construct(CPpmd8 *p)
{
unsigned i, k, m;
@@ -93,14 +93,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)
{
@@ -414,7 +414,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;
@@ -1054,7 +1054,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)
@@ -1090,7 +1090,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;
@@ -1105,7 +1105,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;
@@ -1115,7 +1115,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;
@@ -1123,7 +1123,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)
@@ -1139,7 +1139,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;
@@ -1173,7 +1173,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)
@@ -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
{
@@ -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 {
@@ -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 <bcrypt.h>
/* 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 <wincrypt.h>
#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;
+264 -200
View File
@@ -36,6 +36,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -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;
}
@@ -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;
@@ -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);
}
@@ -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;
@@ -457,6 +457,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;
@@ -928,8 +932,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) {
@@ -1042,8 +1046,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) {
@@ -1068,8 +1072,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) {
@@ -1421,18 +1425,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");
@@ -1441,6 +1445,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;
@@ -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;
@@ -37,10 +37,11 @@
#include <winioctl.h>
#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) {
@@ -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
@@ -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 ,
@@ -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));
}
@@ -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));
}
@@ -40,6 +40,9 @@
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#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
}
@@ -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));
}
@@ -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;
@@ -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
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -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. <sigh> */
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);
}
@@ -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<<state->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<<compress->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]);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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);
@@ -47,9 +47,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)
@@ -69,7 +67,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 *,
@@ -213,7 +211,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)
{
@@ -228,7 +226,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) {
@@ -241,12 +239,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;
@@ -264,42 +262,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;
@@ -311,51 +309,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);
}
@@ -370,46 +368,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;
@@ -417,7 +415,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;
@@ -427,43 +425,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;
@@ -471,10 +466,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
@@ -483,15 +476,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);
@@ -500,17 +495,18 @@ 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);
#ifdef __clang_analyzer__
assert(d);
#endif
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;
@@ -519,22 +515,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);
@@ -543,28 +545,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);
@@ -580,30 +582,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;
@@ -612,7 +614,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);
@@ -621,13 +623,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) {
@@ -656,7 +658,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);
@@ -664,9 +666,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);
@@ -678,7 +680,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) {
@@ -711,7 +713,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);
@@ -722,23 +724,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);
}
@@ -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);
}
@@ -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);
}
@@ -57,6 +57,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"
@@ -71,16 +72,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
@@ -101,8 +103,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 */
@@ -176,50 +178,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;
};
@@ -231,8 +231,6 @@ struct _7z_stream_info {
};
struct _7z_header_info {
uint64_t dataIndex;
unsigned char *emptyStreamBools;
unsigned char *emptyFileBools;
unsigned char *antiBools;
@@ -245,8 +243,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)
@@ -270,24 +268,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;
@@ -308,16 +306,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.
@@ -338,7 +336,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
@@ -349,12 +347,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;
@@ -369,7 +365,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];
@@ -386,13 +382,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;
};
@@ -400,10 +394,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 *,
@@ -411,14 +409,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 *);
@@ -429,10 +428,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 *);
@@ -446,16 +446,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 *,
@@ -465,9 +464,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
@@ -513,19 +513,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;
}
@@ -533,65 +532,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);
@@ -600,8 +627,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);
@@ -615,79 +641,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
@@ -695,8 +675,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;
}
}
@@ -714,6 +696,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)
*/
@@ -721,12 +707,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);
@@ -736,34 +724,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;
@@ -786,7 +780,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 {
@@ -794,24 +788,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)(
@@ -824,45 +826,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)(
@@ -871,9 +878,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;
@@ -882,19 +888,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
@@ -908,8 +915,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;
@@ -919,20 +925,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) {
@@ -946,9 +952,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 && fidx<folder->numCoders; fidx++) {
for (fidx = 0; fidx < folder->numCoders; fidx++) {
switch(folder->coders[fidx].codec) {
case _7Z_CRYPTO_MAIN_ZIP:
case _7Z_CRYPTO_RAR_29:
@@ -961,13 +969,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) {
@@ -979,7 +980,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;
}
@@ -995,7 +996,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);
@@ -1008,29 +1009,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. */
@@ -1041,6 +1032,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.
@@ -1097,10 +1095,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);
}
@@ -1108,12 +1102,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;
}
@@ -1131,8 +1123,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);
@@ -1161,7 +1153,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;
@@ -1178,10 +1170,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);
@@ -1194,9 +1184,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. */
@@ -1207,9 +1197,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);
@@ -1220,24 +1209,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.
*/
@@ -1285,52 +1280,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);
}
@@ -1359,8 +1351,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;
@@ -1445,7 +1438,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;
@@ -1485,7 +1478,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);
}
}
@@ -1497,7 +1491,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);
@@ -1565,7 +1559,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;
@@ -1588,7 +1582,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;
@@ -1639,8 +1633,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:
@@ -1654,7 +1646,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:
@@ -1665,11 +1657,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);
}
@@ -1721,7 +1714,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;
@@ -1730,7 +1723,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 -=
@@ -1755,8 +1748,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;
@@ -1784,7 +1777,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);
}
@@ -1794,10 +1787,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. */
@@ -1827,6 +1824,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;
@@ -1847,15 +1848,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;
@@ -1865,7 +1871,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) {
@@ -1923,14 +1929,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:
@@ -1945,7 +1950,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) {
@@ -1953,9 +1958,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;
@@ -1971,7 +1977,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;
@@ -1979,14 +1985,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 -=
@@ -2034,7 +2040,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
@@ -2047,8 +2053,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;
@@ -2057,18 +2076,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);
}
@@ -2076,7 +2098,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) {
@@ -2085,7 +2108,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);
@@ -2102,7 +2125,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);
@@ -2149,25 +2172,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]
@@ -2179,13 +2200,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);
}
@@ -2197,9 +2218,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);
@@ -2208,7 +2229,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);
/*
@@ -2224,40 +2245,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++) {
@@ -2275,7 +2295,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. */
@@ -2286,43 +2306,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 ||
@@ -2330,34 +2348,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;
@@ -2367,12 +2378,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);
}
}
@@ -2385,7 +2394,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++)
@@ -2397,9 +2406,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));
@@ -2412,10 +2422,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.
@@ -2425,7 +2441,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++) {
@@ -2434,9 +2450,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,
@@ -2457,14 +2471,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;
}
}
@@ -2478,7 +2492,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];
@@ -2499,16 +2513,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)
@@ -2530,11 +2544,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));
@@ -2548,14 +2562,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);
@@ -2563,6 +2574,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,
@@ -2578,8 +2599,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;
@@ -2587,11 +2608,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]);
@@ -2606,22 +2626,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) {
@@ -2633,7 +2652,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++) {
@@ -2669,16 +2688,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);
@@ -2691,9 +2710,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);
}
@@ -2701,7 +2720,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)
@@ -2714,12 +2733,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)
@@ -2728,7 +2744,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);
@@ -2751,18 +2767,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) {
/*
@@ -2781,13 +2817,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);
@@ -2811,12 +2860,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;
@@ -2824,7 +2873,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)
@@ -2833,9 +2882,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;
@@ -2843,12 +2893,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++) {
@@ -2891,7 +2941,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:
@@ -2964,21 +3014,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);
@@ -3031,7 +3080,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;
@@ -3040,14 +3089,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) {
@@ -3104,27 +3149,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;
}
@@ -3132,14 +3175,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;
@@ -3175,7 +3216,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) {
@@ -3193,9 +3235,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);
}
@@ -3206,10 +3251,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);
@@ -3241,26 +3286,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");
@@ -3268,7 +3316,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");
@@ -3277,24 +3325,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;
@@ -3405,10 +3465,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);
@@ -3454,14 +3514,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
@@ -3471,10 +3542,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;
@@ -3483,18 +3554,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) {
@@ -3515,10 +3585,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,
@@ -3564,7 +3632,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,
@@ -3580,7 +3648,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;
@@ -3618,10 +3686,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);
@@ -3630,9 +3698,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++;
@@ -3644,8 +3717,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) {
@@ -3698,7 +3771,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++;
}
@@ -3716,9 +3789,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) {
@@ -3742,8 +3816,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;
@@ -3758,10 +3836,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;
/*
@@ -3773,15 +3851,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) {
@@ -3799,7 +3871,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++;
@@ -3819,8 +3891,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]);
@@ -3840,7 +3920,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};
@@ -3893,14 +3974,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),
@@ -3908,13 +3987,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++) {
@@ -3925,7 +4007,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
@@ -3939,6 +4021,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]);
@@ -3996,7 +4086,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;
@@ -4011,26 +4101,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) {
@@ -4039,11 +4143,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);
}
/*
@@ -4092,7 +4196,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;
@@ -4111,9 +4215,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;
@@ -4173,16 +4275,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);
}
}
@@ -4213,20 +4312,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);
@@ -4242,10 +4335,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);
}
}
@@ -4288,10 +4378,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;
@@ -4300,10 +4387,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);
}
}
@@ -4416,7 +4500,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;
@@ -4514,15 +4598,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];
@@ -4531,7 +4610,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];
@@ -4548,3 +4627,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);
}
@@ -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);
@@ -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);
@@ -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,65 +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;
/* Initialize table to invalid values */
for (i = 0; i < tbl_size; i++) {
tbl[i] = (uint16_t)hf->len_size;
}
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];
}
@@ -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;
@@ -1106,17 +1125,12 @@ record_hardlink(struct archive_read *a,
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");
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid hardlink entry with no pathname");
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;
@@ -1128,5 +1142,11 @@ record_hardlink(struct archive_read *a,
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;
return (ARCHIVE_OK);
}
@@ -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 */
@@ -3137,12 +3130,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);
@@ -3170,7 +3163,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;
@@ -3189,7 +3182,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)
@@ -3232,17 +3225,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:
@@ -3487,6 +3469,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;
@@ -3511,26 +3495,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
@@ -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
@@ -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. */
@@ -44,6 +44,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"
@@ -156,7 +157,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
@@ -203,8 +204,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;
@@ -229,8 +230,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;
@@ -501,7 +500,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 (;;) {
@@ -596,7 +595,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));
@@ -711,7 +710,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))
@@ -779,7 +778,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;
}
@@ -879,10 +878,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,
@@ -909,9 +907,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;
@@ -922,8 +920,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
@@ -957,7 +953,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;
@@ -1007,7 +1003,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);
}
@@ -1102,7 +1098,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) {
@@ -1143,8 +1139,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);
@@ -1153,12 +1149,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);
@@ -1191,9 +1185,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)
{
@@ -1296,6 +1290,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;
@@ -1344,9 +1344,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);
@@ -1356,7 +1355,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);
}
@@ -1364,9 +1363,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;
@@ -1382,8 +1381,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) {
@@ -1434,14 +1431,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);
}
@@ -1473,7 +1470,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. */
@@ -1505,7 +1502,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);
}
@@ -1515,27 +1512,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);
@@ -1549,7 +1542,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;
@@ -1674,16 +1667,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;
}
@@ -1703,7 +1698,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;
@@ -1717,7 +1712,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;
@@ -1736,7 +1731,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;
@@ -1747,7 +1742,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;
@@ -1778,7 +1773,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;
@@ -1831,10 +1826,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);
@@ -1850,7 +1841,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);
}
@@ -1972,22 +1963,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;
@@ -2002,7 +1992,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);
}
@@ -2014,10 +2004,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))
{
@@ -2028,7 +2017,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;
@@ -2040,7 +2029,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;
@@ -2060,18 +2049,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)
{
@@ -2123,7 +2110,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;
@@ -2157,7 +2144,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;
}
@@ -2174,7 +2161,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)
{
@@ -2188,7 +2175,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)
@@ -2203,7 +2190,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:
@@ -2215,7 +2202,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);
}
@@ -2224,7 +2211,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;
@@ -2236,7 +2223,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;
@@ -2282,7 +2269,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))
@@ -2313,11 +2300,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);
@@ -2365,7 +2352,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
@@ -2381,7 +2368,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,
@@ -2395,7 +2382,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;
@@ -2405,13 +2392,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);
}
}
}
@@ -2419,6 +2406,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;
@@ -2461,7 +2455,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)
{
@@ -2475,8 +2469,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) {
@@ -2550,7 +2544,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.
@@ -2564,13 +2559,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;
@@ -2585,13 +2580,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);
@@ -2610,10 +2605,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)
@@ -2622,7 +2617,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 */
@@ -2688,7 +2682,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;
@@ -2701,7 +2695,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;
@@ -2753,7 +2747,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);
}
/*
@@ -2771,12 +2765,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);
}
@@ -2796,7 +2790,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++;
@@ -2812,7 +2806,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 */
@@ -2827,11 +2821,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;
@@ -2851,6 +2850,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);
}
@@ -2865,14 +2868,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);
@@ -2956,9 +2959,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)
@@ -3135,26 +3138,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);
@@ -3169,8 +3177,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)
{
@@ -3182,7 +3190,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);
}
}
@@ -3196,7 +3204,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],
@@ -3220,13 +3228,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;
@@ -3263,7 +3271,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 };
@@ -3318,7 +3326,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;
@@ -3431,7 +3440,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;
@@ -3552,7 +3561,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)
{
@@ -3593,7 +3608,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;
}
@@ -3646,7 +3660,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;
@@ -3842,7 +3856,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]);
@@ -3899,7 +3913,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;
@@ -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 <relative_addr_uint32> (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);
* <FILE> 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))) {
#ifdef __clang_analyzer__
/* Tell clang-analyzer that 'filt' does not leak.
add_new_filter passes off ownership. */
@@ -3094,7 +3130,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;
@@ -3110,7 +3146,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;
@@ -3120,15 +3156,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.
*
@@ -3139,26 +3182,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
@@ -3166,18 +3209,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;
}
@@ -3197,15 +3240,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;
@@ -3214,17 +3267,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) {
@@ -3248,28 +3301,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) {
@@ -3279,7 +3332,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;
@@ -3287,7 +3340,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. */
@@ -3310,11 +3363,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) {
@@ -3325,38 +3379,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;
}
@@ -3414,15 +3468,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.
*
@@ -3431,10 +3485,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);
@@ -3472,7 +3526,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;
@@ -3491,12 +3545,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");
@@ -3504,26 +3558,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
@@ -3532,41 +3587,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
@@ -3577,17 +3643,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. */
@@ -3596,19 +3663,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;
@@ -3628,7 +3695,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;
}
@@ -3636,12 +3703,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
@@ -3650,9 +3726,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
@@ -3676,7 +3752,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.
@@ -3688,16 +3764,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. */
@@ -3706,7 +3782,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
@@ -3719,22 +3795,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;
@@ -3744,13 +3820,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;
@@ -3765,14 +3841,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;
@@ -3783,18 +3859,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;
@@ -3802,24 +3878,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;
}
@@ -3866,31 +3942,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
@@ -3898,15 +3975,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;
}
@@ -3924,19 +4001,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,
@@ -3946,15 +4021,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
@@ -3971,9 +4046,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;
@@ -3994,19 +4069,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
@@ -4015,7 +4090,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;
}
@@ -4032,16 +4107,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 {
@@ -4049,12 +4124,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 */
@@ -4071,13 +4146,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;
}
@@ -4090,15 +4165,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 {
@@ -4122,39 +4197,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.
@@ -4169,15 +4244,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
}
}
@@ -4205,23 +4280,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
@@ -4231,28 +4306,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.
@@ -4262,7 +4337,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);
}
@@ -4270,21 +4345,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
@@ -4293,14 +4368,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
@@ -4313,11 +4388,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;
@@ -4326,28 +4401,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;
@@ -4361,9 +4438,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;
}
}
@@ -4371,50 +4448,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,
@@ -4427,8 +4504,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;
@@ -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);
}
@@ -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);
}
@@ -2256,18 +2271,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;
@@ -2287,12 +2302,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);
}
@@ -2349,7 +2368,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]) {
@@ -2433,7 +2451,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"
@@ -2508,15 +2526,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 "
@@ -2550,7 +2573,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"
@@ -2580,7 +2603,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"
@@ -2633,7 +2656,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,
@@ -2680,7 +2703,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"
@@ -2710,7 +2733,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"
@@ -2737,16 +2760,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) {
@@ -2774,7 +2807,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 "
@@ -2818,8 +2851,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);
}
@@ -2886,7 +2924,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)
@@ -2895,10 +2934,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;
@@ -2914,34 +2949,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;
}
}
/*
@@ -2951,6 +3025,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;
@@ -2961,8 +3036,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')
@@ -3041,7 +3122,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);
@@ -3104,7 +3185,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];
@@ -3122,7 +3202,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 "
@@ -3240,14 +3320,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
@@ -3268,10 +3346,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--;
}
@@ -3367,13 +3445,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;
@@ -3417,13 +3492,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.
*/
@@ -3437,25 +3508,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
@@ -3535,9 +3603,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);
@@ -3609,24 +3676,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);
@@ -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 <sys/stat.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@@ -56,40 +63,35 @@
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include "archive.h"
#include "archive_entry.h"
#include "archive_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 */
@@ -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:
@@ -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);
@@ -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);
}
@@ -24,11 +24,20 @@
*/
#include "archive_platform.h"
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "archive_integer.h"
#include "archive_private.h"
#include "archive_time_private.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#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;
}
@@ -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
@@ -42,18 +42,12 @@
#include <string.h>
#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 <bcrypt.h>
/* 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 <wincrypt.h>
#endif
#endif
#ifdef HAVE_ZLIB_H
#include <cm3p/zlib.h>
@@ -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);
@@ -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 *
@@ -63,23 +63,6 @@
#include <windows.h>
#include <share.h>
#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,
@@ -192,7 +192,6 @@
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* directory */
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */
#endif
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */
@@ -306,6 +305,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)
@@ -316,17 +335,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 */
@@ -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);
}
@@ -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 },
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#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);
}
}
@@ -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 */
@@ -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);
}
}
@@ -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);
}
}
@@ -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 */
@@ -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);
}
}
@@ -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 */
@@ -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
@@ -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);
}
}
@@ -28,6 +28,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#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);
}
}

Some files were not shown because too many files have changed in this diff Show More