Merge branch 'upstream-LibArchive' into update-libarchive

* upstream-LibArchive:
  LibArchive 2020-02-11 (3288ebb0)

Also manually restore content from upstream libarchive's main
`CMakeLists.txt` file that was removed by previous commits and
exclude it with `IF(0)` blocks.  Do this as an evil merge so
that `git blame -C` can follow the content to upstream.
This commit is contained in:
Brad King
2020-02-13 12:57:52 -05:00
151 changed files with 11639 additions and 1324 deletions
+453 -20
View File
@@ -1,9 +1,43 @@
#
IF(0) # CMake handles policy settings in its own build.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
if(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW) #3.12.0 `find_package()`` uses ``<PackageName>_ROOT`` variables.
endif()
ENDIF()
#
PROJECT(libarchive C)
#
SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake")
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${libarchive_BINARY_DIR}/bin)
endif()
IF(0) # CMake handles build type selection in its own build.
#
# Set the Build type for make based generators.
# You can choose following types:
# Debug : Debug build
# Release : Release build
# RelWithDebInfo : Release build with Debug Info
# MinSizeRel : Release Min Size build
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
ENDIF(NOT CMAKE_BUILD_TYPE)
# Set a value type to properly display CMAKE_BUILD_TYPE on GUI if the
# value type is "UNINITIALIZED".
GET_PROPERTY(cached_type CACHE CMAKE_BUILD_TYPE PROPERTY TYPE)
IF("${cached_type}" STREQUAL "UNINITIALIZED")
SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Build Type" FORCE)
ENDIF("${cached_type}" STREQUAL "UNINITIALIZED")
# Check the Build Type.
IF(NOT "${CMAKE_BUILD_TYPE}"
MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$")
MESSAGE(FATAL_ERROR
"Unknown keyword for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\n"
"Acceptable keywords: Debug,Release,RelWithDebInfo,MinSizeRel")
ENDIF(NOT "${CMAKE_BUILD_TYPE}"
MATCHES "^(Debug|Release|RelWithDebInfo|MinSizeRel)\$")
ENDIF()
# On MacOS, prefer MacPorts libraries to system libraries.
# I haven't come up with a compelling argument for this to be conditional.
@@ -57,6 +91,11 @@ SET(CMAKE_REQUIRED_DEFINITIONS)
SET(CMAKE_REQUIRED_INCLUDES)
SET(CMAKE_REQUIRED_LIBRARIES)
SET(CMAKE_REQUIRED_FLAGS)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." ON)
else ()
OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." OFF)
endif ()
# Disable warnings to avoid changing 3rd party code.
IF(CMAKE_C_COMPILER_ID MATCHES
@@ -66,14 +105,116 @@ ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
ENDIF()
IF(0) # CMake does not need flags specific to libarchive upstream development.
# Especially for early development, we want to be a little
# aggressive about diagnosing build problems; this can get
# relaxed somewhat in final shipping versions.
IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$")
SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security")
#################################################################
# Set compile flags for all build types.
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security")
if (ENABLE_WERROR)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
endif ()
#################################################################
# Set compile flags for debug build.
# This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug"
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual")
ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$")
IF (CMAKE_C_COMPILER_ID MATCHES "^Clang$")
SET(CMAKE_REQUIRED_FLAGS "-Wall -Wformat -Wformat-security")
#################################################################
# Set compile flags for all build types.
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security")
if (ENABLE_WERROR)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
endif ()
#################################################################
# Set compile flags for debug build.
# This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug"
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wmissing-prototypes")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wcast-qual")
ENDIF (CMAKE_C_COMPILER_ID MATCHES "^Clang$")
IF (CMAKE_C_COMPILER_ID MATCHES "^XL$")
SET(CMAKE_C_COMPILER "xlc_r")
SET(CMAKE_REQUIRED_FLAGS "-qflag=e:e -qformat=sec")
#################################################################
# Set compile flags for all build types.
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qflag=e:e -qformat=sec")
if (ENABLE_WERROR)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qhalt=w")
endif ()
#################################################################
# Set compile flags for debug build.
# This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug"
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qflag=w:w")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qinfo=pro:use")
ENDIF(CMAKE_C_COMPILER_ID MATCHES "^XL$")
IF (MSVC)
if (ENABLE_WERROR)
# /WX option is the same as gcc's -Werror option.
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
endif ()
#################################################################
# Set compile flags for debug build.
# This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug"
# Enable level 4 C4062: The enumerate has no associated handler in a switch
# statement and there is no default that can catch it.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4062")
# Enable level 4 C4254: A larger bit field was assigned to a smaller bit
# field.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4254")
# Enable level 4 C4295: An array was initialized but the last character in
# the array is not a null; accessing the array may
# produce unexpected results.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4295")
# Enable level 4 C4296: An unsigned variable was used in a comparison
# operation with zero.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4296")
# Enable level 4 C4389: An operation involved signed and unsigned variables.
# This could result in a loss of data.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4389")
# Enable level 4 C4505: The given function is local and not referenced in
# the body of the module; therefore, the function is
# dead code.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4505")
# Enable level 4 C4514: The optimizer removed an inline function that is not
# called.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4514")
# Enable level 4 C4702: Unreachable code.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4702")
# Enable level 4 C4706: The test value in a conditional expression was the
# result of an assignment.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4706")
# /Oi option enables built-in functions.
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi")
#################################################################
# Set compile flags for release build.
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Oi")
ENDIF (MSVC)
ENDIF()
# Enable CTest/CDash support
include(CTest)
OPTION(ENABLE_NETTLE "Enable use of Nettle" ON)
OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF)
OPTION(ENABLE_NETTLE "Enable use of Nettle" OFF)
OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
OPTION(ENABLE_LIBB2 "Enable the use of the system LIBB2 library if found" ON)
OPTION(ENABLE_LZ4 "Enable the use of the system LZ4 library if found" ON)
OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF)
OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON)
OPTION(ENABLE_ZSTD "Enable the use of the system zstd library if found" ON)
OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON)
OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON)
@@ -84,9 +225,34 @@ OPTION(ENABLE_LibGCC "Enable the use of the system LibGCC library if found" ON)
# CNG is used for encrypt/decrypt Zip archives on Windows.
OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON)
IF(0) # CMake does not build libarchive's command-line tools.
OPTION(ENABLE_TAR "Enable tar building" ON)
OPTION(ENABLE_TAR_SHARED "Enable dynamic build of tar" FALSE)
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)
ENDIF()
OPTION(ENABLE_XATTR "Enable extended attribute support" ON)
OPTION(ENABLE_ACL "Enable ACL support" ON)
OPTION(ENABLE_ICONV "Enable iconv support" ON)
IF(0) # CMake does not build libarchive's tests.
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)
SET(POSIX_REGEX_LIB "AUTO" CACHE STRING "Choose what library should provide POSIX regular expression support")
SET(ENABLE_SAFESEH "AUTO" CACHE STRING "Enable use of /SAFESEH linker flag (MSVC only)")
SET(WINDOWS_VERSION "WIN7" CACHE STRING "Set Windows version to use (Windows only)")
IF(ENABLE_COVERAGE)
include(LibarchiveCodeCoverage)
ENDIF(ENABLE_COVERAGE)
IF(ENABLE_TEST)
ENABLE_TESTING()
ENDIF(ENABLE_TEST)
ENDIF()
IF(WIN32)
#ELSEIF(WINDOWS_VERSION STREQUAL "WINXP")
@@ -95,12 +261,65 @@ IF(WIN32)
SET(WINVER 0x0501)
ENDIF(WIN32)
IF(0) # CMake hard-codes its own supported version of Windows.
IF(WIN32)
IF(WINDOWS_VERSION STREQUAL "WIN8")
SET(NTDDI_VERSION 0x06020000)
SET(_WIN32_WINNT 0x0602)
SET(WINVER 0x0602)
ELSEIF(WINDOWS_VERSION STREQUAL "WIN7")
SET(NTDDI_VERSION 0x06010000)
SET(_WIN32_WINNT 0x0601)
SET(WINVER 0x0601)
ELSEIF(WINDOWS_VERSION STREQUAL "WS08")
SET(NTDDI_VERSION 0x06000100)
SET(_WIN32_WINNT 0x0600)
SET(WINVER 0x0600)
ELSEIF(WINDOWS_VERSION STREQUAL "VISTA")
SET(NTDDI_VERSION 0x06000000)
SET(_WIN32_WINNT 0x0600)
SET(WINVER 0x0600)
ELSEIF(WINDOWS_VERSION STREQUAL "WS03")
SET(NTDDI_VERSION 0x05020000)
SET(_WIN32_WINNT 0x0502)
SET(WINVER 0x0502)
ELSEIF(WINDOWS_VERSION STREQUAL "WINXP")
SET(NTDDI_VERSION 0x05010000)
SET(_WIN32_WINNT 0x0501)
SET(WINVER 0x0501)
ELSE(WINDOWS_VERSION STREQUAL "WIN8")
# Default to Windows Server 2003 API if we don't recognize the specifier
SET(NTDDI_VERSION 0x05020000)
SET(_WIN32_WINNT 0x0502)
SET(WINVER 0x0502)
ENDIF(WINDOWS_VERSION STREQUAL "WIN8")
ENDIF(WIN32)
IF(MSVC)
IF(ENABLE_SAFESEH STREQUAL "YES")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH")
SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH")
SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH")
ELSEIF(ENABLE_SAFESEH STREQUAL "NO")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
SET(ENV{LDFLAGS} "$ENV{LDFLAGS} /SAFESEH:NO")
ENDIF(ENABLE_SAFESEH STREQUAL "YES")
ENDIF(MSVC)
ENDIF()
set(HAVE_PTHREAD_H 0) # no threads in CMake
IF("${CMAKE_C_PLATFORM_ID}" MATCHES "^(HP-UX)$")
ADD_DEFINITIONS(-D_XOPEN_SOURCE=500) # Ask wchar.h for mbstate_t
ENDIF()
IF(MINGW)
ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO)
ENDIF()
#
INCLUDE(CheckCSourceCompiles)
INCLUDE(CheckCSourceRuns)
@@ -289,12 +508,15 @@ MARK_AS_ADVANCED(CLEAR BZIP2_INCLUDE_DIR)
IF(ENABLE_LZMA)
FIND_PACKAGE(LibLZMA)
ELSE()
SET(LIBZMA_FOUND FALSE) # Override cached value
SET(LIBLZMA_FOUND FALSE) # Override cached value
ENDIF()
IF(LIBLZMA_FOUND)
SET(HAVE_LIBLZMA 1)
SET(HAVE_LZMA_H 1)
CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR})
SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES})
INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS})
LIST(APPEND ADDITIONAL_LIBS ${LIBLZMA_LIBRARIES})
IF(CMAKE_USE_SYSTEM_LIBLZMA)
@@ -310,6 +532,7 @@ IF(LIBLZMA_FOUND)
ELSE()
ADD_DEFINITIONS(-DLZMA_API_STATIC)
ENDIF()
CMAKE_POP_CHECK_STATE()
ELSE(LIBLZMA_FOUND)
# LZMA not found and will not be used.
ENDIF(LIBLZMA_FOUND)
@@ -345,6 +568,35 @@ MARK_AS_ADVANCED(CLEAR LZO2_LIBRARY)
ENDIF()
IF(0) # CMake does not need LZ4 support in libarchive
#
# Find libb2
#
IF(ENABLE_LIBB2)
IF (LIBB2_INCLUDE_DIR)
# Already in cache, be silent
SET(LIBB2_FIND_QUIETLY TRUE)
ENDIF (LIBB2_INCLUDE_DIR)
FIND_PATH(LIBB2_INCLUDE_DIR blake2.h)
FIND_LIBRARY(LIBB2_LIBRARY NAMES b2 libb2)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBB2 DEFAULT_MSG LIBB2_LIBRARY LIBB2_INCLUDE_DIR)
ELSE(ENABLE_LIBB2)
SET(LIBB2_FOUND FALSE) # Override cached value
ENDIF(ENABLE_LIBB2)
IF(LIBB2_FOUND)
SET(HAVE_LIBB2 1)
SET(HAVE_BLAKE2_H 1)
SET(ARCHIVE_BLAKE2 FALSE)
LIST(APPEND ADDITIONAL_LIBS ${LIBB2_LIBRARY})
CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_LIBRARIES ${LIBB2_LIBRARY})
SET(CMAKE_REQUIRED_INCLUDES ${LIBB2_INCLUDE_DIR})
CHECK_FUNCTION_EXISTS(blake2sp_init HAVE_LIBB2)
CMAKE_POP_CHECK_STATE()
ELSE(LIBB2_FOUND)
SET(ARCHIVE_BLAKE2 TRUE)
ENDIF(LIBB2_FOUND)
#
# Find LZ4
#
IF(ENABLE_LZ4)
@@ -379,27 +631,39 @@ ENDIF()
#
# Find Zstd
#
IF (ZSTD_INCLUDE_DIR)
# Already in cache, be silent
SET(ZSTD_FIND_QUIETLY TRUE)
ENDIF (ZSTD_INCLUDE_DIR)
IF(ENABLE_ZSTD)
IF (ZSTD_INCLUDE_DIR)
# Already in cache, be silent
SET(ZSTD_FIND_QUIETLY TRUE)
ENDIF (ZSTD_INCLUDE_DIR)
FIND_PATH(ZSTD_INCLUDE_DIR zstd.h)
FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
FIND_PATH(ZSTD_INCLUDE_DIR zstd.h)
FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
ELSE(ENABLE_ZSTD)
SET(ZSTD_FOUND FALSE) # Override cached value
ENDIF(ENABLE_ZSTD)
IF(ZSTD_FOUND)
SET(HAVE_ZSTD_H 1)
INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR})
LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY})
SET(HAVE_LIBZSTD 1)
IF(0) # CMake expects the zstd library to work.
CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY})
SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR})
CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD)
#
# TODO: test for static library.
#
CMAKE_POP_CHECK_STATE()
ENDIF()
ENDIF(ZSTD_FOUND)
MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY)
#
# Check headers
#
@@ -419,9 +683,11 @@ LA_CHECK_INCLUDE_FILE("sys/types.h" HAVE_SYS_TYPES_H)
# Alphabetize the rest unless there's a compelling reason
IF(ENABLE_ACL)
LA_CHECK_INCLUDE_FILE("acl/libacl.h" HAVE_ACL_LIBACL_H)
ELSE(ENABLE_ACL)
LA_CHECK_INCLUDE_FILE("attr/xattr.h" HAVE_ATTR_XATTR_H)
ELSE()
SET(HAVE_ACL_LIBACL_H FALSE)
ENDIF(ENABLE_ACL)
SET(HAVE_ATTR_XATTR_H FALSE)
ENDIF()
LA_CHECK_INCLUDE_FILE("ctype.h" HAVE_CTYPE_H)
LA_CHECK_INCLUDE_FILE("copyfile.h" HAVE_COPYFILE_H)
LA_CHECK_INCLUDE_FILE("direct.h" HAVE_DIRECT_H)
@@ -518,6 +784,26 @@ CHECK_C_SOURCE_COMPILES(
int main() { return 0;}"
SAFE_TO_DEFINE_EXTENSIONS)
#
# Find mbed TLS
#
IF(ENABLE_MBEDTLS)
FIND_PACKAGE(MbedTLS)
IF(MBEDTLS_FOUND)
SET(HAVE_LIBMBEDCRYPTO 1)
LIST(APPEND ADDITIONAL_LIBS ${MBEDCRYPTO_LIBRARY})
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIRS})
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)
ENDIF(MBEDTLS_FOUND)
MARK_AS_ADVANCED(CLEAR MBEDTLS_INCLUDE_DIRS)
MARK_AS_ADVANCED(CLEAR MBEDCRYPTO_LIBRARY)
ENDIF(ENABLE_MBEDTLS)
#
# Find Nettle
#
@@ -579,6 +865,8 @@ MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION)
STRING(TOUPPER "${ALGORITHM}" algorithm)
IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND)
SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE)
ELSEIF("${IMPLEMENTATION}" MATCHES "^MBEDTLS$" AND NOT MBEDTLS_FOUND)
SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE)
ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NOT NETTLE_FOUND)
SET(ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION} FALSE)
ENDIF("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND NOT OPENSSL_FOUND)
@@ -594,6 +882,11 @@ MACRO(CHECK_CRYPTO ALGORITHMS IMPLEMENTATION)
"${TRY_CRYPTO_REQUIRED_INCLUDES};${OPENSSL_INCLUDE_DIR}")
SET(TRY_CRYPTO_REQUIRED_LIBS
"-DLINK_LIBRARIES:STRING=${OPENSSL_LIBRARIES}")
ELSEIF("${IMPLEMENTATION}" MATCHES "^MBEDTLS$" AND MBEDTLS_FOUND)
SET(TRY_CRYPTO_REQUIRED_INCLUDES
"${TRY_CRYPTO_REQUIRED_INCLUDES};${MBEDTLS_INCLUDE_DIRS}")
SET(TRY_CRYPTO_REQUIRED_LIBS
"-DLINK_LIBRARIES:STRING=${MBEDCRYPTO_LIBRARY}")
ELSEIF("${IMPLEMENTATION}" MATCHES "^NETTLE$" AND NETTLE_FOUND)
SET(TRY_CRYPTO_REQUIRED_INCLUDES
"${TRY_CRYPTO_REQUIRED_INCLUDES};${NETTLE_INCLUDE_DIR}")
@@ -758,18 +1051,19 @@ ENDMACRO(CHECK_CRYPTO_WIN CRYPTO_LIST)
MACRO(CHECK_ICONV LIB TRY_ICONV_CONST)
IF(NOT HAVE_ICONV)
CMAKE_PUSH_CHECK_STATE() # Save the state of the variables
IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
CMAKE_C_COMPILER_ID STREQUAL "Clang")
IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR
CMAKE_C_COMPILER_ID MATCHES "^Clang$")
#
# During checking iconv proto type, we should use -Werror to avoid the
# success of iconv detection with a warnig which success is a miss
# detection. So this needs for all build mode(even it's a release mode).
#
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror")
ENDIF ()
IF (CMAKE_C_COMPILER_ID STREQUAL "XL")
ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR
CMAKE_C_COMPILER_ID MATCHES "^Clang$")
IF (CMAKE_C_COMPILER_ID MATCHES "^XL$")
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -qhalt=w -qflag=w:w")
ENDIF ()
ENDIF (CMAKE_C_COMPILER_ID MATCHES "^XL$")
IF (MSVC)
# NOTE: /WX option is the same as gcc's -Werror option.
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /WX")
@@ -940,19 +1234,134 @@ MARK_AS_ADVANCED(CLEAR LIBXML2_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR LIBXML2_LIBRARIES)
ENDIF()
#
# POSIX Regular Expression support
#
IF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$")
#
# If PCREPOSIX is not found or not requested, try using regex
# from libc or libregex
#
FIND_PATH(REGEX_INCLUDE_DIR regex.h)
IF(REGEX_INCLUDE_DIR)
CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBC)
#
# If libc does not provide regex, find libregex.
#
IF(NOT HAVE_REGCOMP_LIBC)
CMAKE_PUSH_CHECK_STATE() # Save the state of the variables
FIND_LIBRARY(REGEX_LIBRARY regex)
IF(REGEX_LIBRARY)
SET(CMAKE_REQUIRED_LIBRARIES ${REGEX_LIBRARY})
CHECK_FUNCTION_EXISTS_GLIBC(regcomp HAVE_REGCOMP_LIBREGEX)
IF(HAVE_REGCOMP_LIBREGEX)
LIST(APPEND ADDITIONAL_LIBS ${REGEX_LIBRARY})
#
# If regex.h is not found, retry looking for regex.h at
# REGEX_INCLUDE_DIR
#
IF(NOT HAVE_REGEX_H)
UNSET(HAVE_REGEX_H CACHE)
INCLUDE_DIRECTORIES(${REGEX_INCLUDE_DIR})
SET(CMAKE_REQUIRED_INCLUDES ${REGEX_INCLUDE_DIR})
LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H)
ENDIF(NOT HAVE_REGEX_H)
# Test if a macro is needed for the library.
TRY_MACRO_FOR_LIBRARY(
"${REGEX_INCLUDE_DIR}" "${REGEX_LIBRARY}"
COMPILES
"#include <stddef.h>\n#include <regex.h>\nint main() {regex_t r;return regcomp(&r, \"\", 0);}"
"USE_REGEX_DLL;USE_REGEX_STATIC")
IF(USE_REGEX_DLL)
ADD_DEFINITIONS(-DUSE_REGEX_DLL)
ELSEIF(USE_REGEX_STATIC)
ADD_DEFINITIONS(-DUSE_REGEX_STATIC)
ENDIF(USE_REGEX_DLL)
ENDIF(HAVE_REGCOMP_LIBREGEX)
ENDIF(REGEX_LIBRARY)
CMAKE_POP_CHECK_STATE() # Restore the state of the variables
ENDIF(NOT HAVE_REGCOMP_LIBC)
ENDIF(REGEX_INCLUDE_DIR)
IF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX)
SET(FOUND_POSIX_REGEX_LIB 1)
ENDIF(HAVE_REGCOMP_LIBC OR HAVE_REGCOMP_LIBREGEX)
ENDIF(POSIX_REGEX_LIB MATCHES "^(AUTO|LIBC|LIBREGEX)$")
IF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$")
#
# If requested, try finding library for PCREPOSIX
#
IF(ENABLE_LibGCC)
FIND_PACKAGE(LibGCC)
ELSE()
SET(LIBGCC_FOUND FALSE) # Override cached value
ENDIF()
IF(ENABLE_PCREPOSIX)
FIND_PACKAGE(PCREPOSIX)
ELSE()
SET(PCREPOSIX_FOUND FALSE) # Override cached value
ENDIF()
IF(PCREPOSIX_FOUND)
INCLUDE_DIRECTORIES(${PCRE_INCLUDE_DIR})
LIST(APPEND ADDITIONAL_LIBS ${PCREPOSIX_LIBRARIES})
# Test if a macro is needed for the library.
TRY_MACRO_FOR_LIBRARY(
"${PCRE_INCLUDE_DIR}" "${PCREPOSIX_LIBRARIES}"
COMPILES
"#include <pcreposix.h>\nint main() {regex_t r;return regcomp(&r, \"\", 0);}"
"WITHOUT_PCRE_STATIC;PCRE_STATIC")
IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ADD_DEFINITIONS(-DPCRE_STATIC)
ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND PCRE_FOUND)
# Determine if pcre static libraries are to be used.
LIST(APPEND ADDITIONAL_LIBS ${PCRE_LIBRARIES})
SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES})
MESSAGE(STATUS "trying again with -lpcre included")
TRY_MACRO_FOR_LIBRARY(
"${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}"
COMPILES
"#include <pcreposix.h>\nint main() {regex_t r;return regcomp(&r, \"\", 0);}"
"WITHOUT_PCRE_STATIC;PCRE_STATIC")
IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ADD_DEFINITIONS(-DPCRE_STATIC)
ELSEIF(NOT WITHOUT_PCRE_STATIC AND NOT PCRE_STATIC AND MSVC AND LIBGCC_FOUND)
# When doing a Visual Studio build using pcre static libraries
# built using the mingw toolchain, -lgcc is needed to resolve
# ___chkstk_ms.
MESSAGE(STATUS "Visual Studio build detected, trying again with -lgcc included")
LIST(APPEND ADDITIONAL_LIBS ${LIBGCC_LIBRARIES})
SET(TMP_LIBRARIES ${PCREPOSIX_LIBRARIES} ${PCRE_LIBRARIES} ${LIBGCC_LIBRARIES})
TRY_MACRO_FOR_LIBRARY(
"${PCRE_INCLUDE_DIR}" "${TMP_LIBRARIES}"
COMPILES
"#include <pcreposix.h>\nint main() {regex_t r;return regcomp(&r, \"\", 0);}"
"WITHOUT_PCRE_STATIC;PCRE_STATIC")
IF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ADD_DEFINITIONS(-DPCRE_STATIC)
ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ENDIF(NOT WITHOUT_PCRE_STATIC AND PCRE_STATIC)
ENDIF(PCREPOSIX_FOUND)
MARK_AS_ADVANCED(CLEAR PCRE_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR PCREPOSIX_LIBRARIES)
MARK_AS_ADVANCED(CLEAR PCRE_LIBRARIES)
MARK_AS_ADVANCED(CLEAR LIBGCC_LIBRARIES)
ENDIF(NOT FOUND_POSIX_REGEX_LIB AND POSIX_REGEX_LIB MATCHES "^(AUTO|LIBPCREPOSIX)$")
#
# Check functions
#
CMAKE_PUSH_CHECK_STATE() # Save the state of the variables
IF (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
CMAKE_C_COMPILER_ID STREQUAL "Clang")
IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR
CMAKE_C_COMPILER_ID MATCHES "^Clang$")
#
# During checking functions, we should use -fno-builtin to avoid the
# failure of function detection which failure is an error "conflicting
# types for built-in function" caused by using -Werror option.
#
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin")
ENDIF ()
ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR
CMAKE_C_COMPILER_ID MATCHES "^Clang$")
CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode)
CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_ARC4RANDOM_BUF)
CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS)
@@ -1016,6 +1425,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR)
CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK)
CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM)
CHECK_FUNCTION_EXISTS_GLIBC(tzset HAVE_TZSET)
CHECK_FUNCTION_EXISTS_GLIBC(unlinkat HAVE_UNLINKAT)
CHECK_FUNCTION_EXISTS_GLIBC(unsetenv HAVE_UNSETENV)
CHECK_FUNCTION_EXISTS_GLIBC(utime HAVE_UTIME)
CHECK_FUNCTION_EXISTS_GLIBC(utimes HAVE_UTIMES)
@@ -1029,6 +1439,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB)
CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S)
CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64)
CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE)
CHECK_FUNCTION_EXISTS_GLIBC(_gmtime64_s HAVE__GMTIME64_S)
CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S)
CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64)
@@ -1297,6 +1708,11 @@ IF(ENABLE_XATTR)
CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_LIBATTR)
IF(HAVE_LIBATTR)
SET(CMAKE_REQUIRED_LIBRARIES "attr")
ELSE()
CHECK_LIBRARY_EXISTS(gnu "setxattr" "" HAVE_LIBATTR_GNU)
IF(HAVE_LIBATTR_GNU)
SET(CMAKE_REQUIRED_LIBRARIES "gnu")
ENDIF()
ENDIF(HAVE_LIBATTR)
CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER)
CHECK_SYMBOL_EXISTS(XATTR_NOFOLLOW "sys/xattr.h" HAVE_DECL_XATTR_NOFOLLOW)
@@ -1620,6 +2036,7 @@ 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)
@@ -1634,6 +2051,11 @@ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in
INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
ADD_DEFINITIONS(-DHAVE_CONFIG_H)
IF(0) # CMake does not build libarchive's packages.
# Handle generation of the libarchive.pc file for pkg-config
INCLUDE(CreatePkgConfigFile)
ENDIF()
#
# Register installation of PDF documents.
#
@@ -1659,11 +2081,22 @@ IF(MSVC)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ENDIF(MSVC)
IF(0) # CMake does not build libarchive's tests.
IF(ENABLE_TEST)
ADD_CUSTOM_TARGET(run_all_tests)
ENDIF(ENABLE_TEST)
ENDIF()
# We need CoreServices on Mac OS.
IF(APPLE)
LIST(APPEND ADDITIONAL_LIBS "-framework CoreServices")
ENDIF(APPLE)
add_subdirectory(libarchive)
IF(0) # CMake does not build libarchive's command-line tools.
add_subdirectory(cat)
add_subdirectory(tar)
add_subdirectory(cpio)
ENDIF()
install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibarchive)
+8 -1
View File
@@ -23,6 +23,13 @@ the actual statements in the files are controlling.
* The following source files are in the public domain:
libarchive/archive_getdate.c
* The following source files are triple-licensed with the ability to choose
from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:
libarchive/archive_blake2.h
libarchive/archive_blake2_impl.h
libarchive/archive_blake2s_ref.c
libarchive/archive_blake2sp_ref.c
* The build files---including Makefiles, configure scripts,
and auxiliary scripts used as part of the compile process---have
widely varying licensing terms. Please check individual files before
@@ -34,7 +41,7 @@ do use the license below. The varying licensing of the build scripts
seems to be an unavoidable mess.
Copyright (c) 2003-2009 <author(s)>
Copyright (c) 2003-2018 <author(s)>
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -0,0 +1,13 @@
find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h)
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MBEDTLS DEFAULT_MSG
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
@@ -683,6 +683,12 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `bz2' library (-lbz2). */
#cmakedefine HAVE_LIBBZ2 1
/* Define to 1 if you have the `b2' library (-lb2). */
#cmakedefine HAVE_LIBB2 1
/* Define to 1 if you have the <blake2.h> header file. */
#cmakedefine HAVE_BLAKE2_H 1
/* Define to 1 if you have the `charset' library (-lcharset). */
#cmakedefine HAVE_LIBCHARSET 1
@@ -707,6 +713,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `lzo2' library (-llzo2). */
#cmakedefine HAVE_LIBLZO2 1
/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
#cmakedefine HAVE_LIBMBEDCRYPTO 1
/* Define to 1 if you have the `nettle' library (-lnettle). */
#cmakedefine HAVE_LIBNETTLE 1
@@ -1111,6 +1120,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1
/* Define to 1 if you have the `unlinkat' function. */
#cmakedefine HAVE_UNLINKAT 1
/* Define to 1 if you have the `unsetenv' function. */
#cmakedefine HAVE_UNSETENV 1
@@ -1204,6 +1216,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `_get_timezone' function. */
#cmakedefine HAVE__GET_TIMEZONE 1
/* Define to 1 if you have the `_gmtime64_s' function. */
#cmakedefine HAVE__GMTIME64_S 1
/* Define to 1 if you have the `_localtime64_s' function. */
#cmakedefine HAVE__LOCALTIME64_S 1
+1 -1
View File
@@ -1 +1 @@
3003003
3004002
@@ -51,6 +51,8 @@ SET(libarchive_SOURCES
archive_platform_acl.h
archive_platform_xattr.h
archive_ppmd_private.h
archive_ppmd8.c
archive_ppmd8_private.h
archive_ppmd7.c
archive_ppmd7_private.h
archive_private.h
@@ -100,6 +102,7 @@ SET(libarchive_SOURCES
archive_read_support_format_lha.c
archive_read_support_format_mtree.c
archive_read_support_format_rar.c
archive_read_support_format_rar5.c
archive_read_support_format_raw.c
archive_read_support_format_tar.c
archive_read_support_format_warc.c
@@ -147,6 +150,7 @@ SET(libarchive_SOURCES
archive_write_set_format_iso9660.c
archive_write_set_format_mtree.c
archive_write_set_format_pax.c
archive_write_set_format_private.h
archive_write_set_format_raw.c
archive_write_set_format_shar.c
archive_write_set_format_ustar.c
@@ -167,6 +171,7 @@ SET(libarchive_MANS
archive_entry.3
archive_entry_acl.3
archive_entry_linkify.3
archive_entry_misc.3
archive_entry_paths.3
archive_entry_perms.3
archive_entry_stat.3
@@ -215,6 +220,11 @@ IF(WIN32 AND NOT CYGWIN)
LIST(APPEND libarchive_SOURCES filter_fork_windows.c)
ENDIF(WIN32 AND NOT CYGWIN)
IF(ARCHIVE_BLAKE2)
LIST(APPEND libarchive_SOURCES archive_blake2sp_ref.c)
LIST(APPEND libarchive_SOURCES archive_blake2s_ref.c)
ENDIF(ARCHIVE_BLAKE2)
IF(ARCHIVE_ACL_DARWIN)
LIST(APPEND libarchive_SOURCES archive_disk_acl_darwin.c)
ELSEIF(ARCHIVE_ACL_FREEBSD)
@@ -228,3 +238,33 @@ ENDIF()
# CMake needs just one static "cmlibarchive" library.
ADD_LIBRARY(cmlibarchive STATIC ${libarchive_SOURCES} ${include_HEADERS})
TARGET_LINK_LIBRARIES(cmlibarchive ${ADDITIONAL_LIBS})
IF(0) # CMake does not build libarchive's full package.
# Libarchive is a shared library
ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .)
TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
# archive_static is a static library
ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS})
TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS})
SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS
LIBARCHIVE_STATIC)
# On Posix systems, libarchive.so and libarchive.a can co-exist.
IF(NOT WIN32 OR CYGWIN)
SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive)
ENDIF(NOT WIN32 OR CYGWIN)
IF(ENABLE_INSTALL)
# How to install the libraries
INSTALL(TARGETS archive archive_static
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL_MAN(${libarchive_MANS})
INSTALL(FILES ${include_HEADERS} DESTINATION include)
ENDIF()
add_subdirectory(test)
ENDIF()
+9 -3
View File
@@ -36,7 +36,7 @@
* assert that ARCHIVE_VERSION_NUMBER >= 2012108.
*/
/* Note: Compiler will complain if this does not match archive_entry.h! */
#define ARCHIVE_VERSION_NUMBER 3003003
#define ARCHIVE_VERSION_NUMBER 3004002
#include <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -52,7 +52,7 @@
*/
#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
# include <stdint.h>
#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__)
#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)
# include <inttypes.h>
#endif
@@ -152,7 +152,7 @@ __LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
#define ARCHIVE_VERSION_ONLY_STRING "3.3.3"
#define ARCHIVE_VERSION_ONLY_STRING "3.4.2"
#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
__LA_DECL const char * archive_version_string(void);
@@ -337,6 +337,7 @@ typedef const char *archive_passphrase_callback(struct archive *,
#define ARCHIVE_FORMAT_RAR 0xD0000
#define ARCHIVE_FORMAT_7ZIP 0xE0000
#define ARCHIVE_FORMAT_WARC 0xF0000
#define ARCHIVE_FORMAT_RAR_V5 0x100000
/*
* Codes returned by archive_read_format_capabilities().
@@ -446,6 +447,7 @@ __LA_DECL int archive_read_support_format_iso9660(struct archive *);
__LA_DECL int archive_read_support_format_lha(struct archive *);
__LA_DECL int archive_read_support_format_mtree(struct archive *);
__LA_DECL int archive_read_support_format_rar(struct archive *);
__LA_DECL int archive_read_support_format_rar5(struct archive *);
__LA_DECL int archive_read_support_format_raw(struct archive *);
__LA_DECL int archive_read_support_format_tar(struct archive *);
__LA_DECL int archive_read_support_format_warc(struct archive *);
@@ -688,6 +690,8 @@ __LA_DECL int archive_read_set_passphrase_callback(struct archive *,
#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
/* Default: Do not clear no-change flags when unlinking object */
#define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000)
/* Default: Do not extract atomically (using rename) */
#define ARCHIVE_EXTRACT_SAFE_WRITES (0x40000)
__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
int flags);
@@ -1090,6 +1094,8 @@ __LA_DECL int archive_match_excluded(struct archive *,
*/
__LA_DECL int archive_match_path_excluded(struct archive *,
struct archive_entry *);
/* Control recursive inclusion of directory content when directory is included. Default on. */
__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int);
/* Add exclusion pathname pattern. */
__LA_DECL int archive_match_exclude_pattern(struct archive *, const char *);
__LA_DECL int archive_match_exclude_pattern_w(struct archive *,
+57 -31
View File
@@ -138,14 +138,10 @@ archive_acl_clear(struct archive_acl *acl)
free(acl->acl_head);
acl->acl_head = ap;
}
if (acl->acl_text_w != NULL) {
free(acl->acl_text_w);
acl->acl_text_w = NULL;
}
if (acl->acl_text != NULL) {
free(acl->acl_text);
acl->acl_text = NULL;
}
free(acl->acl_text_w);
acl->acl_text_w = NULL;
free(acl->acl_text);
acl->acl_text = NULL;
acl->acl_p = NULL;
acl->acl_types = 0;
acl->acl_state = 0; /* Not counting. */
@@ -324,14 +320,10 @@ acl_new_entry(struct archive_acl *acl,
return (NULL);
}
if (acl->acl_text_w != NULL) {
free(acl->acl_text_w);
acl->acl_text_w = NULL;
}
if (acl->acl_text != NULL) {
free(acl->acl_text);
acl->acl_text = NULL;
}
free(acl->acl_text_w);
acl->acl_text_w = NULL;
free(acl->acl_text);
acl->acl_text = NULL;
/*
* If there's a matching entry already in the list, overwrite it.
@@ -753,8 +745,10 @@ archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
wname, ap->permset, id);
count++;
} else if (r < 0 && errno == ENOMEM)
} else if (r < 0 && errno == ENOMEM) {
free(ws);
return (NULL);
}
}
/* Add terminating character */
@@ -975,8 +969,10 @@ archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
prefix = NULL;
r = archive_mstring_get_mbs_l(
&ap->name, &name, &len, sc);
if (r != 0)
if (r != 0) {
free(s);
return (NULL);
}
if (count > 0)
*p++ = separator;
if (name == NULL ||
@@ -1581,17 +1577,29 @@ next_field_w(const wchar_t **wp, const wchar_t **start,
/* Scan for the separator. */
while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
**wp != L'\n') {
**wp != L'\n' && **wp != L'#') {
(*wp)++;
}
*sep = **wp;
/* Trim trailing whitespace to locate end of field. */
*end = *wp - 1;
while (**end == L' ' || **end == L'\t' || **end == L'\n') {
(*end)--;
/* Locate end of field, trim trailing whitespace if necessary */
if (*wp == *start) {
*end = *wp;
} else {
*end = *wp - 1;
while (**end == L' ' || **end == L'\t' || **end == L'\n') {
(*end)--;
}
(*end)++;
}
/* Handle in-field comments */
if (*sep == L'#') {
while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
(*wp)++;
}
*sep = **wp;
}
(*end)++;
/* Adjust scanner location. */
if (**wp != L'\0')
@@ -1642,7 +1650,7 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text,
ret = ARCHIVE_OK;
types = 0;
while (text != NULL && *text != '\0') {
while (text != NULL && *text != '\0') {
/*
* Parse the fields out of the next entry,
* advance 'text' to start of next entry.
@@ -1707,6 +1715,11 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text,
st = field[n].start + 1;
len = field[n].end - field[n].start;
if (len == 0) {
ret = ARCHIVE_WARN;
continue;
}
switch (*s) {
case 'u':
if (len == 1 || (len == 4
@@ -2053,17 +2066,30 @@ next_field(const char **p, const char **start,
*start = *p;
/* Scan for the separator. */
while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') {
while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
**p != '#') {
(*p)++;
}
*sep = **p;
/* Trim trailing whitespace to locate end of field. */
*end = *p - 1;
while (**end == ' ' || **end == '\t' || **end == '\n') {
(*end)--;
/* Locate end of field, trim trailing whitespace if necessary */
if (*p == *start) {
*end = *p;
} else {
*end = *p - 1;
while (**end == ' ' || **end == '\t' || **end == '\n') {
(*end)--;
}
(*end)++;
}
/* Handle in-field comments */
if (*sep == '#') {
while (**p != '\0' && **p != ',' && **p != '\n') {
(*p)++;
}
*sep = **p;
}
(*end)++;
/* Adjust scanner location. */
if (**p != '\0')
@@ -25,13 +25,13 @@
* $FreeBSD$
*/
#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED
#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED
#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
#include "archive_string.h"
struct archive_acl_entry {
@@ -0,0 +1,195 @@
/*
BLAKE2 reference source code package - reference C implementations
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
your option. The terms of these licenses can be found at:
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- OpenSSL license : https://www.openssl.org/source/license.html
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
More information about the BLAKE2 hash function can be found at
https://blake2.net.
*/
#ifndef ARCHIVE_BLAKE2_H
#define ARCHIVE_BLAKE2_H
#include <stddef.h>
#include <stdint.h>
#if defined(_MSC_VER)
#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
#else
#define BLAKE2_PACKED(x) x __attribute__((packed))
#endif
#if defined(__cplusplus)
extern "C" {
#endif
enum blake2s_constant
{
BLAKE2S_BLOCKBYTES = 64,
BLAKE2S_OUTBYTES = 32,
BLAKE2S_KEYBYTES = 32,
BLAKE2S_SALTBYTES = 8,
BLAKE2S_PERSONALBYTES = 8
};
enum blake2b_constant
{
BLAKE2B_BLOCKBYTES = 128,
BLAKE2B_OUTBYTES = 64,
BLAKE2B_KEYBYTES = 64,
BLAKE2B_SALTBYTES = 16,
BLAKE2B_PERSONALBYTES = 16
};
typedef struct blake2s_state__
{
uint32_t h[8];
uint32_t t[2];
uint32_t f[2];
uint8_t buf[BLAKE2S_BLOCKBYTES];
size_t buflen;
size_t outlen;
uint8_t last_node;
} blake2s_state;
typedef struct blake2b_state__
{
uint64_t h[8];
uint64_t t[2];
uint64_t f[2];
uint8_t buf[BLAKE2B_BLOCKBYTES];
size_t buflen;
size_t outlen;
uint8_t last_node;
} blake2b_state;
typedef struct blake2sp_state__
{
blake2s_state S[8][1];
blake2s_state R[1];
uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
size_t buflen;
size_t outlen;
} blake2sp_state;
typedef struct blake2bp_state__
{
blake2b_state S[4][1];
blake2b_state R[1];
uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
size_t buflen;
size_t outlen;
} blake2bp_state;
BLAKE2_PACKED(struct blake2s_param__
{
uint8_t digest_length; /* 1 */
uint8_t key_length; /* 2 */
uint8_t fanout; /* 3 */
uint8_t depth; /* 4 */
uint32_t leaf_length; /* 8 */
uint32_t node_offset; /* 12 */
uint16_t xof_length; /* 14 */
uint8_t node_depth; /* 15 */
uint8_t inner_length; /* 16 */
/* uint8_t reserved[0]; */
uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */
uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */
});
typedef struct blake2s_param__ blake2s_param;
BLAKE2_PACKED(struct blake2b_param__
{
uint8_t digest_length; /* 1 */
uint8_t key_length; /* 2 */
uint8_t fanout; /* 3 */
uint8_t depth; /* 4 */
uint32_t leaf_length; /* 8 */
uint32_t node_offset; /* 12 */
uint32_t xof_length; /* 16 */
uint8_t node_depth; /* 17 */
uint8_t inner_length; /* 18 */
uint8_t reserved[14]; /* 32 */
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
});
typedef struct blake2b_param__ blake2b_param;
typedef struct blake2xs_state__
{
blake2s_state S[1];
blake2s_param P[1];
} blake2xs_state;
typedef struct blake2xb_state__
{
blake2b_state S[1];
blake2b_param P[1];
} blake2xb_state;
/* Padded structs result in a compile-time error */
enum {
BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),
BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)
};
/* Streaming API */
int blake2s_init( blake2s_state *S, size_t outlen );
int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
int blake2s_final( blake2s_state *S, void *out, size_t outlen );
int blake2b_init( blake2b_state *S, size_t outlen );
int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
int blake2b_final( blake2b_state *S, void *out, size_t outlen );
int blake2sp_init( blake2sp_state *S, size_t outlen );
int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
int blake2bp_init( blake2bp_state *S, size_t outlen );
int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
/* Variable output length API */
int blake2xs_init( blake2xs_state *S, const size_t outlen );
int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
int blake2xb_init( blake2xb_state *S, const size_t outlen );
int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
/* Simple API */
int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
/* This is simply an alias for blake2b */
int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
#if defined(__cplusplus)
}
#endif
#endif
@@ -0,0 +1,161 @@
/*
BLAKE2 reference source code package - reference C implementations
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
your option. The terms of these licenses can be found at:
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- OpenSSL license : https://www.openssl.org/source/license.html
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
More information about the BLAKE2 hash function can be found at
https://blake2.net.
*/
#ifndef ARCHIVE_BLAKE2_IMPL_H
#define ARCHIVE_BLAKE2_IMPL_H
#include <stdint.h>
#include <string.h>
#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
#if defined(_MSC_VER)
#define BLAKE2_INLINE __inline
#elif defined(__GNUC__)
#define BLAKE2_INLINE __inline__
#else
#define BLAKE2_INLINE
#endif
#else
#define BLAKE2_INLINE inline
#endif
static BLAKE2_INLINE uint32_t load32( const void *src )
{
#if defined(NATIVE_LITTLE_ENDIAN)
uint32_t w;
memcpy(&w, src, sizeof w);
return w;
#else
const uint8_t *p = ( const uint8_t * )src;
return (( uint32_t )( p[0] ) << 0) |
(( uint32_t )( p[1] ) << 8) |
(( uint32_t )( p[2] ) << 16) |
(( uint32_t )( p[3] ) << 24) ;
#endif
}
static BLAKE2_INLINE uint64_t load64( const void *src )
{
#if defined(NATIVE_LITTLE_ENDIAN)
uint64_t w;
memcpy(&w, src, sizeof w);
return w;
#else
const uint8_t *p = ( const uint8_t * )src;
return (( uint64_t )( p[0] ) << 0) |
(( uint64_t )( p[1] ) << 8) |
(( uint64_t )( p[2] ) << 16) |
(( uint64_t )( p[3] ) << 24) |
(( uint64_t )( p[4] ) << 32) |
(( uint64_t )( p[5] ) << 40) |
(( uint64_t )( p[6] ) << 48) |
(( uint64_t )( p[7] ) << 56) ;
#endif
}
static BLAKE2_INLINE uint16_t load16( const void *src )
{
#if defined(NATIVE_LITTLE_ENDIAN)
uint16_t w;
memcpy(&w, src, sizeof w);
return w;
#else
const uint8_t *p = ( const uint8_t * )src;
return ( uint16_t )((( uint32_t )( p[0] ) << 0) |
(( uint32_t )( p[1] ) << 8));
#endif
}
static BLAKE2_INLINE void store16( void *dst, uint16_t w )
{
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t *p = ( uint8_t * )dst;
*p++ = ( uint8_t )w; w >>= 8;
*p++ = ( uint8_t )w;
#endif
}
static BLAKE2_INLINE void store32( void *dst, uint32_t w )
{
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t *p = ( uint8_t * )dst;
p[0] = (uint8_t)(w >> 0);
p[1] = (uint8_t)(w >> 8);
p[2] = (uint8_t)(w >> 16);
p[3] = (uint8_t)(w >> 24);
#endif
}
static BLAKE2_INLINE void store64( void *dst, uint64_t w )
{
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t *p = ( uint8_t * )dst;
p[0] = (uint8_t)(w >> 0);
p[1] = (uint8_t)(w >> 8);
p[2] = (uint8_t)(w >> 16);
p[3] = (uint8_t)(w >> 24);
p[4] = (uint8_t)(w >> 32);
p[5] = (uint8_t)(w >> 40);
p[6] = (uint8_t)(w >> 48);
p[7] = (uint8_t)(w >> 56);
#endif
}
static BLAKE2_INLINE uint64_t load48( const void *src )
{
const uint8_t *p = ( const uint8_t * )src;
return (( uint64_t )( p[0] ) << 0) |
(( uint64_t )( p[1] ) << 8) |
(( uint64_t )( p[2] ) << 16) |
(( uint64_t )( p[3] ) << 24) |
(( uint64_t )( p[4] ) << 32) |
(( uint64_t )( p[5] ) << 40) ;
}
static BLAKE2_INLINE void store48( void *dst, uint64_t w )
{
uint8_t *p = ( uint8_t * )dst;
p[0] = (uint8_t)(w >> 0);
p[1] = (uint8_t)(w >> 8);
p[2] = (uint8_t)(w >> 16);
p[3] = (uint8_t)(w >> 24);
p[4] = (uint8_t)(w >> 32);
p[5] = (uint8_t)(w >> 40);
}
static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
{
return ( w >> c ) | ( w << ( 32 - c ) );
}
static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
{
return ( w >> c ) | ( w << ( 64 - c ) );
}
/* prevents compiler optimizing out memset() */
static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)
{
static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
memset_v(v, 0, n);
}
#endif
@@ -0,0 +1,367 @@
/*
BLAKE2 reference source code package - reference C implementations
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
your option. The terms of these licenses can be found at:
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- OpenSSL license : https://www.openssl.org/source/license.html
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
More information about the BLAKE2 hash function can be found at
https://blake2.net.
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "archive_blake2.h"
#include "archive_blake2_impl.h"
static const uint32_t blake2s_IV[8] =
{
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};
static const uint8_t blake2s_sigma[10][16] =
{
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
};
static void blake2s_set_lastnode( blake2s_state *S )
{
S->f[1] = (uint32_t)-1;
}
/* Some helper functions, not necessarily useful */
static int blake2s_is_lastblock( const blake2s_state *S )
{
return S->f[0] != 0;
}
static void blake2s_set_lastblock( blake2s_state *S )
{
if( S->last_node ) blake2s_set_lastnode( S );
S->f[0] = (uint32_t)-1;
}
static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
{
S->t[0] += inc;
S->t[1] += ( S->t[0] < inc );
}
static void blake2s_init0( blake2s_state *S )
{
size_t i;
memset( S, 0, sizeof( blake2s_state ) );
for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
}
/* init2 xors IV with input parameter block */
int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
{
const unsigned char *p = ( const unsigned char * )( P );
size_t i;
blake2s_init0( S );
/* IV XOR ParamBlock */
for( i = 0; i < 8; ++i )
S->h[i] ^= load32( &p[i * 4] );
S->outlen = P->digest_length;
return 0;
}
/* Sequential blake2s initialization */
int blake2s_init( blake2s_state *S, size_t outlen )
{
blake2s_param P[1];
/* Move interval verification here? */
if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
P->digest_length = (uint8_t)outlen;
P->key_length = 0;
P->fanout = 1;
P->depth = 1;
store32( &P->leaf_length, 0 );
store32( &P->node_offset, 0 );
store16( &P->xof_length, 0 );
P->node_depth = 0;
P->inner_length = 0;
/* memset(P->reserved, 0, sizeof(P->reserved) ); */
memset( P->salt, 0, sizeof( P->salt ) );
memset( P->personal, 0, sizeof( P->personal ) );
return blake2s_init_param( S, P );
}
int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
{
blake2s_param P[1];
if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
P->digest_length = (uint8_t)outlen;
P->key_length = (uint8_t)keylen;
P->fanout = 1;
P->depth = 1;
store32( &P->leaf_length, 0 );
store32( &P->node_offset, 0 );
store16( &P->xof_length, 0 );
P->node_depth = 0;
P->inner_length = 0;
/* memset(P->reserved, 0, sizeof(P->reserved) ); */
memset( P->salt, 0, sizeof( P->salt ) );
memset( P->personal, 0, sizeof( P->personal ) );
if( blake2s_init_param( S, P ) < 0 ) return -1;
{
uint8_t block[BLAKE2S_BLOCKBYTES];
memset( block, 0, BLAKE2S_BLOCKBYTES );
memcpy( block, key, keylen );
blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
}
return 0;
}
#define G(r,i,a,b,c,d) \
do { \
a = a + b + m[blake2s_sigma[r][2*i+0]]; \
d = rotr32(d ^ a, 16); \
c = c + d; \
b = rotr32(b ^ c, 12); \
a = a + b + m[blake2s_sigma[r][2*i+1]]; \
d = rotr32(d ^ a, 8); \
c = c + d; \
b = rotr32(b ^ c, 7); \
} while(0)
#define ROUND(r) \
do { \
G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
G(r,2,v[ 2],v[ 6],v[10],v[14]); \
G(r,3,v[ 3],v[ 7],v[11],v[15]); \
G(r,4,v[ 0],v[ 5],v[10],v[15]); \
G(r,5,v[ 1],v[ 6],v[11],v[12]); \
G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
} while(0)
static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] )
{
uint32_t m[16];
uint32_t v[16];
size_t i;
for( i = 0; i < 16; ++i ) {
m[i] = load32( in + i * sizeof( m[i] ) );
}
for( i = 0; i < 8; ++i ) {
v[i] = S->h[i];
}
v[ 8] = blake2s_IV[0];
v[ 9] = blake2s_IV[1];
v[10] = blake2s_IV[2];
v[11] = blake2s_IV[3];
v[12] = S->t[0] ^ blake2s_IV[4];
v[13] = S->t[1] ^ blake2s_IV[5];
v[14] = S->f[0] ^ blake2s_IV[6];
v[15] = S->f[1] ^ blake2s_IV[7];
ROUND( 0 );
ROUND( 1 );
ROUND( 2 );
ROUND( 3 );
ROUND( 4 );
ROUND( 5 );
ROUND( 6 );
ROUND( 7 );
ROUND( 8 );
ROUND( 9 );
for( i = 0; i < 8; ++i ) {
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
}
#undef G
#undef ROUND
int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
{
const unsigned char * in = (const unsigned char *)pin;
if( inlen > 0 )
{
size_t left = S->buflen;
size_t fill = BLAKE2S_BLOCKBYTES - left;
if( inlen > fill )
{
S->buflen = 0;
memcpy( S->buf + left, in, fill ); /* Fill buffer */
blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
blake2s_compress( S, S->buf ); /* Compress */
in += fill; inlen -= fill;
while(inlen > BLAKE2S_BLOCKBYTES) {
blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES);
blake2s_compress( S, in );
in += BLAKE2S_BLOCKBYTES;
inlen -= BLAKE2S_BLOCKBYTES;
}
}
memcpy( S->buf + S->buflen, in, inlen );
S->buflen += inlen;
}
return 0;
}
int blake2s_final( blake2s_state *S, void *out, size_t outlen )
{
uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
size_t i;
if( out == NULL || outlen < S->outlen )
return -1;
if( blake2s_is_lastblock( S ) )
return -1;
blake2s_increment_counter( S, ( uint32_t )S->buflen );
blake2s_set_lastblock( S );
memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
blake2s_compress( S, S->buf );
for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
memcpy( out, buffer, outlen );
secure_zero_memory(buffer, sizeof(buffer));
return 0;
}
int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
{
blake2s_state S[1];
/* Verify parameters */
if ( NULL == in && inlen > 0 ) return -1;
if ( NULL == out ) return -1;
if ( NULL == key && keylen > 0) return -1;
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
if( keylen > BLAKE2S_KEYBYTES ) return -1;
if( keylen > 0 )
{
if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
}
else
{
if( blake2s_init( S, outlen ) < 0 ) return -1;
}
blake2s_update( S, ( const uint8_t * )in, inlen );
blake2s_final( S, out, outlen );
return 0;
}
#if defined(SUPERCOP)
int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
{
return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
}
#endif
#if defined(BLAKE2S_SELFTEST)
#include <string.h>
#include "blake2-kat.h"
int main( void )
{
uint8_t key[BLAKE2S_KEYBYTES];
uint8_t buf[BLAKE2_KAT_LENGTH];
size_t i, step;
for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
key[i] = ( uint8_t )i;
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
buf[i] = ( uint8_t )i;
/* Test simple API */
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
{
uint8_t hash[BLAKE2S_OUTBYTES];
blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )
{
goto fail;
}
}
/* Test streaming API */
for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
uint8_t hash[BLAKE2S_OUTBYTES];
blake2s_state S;
uint8_t * p = buf;
size_t mlen = i;
int err = 0;
if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
goto fail;
}
while (mlen >= step) {
if ( (err = blake2s_update(&S, p, step)) < 0 ) {
goto fail;
}
mlen -= step;
p += step;
}
if ( (err = blake2s_update(&S, p, mlen)) < 0) {
goto fail;
}
if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
goto fail;
}
if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) {
goto fail;
}
}
}
puts( "ok" );
return 0;
fail:
puts("error");
return -1;
}
#endif
@@ -0,0 +1,359 @@
/*
BLAKE2 reference source code package - reference C implementations
Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
your option. The terms of these licenses can be found at:
- CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- OpenSSL license : https://www.openssl.org/source/license.html
- Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
More information about the BLAKE2 hash function can be found at
https://blake2.net.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#if defined(_OPENMP)
#include <omp.h>
#endif
#include "archive_blake2.h"
#include "archive_blake2_impl.h"
#define PARALLELISM_DEGREE 8
/*
blake2sp_init_param defaults to setting the expecting output length
from the digest_length parameter block field.
In some cases, however, we do not want this, as the output length
of these instances is given by inner_length instead.
*/
static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
{
int err = blake2s_init_param(S, P);
S->outlen = P->inner_length;
return err;
}
static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint32_t offset )
{
blake2s_param P[1];
P->digest_length = (uint8_t)outlen;
P->key_length = (uint8_t)keylen;
P->fanout = PARALLELISM_DEGREE;
P->depth = 2;
store32( &P->leaf_length, 0 );
store32( &P->node_offset, offset );
store16( &P->xof_length, 0 );
P->node_depth = 0;
P->inner_length = BLAKE2S_OUTBYTES;
memset( P->salt, 0, sizeof( P->salt ) );
memset( P->personal, 0, sizeof( P->personal ) );
return blake2sp_init_leaf_param( S, P );
}
static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen )
{
blake2s_param P[1];
P->digest_length = (uint8_t)outlen;
P->key_length = (uint8_t)keylen;
P->fanout = PARALLELISM_DEGREE;
P->depth = 2;
store32( &P->leaf_length, 0 );
store32( &P->node_offset, 0 );
store16( &P->xof_length, 0 );
P->node_depth = 1;
P->inner_length = BLAKE2S_OUTBYTES;
memset( P->salt, 0, sizeof( P->salt ) );
memset( P->personal, 0, sizeof( P->personal ) );
return blake2s_init_param( S, P );
}
int blake2sp_init( blake2sp_state *S, size_t outlen )
{
size_t i;
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
memset( S->buf, 0, sizeof( S->buf ) );
S->buflen = 0;
S->outlen = outlen;
if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )
return -1;
for( i = 0; i < PARALLELISM_DEGREE; ++i )
if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1;
S->R->last_node = 1;
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
return 0;
}
int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
{
size_t i;
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
memset( S->buf, 0, sizeof( S->buf ) );
S->buflen = 0;
S->outlen = outlen;
if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )
return -1;
for( i = 0; i < PARALLELISM_DEGREE; ++i )
if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;
S->R->last_node = 1;
S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
{
uint8_t block[BLAKE2S_BLOCKBYTES];
memset( block, 0, BLAKE2S_BLOCKBYTES );
memcpy( block, key, keylen );
for( i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
}
return 0;
}
int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
{
const unsigned char * in = (const unsigned char *)pin;
size_t left = S->buflen;
size_t fill = sizeof( S->buf ) - left;
size_t i;
if( left && inlen >= fill )
{
memcpy( S->buf + left, in, fill );
for( i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
in += fill;
inlen -= fill;
left = 0;
}
#if defined(_OPENMP)
#pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
#else
for( i = 0; i < PARALLELISM_DEGREE; ++i )
#endif
{
#if defined(_OPENMP)
size_t i = omp_get_thread_num();
#endif
size_t inlen__ = inlen;
const unsigned char *in__ = ( const unsigned char * )in;
in__ += i * BLAKE2S_BLOCKBYTES;
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
{
blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
}
}
in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
if( inlen > 0 )
memcpy( S->buf + left, in, inlen );
S->buflen = left + inlen;
return 0;
}
int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
{
uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
size_t i;
if(out == NULL || outlen < S->outlen) {
return -1;
}
for( i = 0; i < PARALLELISM_DEGREE; ++i )
{
if( S->buflen > i * BLAKE2S_BLOCKBYTES )
{
size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
}
blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
}
for( i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
return blake2s_final( S->R, out, S->outlen );
}
int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
{
uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
blake2s_state S[PARALLELISM_DEGREE][1];
blake2s_state FS[1];
size_t i;
/* Verify parameters */
if ( NULL == in && inlen > 0 ) return -1;
if ( NULL == out ) return -1;
if ( NULL == key && keylen > 0) return -1;
if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
if( keylen > BLAKE2S_KEYBYTES ) return -1;
for( i = 0; i < PARALLELISM_DEGREE; ++i )
if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;
S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */
if( keylen > 0 )
{
uint8_t block[BLAKE2S_BLOCKBYTES];
memset( block, 0, BLAKE2S_BLOCKBYTES );
memcpy( block, key, keylen );
for( i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
}
#if defined(_OPENMP)
#pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
#else
for( i = 0; i < PARALLELISM_DEGREE; ++i )
#endif
{
#if defined(_OPENMP)
size_t i = omp_get_thread_num();
#endif
size_t inlen__ = inlen;
const unsigned char *in__ = ( const unsigned char * )in;
in__ += i * BLAKE2S_BLOCKBYTES;
while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
{
blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
}
if( inlen__ > i * BLAKE2S_BLOCKBYTES )
{
const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
blake2s_update( S[i], in__, len );
}
blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
}
if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
return -1;
FS->last_node = 1;
for( i = 0; i < PARALLELISM_DEGREE; ++i )
blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
return blake2s_final( FS, out, outlen );
}
#if defined(BLAKE2SP_SELFTEST)
#include <string.h>
#include "blake2-kat.h"
int main( void )
{
uint8_t key[BLAKE2S_KEYBYTES];
uint8_t buf[BLAKE2_KAT_LENGTH];
size_t i, step;
for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
key[i] = ( uint8_t )i;
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
buf[i] = ( uint8_t )i;
/* Test simple API */
for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
{
uint8_t hash[BLAKE2S_OUTBYTES];
blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
{
goto fail;
}
}
/* Test streaming API */
for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
uint8_t hash[BLAKE2S_OUTBYTES];
blake2sp_state S;
uint8_t * p = buf;
size_t mlen = i;
int err = 0;
if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
goto fail;
}
while (mlen >= step) {
if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
goto fail;
}
mlen -= step;
p += step;
}
if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
goto fail;
}
if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
goto fail;
}
if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) {
goto fail;
}
}
}
puts( "ok" );
return 0;
fail:
puts("error");
return -1;
}
#endif
@@ -25,15 +25,15 @@
* $FreeBSD$
*/
#ifndef ARCHIVE_CMDLINE_PRIVATE_H
#define ARCHIVE_CMDLINE_PRIVATE_H
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST
#error This header is only to be used internally to libarchive.
#endif
#endif
#ifndef ARCHIVE_CMDLINE_PRIVATE_H
#define ARCHIVE_CMDLINE_PRIVATE_H
struct archive_cmdline {
char *path;
char **argv;
@@ -25,6 +25,9 @@
* $FreeBSD: head/lib/libarchive/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $
*/
#ifndef ARCHIVE_CRC32_H
#define ARCHIVE_CRC32_H
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
@@ -76,3 +79,5 @@ crc32(unsigned long crc, const void *_p, size_t len)
crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
return (crc ^ 0xffffffffUL);
}
#endif
@@ -85,6 +85,35 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
return (BCRYPT_SUCCESS(status)) ? 0: -1;
}
#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H)
static int
pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
size_t salt_len, unsigned rounds, uint8_t *derived_key,
size_t derived_key_len)
{
mbedtls_md_context_t ctx;
const mbedtls_md_info_t *info;
int ret;
mbedtls_md_init(&ctx);
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
if (info == NULL) {
mbedtls_md_free(&ctx);
return (-1);
}
ret = mbedtls_md_setup(&ctx, info, 1);
if (ret != 0) {
mbedtls_md_free(&ctx);
return (-1);
}
ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw,
pw_len, salt, salt_len, rounds, derived_key_len, derived_key);
mbedtls_md_free(&ctx);
return (ret);
}
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H)
static int
@@ -269,6 +298,39 @@ aes_ctr_release(archive_crypto_ctx *ctx)
return 0;
}
#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
static int
aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
{
mbedtls_aes_init(&ctx->ctx);
ctx->key_len = key_len;
memcpy(ctx->key, key, key_len);
memset(ctx->nonce, 0, sizeof(ctx->nonce));
ctx->encr_pos = AES_BLOCK_SIZE;
return 0;
}
static int
aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
{
if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key,
ctx->key_len * 8) != 0)
return (-1);
if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce,
ctx->encr_buf) != 0)
return (-1);
return 0;
}
static int
aes_ctr_release(archive_crypto_ctx *ctx)
{
mbedtls_aes_free(&ctx->ctx);
memset(ctx, 0, sizeof(*ctx));
return 0;
}
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
static int
@@ -316,7 +378,14 @@ aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
memcpy(ctx->key, key, key_len);
memset(ctx->nonce, 0, sizeof(ctx->nonce));
ctx->encr_pos = AES_BLOCK_SIZE;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
if (!EVP_CIPHER_CTX_reset(ctx->ctx)) {
EVP_CIPHER_CTX_free(ctx->ctx);
ctx->ctx = NULL;
}
#else
EVP_CIPHER_CTX_init(ctx->ctx);
#endif
return 0;
}
@@ -23,13 +23,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
/*
* On systems that do not support any recognized crypto libraries,
* the archive_cryptor.c file will normally define no usable symbols.
@@ -83,6 +82,23 @@ typedef struct {
unsigned encr_pos;
} archive_crypto_ctx;
#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
#include <mbedtls/aes.h>
#include <mbedtls/md.h>
#include <mbedtls/pkcs5.h>
#define AES_MAX_KEY_SIZE 32
#define AES_BLOCK_SIZE 16
typedef struct {
mbedtls_aes_context ctx;
uint8_t key[AES_MAX_KEY_SIZE];
unsigned key_len;
uint8_t nonce[AES_BLOCK_SIZE];
uint8_t encr_buf[AES_BLOCK_SIZE];
unsigned encr_pos;
} archive_crypto_ctx;
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
#if defined(HAVE_NETTLE_PBKDF2_H)
#include <nettle/pbkdf2.h>
@@ -178,6 +178,40 @@ __archive_libsystem_md5final(archive_md5_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
static int
__archive_mbedtls_md5init(archive_md5_ctx *ctx)
{
mbedtls_md5_init(ctx);
if (mbedtls_md5_starts_ret(ctx) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_md5update(archive_md5_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_md5final(archive_md5_ctx *ctx, void *md)
{
if (mbedtls_md5_finish_ret(ctx, md) == 0) {
mbedtls_md5_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_md5_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
static int
@@ -335,6 +369,40 @@ __archive_libmd_ripemd160final(archive_rmd160_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
static int
__archive_mbedtls_ripemd160init(archive_rmd160_ctx *ctx)
{
mbedtls_ripemd160_init(ctx);
if (mbedtls_ripemd160_starts_ret(ctx) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_ripemd160final(archive_rmd160_ctx *ctx, void *md)
{
if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
mbedtls_ripemd160_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_ripemd160_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
static int
@@ -491,6 +559,40 @@ __archive_libsystem_sha1final(archive_sha1_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
static int
__archive_mbedtls_sha1init(archive_sha1_ctx *ctx)
{
mbedtls_sha1_init(ctx);
if (mbedtls_sha1_starts_ret(ctx) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha1update(archive_sha1_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha1final(archive_sha1_ctx *ctx, void *md)
{
if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
mbedtls_sha1_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_sha1_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
static int
@@ -720,6 +822,40 @@ __archive_libsystem_sha256final(archive_sha256_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
static int
__archive_mbedtls_sha256init(archive_sha256_ctx *ctx)
{
mbedtls_sha256_init(ctx);
if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha256update(archive_sha256_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha256final(archive_sha256_ctx *ctx, void *md)
{
if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
mbedtls_sha256_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_sha256_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
static int
@@ -921,6 +1057,40 @@ __archive_libsystem_sha384final(archive_sha384_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
static int
__archive_mbedtls_sha384init(archive_sha384_ctx *ctx)
{
mbedtls_sha512_init(ctx);
if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha384update(archive_sha384_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha384final(archive_sha384_ctx *ctx, void *md)
{
if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
mbedtls_sha512_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_sha512_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
static int
@@ -1146,6 +1316,40 @@ __archive_libsystem_sha512final(archive_sha512_ctx *ctx, void *md)
return (ARCHIVE_OK);
}
#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
static int
__archive_mbedtls_sha512init(archive_sha512_ctx *ctx)
{
mbedtls_sha512_init(ctx);
if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha512update(archive_sha512_ctx *ctx, const void *indata,
size_t insize)
{
if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
return (ARCHIVE_OK);
else
return (ARCHIVE_FATAL);
}
static int
__archive_mbedtls_sha512final(archive_sha512_ctx *ctx, void *md)
{
if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
mbedtls_sha512_free(ctx);
return (ARCHIVE_OK);
} else {
mbedtls_sha512_free(ctx);
return (ARCHIVE_FATAL);
}
}
#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
static int
@@ -1276,6 +1480,10 @@ const struct archive_digest __archive_digest =
&__archive_libsystem_md5init,
&__archive_libsystem_md5update,
&__archive_libsystem_md5final,
#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
&__archive_mbedtls_md5init,
&__archive_mbedtls_md5update,
&__archive_mbedtls_md5final,
#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
&__archive_nettle_md5init,
&__archive_nettle_md5update,
@@ -1303,6 +1511,10 @@ const struct archive_digest __archive_digest =
&__archive_libmd_ripemd160init,
&__archive_libmd_ripemd160update,
&__archive_libmd_ripemd160final,
#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
&__archive_mbedtls_ripemd160init,
&__archive_mbedtls_ripemd160update,
&__archive_mbedtls_ripemd160final,
#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
&__archive_nettle_ripemd160init,
&__archive_nettle_ripemd160update,
@@ -1330,6 +1542,10 @@ const struct archive_digest __archive_digest =
&__archive_libsystem_sha1init,
&__archive_libsystem_sha1update,
&__archive_libsystem_sha1final,
#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
&__archive_mbedtls_sha1init,
&__archive_mbedtls_sha1update,
&__archive_mbedtls_sha1final,
#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
&__archive_nettle_sha1init,
&__archive_nettle_sha1update,
@@ -1369,6 +1585,10 @@ const struct archive_digest __archive_digest =
&__archive_libsystem_sha256init,
&__archive_libsystem_sha256update,
&__archive_libsystem_sha256final,
#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
&__archive_mbedtls_sha256init,
&__archive_mbedtls_sha256update,
&__archive_mbedtls_sha256final,
#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
&__archive_nettle_sha256init,
&__archive_nettle_sha256update,
@@ -1404,6 +1624,10 @@ const struct archive_digest __archive_digest =
&__archive_libsystem_sha384init,
&__archive_libsystem_sha384update,
&__archive_libsystem_sha384final,
#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
&__archive_mbedtls_sha384init,
&__archive_mbedtls_sha384update,
&__archive_mbedtls_sha384final,
#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
&__archive_nettle_sha384init,
&__archive_nettle_sha384update,
@@ -1443,6 +1667,10 @@ const struct archive_digest __archive_digest =
&__archive_libsystem_sha512init,
&__archive_libsystem_sha512update,
&__archive_libsystem_sha512final
#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
&__archive_mbedtls_sha512init,
&__archive_mbedtls_sha512update,
&__archive_mbedtls_sha512final
#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
&__archive_nettle_sha512init,
&__archive_nettle_sha512update,
@@ -24,13 +24,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED
#define ARCHIVE_CRYPTO_PRIVATE_H_INCLUDED
/*
* Crypto support in various Operating Systems:
*
@@ -112,6 +111,24 @@
#include <CommonCrypto/CommonDigest.h>
#endif
/* mbed TLS crypto headers */
#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
#include <mbedtls/md5.h>
#endif
#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
#include <mbedtls/ripemd160.h>
#endif
#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
#include <mbedtls/sha1.h>
#endif
#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
#include <mbedtls/sha256.h>
#endif
#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
#include <mbedtls/sha512.h>
#endif
/* Nettle crypto headers */
#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)
#include <nettle/md5.h>
@@ -159,6 +176,8 @@ typedef MD5_CTX archive_md5_ctx;
typedef MD5_CTX archive_md5_ctx;
#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
typedef CC_MD5_CTX archive_md5_ctx;
#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
typedef mbedtls_md5_context archive_md5_ctx;
#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
typedef struct md5_ctx archive_md5_ctx;
#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
@@ -173,6 +192,8 @@ typedef unsigned char archive_md5_ctx;
typedef RMD160_CTX archive_rmd160_ctx;
#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
typedef RIPEMD160_CTX archive_rmd160_ctx;
#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
typedef mbedtls_ripemd160_context archive_rmd160_ctx;
#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
typedef struct ripemd160_ctx archive_rmd160_ctx;
#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
@@ -187,6 +208,8 @@ typedef SHA1_CTX archive_sha1_ctx;
typedef SHA1_CTX archive_sha1_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
typedef CC_SHA1_CTX archive_sha1_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
typedef mbedtls_sha1_context archive_sha1_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
typedef struct sha1_ctx archive_sha1_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
@@ -207,6 +230,8 @@ typedef SHA2_CTX archive_sha256_ctx;
typedef SHA256_CTX archive_sha256_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
typedef CC_SHA256_CTX archive_sha256_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
typedef mbedtls_sha256_context archive_sha256_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
typedef struct sha256_ctx archive_sha256_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
@@ -225,6 +250,8 @@ typedef SHA384_CTX archive_sha384_ctx;
typedef SHA2_CTX archive_sha384_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
typedef CC_SHA512_CTX archive_sha384_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
typedef mbedtls_sha512_context archive_sha384_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
typedef struct sha384_ctx archive_sha384_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
@@ -245,6 +272,8 @@ typedef SHA2_CTX archive_sha512_ctx;
typedef SHA512_CTX archive_sha512_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
typedef CC_SHA512_CTX archive_sha512_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
typedef mbedtls_sha512_context archive_sha512_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
typedef struct sha512_ctx archive_sha512_ctx;
#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
@@ -259,6 +288,7 @@ typedef unsigned char archive_sha512_ctx;
#if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\
defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \
defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\
defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
defined(ARCHIVE_CRYPTO_MD5_WIN)
@@ -272,6 +302,7 @@ typedef unsigned char archive_sha512_ctx;
__archive_digest.md5update(ctx, buf, n)
#if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\
defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\
defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
#define ARCHIVE_HAS_RMD160
@@ -286,6 +317,7 @@ typedef unsigned char archive_sha512_ctx;
#if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\
defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \
defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
defined(ARCHIVE_CRYPTO_SHA1_WIN)
@@ -303,6 +335,7 @@ typedef unsigned char archive_sha512_ctx;
defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
defined(ARCHIVE_CRYPTO_SHA256_WIN)
@@ -319,6 +352,7 @@ typedef unsigned char archive_sha512_ctx;
defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
defined(ARCHIVE_CRYPTO_SHA384_WIN)
@@ -336,6 +370,7 @@ typedef unsigned char archive_sha512_ctx;
defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\
defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\
defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\
defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\
defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\
defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\
defined(ARCHIVE_CRYPTO_SHA512_WIN)
@@ -145,10 +145,8 @@ sunacl_get(int cmd, int *aclcnt, int fd, const char *path)
cnt = facl(fd, cmd, cnt, aclp);
}
} else {
if (aclp != NULL) {
free(aclp);
aclp = NULL;
}
free(aclp);
aclp = NULL;
break;
}
}
@@ -28,16 +28,15 @@
* Borrowed from FreeBSD's <sys/endian.h>
*/
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_ENDIAN_H_INCLUDED
#define ARCHIVE_ENDIAN_H_INCLUDED
/* Note: This is a purely internal header! */
/* Do not use this outside of libarchive internal code! */
#ifndef ARCHIVE_ENDIAN_H_INCLUDED
#define ARCHIVE_ENDIAN_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
/*
* Disabling inline keyword for compilers known to choke on it:
@@ -32,7 +32,7 @@
.Nm archive_entry_clear ,
.Nm archive_entry_clone ,
.Nm archive_entry_free ,
.Nm archive_entry_new ,
.Nm archive_entry_new
.Nd functions for managing archive entry descriptions
.Sh LIBRARY
Streaming Archive Library (libarchive, -larchive)
@@ -126,7 +126,6 @@ using the current locale.
Similarly, if you store a wide string and then store a
narrow string for the same data, the previously-set wide string will
be discarded in favor of the new data.
.Pp
.\" .Sh EXAMPLE
.\" .Sh RETURN VALUES
.\" .Sh ERRORS
@@ -134,8 +133,8 @@ be discarded in favor of the new data.
.Xr archive_entry_acl 3 ,
.Xr archive_entry_paths 3 ,
.Xr archive_entry_perms 3 ,
.Xr archive_entry_time 3
.Xr libarchive 3 ,
.Xr archive_entry_time 3 ,
.Xr libarchive 3
.Sh HISTORY
The
.Nm libarchive
+177 -149
View File
@@ -168,6 +168,7 @@ archive_entry_clear(struct archive_entry *entry)
archive_entry_xattr_clear(entry);
archive_entry_sparse_clear(entry);
free(entry->stat);
entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
memset(entry, 0, sizeof(*entry));
return entry;
}
@@ -202,6 +203,9 @@ archive_entry_clone(struct archive_entry *entry)
entry2->ae_set = entry->ae_set;
archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);
/* Copy symlink type */
entry2->ae_symlink_type = entry->ae_symlink_type;
/* Copy encryption status */
entry2->encryption = entry->encryption;
@@ -253,6 +257,7 @@ archive_entry_new2(struct archive *a)
if (entry == NULL)
return (NULL);
entry->archive = a;
entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
return (entry);
}
@@ -675,6 +680,12 @@ archive_entry_symlink(struct archive_entry *entry)
return (NULL);
}
int
archive_entry_symlink_type(struct archive_entry *entry)
{
return (entry->ae_symlink_type);
}
const char *
archive_entry_symlink_utf8(struct archive_entry *entry)
{
@@ -1245,6 +1256,12 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
entry->ae_set &= ~AE_SET_SYMLINK;
}
void
archive_entry_set_symlink_type(struct archive_entry *entry, int type)
{
entry->ae_symlink_type = type;
}
void
archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname)
{
@@ -1560,10 +1577,8 @@ archive_entry_acl_text_compat(int *flags)
const wchar_t *
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
{
if (entry->acl.acl_text_w != NULL) {
free(entry->acl.acl_text_w);
entry->acl.acl_text_w = NULL;
}
free(entry->acl.acl_text_w);
entry->acl.acl_text_w = NULL;
if (archive_entry_acl_text_compat(&flags) == 0)
entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
NULL, flags, entry->archive);
@@ -1574,10 +1589,8 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags)
const char *
archive_entry_acl_text(struct archive_entry *entry, int flags)
{
if (entry->acl.acl_text != NULL) {
free(entry->acl.acl_text);
entry->acl.acl_text = NULL;
}
free(entry->acl.acl_text);
entry->acl.acl_text = NULL;
if (archive_entry_acl_text_compat(&flags) == 0)
entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
flags, NULL);
@@ -1590,10 +1603,8 @@ int
_archive_entry_acl_text_l(struct archive_entry *entry, int flags,
const char **acl_text, size_t *len, struct archive_string_conv *sc)
{
if (entry->acl.acl_text != NULL) {
free(entry->acl.acl_text);
entry->acl.acl_text = NULL;
}
free(entry->acl.acl_text);
entry->acl.acl_text = NULL;
if (archive_entry_acl_text_compat(&flags) == 0)
entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
@@ -1638,198 +1649,215 @@ _archive_entry_acl_text_l(struct archive_entry *entry, int flags,
* SUCH DAMAGE.
*/
/*
* Supported file flags on FreeBSD and Mac OS:
* sappnd,sappend SF_APPEND
* arch,archived SF_ARCHIVED
* schg,schange,simmutable SF_IMMUTABLE
* sunlnk,sunlink SF_NOUNLINK (FreeBSD only)
* uappnd,uappend UF_APPEND
* compressed UF_COMPRESSED (Mac OS only)
* hidden,uhidden UF_HIDDEN
* uchg,uchange,uimmutable UF_IMMUTABLE
* nodump UF_NODUMP
* uunlnk,uunlink UF_NOUNLINK (FreeBSD only)
* offline,uoffline UF_OFFLINE (FreeBSD only)
* opaque UF_OPAQUE
* rdonly,urdonly,readonly UF_READONLY (FreeBSD only)
* reparse,ureparse UF_REPARSE (FreeBSD only)
* sparse,usparse UF_SPARSE (FreeBSD only)
* system,usystem UF_SYSTEM (FreeBSD only)
*
* See chflags(2) for more information
*
* Supported file attributes on Linux:
* a append only FS_APPEND_FL sappnd
* A no atime updates FS_NOATIME_FL atime
* c compress FS_COMPR_FL compress
* C no copy on write FS_NOCOW_FL cow
* d no dump FS_NODUMP_FL dump
* D synchronous directory updates FS_DIRSYNC_FL dirsync
* i immutable FS_IMMUTABLE_FL schg
* j data journalling FS_JOURNAL_DATA_FL journal
* P project hierarchy FS_PROJINHERIT_FL projinherit
* s secure deletion FS_SECRM_FL securedeletion
* S synchronous updates FS_SYNC_FL sync
* t no tail-merging FS_NOTAIL_FL tail
* T top of directory hierarchy FS_TOPDIR_FL topdir
* u undeletable FS_UNRM_FL undel
*
* See ioctl_iflags(2) for more information
*
* Equivalent file flags supported on FreeBSD / Mac OS and Linux:
* SF_APPEND FS_APPEND_FL sappnd
* SF_IMMUTABLE FS_IMMUTABLE_FL schg
* UF_NODUMP FS_NODUMP_FL nodump
*/
static const struct flag {
const char *name;
const wchar_t *wname;
unsigned long set;
unsigned long clear;
} flags[] = {
} fileflags[] = {
/* Preferred (shorter) names per flag first, all prefixed by "no" */
#ifdef SF_APPEND
{ "nosappnd", L"nosappnd", SF_APPEND, 0 },
{ "nosappend", L"nosappend", SF_APPEND, 0 },
{ "nosappnd", L"nosappnd", SF_APPEND, 0},
{ "nosappend", L"nosappend", SF_APPEND, 0},
#endif
#if defined(FS_APPEND_FL) /* 'a' */
{ "nosappnd", L"nosappnd", FS_APPEND_FL, 0 },
{ "nosappend", L"nosappend", FS_APPEND_FL, 0 },
{ "nosappnd", L"nosappnd", FS_APPEND_FL, 0},
{ "nosappend", L"nosappend", FS_APPEND_FL, 0},
#elif defined(EXT2_APPEND_FL) /* 'a' */
{ "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 },
{ "nosappend", L"nosappend", EXT2_APPEND_FL, 0 },
{ "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0},
{ "nosappend", L"nosappend", EXT2_APPEND_FL, 0},
#endif
#ifdef SF_ARCHIVED
{ "noarch", L"noarch", SF_ARCHIVED, 0 },
{ "noarchived", L"noarchived", SF_ARCHIVED, 0 },
{ "noarch", L"noarch", SF_ARCHIVED, 0},
{ "noarchived", L"noarchived", SF_ARCHIVED, 0},
#endif
#ifdef SF_IMMUTABLE
{ "noschg", L"noschg", SF_IMMUTABLE, 0 },
{ "noschange", L"noschange", SF_IMMUTABLE, 0 },
{ "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 },
{ "noschg", L"noschg", SF_IMMUTABLE, 0},
{ "noschange", L"noschange", SF_IMMUTABLE, 0},
{ "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0},
#endif
#if defined(FS_IMMUTABLE_FL) /* 'i' */
{ "noschg", L"noschg", FS_IMMUTABLE_FL, 0 },
{ "noschange", L"noschange", FS_IMMUTABLE_FL, 0 },
{ "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 },
{ "noschg", L"noschg", FS_IMMUTABLE_FL, 0},
{ "noschange", L"noschange", FS_IMMUTABLE_FL, 0},
{ "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0},
#elif defined(EXT2_IMMUTABLE_FL) /* 'i' */
{ "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 },
{ "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 },
{ "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 },
{ "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0},
{ "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0},
{ "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0},
#endif
#ifdef SF_NOUNLINK
{ "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 },
{ "nosunlink", L"nosunlink", SF_NOUNLINK, 0 },
#endif
#ifdef SF_SNAPSHOT
{ "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 },
{ "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0},
{ "nosunlink", L"nosunlink", SF_NOUNLINK, 0},
#endif
#ifdef UF_APPEND
{ "nouappnd", L"nouappnd", UF_APPEND, 0 },
{ "nouappend", L"nouappend", UF_APPEND, 0 },
{ "nouappnd", L"nouappnd", UF_APPEND, 0},
{ "nouappend", L"nouappend", UF_APPEND, 0},
#endif
#ifdef UF_IMMUTABLE
{ "nouchg", L"nouchg", UF_IMMUTABLE, 0 },
{ "nouchange", L"nouchange", UF_IMMUTABLE, 0 },
{ "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 },
{ "nouchg", L"nouchg", UF_IMMUTABLE, 0},
{ "nouchange", L"nouchange", UF_IMMUTABLE, 0},
{ "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0},
#endif
#ifdef UF_NODUMP
{ "nodump", L"nodump", 0, UF_NODUMP},
#endif
#if defined(FS_NODUMP_FL) /* 'd' */
{ "nodump", L"nodump", 0, FS_NODUMP_FL},
#elif defined(EXT2_NODUMP_FL) /* 'd' */
#elif defined(EXT2_NODUMP_FL)
{ "nodump", L"nodump", 0, EXT2_NODUMP_FL},
#endif
#ifdef UF_OPAQUE
{ "noopaque", L"noopaque", UF_OPAQUE, 0 },
{ "noopaque", L"noopaque", UF_OPAQUE, 0},
#endif
#ifdef UF_NOUNLINK
{ "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 },
{ "nouunlink", L"nouunlink", UF_NOUNLINK, 0 },
{ "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0},
{ "nouunlink", L"nouunlink", UF_NOUNLINK, 0},
#endif
#ifdef UF_COMPRESSED
{ "nocompressed",L"nocompressed", UF_COMPRESSED, 0 },
/* Mac OS */
{ "nocompressed", L"nocompressed", UF_COMPRESSED, 0},
#endif
#ifdef UF_HIDDEN
{ "nohidden", L"nohidden", UF_HIDDEN, 0 },
{ "nohidden", L"nohidden", UF_HIDDEN, 0},
{ "nouhidden", L"nouhidden", UF_HIDDEN, 0},
#endif
#if defined(FS_UNRM_FL)
{ "nouunlink", L"nouunlink", FS_UNRM_FL, 0},
#ifdef FILE_ATTRIBUTE_HIDDEN
{ "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0},
{ "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0},
#endif
#ifdef UF_OFFLINE
{ "nooffline", L"nooffline", UF_OFFLINE, 0},
{ "nouoffline", L"nouoffline", UF_OFFLINE, 0},
#endif
#ifdef UF_READONLY
{ "nordonly", L"nordonly", UF_READONLY, 0},
{ "nourdonly", L"nourdonly", UF_READONLY, 0},
{ "noreadonly", L"noreadonly", UF_READONLY, 0},
#endif
#ifdef FILE_ATTRIBUTE_READONLY
{ "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0},
{ "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0},
{ "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0},
#endif
#ifdef UF_SPARSE
{ "nosparse", L"nosparse", UF_SPARSE, 0},
{ "nousparse", L"nousparse", UF_SPARSE, 0},
#endif
#ifdef UF_REPARSE
{ "noreparse", L"noreparse", UF_REPARSE, 0},
{ "noureparse", L"noureparse", UF_REPARSE, 0},
#endif
#ifdef UF_SYSTEM
{ "nosystem", L"nosystem", UF_SYSTEM, 0},
{ "nousystem", L"nousystem", UF_SYSTEM, 0},
#endif
#ifdef FILE_ATTRIBUTE_SYSTEM
{ "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0},
{ "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0},
#endif
#if defined(FS_UNRM_FL) /* 'u' */
{ "noundel", L"noundel", FS_UNRM_FL, 0},
#elif defined(EXT2_UNRM_FL)
{ "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0},
{ "noundel", L"noundel", EXT2_UNRM_FL, 0},
#endif
#if defined(FS_BTREE_FL)
{ "nobtree", L"nobtree", FS_BTREE_FL, 0 },
#elif defined(EXT2_BTREE_FL)
{ "nobtree", L"nobtree", EXT2_BTREE_FL, 0 },
#if defined(FS_COMPR_FL) /* 'c' */
{ "nocompress", L"nocompress", FS_COMPR_FL, 0},
#elif defined(EXT2_COMPR_FL)
{ "nocompress", L"nocompress", EXT2_COMPR_FL, 0},
#endif
#if defined(FS_ECOMPR_FL)
{ "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 },
#elif defined(EXT2_ECOMPR_FL)
{ "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 },
#if defined(FS_NOATIME_FL) /* 'A' */
{ "noatime", L"noatime", 0, FS_NOATIME_FL},
#elif defined(EXT2_NOATIME_FL)
{ "noatime", L"noatime", 0, EXT2_NOATIME_FL},
#endif
#if defined(FS_COMPR_FL) /* 'c' */
{ "nocompress", L"nocompress", FS_COMPR_FL, 0 },
#elif defined(EXT2_COMPR_FL) /* 'c' */
{ "nocompress", L"nocompress", EXT2_COMPR_FL, 0 },
#endif
#if defined(FS_NOATIME_FL) /* 'A' */
{ "noatime", L"noatime", 0, FS_NOATIME_FL},
#elif defined(EXT2_NOATIME_FL) /* 'A' */
{ "noatime", L"noatime", 0, EXT2_NOATIME_FL},
#endif
#if defined(FS_DIRTY_FL)
{ "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0},
#elif defined(EXT2_DIRTY_FL)
{ "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0},
#endif
#if defined(FS_COMPRBLK_FL)
#if defined(FS_NOCOMPR_FL)
{ "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL},
#else
{ "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0},
#endif
#elif defined(EXT2_COMPRBLK_FL)
#if defined(EXT2_NOCOMPR_FL)
{ "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL},
#else
{ "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0},
#endif
#endif
#if defined(FS_DIRSYNC_FL)
{ "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0},
#if defined(FS_DIRSYNC_FL) /* 'D' */
{ "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0},
#elif defined(EXT2_DIRSYNC_FL)
{ "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0},
{ "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0},
#endif
#if defined(FS_INDEX_FL)
{ "nohashidx", L"nohashidx", FS_INDEX_FL, 0},
#elif defined(EXT2_INDEX_FL)
{ "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0},
#endif
#if defined(FS_IMAGIC_FL)
{ "noimagic", L"noimagic", FS_IMAGIC_FL, 0},
#elif defined(EXT2_IMAGIC_FL)
{ "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0},
#endif
#if defined(FS_JOURNAL_DATA_FL)
{ "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0},
#if defined(FS_JOURNAL_DATA_FL) /* 'j' */
{ "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0},
{ "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0},
#elif defined(EXT3_JOURNAL_DATA_FL)
{ "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0},
{ "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0},
{ "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0},
#endif
#if defined(FS_SECRM_FL)
{ "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0},
#if defined(FS_SECRM_FL) /* 's' */
{ "nosecdel", L"nosecdel", FS_SECRM_FL, 0},
{ "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0},
#elif defined(EXT2_SECRM_FL)
{ "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0},
{ "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0},
{ "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0},
#endif
#if defined(FS_SYNC_FL)
{ "nosync", L"nosync", FS_SYNC_FL, 0},
#if defined(FS_SYNC_FL) /* 'S' */
{ "nosync", L"nosync", FS_SYNC_FL, 0},
#elif defined(EXT2_SYNC_FL)
{ "nosync", L"nosync", EXT2_SYNC_FL, 0},
{ "nosync", L"nosync", EXT2_SYNC_FL, 0},
#endif
#if defined(FS_NOTAIL_FL)
{ "notail", L"notail", 0, FS_NOTAIL_FL},
#if defined(FS_NOTAIL_FL) /* 't' */
{ "notail", L"notail", 0, FS_NOTAIL_FL},
#elif defined(EXT2_NOTAIL_FL)
{ "notail", L"notail", 0, EXT2_NOTAIL_FL},
{ "notail", L"notail", 0, EXT2_NOTAIL_FL},
#endif
#if defined(FS_TOPDIR_FL)
{ "notopdir", L"notopdir", FS_TOPDIR_FL, 0},
#if defined(FS_TOPDIR_FL) /* 'T' */
{ "notopdir", L"notopdir", FS_TOPDIR_FL, 0},
#elif defined(EXT2_TOPDIR_FL)
{ "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0},
{ "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0},
#endif
#ifdef FS_ENCRYPT_FL
{ "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0},
#ifdef FS_NOCOW_FL /* 'C' */
{ "nocow", L"nocow", 0, FS_NOCOW_FL},
#endif
#ifdef FS_HUGE_FILE_FL
{ "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0},
#ifdef FS_PROJINHERIT_FL /* 'P' */
{ "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0},
#endif
#ifdef FS_EXTENT_FL
{ "noextent", L"noextent", FS_EXTENT_FL, 0},
#endif
#ifdef FS_EA_INODE_FL
{ "noeainode", L"noeainode", FS_EA_INODE_FL, 0},
#endif
#ifdef FS_EOFBLOCKS_FL
{ "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0},
#endif
#ifdef FS_NOCOW_FL
{ "nocow", L"nocow", FS_NOCOW_FL, 0},
#endif
#ifdef FS_INLINE_DATA_FL
{ "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0},
#endif
#ifdef FS_PROJINHERIT_FL
{ "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0},
#endif
#if defined(FS_RESERVED_FL)
{ "noreserved", L"noreserved", FS_RESERVED_FL, 0},
#elif defined(EXT2_RESERVED_FL)
{ "noreserved", L"noreserved", EXT2_RESERVED_FL, 0},
#endif
{ NULL, NULL, 0, 0 }
{ NULL, NULL, 0, 0}
};
/*
@@ -1848,7 +1876,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
bits = bitset | bitclear;
length = 0;
for (flag = flags; flag->name != NULL; flag++)
for (flag = fileflags; flag->name != NULL; flag++)
if (bits & (flag->set | flag->clear)) {
length += strlen(flag->name) + 1;
bits &= ~(flag->set | flag->clear);
@@ -1861,7 +1889,7 @@ ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
return (NULL);
dp = string;
for (flag = flags; flag->name != NULL; flag++) {
for (flag = fileflags; flag->name != NULL; flag++) {
if (bitset & flag->set || bitclear & flag->clear) {
sp = flag->name + 2;
} else if (bitset & flag->clear || bitclear & flag->set) {
@@ -1913,7 +1941,7 @@ ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
*end != ' ' && *end != ',')
end++;
length = end - start;
for (flag = flags; flag->name != NULL; flag++) {
for (flag = fileflags; flag->name != NULL; flag++) {
size_t flag_length = strlen(flag->name);
if (length == flag_length
&& memcmp(start, flag->name, length) == 0) {
@@ -1981,7 +2009,7 @@ ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
*end != L' ' && *end != L',')
end++;
length = end - start;
for (flag = flags; flag->wname != NULL; flag++) {
for (flag = fileflags; flag->wname != NULL; flag++) {
size_t flag_length = wcslen(flag->wname);
if (length == flag_length
&& wmemcmp(start, flag->wname, length) == 0) {
@@ -30,7 +30,7 @@
#define ARCHIVE_ENTRY_H_INCLUDED
/* Note: Compiler will complain if this does not match archive.h! */
#define ARCHIVE_VERSION_NUMBER 3003003
#define ARCHIVE_VERSION_NUMBER 3004002
/*
* Note: archive_entry.h is for use outside of libarchive; the
@@ -187,6 +187,13 @@ struct archive_entry;
#define AE_IFDIR ((__LA_MODE_T)0040000)
#define AE_IFIFO ((__LA_MODE_T)0010000)
/*
* Symlink types
*/
#define AE_SYMLINK_TYPE_UNDEFINED 0
#define AE_SYMLINK_TYPE_FILE 1
#define AE_SYMLINK_TYPE_DIRECTORY 2
/*
* Basic object manipulation
*/
@@ -272,6 +279,7 @@ __LA_DECL int archive_entry_size_is_set(struct archive_entry *);
__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
__LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *);
__LA_DECL int archive_entry_symlink_type(struct archive_entry *);
__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
__LA_DECL la_int64_t archive_entry_uid(struct archive_entry *);
__LA_DECL const char *archive_entry_uname(struct archive_entry *);
@@ -347,6 +355,7 @@ __LA_DECL void archive_entry_unset_size(struct archive_entry *);
__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
__LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int);
__LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *);
__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
@@ -512,9 +521,6 @@ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type
__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
int * /* type */, int * /* permset */, int * /* tag */,
int * /* qual */, const char ** /* name */);
__LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */,
int * /* type */, int * /* permset */, int * /* tag */,
int * /* qual */, const wchar_t ** /* name */);
/*
* Construct a text-format ACL. The flags argument is a bitmask that
@@ -689,7 +695,6 @@ __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
struct archive_entry **, struct archive_entry **);
__LA_DECL struct archive_entry *archive_entry_partial_links(
struct archive_entry_linkresolver *res, unsigned int *links);
#ifdef __cplusplus
}
#endif
@@ -34,7 +34,6 @@
.Nm archive_entry_acl_from_text ,
.Nm archive_entry_acl_from_text_w ,
.Nm archive_entry_acl_next ,
.Nm archive_entry_acl_next_w ,
.Nm archive_entry_acl_reset ,
.Nm archive_entry_acl_to_text ,
.Nm archive_entry_acl_to_text_w ,
@@ -89,16 +88,6 @@ Streaming Archive Library (libarchive, -larchive)
.Fa "const char **ret_name"
.Fc
.Ft int
.Fo archive_entry_acl_next_w
.Fa "struct archive_entry *a"
.Fa "int type"
.Fa "int *ret_type"
.Fa "int *ret_permset"
.Fa "int *ret_tag"
.Fa "int *ret_qual"
.Fa "const wchar_t **ret_name"
.Fc
.Ft int
.Fn archive_entry_acl_reset "struct archive_entry *a" "int type"
.Ft char *
.Fo archive_entry_acl_to_text
@@ -118,15 +107,16 @@ Streaming Archive Library (libarchive, -larchive)
.Sh DESCRIPTION
The
.Dq Access Control Lists (ACLs)
extend the standard Unix perssion model.
extend the standard Unix permission model.
The ACL interface of
.Nm libarchive
supports both POSIX.1e and NFSv4 style ACLs. Use of ACLs is restricted by
supports both POSIX.1e and NFSv4 style ACLs.
Use of ACLs is restricted by
various levels of ACL support in operating systems, file systems and archive
formats.
.Ss POSIX.1e Access Control Lists
A POSIX.1e ACL consists of a number of independent entries.
Each entry specifies the permission set as bitmask of basic permissions.
Each entry specifies the permission set as a bitmask of basic permissions.
Valid permissions in the
.Fa permset
are:
@@ -147,13 +137,13 @@ The user specified by the name field.
.It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
The owner of the file.
.It Dv ARCHIVE_ENTRY_ACL_GROUP
The group specied by the name field.
The group specified by the name field.
.It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
The group who owns the file.
The group which owns the file.
.It Dv ARCHIVE_ENTRY_ACL_MASK
The maximum permissions to be obtained via group permissions.
.It Dv ARCHIVE_ENTRY_ACL_OTHER
Any principal who is not file owner or a member of the owning group.
Any principal who is not the file owner or a member of the owning group.
.El
.Pp
The principals
@@ -164,12 +154,12 @@ and
are equivalent to user, group and other in the classic Unix permission
model and specify non-extended ACL entries.
.Pp
All files with have an access ACL
All files have an access ACL
.Pq Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS .
This specifies the permissions required for access to the file itself.
Directories have an additional ACL
.Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT ,
which controls the initial access ACL for newly created directory entries.
which controls the initial access ACL for newly-created directory entries.
.Ss NFSv4 Access Control Lists
A NFSv4 ACL consists of multiple individual entries called Access Control
Entries (ACEs).
@@ -197,11 +187,11 @@ The user specified by the name field.
.It Dv ARCHIVE_ENTRY_ACL_USER_OBJ
The owner of the file.
.It Dv ARCHIVE_ENTRY_ACL_GROUP
The group specied by the name field.
The group specified by the name field.
.It Dv ARCHIVE_ENTRY_ACL_GROUP_OBJ
The group who owns the file.
The group which owns the file.
.It Dv ARCHIVE_ENTRY_ACL_EVERYONE
Any principal who is not file owner or a member of the owning group.
Any principal who is not the file owner or a member of the owning group.
.El
.Pp
Entries with the
@@ -216,9 +206,10 @@ integer.
.Pp
NFSv4 ACE permissions and flags are stored in the same
.Fa permset
bitfield. Some permissions share the same constant and permission character but
have different effect on directories than on files. The following ACE
permissions are supported:
bitfield.
Some permissions share the same constant and permission character
but have different effect on directories than on files.
The following ACE permissions are supported:
.Bl -tag -offset indent -compact -width ARCHIV
.It Dv ARCHIVE_ENTRY_ACL_READ_DATA ( Sy r )
Read data (file).
@@ -265,7 +256,8 @@ Inherit parent directory ACE to subdirectories.
.It Dv ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY ( Sy i )
Only inherit, do not apply the permission on the directory itself.
.It Dv ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT ( Sy n )
Do not propagate inherit flags. Only first-level entries inherit ACLs.
Do not propagate inherit flags.
Only first-level entries inherit ACLs.
.It Dv ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS ( Sy S )
Trigger alarm or audit on successful access.
.It Dv ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS ( Sy F )
@@ -279,8 +271,8 @@ and
.Fn archive_entry_acl_add_entry_w
add a single ACL entry.
For the access ACL and non-extended principals, the classic Unix permissions
are updated. An archive entry cannot contain both POSIX.1e and NFSv4 ACL
entries.
are updated.
An archive entry cannot contain both POSIX.1e and NFSv4 ACL entries.
.Pp
.Fn archive_entry_acl_clear
removes all ACL entries and resets the enumeration pointer.
@@ -300,7 +292,8 @@ for POSIX.1e ACLs and
.It Dv ARCHIVE_ENTRY_ACL_TYPE_AUDIT
.It Dv ARCHIVE_ENTRY_ACL_TYPE_ALARM
.El
for NFSv4 ACLs. For POSIX.1e ACLs if
for NFSv4 ACLs.
For POSIX.1e ACLs if
.Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS
is included and at least one extended ACL entry is found,
the three non-extended ACLs are added.
@@ -312,7 +305,8 @@ add new
.Pq or merge with existing
ACL entries from
.Pq wide
text. The argument
text.
The argument
.Fa type
may take one of the following values:
.Bl -tag -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_DEFAULT"
@@ -322,11 +316,13 @@ may take one of the following values:
.El
Supports all formats that can be created with
.Fn archive_entry_acl_to_text
or respective
or respectively
.Fn archive_entry_acl_to_text_w .
Existing ACL entries are preserved. To get a clean new ACL from text
Existing ACL entries are preserved.
To get a clean new ACL from text
.Fn archive_entry_acl_clear
must be called first. Entries prefixed with
must be called first.
Entries prefixed with
.Dq default:
are treated as
.Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT
@@ -342,8 +338,6 @@ character
are skipped.
.Pp
.Fn archive_entry_acl_next
and
.Fn archive_entry_acl_next_w
return the next entry of the ACL list.
This functions may only be called after
.Fn archive_entry_acl_reset
@@ -351,10 +345,8 @@ has indicated the presence of extended ACL entries.
.Pp
.Fn archive_entry_acl_reset
prepare reading the list of ACL entries with
.Fn archive_entry_acl_next
or
.Fn archive_entry_acl_next_w .
The function returns either 0, if no non-extended ACLs are found.
.Fn archive_entry_acl_next .
The function returns 0 if no non-extended ACLs are found.
In this case, the access permissions should be obtained by
.Xr archive_entry_mode 3
or set using
@@ -367,7 +359,8 @@ and
.Fn archive_entry_acl_to_text_w
convert the ACL entries for the given type into a
.Pq wide
string of ACL entries separated by newline. If the pointer
string of ACL entries separated by newline.
If the pointer
.Fa len_p
is not NULL, then the function shall return the length of the string
.Pq not including the NULL terminator
@@ -415,7 +408,8 @@ are prefixed with
.Dq default: .
.Pp
.Fn archive_entry_acl_types
get ACL entry types contained in an archive entry's ACL. As POSIX.1e and NFSv4
get ACL entry types contained in an archive entry's ACL.
As POSIX.1e and NFSv4
ACL entries cannot be mixed, this function is a very efficient way to detect if
an ACL already contains POSIX.1e or NFSv4 ACL entries.
.Sh RETURN VALUES
@@ -438,9 +432,7 @@ if all entries were successfully parsed and
if one or more entries were invalid or non-parseable.
.Pp
.Fn archive_entry_acl_next
and
.Fn archive_entry_acl_next_w
return
returns
.Dv ARCHIVE_OK
on success,
.Dv ARCHIVE_EOF
@@ -25,13 +25,13 @@
* $FreeBSD$
*/
#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED
#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED
#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED
struct archive_entry;
struct archive_string_conv;
@@ -0,0 +1,63 @@
.\" Copyright (c) 2019 Martin Matuska
.\" 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.
.\"
.Dd April 15, 2019
.Dt ARCHIVE_ENTRY_MISC 3
.Os
.Sh NAME
.Nm archive_entry_symlink_type ,
.Nm archive_entry_set_symlink_type
.Nd miscellaneous functions for manipulating properties of archive_entry
.Sh LIBRARY
Streaming Archive Library (libarchive, -larchive)
.Sh SYNOPSIS
.In archive_entry.h
.Ft int
.Fn archive_entry_symlink_type "struct archive_entry *a"
.Ft void
.Fn archive_entry_set_symlink_type "struct archive_entry *a" "int"
.Sh DESCRIPTION
The function
.Fn archive_entry_symlink_type
returns and the function
.Fn archive_entry_set_symlink_type
sets the type of the symbolic link stored in an archive entry.
These functions
have special meaning on operating systems that support multiple symbolic link
types (e.g. Microsoft Windows).
.Pp
Supported values are:
.Bl -tag -width "AE_SYMLINK_TYPE_DIRECTORY" -compact
.It AE_SYMLINK_TYPE_UNDEFINED
Symbolic link target type is not defined (default on unix systems)
.It AE_SYMLINK_TYPE_FILE
Symbolic link points to a file
.It AE_SYMLINK_TYPE_DIRECTORY
Symbolic link points to a directory
.El
.Sh SEE ALSO
.Xr archive_entry 3 ,
.Xr archive_entry_paths 3 ,
.Xr archive_entry_stat 3 ,
.Xr libarchive 3
@@ -133,7 +133,7 @@ The accessor functions are named
.Fn XXX_w .
.It UTF-8
Unicode strings encoded as UTF-8.
This are convience functions to update both the multibyte and wide
These are convenience functions to update both the multibyte and wide
character strings at the same time.
.El
.Pp
@@ -141,13 +141,13 @@ The sourcepath is a pure filesystem concept and never stored in an
archive directly.
.Pp
For that reason, it is only available as multibyte string.
The link path is a convience function for conditionally setting
The link path is a convenience function for conditionally setting
hardlink or symlink destination.
It doesn't have a corresponding get accessor function.
.Pp
.Fn archive_entry_set_XXX
is an alias for
is an alias for
.Fn archive_entry_copy_XXX .
.Sh SEE ALSO
.Xr archive_entry 3
.Xr libarchive 3 ,
.Xr archive_entry 3 ,
.Xr libarchive 3
@@ -126,7 +126,7 @@ The corresponding functions
and
.Fn archive_entry_set_perm
store the given user id, group id and permission in the entry.
The permission is also set as side effect of calling
The permission is also set as a side effect of calling
.Fn archive_entry_set_mode .
.Pp
.Fn archive_entry_strmode
@@ -143,12 +143,12 @@ The accessor functions are named
.Fn XXX_w .
.It UTF-8
Unicode strings encoded as UTF-8.
This are convience functions to update both the multibyte and wide
These are convenience functions to update both the multibyte and wide
character strings at the same time.
.El
.Pp
.Fn archive_entry_set_XXX
is an alias for
is an alias for
.Fn archive_entry_copy_XXX .
.Ss File Flags
File flags are transparently converted between a bitmap
@@ -182,7 +182,7 @@ The
.Fn archive_entry_copy_fflags_text
and
.Fn archive_entry_copy_fflags_text_w
functions parse the provided text and sets the internal bitmap values.
functions parse the provided text and set the internal bitmap values.
This is a platform-specific operation; names that are not meaningful
on the current platform will be ignored.
The function returns a pointer to the start of the first name that was not
@@ -197,8 +197,8 @@ which stops parsing at the first unrecognized name.)
.Xr archive_entry 3 ,
.Xr archive_entry_acl 3 ,
.Xr archive_read_disk 3 ,
.Xr archive_write_disk 3
.Xr libarchive 3 ,
.Xr archive_write_disk 3 ,
.Xr libarchive 3
.Sh BUGS
The platform types
.Vt uid_t
@@ -25,13 +25,13 @@
* $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $
*/
#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
#include "archive_acl_private.h"
#include "archive_string.h"
@@ -176,6 +176,9 @@ struct archive_entry {
/* Miscellaneous. */
char strmode[12];
/* Symlink type support */
int ae_symlink_type;
};
#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
@@ -54,7 +54,7 @@
.Nm archive_entry_rdevmajor ,
.Nm archive_entry_set_rdevmajor ,
.Nm archive_entry_rdevminor ,
.Nm archive_entry_set_rdevminor ,
.Nm archive_entry_set_rdevminor
.Nd accessor functions for manipulating archive entry descriptions
.Sh LIBRARY
Streaming Archive Library (libarchive, -larchive)
@@ -267,8 +267,8 @@ platforms.
Some archive formats use the combined form, while other formats use
the split form.
.Sh SEE ALSO
.Xr stat 2 ,
.Xr archive_entry_acl 3 ,
.Xr archive_entry_perms 3 ,
.Xr archive_entry_time 3 ,
.Xr libarchive 3 ,
.Xr stat 2
.Xr libarchive 3
@@ -48,7 +48,7 @@
.Nm archive_entry_mtime_nsec ,
.Nm archive_entry_mtime_is_set ,
.Nm archive_entry_set_mtime ,
.Nm archive_entry_unset_mtime ,
.Nm archive_entry_unset_mtime
.Nd functions for manipulating times in archive entry descriptions
.Sh LIBRARY
Streaming Archive Library (libarchive, -larchive)
@@ -113,8 +113,8 @@ The current state can be queried using
.Fn XXX_is_set .
Unset time fields have a second and nanosecond field of 0.
.Sh SEE ALSO
.Xr archive_entry 3
.Xr libarchive 3 ,
.Xr archive_entry 3 ,
.Xr libarchive 3
.Sh HISTORY
The
.Nm libarchive
@@ -27,6 +27,7 @@
** This code is in the public domain and has no copyright.
*/
#include "archive_platform.h"
#ifdef __FreeBSD__
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -694,8 +695,16 @@ Convert(time_t Month, time_t Day, time_t Year,
signed char DaysInMonth[12] = {
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
time_t Julian;
int i;
time_t Julian;
int i;
struct tm *ltime;
#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
struct tm tmbuf;
#endif
#if defined(HAVE__LOCALTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
if (Year < 69)
Year += 2000;
@@ -722,21 +731,64 @@ Convert(time_t Month, time_t Day, time_t Year,
Julian *= DAY;
Julian += Timezone;
Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
#if defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Julian, &tmbuf);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = Julian;
terr = _localtime64_s(&tmbuf, &tmptime);
if (terr)
ltime = NULL;
else
ltime = &tmbuf;
#else
ltime = localtime(&Julian);
#endif
if (DSTmode == DSTon
|| (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
|| (DSTmode == DSTmaybe && ltime->tm_isdst))
Julian -= HOUR;
return Julian;
}
static time_t
DSTcorrect(time_t Start, time_t Future)
{
time_t StartDay;
time_t FutureDay;
time_t StartDay;
time_t FutureDay;
struct tm *ltime;
#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
struct tm tmbuf;
#endif
#if defined(HAVE__LOCALTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
StartDay = (localtime(&Start)->tm_hour + 1) % 24;
FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
#if defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Start, &tmbuf);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = Start;
terr = _localtime64_s(&tmbuf, &tmptime);
if (terr)
ltime = NULL;
else
ltime = &tmbuf;
#else
ltime = localtime(&Start);
#endif
StartDay = (ltime->tm_hour + 1) % 24;
#if defined(HAVE_LOCALTIME_R)
ltime = localtime_r(&Future, &tmbuf);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = Future;
terr = _localtime64_s(&tmbuf, &tmptime);
if (terr)
ltime = NULL;
else
ltime = &tmbuf;
#else
ltime = localtime(&Future);
#endif
FutureDay = (ltime->tm_hour + 1) % 24;
return (Future - Start) + (StartDay - FutureDay) * HOUR;
}
@@ -747,9 +799,27 @@ RelativeDate(time_t Start, time_t zone, int dstmode,
{
struct tm *tm;
time_t t, now;
#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
struct tm tmbuf;
#endif
#if defined(HAVE__GMTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
t = Start - zone;
#if defined(HAVE_GMTIME_R)
tm = gmtime_r(&t, &tmbuf);
#elif defined(HAVE__GMTIME64_S)
tmptime = t;
terr = _gmtime64_s(&tmbuf, &tmptime);
if (terr)
tm = NULL;
else
tm = &tmbuf;
#else
tm = gmtime(&t);
#endif
now = Start;
now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
@@ -765,10 +835,28 @@ RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
struct tm *tm;
time_t Month;
time_t Year;
#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
struct tm tmbuf;
#endif
#if defined(HAVE__LOCALTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
if (RelMonth == 0)
return 0;
#if defined(HAVE_LOCALTIME_R)
tm = localtime_r(&Start, &tmbuf);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = Start;
terr = _localtime64_s(&tmbuf, &tmptime);
if (terr)
tm = NULL;
else
tm = &tmbuf;
#else
tm = localtime(&Start);
#endif
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
Year = Month / 12;
Month = Month % 12 + 1;
@@ -905,6 +993,10 @@ __archive_get_date(time_t now, const char *p)
time_t Start;
time_t tod;
long tzone;
#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
/* Clear out the parsed token array. */
memset(tokens, 0, sizeof(tokens));
@@ -913,20 +1005,44 @@ __archive_get_date(time_t now, const char *p)
gds = &_gds;
/* Look up the current time. */
#if defined(HAVE_LOCALTIME_R)
tm = localtime_r(&now, &local);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = now;
terr = _localtime64_s(&local, &tmptime);
if (terr)
tm = NULL;
else
tm = &local;
#else
memset(&local, 0, sizeof(local));
tm = localtime (&now);
tm = localtime(&now);
#endif
if (tm == NULL)
return -1;
#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S)
local = *tm;
#endif
/* Look up UTC if we can and use that to determine the current
* timezone offset. */
#if defined(HAVE_GMTIME_R)
gmt_ptr = gmtime_r(&now, &gmt);
#elif defined(HAVE__GMTIME64_S)
tmptime = now;
terr = _gmtime64_s(&gmt, &tmptime);
if (terr)
gmt_ptr = NULL;
else
gmt_ptr = &gmt;
#else
memset(&gmt, 0, sizeof(gmt));
gmt_ptr = gmtime (&now);
gmt_ptr = gmtime(&now);
if (gmt_ptr != NULL) {
/* Copy, in case localtime and gmtime use the same buffer. */
gmt = *gmt_ptr;
}
#endif
if (gmt_ptr != NULL)
tzone = difftm (&gmt, &local);
else
@@ -960,7 +1076,18 @@ __archive_get_date(time_t now, const char *p)
* time components instead of the local timezone. */
if (gds->HaveZone && gmt_ptr != NULL) {
now -= gds->Timezone;
gmt_ptr = gmtime (&now);
#if defined(HAVE_GMTIME_R)
gmt_ptr = gmtime_r(&now, &gmt);
#elif defined(HAVE__GMTIME64_S)
tmptime = now;
terr = _gmtime64_s(&gmt, &tmptime);
if (terr)
gmt_ptr = NULL;
else
gmt_ptr = &gmt;
#else
gmt_ptr = gmtime(&now);
#endif
if (gmt_ptr != NULL)
local = *gmt_ptr;
now += gds->Timezone;
@@ -25,13 +25,13 @@
* $FreeBSD$
*/
#ifndef ARCHIVE_GETDATE_H_INCLUDED
#define ARCHIVE_GETDATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_GETDATE_H_INCLUDED
#define ARCHIVE_GETDATE_H_INCLUDED
#include <time.h>
time_t __archive_get_date(time_t now, const char *);
@@ -83,6 +83,9 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
static int
__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
{
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
BCRYPT_ALG_HANDLE hAlg;
BCRYPT_HASH_HANDLE hHash;
DWORD hash_len;
@@ -147,6 +150,53 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
}
}
#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
static int
__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
{
const mbedtls_md_info_t *info;
int ret;
mbedtls_md_init(ctx);
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
if (info == NULL) {
mbedtls_md_free(ctx);
return (-1);
}
ret = mbedtls_md_setup(ctx, info, 1);
if (ret != 0) {
mbedtls_md_free(ctx);
return (-1);
}
ret = mbedtls_md_hmac_starts(ctx, key, key_len);
if (ret != 0) {
mbedtls_md_free(ctx);
return (-1);
}
return 0;
}
static void
__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
size_t data_len)
{
mbedtls_md_hmac_update(ctx, data, data_len);
}
static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
{
(void)out_len; /* UNUSED */
mbedtls_md_hmac_finish(ctx, out);
}
static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
{
mbedtls_md_free(ctx);
memset(ctx, 0, sizeof(*ctx));
}
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
static int
@@ -198,6 +248,7 @@ static void
__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
{
unsigned int len = (unsigned int)*out_len;
HMAC_Final(*ctx, out, &len);
*out_len = len;
}
@@ -23,13 +23,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED
#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
/*
* On systems that do not support any recognized crypto libraries,
* the archive_hmac.c file is expected to define no usable symbols.
@@ -64,6 +63,11 @@ typedef struct {
} archive_hmac_sha1_ctx;
#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
#include <mbedtls/md.h>
typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
#include <nettle/hmac.h>
@@ -93,6 +93,9 @@ struct archive_match {
/* exclusion/inclusion set flag. */
int setflag;
/* Recursively include directory content? */
int recursive_include;
/*
* Matching filename patterns.
*/
@@ -223,6 +226,7 @@ archive_match_new(void)
return (NULL);
a->archive.magic = ARCHIVE_MATCH_MAGIC;
a->archive.state = ARCHIVE_STATE_NEW;
a->recursive_include = 1;
match_list_init(&(a->inclusions));
match_list_init(&(a->exclusions));
__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
@@ -470,6 +474,28 @@ archive_match_path_excluded(struct archive *_a,
#endif
}
/*
* When recursive inclusion of directory content is enabled,
* an inclusion pattern that matches a directory will also
* include everything beneath that directory. Enabled by default.
*
* For compatibility with GNU tar, exclusion patterns always
* match if a subset of the full patch matches (i.e., they are
* are not rooted at the beginning of the path) and thus there
* is no corresponding non-recursive exclusion mode.
*/
int
archive_match_set_inclusion_recursion(struct archive *_a, int enabled)
{
struct archive_match *a;
archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
a = (struct archive_match *)_a;
a->recursive_include = enabled;
return (ARCHIVE_OK);
}
/*
* Utility functions to get statistic information for inclusion patterns.
*/
@@ -781,7 +807,10 @@ static int
match_path_inclusion(struct archive_match *a, struct match *m,
int mbs, const void *pn)
{
int flag = PATHMATCH_NO_ANCHOR_END;
/* Recursive operation requires only a prefix match. */
int flag = a->recursive_include ?
PATHMATCH_NO_ANCHOR_END :
0;
int r;
if (mbs) {
@@ -1232,7 +1261,7 @@ set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
archive_set_error(&(a->archive), EINVAL, "pathname is empty");
return (ARCHIVE_FAILED);
}
if (stat(path, &st) != 0) {
if (la_stat(path, &st) != 0) {
archive_set_error(&(a->archive), errno, "Failed to stat()");
return (ARCHIVE_FAILED);
}
@@ -22,9 +22,14 @@
* (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_OPENSSL_EVP_PRIVATE_H_INCLUDED
#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#include <openssl/evp.h>
#include <openssl/opensslv.h>
@@ -22,9 +22,14 @@
* (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_OPENSSL_HMAC_PRIVATE_H_INCLUDED
#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#include <openssl/hmac.h>
#include <openssl/opensslv.h>
@@ -23,6 +23,9 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
@@ -45,3 +48,4 @@ _archive_set_either_option(struct archive *a,
const char *m, const char *o, const char *v,
option_handler use_format_option, option_handler use_filter_option);
#endif
@@ -31,8 +31,8 @@
/* Originally from NetBSD's mknod(8) source. */
#ifndef _PACK_DEV_H
#define _PACK_DEV_H
#ifndef ARCHIVE_PACK_DEV_H
#define ARCHIVE_PACK_DEV_H
typedef dev_t pack_t(int, unsigned long [], const char **);
@@ -46,4 +46,4 @@ pack_t pack_native;
(((y) << 12) & 0xfff00000) | \
(((y) << 0) & 0x000000ff)))
#endif /* _PACK_DEV_H */
#endif /* ARCHIVE_PACK_DEV_H */
@@ -26,15 +26,15 @@
* $FreeBSD$
*/
#ifndef ARCHIVE_PATHMATCH_H
#define ARCHIVE_PATHMATCH_H
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST
#error This header is only to be used internally to libarchive.
#endif
#endif
#ifndef ARCHIVE_PATHMATCH_H
#define ARCHIVE_PATHMATCH_H
/* Don't anchor at beginning unless the pattern starts with "^" */
#define PATHMATCH_NO_ANCHOR_START 1
/* Don't anchor at end unless the pattern ends with "$" */
@@ -69,6 +69,8 @@
* either Windows or Posix APIs. */
#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
#include "archive_windows.h"
#else
#define la_stat(path,stref) stat(path,stref)
#endif
/*
@@ -30,6 +30,12 @@
#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED
#define ARCHIVE_PLATFORM_ACL_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST_COMMON
#error This header is only to be used internally to libarchive.
#endif
#endif
/*
* Determine what ACL types are supported
*/
@@ -30,6 +30,12 @@
#ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED
#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST_COMMON
#error This header is only to be used internally to libarchive.
#endif
#endif
/*
* Determine if we support extended attributes
*/
@@ -1000,7 +1000,7 @@ static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p)
static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total)
{
p->Low += start * (p->Range /= total);
p->Low += (UInt64)start * (UInt64)(p->Range /= total);
p->Range *= size;
while (p->Range < kTopValue)
{
@@ -6,13 +6,13 @@ This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
#include "archive_ppmd_private.h"
#define PPMD7_MIN_ORDER 2
@@ -0,0 +1,1287 @@
/* Ppmd8.c -- PPMdI codec
2016-05-21 : Igor Pavlov : Public domain
This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */
#include "archive_platform.h"
#include <string.h>
#include "archive_ppmd8_private.h"
const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
#define MAX_FREQ 124
#define UNIT_SIZE 12
#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
#define U2I(nu) (p->Units2Indx[(nu) - 1])
#define I2U(indx) (p->Indx2Units[indx])
#ifdef PPMD_32BIT
#define REF(ptr) (ptr)
#else
#define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
#endif
#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))
#define STATS(ctx) Ppmd8_GetStats(p, ctx)
#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)
#define SUFFIX(ctx) CTX((ctx)->Suffix)
#define kTop (1 << 24)
#define kBot (1 << 15)
typedef CPpmd8_Context * CTX_PTR;
struct CPpmd8_Node_;
typedef
#ifdef PPMD_32BIT
struct CPpmd8_Node_ *
#else
UInt32
#endif
CPpmd8_Node_Ref;
typedef struct CPpmd8_Node_
{
UInt32 Stamp;
CPpmd8_Node_Ref Next;
UInt32 NU;
} CPpmd8_Node;
#ifdef PPMD_32BIT
#define NODE(ptr) (ptr)
#else
#define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))
#endif
#define EMPTY_NODE 0xFFFFFFFF
void Ppmd8_Construct(CPpmd8 *p)
{
unsigned i, k, m;
p->Base = 0;
for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
{
unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
do { p->Units2Indx[k++] = (Byte)i; } while (--step);
p->Indx2Units[i] = (Byte)k;
}
p->NS2BSIndx[0] = (0 << 1);
p->NS2BSIndx[1] = (1 << 1);
memset(p->NS2BSIndx + 2, (2 << 1), 9);
memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
for (i = 0; i < 5; i++)
p->NS2Indx[i] = (Byte)i;
for (m = i, k = 1; i < 260; i++)
{
p->NS2Indx[i] = (Byte)m;
if (--k == 0)
k = (++m) - 4;
}
}
void Ppmd8_Free(CPpmd8 *p)
{
free(p->Base);
p->Size = 0;
p->Base = 0;
}
Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
{
if (p->Base == 0 || p->Size != size)
{
Ppmd8_Free(p);
p->AlignOffset =
#ifdef PPMD_32BIT
(4 - size) & 3;
#else
4 - (size & 3);
#endif
if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0)
return False;
p->Size = size;
}
return True;
}
static void InsertNode(CPpmd8 *p, void *node, unsigned indx)
{
((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;
((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];
((CPpmd8_Node *)node)->NU = I2U(indx);
p->FreeList[indx] = REF(node);
p->Stamps[indx]++;
}
static void *RemoveNode(CPpmd8 *p, unsigned indx)
{
CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);
p->FreeList[indx] = node->Next;
p->Stamps[indx]--;
return node;
}
static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
{
unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
ptr = (Byte *)ptr + U2B(I2U(newIndx));
if (I2U(i = U2I(nu)) != nu)
{
unsigned k = I2U(--i);
InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
}
InsertNode(p, ptr, i);
}
static void GlueFreeBlocks(CPpmd8 *p)
{
CPpmd8_Node_Ref head = 0;
CPpmd8_Node_Ref *prev = &head;
unsigned i;
p->GlueCount = 1 << 13;
memset(p->Stamps, 0, sizeof(p->Stamps));
/* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.
All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */
if (p->LoUnit != p->HiUnit)
((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
/* Glue free blocks */
for (i = 0; i < PPMD_NUM_INDEXES; i++)
{
CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];
p->FreeList[i] = 0;
while (next != 0)
{
CPpmd8_Node *node = NODE(next);
if (node->NU != 0)
{
CPpmd8_Node *node2;
*prev = next;
prev = &(node->Next);
while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)
{
node->NU += node2->NU;
node2->NU = 0;
}
}
next = node->Next;
}
}
*prev = 0;
/* Fill lists of free blocks */
while (head != 0)
{
CPpmd8_Node *node = NODE(head);
unsigned nu;
head = node->Next;
nu = node->NU;
if (nu == 0)
continue;
for (; nu > 128; nu -= 128, node += 128)
InsertNode(p, node, PPMD_NUM_INDEXES - 1);
if (I2U(i = U2I(nu)) != nu)
{
unsigned k = I2U(--i);
InsertNode(p, node + k, nu - k - 1);
}
InsertNode(p, node, i);
}
}
static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)
{
unsigned i;
void *retVal;
if (p->GlueCount == 0)
{
GlueFreeBlocks(p);
if (p->FreeList[indx] != 0)
return RemoveNode(p, indx);
}
i = indx;
do
{
if (++i == PPMD_NUM_INDEXES)
{
UInt32 numBytes = U2B(I2U(indx));
p->GlueCount--;
return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
}
}
while (p->FreeList[i] == 0);
retVal = RemoveNode(p, i);
SplitBlock(p, retVal, i, indx);
return retVal;
}
static void *AllocUnits(CPpmd8 *p, unsigned indx)
{
UInt32 numBytes;
if (p->FreeList[indx] != 0)
return RemoveNode(p, indx);
numBytes = U2B(I2U(indx));
if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
{
void *retVal = p->LoUnit;
p->LoUnit += numBytes;
return retVal;
}
return AllocUnitsRare(p, indx);
}
#define MyMem12Cpy(dest, src, num) \
{ UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
{
unsigned i0 = U2I(oldNU);
unsigned i1 = U2I(newNU);
if (i0 == i1)
return oldPtr;
if (p->FreeList[i1] != 0)
{
void *ptr = RemoveNode(p, i1);
MyMem12Cpy(ptr, oldPtr, newNU);
InsertNode(p, oldPtr, i0);
return ptr;
}
SplitBlock(p, oldPtr, i0, i1);
return oldPtr;
}
static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)
{
InsertNode(p, ptr, U2I(nu));
}
static void SpecialFreeUnit(CPpmd8 *p, void *ptr)
{
if ((Byte *)ptr != p->UnitsStart)
InsertNode(p, ptr, 0);
else
{
#ifdef PPMD8_FREEZE_SUPPORT
*(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */
#endif
p->UnitsStart += UNIT_SIZE;
}
}
static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)
{
unsigned indx = U2I(nu);
void *ptr;
if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])
return oldPtr;
ptr = RemoveNode(p, indx);
MyMem12Cpy(ptr, oldPtr, nu);
if ((Byte*)oldPtr != p->UnitsStart)
InsertNode(p, oldPtr, indx);
else
p->UnitsStart += U2B(I2U(indx));
return ptr;
}
static void ExpandTextArea(CPpmd8 *p)
{
UInt32 count[PPMD_NUM_INDEXES];
unsigned i;
memset(count, 0, sizeof(count));
if (p->LoUnit != p->HiUnit)
((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
{
CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;
for (; node->Stamp == EMPTY_NODE; node += node->NU)
{
node->Stamp = 0;
count[U2I(node->NU)]++;
}
p->UnitsStart = (Byte *)node;
}
for (i = 0; i < PPMD_NUM_INDEXES; i++)
{
CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];
while (count[i] != 0)
{
CPpmd8_Node *node = NODE(*next);
while (node->Stamp == 0)
{
*next = node->Next;
node = NODE(*next);
p->Stamps[i]--;
if (--count[i] == 0)
break;
}
next = &node->Next;
}
}
}
#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
{
(p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
(p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
}
#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }
static void RestartModel(CPpmd8 *p)
{
unsigned i, k, m, r;
memset(p->FreeList, 0, sizeof(p->FreeList));
memset(p->Stamps, 0, sizeof(p->Stamps));
RESET_TEXT(0);
p->HiUnit = p->Text + p->Size;
p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
p->GlueCount = 0;
p->OrderFall = p->MaxOrder;
p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
p->PrevSuccess = 0;
p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
p->MinContext->Suffix = 0;
p->MinContext->NumStats = 255;
p->MinContext->Flags = 0;
p->MinContext->SummFreq = 256 + 1;
p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
p->LoUnit += U2B(256 / 2);
p->MinContext->Stats = REF(p->FoundState);
for (i = 0; i < 256; i++)
{
CPpmd_State *s = &p->FoundState[i];
s->Symbol = (Byte)i;
s->Freq = 1;
SetSuccessor(s, 0);
}
for (i = m = 0; m < 25; m++)
{
while (p->NS2Indx[i] == m)
i++;
for (k = 0; k < 8; k++)
{
UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));
UInt16 *dest = p->BinSumm[m] + k;
for (r = 0; r < 64; r += 8)
dest[r] = val;
}
}
for (i = m = 0; m < 24; m++)
{
while (p->NS2Indx[i + 3] == m + 3)
i++;
for (k = 0; k < 32; k++)
{
CPpmd_See *s = &p->See[m][k];
s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));
s->Count = 7;
}
}
}
void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
{
p->MaxOrder = maxOrder;
p->RestoreMethod = restoreMethod;
RestartModel(p);
p->DummySee.Shift = PPMD_PERIOD_BITS;
p->DummySee.Summ = 0; /* unused */
p->DummySee.Count = 64; /* unused */
}
static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)
{
unsigned i = ctx->NumStats, escFreq, sumFreq, flags;
CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);
ctx->Stats = REF(s);
#ifdef PPMD8_FREEZE_SUPPORT
/* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */
scale |= (ctx->SummFreq >= ((UInt32)1 << 15));
#endif
flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);
escFreq = ctx->SummFreq - s->Freq;
sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));
do
{
escFreq -= (++s)->Freq;
sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));
flags |= 0x08 * (s->Symbol >= 0x40);
}
while (--i);
ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));
ctx->Flags = (Byte)flags;
}
static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
{
CPpmd_State tmp = *t1;
*t1 = *t2;
*t2 = tmp;
}
static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)
{
int i;
unsigned tmp;
CPpmd_State *s;
if (!ctx->NumStats)
{
s = ONE_STATE(ctx);
if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)
{
if (order < p->MaxOrder)
SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
else
SetSuccessor(s, 0);
if (SUCCESSOR(s) || order <= 9) /* O_BOUND */
return REF(ctx);
}
SpecialFreeUnit(p, ctx);
return 0;
}
ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));
for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)
if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)
{
CPpmd_State *s2 = STATS(ctx) + (i--);
SetSuccessor(s, 0);
SwapStates(s, s2);
}
else if (order < p->MaxOrder)
SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
else
SetSuccessor(s, 0);
if (i != ctx->NumStats && order)
{
ctx->NumStats = (Byte)i;
s = STATS(ctx);
if (i < 0)
{
FreeUnits(p, s, tmp);
SpecialFreeUnit(p, ctx);
return 0;
}
if (i == 0)
{
ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
*ONE_STATE(ctx) = *s;
FreeUnits(p, s, tmp);
/* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */
ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3);
}
else
Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);
}
return REF(ctx);
}
#ifdef PPMD8_FREEZE_SUPPORT
static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)
{
CPpmd_State *s;
if (!ctx->NumStats)
{
s = ONE_STATE(ctx);
if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
else
SetSuccessor(s, 0);
/* Suffix context can be removed already, since different (high-order)
Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */
if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))
{
FreeUnits(p, ctx, 1);
return 0;
}
else
return REF(ctx);
}
for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)
if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
else
SetSuccessor(s, 0);
return REF(ctx);
}
#endif
static UInt32 GetUsedMemory(const CPpmd8 *p)
{
UInt32 v = 0;
unsigned i;
for (i = 0; i < PPMD_NUM_INDEXES; i++)
v += p->Stamps[i] * I2U(i);
return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);
}
#ifdef PPMD8_FREEZE_SUPPORT
#define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)
#else
#define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)
#endif
static void RestoreModel(CPpmd8 *p, CTX_PTR c1
#ifdef PPMD8_FREEZE_SUPPORT
, CTX_PTR fSuccessor
#endif
)
{
CTX_PTR c;
CPpmd_State *s;
RESET_TEXT(0);
for (c = p->MaxContext; c != c1; c = SUFFIX(c))
if (--(c->NumStats) == 0)
{
s = STATS(c);
c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
*ONE_STATE(c) = *s;
SpecialFreeUnit(p, s);
ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3);
}
else
Refresh(p, c, (c->NumStats+3) >> 1, 0);
for (; c != p->MinContext; c = SUFFIX(c))
if (!c->NumStats)
ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1));
else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)
Refresh(p, c, (c->NumStats + 2) >> 1, 1);
#ifdef PPMD8_FREEZE_SUPPORT
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
{
p->MaxContext = fSuccessor;
p->GlueCount += !(p->Stamps[1] & 1);
}
else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)
{
while (p->MaxContext->Suffix)
p->MaxContext = SUFFIX(p->MaxContext);
RemoveBinContexts(p, p->MaxContext, 0);
p->RestoreMethod++;
p->GlueCount = 0;
p->OrderFall = p->MaxOrder;
}
else
#endif
if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))
RestartModel(p);
else
{
while (p->MaxContext->Suffix)
p->MaxContext = SUFFIX(p->MaxContext);
do
{
CutOff(p, p->MaxContext, 0);
ExpandTextArea(p);
}
while (GetUsedMemory(p) > 3 * (p->Size >> 2));
p->GlueCount = 0;
p->OrderFall = p->MaxOrder;
}
}
static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)
{
CPpmd_State upState;
Byte flags;
CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
/* fixed over Shkarin's code. Maybe it could work without + 1 too. */
CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
unsigned numPs = 0;
if (!skip)
ps[numPs++] = p->FoundState;
while (c->Suffix)
{
CPpmd_Void_Ref successor;
CPpmd_State *s;
c = SUFFIX(c);
if (s1)
{
s = s1;
s1 = NULL;
}
else if (c->NumStats != 0)
{
for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
if (s->Freq < MAX_FREQ - 9)
{
s->Freq++;
c->SummFreq++;
}
}
else
{
s = ONE_STATE(c);
s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));
}
successor = SUCCESSOR(s);
if (successor != upBranch)
{
c = CTX(successor);
if (numPs == 0)
return c;
break;
}
ps[numPs++] = s;
}
upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);
SetSuccessor(&upState, upBranch + 1);
flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40));
if (c->NumStats == 0)
upState.Freq = ONE_STATE(c)->Freq;
else
{
UInt32 cf, s0;
CPpmd_State *s;
for (s = STATS(c); s->Symbol != upState.Symbol; s++);
cf = s->Freq - 1;
s0 = c->SummFreq - c->NumStats - cf;
upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));
}
do
{
/* Create Child */
CTX_PTR c1; /* = AllocContext(p); */
if (p->HiUnit != p->LoUnit)
c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
else if (p->FreeList[0] != 0)
c1 = (CTX_PTR)RemoveNode(p, 0);
else
{
c1 = (CTX_PTR)AllocUnitsRare(p, 0);
if (!c1)
return NULL;
}
c1->NumStats = 0;
c1->Flags = flags;
*ONE_STATE(c1) = upState;
c1->Suffix = REF(c);
SetSuccessor(ps[--numPs], REF(c1));
c = c1;
}
while (numPs != 0);
return c;
}
static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)
{
CPpmd_State *s = NULL;
CTX_PTR c1 = c;
CPpmd_Void_Ref upBranch = REF(p->Text);
#ifdef PPMD8_FREEZE_SUPPORT
/* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */
CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
unsigned numPs = 0;
ps[numPs++] = p->FoundState;
#endif
SetSuccessor(p->FoundState, upBranch);
p->OrderFall++;
for (;;)
{
if (s1)
{
c = SUFFIX(c);
s = s1;
s1 = NULL;
}
else
{
if (!c->Suffix)
{
#ifdef PPMD8_FREEZE_SUPPORT
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
{
do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
RESET_TEXT(1);
p->OrderFall = 1;
}
#endif
return c;
}
c = SUFFIX(c);
if (c->NumStats)
{
if ((s = STATS(c))->Symbol != p->FoundState->Symbol)
do { s++; } while (s->Symbol != p->FoundState->Symbol);
if (s->Freq < MAX_FREQ - 9)
{
s->Freq += 2;
c->SummFreq += 2;
}
}
else
{
s = ONE_STATE(c);
s->Freq = (Byte)(s->Freq + (s->Freq < 32));
}
}
if (SUCCESSOR(s))
break;
#ifdef PPMD8_FREEZE_SUPPORT
ps[numPs++] = s;
#endif
SetSuccessor(s, upBranch);
p->OrderFall++;
}
#ifdef PPMD8_FREEZE_SUPPORT
if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
{
c = CTX(SUCCESSOR(s));
do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
RESET_TEXT(1);
p->OrderFall = 1;
return c;
}
else
#endif
if (SUCCESSOR(s) <= upBranch)
{
CTX_PTR successor;
CPpmd_State *s2 = p->FoundState;
p->FoundState = s;
successor = CreateSuccessors(p, False, NULL, c);
if (successor == NULL)
SetSuccessor(s, 0);
else
SetSuccessor(s, REF(successor));
p->FoundState = s2;
}
if (p->OrderFall == 1 && c1 == p->MaxContext)
{
SetSuccessor(p->FoundState, SUCCESSOR(s));
p->Text--;
}
if (SUCCESSOR(s) == 0)
return NULL;
return CTX(SUCCESSOR(s));
}
static void UpdateModel(CPpmd8 *p)
{
CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
CTX_PTR c;
unsigned s0, ns, fFreq = p->FoundState->Freq;
Byte flag, fSymbol = p->FoundState->Symbol;
CPpmd_State *s = NULL;
if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
{
c = SUFFIX(p->MinContext);
if (c->NumStats == 0)
{
s = ONE_STATE(c);
if (s->Freq < 32)
s->Freq++;
}
else
{
s = STATS(c);
if (s->Symbol != p->FoundState->Symbol)
{
do { s++; } while (s->Symbol != p->FoundState->Symbol);
if (s[0].Freq >= s[-1].Freq)
{
SwapStates(&s[0], &s[-1]);
s--;
}
}
if (s->Freq < MAX_FREQ - 9)
{
s->Freq += 2;
c->SummFreq += 2;
}
}
}
c = p->MaxContext;
if (p->OrderFall == 0 && fSuccessor)
{
CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);
if (cs == 0)
{
SetSuccessor(p->FoundState, 0);
RESTORE_MODEL(c, CTX(fSuccessor));
}
else
{
SetSuccessor(p->FoundState, REF(cs));
p->MaxContext = cs;
}
return;
}
*p->Text++ = p->FoundState->Symbol;
successor = REF(p->Text);
if (p->Text >= p->UnitsStart)
{
RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */
return;
}
if (!fSuccessor)
{
CTX_PTR cs = ReduceOrder(p, s, p->MinContext);
if (cs == NULL)
{
RESTORE_MODEL(c, 0);
return;
}
fSuccessor = REF(cs);
}
else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)
{
CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);
if (cs == NULL)
{
RESTORE_MODEL(c, 0);
return;
}
fSuccessor = REF(cs);
}
if (--p->OrderFall == 0)
{
successor = fSuccessor;
p->Text -= (p->MaxContext != p->MinContext);
}
#ifdef PPMD8_FREEZE_SUPPORT
else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
{
successor = fSuccessor;
RESET_TEXT(0);
p->OrderFall = 0;
}
#endif
s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;
flag = (Byte)(0x08 * (fSymbol >= 0x40));
for (; c != p->MinContext; c = SUFFIX(c))
{
unsigned ns1;
UInt32 cf, sf;
if ((ns1 = c->NumStats) != 0)
{
if ((ns1 & 1) != 0)
{
/* Expand for one UNIT */
unsigned oldNU = (ns1 + 1) >> 1;
unsigned i = U2I(oldNU);
if (i != U2I(oldNU + 1))
{
void *ptr = AllocUnits(p, i + 1);
void *oldPtr;
if (!ptr)
{
RESTORE_MODEL(c, CTX(fSuccessor));
return;
}
oldPtr = STATS(c);
MyMem12Cpy(ptr, oldPtr, oldNU);
InsertNode(p, oldPtr, i);
c->Stats = STATS_REF(ptr);
}
}
c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));
}
else
{
CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0);
if (!s2)
{
RESTORE_MODEL(c, CTX(fSuccessor));
return;
}
*s2 = *ONE_STATE(c);
c->Stats = REF(s2);
if (s2->Freq < MAX_FREQ / 4 - 1)
s2->Freq <<= 1;
else
s2->Freq = MAX_FREQ - 4;
c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2));
}
cf = 2 * fFreq * (c->SummFreq + 6);
sf = (UInt32)s0 + c->SummFreq;
if (cf < 6 * sf)
{
cf = 1 + (cf > sf) + (cf >= 4 * sf);
c->SummFreq += 4;
}
else
{
cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);
c->SummFreq = (UInt16)(c->SummFreq + cf);
}
{
CPpmd_State *s2 = STATS(c) + ns1 + 1;
SetSuccessor(s2, successor);
s2->Symbol = fSymbol;
s2->Freq = (Byte)cf;
c->Flags |= flag;
c->NumStats = (Byte)(ns1 + 1);
}
}
p->MaxContext = p->MinContext = CTX(fSuccessor);
}
static void Rescale(CPpmd8 *p)
{
unsigned i, adder, sumFreq, escFreq;
CPpmd_State *stats = STATS(p->MinContext);
CPpmd_State *s = p->FoundState;
{
CPpmd_State tmp = *s;
for (; s != stats; s--)
s[0] = s[-1];
*s = tmp;
}
escFreq = p->MinContext->SummFreq - s->Freq;
s->Freq += 4;
adder = (p->OrderFall != 0
#ifdef PPMD8_FREEZE_SUPPORT
|| p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE
#endif
);
s->Freq = (Byte)((s->Freq + adder) >> 1);
sumFreq = s->Freq;
i = p->MinContext->NumStats;
do
{
escFreq -= (++s)->Freq;
s->Freq = (Byte)((s->Freq + adder) >> 1);
sumFreq += s->Freq;
if (s[0].Freq > s[-1].Freq)
{
CPpmd_State *s1 = s;
CPpmd_State tmp = *s1;
do
s1[0] = s1[-1];
while (--s1 != stats && tmp.Freq > s1[-1].Freq);
*s1 = tmp;
}
}
while (--i);
if (s->Freq == 0)
{
unsigned numStats = p->MinContext->NumStats;
unsigned n0, n1;
do { i++; } while ((--s)->Freq == 0);
escFreq += i;
p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);
if (p->MinContext->NumStats == 0)
{
CPpmd_State tmp = *stats;
tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);
if (tmp.Freq > MAX_FREQ / 3)
tmp.Freq = MAX_FREQ / 3;
InsertNode(p, stats, U2I((numStats + 2) >> 1));
p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40));
*(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
return;
}
n0 = (numStats + 2) >> 1;
n1 = (p->MinContext->NumStats + 2) >> 1;
if (n0 != n1)
p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
p->MinContext->Flags &= ~0x08;
p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);
i = p->MinContext->NumStats;
do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);
}
p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
p->MinContext->Flags |= 0x4;
p->FoundState = STATS(p->MinContext);
}
CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
{
CPpmd_See *see;
if (p->MinContext->NumStats != 0xFF)
{
see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] +
(p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) +
2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats <
((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) +
p->MinContext->Flags;
{
unsigned r = (see->Summ >> see->Shift);
see->Summ = (UInt16)(see->Summ - r);
*escFreq = r + (r == 0);
}
}
else
{
see = &p->DummySee;
*escFreq = 1;
}
return see;
}
static void NextContext(CPpmd8 *p)
{
CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)
p->MinContext = p->MaxContext = c;
else
{
UpdateModel(p);
p->MinContext = p->MaxContext;
}
}
void Ppmd8_Update1(CPpmd8 *p)
{
CPpmd_State *s = p->FoundState;
s->Freq += 4;
p->MinContext->SummFreq += 4;
if (s[0].Freq > s[-1].Freq)
{
SwapStates(&s[0], &s[-1]);
p->FoundState = --s;
if (s->Freq > MAX_FREQ)
Rescale(p);
}
NextContext(p);
}
void Ppmd8_Update1_0(CPpmd8 *p)
{
p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);
p->RunLength += p->PrevSuccess;
p->MinContext->SummFreq += 4;
if ((p->FoundState->Freq += 4) > MAX_FREQ)
Rescale(p);
NextContext(p);
}
void Ppmd8_UpdateBin(CPpmd8 *p)
{
p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));
p->PrevSuccess = 1;
p->RunLength++;
NextContext(p);
}
void Ppmd8_Update2(CPpmd8 *p)
{
p->MinContext->SummFreq += 4;
if ((p->FoundState->Freq += 4) > MAX_FREQ)
Rescale(p);
p->RunLength = p->InitRL;
UpdateModel(p);
p->MinContext = p->MaxContext;
}
/* Ppmd8Dec.c -- PPMdI Decoder
2010-04-16 : Igor Pavlov : Public domain
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)
{
unsigned i;
p->Low = 0;
p->Range = 0xFFFFFFFF;
p->Code = 0;
for (i = 0; i < 4; i++)
p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
return (p->Code < 0xFFFFFFFF);
}
static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total)
{
return p->Code / (p->Range /= total);
}
static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)
{
start *= p->Range;
p->Low += start;
p->Code -= start;
p->Range *= size;
while ((p->Low ^ (p->Low + p->Range)) < kTop ||
(p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1)))
{
p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
p->Range <<= 8;
p->Low <<= 8;
}
}
#define MASK(sym) ((signed char *)charMask)[sym]
int Ppmd8_DecodeSymbol(CPpmd8 *p)
{
size_t charMask[256 / sizeof(size_t)];
if (p->MinContext->NumStats != 0)
{
CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext);
unsigned i;
UInt32 count, hiCnt;
if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
{
Byte symbol;
RangeDec_Decode(p, 0, s->Freq);
p->FoundState = s;
symbol = s->Symbol;
Ppmd8_Update1_0(p);
return symbol;
}
p->PrevSuccess = 0;
i = p->MinContext->NumStats;
do
{
if ((hiCnt += (++s)->Freq) > count)
{
Byte symbol;
RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
p->FoundState = s;
symbol = s->Symbol;
Ppmd8_Update1(p);
return symbol;
}
}
while (--i);
if (count >= p->MinContext->SummFreq)
return -2;
RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt);
PPMD_SetAllBitsIn256Bytes(charMask);
MASK(s->Symbol) = 0;
i = p->MinContext->NumStats;
do { MASK((--s)->Symbol) = 0; } while (--i);
}
else
{
UInt16 *prob = Ppmd8_GetBinSumm(p);
if (((p->Code / (p->Range >>= 14)) < *prob))
{
Byte symbol;
RangeDec_Decode(p, 0, *prob);
*prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol;
Ppmd8_UpdateBin(p);
return symbol;
}
RangeDec_Decode(p, *prob, (1 << 14) - *prob);
*prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
p->InitEsc = PPMD8_kExpEscape[*prob >> 10];
PPMD_SetAllBitsIn256Bytes(charMask);
MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0;
p->PrevSuccess = 0;
}
for (;;)
{
CPpmd_State *ps[256], *s;
UInt32 freqSum, count, hiCnt;
CPpmd_See *see;
unsigned i, num, numMasked = p->MinContext->NumStats;
do
{
p->OrderFall++;
if (!p->MinContext->Suffix)
return -1;
p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix);
}
while (p->MinContext->NumStats == numMasked);
hiCnt = 0;
s = Ppmd8_GetStats(p, p->MinContext);
i = 0;
num = p->MinContext->NumStats - numMasked;
do
{
int k = (int)(MASK(s->Symbol));
hiCnt += (s->Freq & k);
ps[i] = s++;
i -= k;
}
while (i != num);
see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum);
freqSum += hiCnt;
count = RangeDec_GetThreshold(p, freqSum);
if (count < hiCnt)
{
Byte symbol;
CPpmd_State **pps = ps;
for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
s = *pps;
RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
Ppmd_See_Update(see);
p->FoundState = s;
symbol = s->Symbol;
Ppmd8_Update2(p);
return symbol;
}
if (count >= freqSum)
return -2;
RangeDec_Decode(p, hiCnt, freqSum - hiCnt);
see->Summ = (UInt16)(see->Summ + freqSum);
do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
}
}
/* H->I changes:
NS2Indx
GlewCount, and Glue method
BinSum
See / EscFreq
CreateSuccessors updates more suffix contexts
UpdateModel consts.
PrevSuccess Update
*/
const IPpmd8 __archive_ppmd8_functions =
{
&Ppmd8_Construct,
&Ppmd8_Alloc,
&Ppmd8_Free,
&Ppmd8_Init,
&Ppmd8_RangeDec_Init,
&Ppmd8_DecodeSymbol,
};
@@ -0,0 +1,148 @@
/* Ppmd8.h -- PPMdI codec
2011-01-27 : Igor Pavlov : Public domain
This code is based on:
PPMd var.I (2002): Dmitry Shkarin : Public domain
Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
#include "archive_ppmd_private.h"
#define PPMD8_MIN_ORDER 2
#define PPMD8_MAX_ORDER 16
struct CPpmd8_Context_;
typedef
#ifdef PPMD_32BIT
struct CPpmd8_Context_ *
#else
UInt32
#endif
CPpmd8_Context_Ref;
#pragma pack(push, 1)
typedef struct CPpmd8_Context_
{
Byte NumStats;
Byte Flags;
UInt16 SummFreq;
CPpmd_State_Ref Stats;
CPpmd8_Context_Ref Suffix;
} CPpmd8_Context;
#pragma pack(pop)
#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed
code is not compatible with original code for some files compressed
in FREEZE mode. So we disable FREEZE mode support. */
enum
{
PPMD8_RESTORE_METHOD_RESTART,
PPMD8_RESTORE_METHOD_CUT_OFF
#ifdef PPMD8_FREEZE_SUPPORT
, PPMD8_RESTORE_METHOD_FREEZE
#endif
};
typedef struct
{
CPpmd8_Context *MinContext, *MaxContext;
CPpmd_State *FoundState;
unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder;
Int32 RunLength, InitRL; /* must be 32-bit at least */
UInt32 Size;
UInt32 GlueCount;
Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
UInt32 AlignOffset;
unsigned RestoreMethod;
/* Range Coder */
UInt32 Range;
UInt32 Code;
UInt32 Low;
union
{
IByteIn *In;
IByteOut *Out;
} Stream;
Byte Indx2Units[PPMD_NUM_INDEXES];
Byte Units2Indx[128];
CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
UInt32 Stamps[PPMD_NUM_INDEXES];
Byte NS2BSIndx[256], NS2Indx[260];
CPpmd_See DummySee, See[24][32];
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 ---------- */
extern const Byte PPMD8_kExpEscape[16];
#ifdef PPMD_32BIT
#define Ppmd8_GetPtr(p, ptr) (ptr)
#define Ppmd8_GetContext(p, ptr) (ptr)
#define Ppmd8_GetStats(p, ctx) ((ctx)->Stats)
#else
#define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
#define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs)))
#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
{
/* Base Functions */
void (*Ppmd8_Construct)(CPpmd8 *p);
Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size);
void (*Ppmd8_Free)(CPpmd8 *p);
void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method);
#define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
/* Decode Functions */
int (*Ppmd8_RangeDec_Init)(CPpmd8 *p);
int (*Ppmd8_DecodeSymbol)(CPpmd8 *p);
} IPpmd8;
extern const IPpmd8 __archive_ppmd8_functions;
#endif
@@ -2,13 +2,13 @@
2010-03-12 : Igor Pavlov : Public domain
This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED
#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED
#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED
#include <stddef.h>
#include "archive_read_private.h"
@@ -25,13 +25,13 @@
* $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $
*/
#ifndef ARCHIVE_PRIVATE_H_INCLUDED
#define ARCHIVE_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_PRIVATE_H_INCLUDED
#define ARCHIVE_PRIVATE_H_INCLUDED
#if HAVE_ICONV_H
#include <iconv.h>
#endif
@@ -153,6 +153,11 @@ void __archive_errx(int retvalue, const char *msg) __LA_DEAD;
void __archive_ensure_cloexec_flag(int fd);
int __archive_mktemp(const char *tmpdir);
#if defined(_WIN32) && !defined(__CYGWIN__)
int __archive_mkstemp(wchar_t *template);
#else
int __archive_mkstemp(char *template);
#endif
int __archive_clean(struct archive *);
@@ -23,13 +23,13 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
/* Random number generator. */
int archive_random(void *buf, size_t nbytes);
+17 -4
View File
@@ -28,8 +28,9 @@
*
* Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp
*/
#ifndef ARCHIVE_RB_H_
#define ARCHIVE_RB_H_
#ifndef ARCHIVE_RB_H_INCLUDED
#define ARCHIVE_RB_H_INCLUDED
struct archive_rb_node {
struct archive_rb_node *rb_nodes[2];
@@ -48,12 +49,24 @@ struct archive_rb_node {
__archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT)
#define ARCHIVE_RB_TREE_MAX(T) \
__archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT)
#define ARCHIVE_RB_TREE_NEXT(T, N) \
__archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)
#define ARCHIVE_RB_TREE_PREV(T, N) \
__archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)
#define ARCHIVE_RB_TREE_FOREACH(N, T) \
for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \
(N) = __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT))
(N) = ARCHIVE_RB_TREE_NEXT((T), (N)))
#define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \
for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \
(N) = __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT))
(N) = ARCHIVE_RB_TREE_PREV((T), (N)))
#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \
for ((N) = ARCHIVE_RB_TREE_MIN(T); \
(N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \
(N) = (S))
#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
for ((N) = ARCHIVE_RB_TREE_MAX(T); \
(N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \
(N) = (S))
/*
* archive_rbto_compare_nodes_fn:
@@ -155,7 +155,7 @@ to close the archive, then call
.Fn archive_read_free
to release all resources, including all memory allocated by the library.
.\"
.Sh EXAMPLE
.Sh EXAMPLES
The following illustrates basic usage of the library.
In this example,
the callback functions are simply wrappers around the standard
@@ -217,16 +217,16 @@ myclose(struct archive *a, void *client_data)
.\" .Sh ERRORS
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read_new 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_extract 3 ,
.Xr archive_read_filter 3 ,
.Xr archive_read_format 3 ,
.Xr archive_read_header 3 ,
.Xr archive_read_new 3 ,
.Xr archive_read_open 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
.Sh HISTORY
The
@@ -433,7 +433,7 @@ archive_read_add_callback_data(struct archive *_a, void *client_data,
return ARCHIVE_FATAL;
}
a->client.dataset = (struct archive_read_data_node *)p;
for (i = a->client.nodes - 1; i > iindex && i > 0; i--) {
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;
a->client.dataset[i].total_size = -1;
@@ -611,6 +611,15 @@ choose_filters(struct archive_read *a)
return (ARCHIVE_FATAL);
}
int
__archive_read_header(struct archive_read *a, struct archive_entry *entry)
{
if (a->filter->read_header)
return a->filter->read_header(a->filter, entry);
else
return (ARCHIVE_OK);
}
/*
* Read header of next entry.
*/
@@ -835,7 +844,8 @@ archive_read_data(struct archive *_a, void *buff, size_t s)
dest = (char *)buff;
while (s > 0) {
if (a->read_data_remaining == 0) {
if (a->read_data_offset == a->read_data_output_offset &&
a->read_data_remaining == 0) {
read_buf = a->read_data_block;
a->read_data_is_posix_read = 1;
a->read_data_requested = s;
@@ -59,16 +59,16 @@ or empty, this function will do nothing and
will be returned.
Otherwise,
.Cm ARCHIVE_OK
will be returned.
will be returned.
.It Fn archive_read_set_passphrase_callback
Register callback function that will be invoked to get a passphrase
for decrption after trying all passphrases registered by the
Register a callback function that will be invoked to get a passphrase
for decryption after trying all the passphrases registered by the
.Fn archive_read_add_passphrase
function failed.
.El
.\" .Sh ERRORS
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_set_options 3
.Xr archive_read_set_options 3 ,
.Xr libarchive 3
@@ -28,7 +28,7 @@
.Dt ARCHIVE_READ_DATA 3
.Os
.Sh NAME
.Nm archive_read_data
.Nm archive_read_data ,
.Nm archive_read_data_block ,
.Nm archive_read_data_skip ,
.Nm archive_read_data_into_fd
@@ -118,7 +118,6 @@ functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_extract 3 ,
.Xr archive_read_filter 3 ,
@@ -127,4 +126,5 @@ functions.
.Xr archive_read_open 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
@@ -99,9 +99,10 @@ following values:
.Bl -tag -compact -width "indent"
.It Cm ARCHIVE_READDISK_HONOR_NODUMP
Skip files and directories with the nodump file attribute (file flag) set.
By default, the nodump file atrribute is ignored.
By default, the nodump file attribute is ignored.
.It Cm ARCHIVE_READDISK_MAC_COPYFILE
Mac OS X specific. Read metadata (ACLs and extended attributes) with
Mac OS X specific.
Read metadata (ACLs and extended attributes) with
.Xr copyfile 3 .
By default, metadata is read using
.Xr copyfile 3 .
@@ -120,7 +121,7 @@ or
for more information on file attributes.
.It Cm ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS
Do not traverse mount points.
By defaut, moint points are traversed.
By default, mount points are traversed.
.It Cm ARCHIVE_READDISK_NO_XATTR
Do not read extended file attributes (xattrs).
By default, extended file attributes are read from disk.
@@ -216,7 +217,7 @@ of some other operation.
(For example, directory traversal libraries often provide this information.)
.Pp
Where necessary, user and group ids are converted to user and group names
using the currently registered lookup functions above.
using the currently-registered lookup functions above.
This affects the file ownership fields and ACL values in the
.Tn struct archive_entry
object.
@@ -226,7 +227,7 @@ More information about the
object and the overall design of the library can be found in the
.Xr libarchive 3
overview.
.Sh EXAMPLE
.Sh EXAMPLES
The following illustrates basic usage of the library by
showing how to use it to copy an item on disk into an archive.
.Bd -literal -offset indent
@@ -291,11 +292,11 @@ and
functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr archive_read 3 ,
.Xr archive_util 3 ,
.Xr archive_write 3 ,
.Xr archive_write_disk 3 ,
.Xr tar 1 ,
.Xr libarchive 3
.Sh HISTORY
The
@@ -163,6 +163,9 @@ archive_read_disk_entry_from_file(struct archive *_a,
int initial_fd = fd;
int r, r1;
archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
"archive_read_disk_entry_from_file");
archive_clear_error(_a);
path = archive_entry_sourcepath(entry);
if (path == NULL)
@@ -188,7 +191,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
}
} else
#endif
if (stat(path, &s) != 0) {
if (la_stat(path, &s) != 0) {
archive_set_error(&a->archive, errno,
"Can't stat %s", path);
return (ARCHIVE_FAILED);
@@ -246,11 +249,11 @@ archive_read_disk_entry_from_file(struct archive *_a,
#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
if (S_ISLNK(st->st_mode)) {
size_t linkbuffer_len = st->st_size + 1;
size_t linkbuffer_len = st->st_size;
char *linkbuffer;
int lnklen;
linkbuffer = malloc(linkbuffer_len);
linkbuffer = malloc(linkbuffer_len + 1);
if (linkbuffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Couldn't read link data");
@@ -277,7 +280,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
free(linkbuffer);
return (ARCHIVE_FAILED);
}
linkbuffer[lnklen] = 0;
linkbuffer[lnklen] = '\0';
archive_entry_set_symlink(entry, linkbuffer);
free(linkbuffer);
}
@@ -694,6 +694,7 @@ _archive_read_data_block(struct archive *_a, const void **buff,
struct tree *t = a->tree;
int r;
ssize_t bytes;
int64_t sparse_bytes;
size_t buffbytes;
int empty_sparse_region = 0;
@@ -728,27 +729,23 @@ _archive_read_data_block(struct archive *_a, const void **buff,
if ((t->flags & needsRestoreTimes) != 0 &&
t->restore_time.noatime == 0)
flags |= O_NOATIME;
do {
#endif
t->entry_fd = open_on_current_dir(t,
tree_current_access_path(t), flags);
__archive_ensure_cloexec_flag(t->entry_fd);
t->entry_fd = open_on_current_dir(t,
tree_current_access_path(t), flags);
__archive_ensure_cloexec_flag(t->entry_fd);
#if defined(O_NOATIME)
/*
* When we did open the file with O_NOATIME flag,
* if successful, set 1 to t->restore_time.noatime
* not to restore an atime of the file later.
* if failed by EPERM, retry it without O_NOATIME flag.
*/
if (flags & O_NOATIME) {
if (t->entry_fd >= 0)
t->restore_time.noatime = 1;
else if (errno == EPERM) {
flags &= ~O_NOATIME;
continue;
}
}
} while (0);
/*
* When we did open the file with O_NOATIME flag,
* if successful, set 1 to t->restore_time.noatime
* not to restore an atime of the file later.
* if failed by EPERM, retry it without O_NOATIME flag.
*/
if (flags & O_NOATIME) {
if (t->entry_fd >= 0)
t->restore_time.noatime = 1;
else if (errno == EPERM)
flags &= ~O_NOATIME;
}
#endif
if (t->entry_fd < 0) {
archive_set_error(&a->archive, errno,
@@ -792,9 +789,9 @@ _archive_read_data_block(struct archive *_a, const void **buff,
a->archive.state = ARCHIVE_STATE_FATAL;
goto abort_read_data;
}
bytes = t->current_sparse->offset - t->entry_total;
t->entry_remaining_bytes -= bytes;
t->entry_total += bytes;
sparse_bytes = t->current_sparse->offset - t->entry_total;
t->entry_remaining_bytes -= sparse_bytes;
t->entry_total += sparse_bytes;
}
/*
@@ -856,7 +853,12 @@ next_entry(struct archive_read_disk *a, struct tree *t,
const struct stat *st; /* info to use for this entry */
const struct stat *lst;/* lstat() information */
const char *name;
int descend, r;
int delayed, delayed_errno, descend, r;
struct archive_string delayed_str;
delayed = ARCHIVE_OK;
delayed_errno = 0;
archive_string_init(&delayed_str);
st = NULL;
lst = NULL;
@@ -885,14 +887,26 @@ next_entry(struct archive_read_disk *a, struct tree *t,
case TREE_REGULAR:
lst = tree_current_lstat(t);
if (lst == NULL) {
if (errno == ENOENT && t->depth > 0) {
delayed = ARCHIVE_WARN;
delayed_errno = errno;
if (delayed_str.length == 0) {
archive_string_sprintf(&delayed_str,
"%s", tree_current_path(t));
} else {
archive_string_sprintf(&delayed_str,
" %s", tree_current_path(t));
}
} else {
archive_set_error(&a->archive, errno,
"%s: Cannot stat",
tree_current_path(t));
tree_enter_initial_dir(t);
return (ARCHIVE_FAILED);
}
}
break;
}
}
} while (lst == NULL);
#ifdef __APPLE__
@@ -1083,6 +1097,17 @@ next_entry(struct archive_read_disk *a, struct tree *t,
r = archive_read_disk_entry_from_file(&(a->archive), entry,
t->entry_fd, st);
if (r == ARCHIVE_OK) {
r = delayed;
if (r != ARCHIVE_OK) {
archive_string_sprintf(&delayed_str, ": %s",
"File removed before we read it");
archive_set_error(&(a->archive), delayed_errno,
"%s", delayed_str.s);
}
}
archive_string_free(&delayed_str);
return (r);
}
@@ -1114,6 +1139,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
t->entry_fd = -1;
}
archive_entry_clear(entry);
for (;;) {
r = next_entry(a, t, entry);
if (t->entry_fd >= 0) {
@@ -1266,10 +1293,23 @@ archive_read_disk_descend(struct archive *_a)
if (t->visit_type != TREE_REGULAR || !t->descend)
return (ARCHIVE_OK);
/*
* We must not treat the initial specified path as a physical dir,
* because if we do then we will try and ascend out of it by opening
* ".." which is (a) wrong and (b) causes spurious permissions errors
* if ".." is not readable by us. Instead, treat it as if it were a
* symlink. (This uses an extra fd, but it can only happen once at the
* top level of a traverse.) But we can't necessarily assume t->st is
* valid here (though t->lst is), which complicates the logic a
* little.
*/
if (tree_current_is_physical_dir(t)) {
tree_push(t, t->basename, t->current_filesystem_id,
t->lst.st_dev, t->lst.st_ino, &t->restore_time);
t->stack->flags |= isDir;
if (t->stack->parent->parent != NULL)
t->stack->flags |= isDir;
else
t->stack->flags |= isDirLink;
} else if (tree_current_is_dir(t)) {
tree_push(t, t->basename, t->current_filesystem_id,
t->st.st_dev, t->st.st_ino, &t->restore_time);
@@ -2122,6 +2162,17 @@ tree_open(const char *path, int symlink_mode, int restore_time)
static struct tree *
tree_reopen(struct tree *t, const char *path, int restore_time)
{
#if defined(O_PATH)
/* Linux */
const int o_flag = O_PATH;
#elif defined(O_SEARCH)
/* SunOS */
const int o_flag = O_SEARCH;
#elif defined(__FreeBSD__) && defined(O_EXEC)
/* FreeBSD */
const int o_flag = O_EXEC;
#endif
t->flags = (restore_time != 0)?needsRestoreTimes:0;
t->flags |= onInitialDir;
t->visit_type = 0;
@@ -2143,6 +2194,16 @@ tree_reopen(struct tree *t, const char *path, int restore_time)
t->stack->flags = needsFirstVisit;
t->maxOpenCount = t->openCount = 1;
t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);
#if defined(O_PATH) || defined(O_SEARCH) || \
(defined(__FreeBSD__) && defined(O_EXEC))
/*
* Most likely reason to fail opening "." is that it's not readable,
* so try again for execute. The consequences of not opening this are
* unhelpful and unnecessary errors later.
*/
if (t->initial_dir_fd < 0)
t->initial_dir_fd = open(".", o_flag | O_CLOEXEC);
#endif
__archive_ensure_cloexec_flag(t->initial_dir_fd);
t->working_dir_fd = tree_dup(t->initial_dir_fd);
return (t);
@@ -2450,7 +2511,7 @@ tree_current_stat(struct tree *t)
#else
if (tree_enter_working_dir(t) != 0)
return NULL;
if (stat(tree_current_access_path(t), &t->st) != 0)
if (la_stat(tree_current_access_path(t), &t->st) != 0)
#endif
return NULL;
t->flags |= hasStat;
@@ -26,13 +26,13 @@
* $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $
*/
#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
#include "archive_platform_acl.h"
struct tree;
@@ -299,8 +299,155 @@ static int close_and_restore_time(HANDLE, struct tree *,
struct restore_time *);
static int setup_sparse_from_disk(struct archive_read_disk *,
struct archive_entry *, HANDLE);
static int la_linkname_from_handle(HANDLE, wchar_t **, int *);
static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *);
static void entry_symlink_from_pathw(struct archive_entry *,
const wchar_t *path);
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
/*
* Reads the target of a symbolic link
*
* Returns 0 on success and -1 on failure
* outbuf is allocated in the function
*/
static int
la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype)
{
DWORD inbytes;
REPARSE_DATA_BUFFER *buf;
BY_HANDLE_FILE_INFORMATION st;
size_t len;
BOOL ret;
BYTE *indata;
wchar_t *tbuf;
ret = GetFileInformationByHandle(h, &st);
if (ret == 0 ||
(st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
return (-1);
}
indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
1024, &inbytes, NULL);
if (ret == 0) {
la_dosmaperr(GetLastError());
free(indata);
return (-1);
}
buf = (REPARSE_DATA_BUFFER *) indata;
if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
free(indata);
/* File is not a symbolic link */
errno = EINVAL;
return (-1);
}
len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
if (len <= 0) {
free(indata);
return (-1);
}
tbuf = malloc(len + 1 * sizeof(wchar_t));
if (tbuf == NULL) {
free(indata);
return (-1);
}
memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
[buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
free(indata);
tbuf[len / sizeof(wchar_t)] = L'\0';
*linkname = tbuf;
/*
* Translate backslashes to slashes for libarchive internal use
*/
while(*tbuf != L'\0') {
if (*tbuf == L'\\')
*tbuf = L'/';
tbuf++;
}
if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
*linktype = AE_SYMLINK_TYPE_FILE;
else
*linktype = AE_SYMLINK_TYPE_DIRECTORY;
return (0);
}
/*
* Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error
*/
static int
la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
{
HANDLE h;
const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OPEN_REPARSE_POINT;
int ret;
h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag,
NULL);
if (h == INVALID_HANDLE_VALUE) {
la_dosmaperr(GetLastError());
return (-1);
}
ret = la_linkname_from_handle(h, outbuf, linktype);
CloseHandle(h);
return (ret);
}
static void
entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)
{
wchar_t *linkname = NULL;
int ret, linktype;
ret = la_linkname_from_pathw(path, &linkname, &linktype);
if (ret != 0)
return;
if (linktype >= 0) {
archive_entry_copy_symlink_w(entry, linkname);
archive_entry_set_symlink_type(entry, linktype);
}
free(linkname);
return;
}
static struct archive_vtable *
archive_read_disk_vtable(void)
@@ -899,6 +1046,19 @@ next_entry(struct archive_read_disk *a, struct tree *t,
}
}
/*
* File attributes
*/
if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
const int supported_attrs =
FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM;
DWORD file_attrs = st->dwFileAttributes & supported_attrs;
if (file_attrs != 0)
archive_entry_set_fflags(entry, file_attrs, 0);
}
/*
* Invoke a meta data filter callback.
*/
@@ -966,6 +1126,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
t->entry_fh = INVALID_HANDLE_VALUE;
}
archive_entry_clear(entry);
while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY)
archive_entry_clear(entry);
@@ -1838,9 +2000,10 @@ entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,
mode |= S_IWUSR | S_IWGRP | S_IWOTH;
if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
findData != NULL &&
findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK)
findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
mode |= S_IFLNK;
else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
entry_symlink_from_pathw(entry, path);
} else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
else {
const wchar_t *p;
@@ -2139,6 +2302,8 @@ archive_read_disk_entry_from_file(struct archive *_a,
fileAttributes = bhfi.dwFileAttributes;
} else {
archive_entry_copy_stat(entry, st);
if (st->st_mode & S_IFLNK)
entry_symlink_from_pathw(entry, path);
h = INVALID_HANDLE_VALUE;
}
@@ -2150,6 +2315,19 @@ archive_read_disk_entry_from_file(struct archive *_a,
if (name != NULL)
archive_entry_copy_gname(entry, name);
/*
* File attributes
*/
if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
const int supported_attrs =
FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_HIDDEN |
FILE_ATTRIBUTE_SYSTEM;
DWORD file_attrs = fileAttributes & supported_attrs;
if (file_attrs != 0)
archive_entry_set_fflags(entry, file_attrs, 0);
}
/*
* Can this file be sparse file ?
*/
@@ -126,7 +126,6 @@ and
functions.
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_filter 3 ,
@@ -134,4 +133,5 @@ functions.
.Xr archive_read_open 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
@@ -147,8 +147,8 @@ and
functions.
.\"
.Sh SEE ALSO
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_format 3 ,
.Xr archive_read_format 3
.Xr archive_read_format 3 ,
.Xr libarchive 3
@@ -102,7 +102,7 @@ For example,
.Fn archive_read_support_format_tar
enables support for a variety of standard tar formats, old-style tar,
ustar, pax interchange format, and many common variants.
.It Fn archive_read_support_format_all
.It Fn archive_read_support_format_all
Enables support for all available formats except the
.Dq raw
format (see below).
@@ -125,7 +125,7 @@ it is not possible to accurately determine a format for
an empty file based purely on contents.
So empty files are treated by libarchive as a distinct
format.
.It Fn archive_read_support_format_raw
.It Fn archive_read_support_format_raw
The
.Dq raw
format handler allows libarchive to be used to read arbitrary data.
@@ -153,11 +153,11 @@ functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_filter 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
.Sh BUGS
Many traditional archiver programs treat
@@ -83,11 +83,11 @@ and
functions.
.\"
.Sh SEE ALSO
.Xr libarchive 3 ,
.Xr archive_read_new 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_filter 3 ,
.Xr archive_read_format 3 ,
.Xr archive_read_new 3 ,
.Xr archive_read_open 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3
.Xr archive_util 3 ,
.Xr libarchive 3
@@ -79,7 +79,6 @@ functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_extract 3 ,
@@ -88,4 +87,5 @@ functions.
.Xr archive_read_open 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
@@ -50,10 +50,10 @@ object can be found in the overview manual page for
.\" .Sh ERRORS
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_filter 3 ,
.Xr archive_read_format 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
@@ -205,7 +205,7 @@ On failure, the callback should invoke
.Fn archive_set_error
to register an error code and message and
return
.Cm ARCHIVE_FATAL.
.Cm ARCHIVE_FATAL .
.\" .Sh EXAMPLE
.\"
.Sh RETURN VALUES
@@ -223,11 +223,11 @@ functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_read_data 3 ,
.Xr archive_read_filter 3 ,
.Xr archive_read_format 3 ,
.Xr archive_read_set_options 3 ,
.Xr archive_util 3 ,
.Xr libarchive 3 ,
.Xr tar 5
@@ -174,8 +174,7 @@ file_close(struct archive *a, void *client_data)
struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
(void)a; /* UNUSED */
if (mine->buffer != NULL)
free(mine->buffer);
free(mine->buffer);
free(mine);
return (ARCHIVE_OK);
}
@@ -25,15 +25,15 @@
* $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $
*/
#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST
#error This header is only to be used internally to libarchive.
#endif
#endif
#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
#define ARCHIVE_READ_PRIVATE_H_INCLUDED
#include "archive.h"
#include "archive_string.h"
#include "archive_private.h"
@@ -98,6 +98,8 @@ struct archive_read_filter {
int (*close)(struct archive_read_filter *self);
/* Function that handles switching from reading one block to the next/prev */
int (*sswitch)(struct archive_read_filter *self, unsigned int iindex);
/* Read any header metadata if available. */
int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
/* My private data. */
void *data;
@@ -250,6 +252,7 @@ int64_t __archive_read_seek(struct archive_read*, int64_t, int);
int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
int64_t __archive_read_consume(struct archive_read *, int64_t);
int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t);
int __archive_read_header(struct archive_read *, struct archive_entry *);
int __archive_read_program(struct archive_read_filter *, const char *);
void __archive_read_free_filters(struct archive_read *);
struct archive_read_extract *__archive_read_get_extract(struct archive_read *);
@@ -73,6 +73,9 @@ archive_read_set_format(struct archive *_a, int code)
case ARCHIVE_FORMAT_RAR:
strcpy(str, "rar");
break;
case ARCHIVE_FORMAT_RAR_V5:
strcpy(str, "rar5");
break;
case ARCHIVE_FORMAT_TAR:
strcpy(str, "tar");
break;
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 2, 2012
.Dd January 31, 2020
.Dt ARCHIVE_READ_OPTIONS 3
.Os
.Sh NAME
@@ -180,6 +180,18 @@ only to modules whose name matches
.\"
.Sh OPTIONS
.Bl -tag -compact -width indent
.It Format cab
.Bl -tag -compact -width indent
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating file names.
.El
.It Format cpio
.Bl -tag -compact -width indent
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating file names.
.El
.It Format iso9660
.Bl -tag -compact -width indent
.It Cm joliet
@@ -193,6 +205,24 @@ Defaults to enabled, use
.Cm !rockridge
to disable.
.El
.It Format lha
.Bl -tag -compact -width indent
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating file names.
.El
.It Format mtree
.Bl -tag -compact -width indent
.It Cm checkfs
Allow reading information missing from the mtree from the file system.
Disabled by default.
.El
.It Format rar
.Bl -tag -compact -width indent
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating file names.
.El
.It Format tar
.Bl -tag -compact -width indent
.It Cm compat-2x
@@ -202,7 +232,7 @@ This option mimics the libarchive 2.x filename handling
so that such archives can be read correctly.
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating filenames.
used when translating file names.
.It Cm mac-ext
Support Mac OS metadata extension that records data in special
files beginning with a period and underscore.
@@ -212,7 +242,8 @@ Use
to disable.
.It Cm read_concatenated_archives
Ignore zeroed blocks in the archive, which occurs when multiple tar archives
have been concatenated together. Without this option, only the contents of
have been concatenated together.
Without this option, only the contents of
the first concatenated archive would be read.
.El
.El
@@ -226,6 +257,6 @@ functions.
.\"
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_read 3 ,
.Xr archive_write_set_options 3 ,
.Xr archive_read 3
.Xr libarchive 3
@@ -37,6 +37,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -45,6 +48,8 @@ __FBSDID("$FreeBSD$");
#endif
#include "archive.h"
#include "archive_entry.h"
#include "archive_endian.h"
#include "archive_private.h"
#include "archive_read_private.h"
@@ -56,6 +61,8 @@ struct private_data {
size_t out_block_size;
int64_t total_out;
unsigned long crc;
uint32_t mtime;
char *name;
char eof; /* True = found end of compressed data. */
};
@@ -123,12 +130,21 @@ archive_read_support_filter_gzip(struct archive *_a)
* count of bits verified, suitable for use by bidder.
*/
static ssize_t
peek_at_header(struct archive_read_filter *filter, int *pbits)
peek_at_header(struct archive_read_filter *filter, int *pbits,
#ifdef HAVE_ZLIB_H
struct private_data *state
#else
void *state
#endif
)
{
const unsigned char *p;
ssize_t avail, len;
int bits = 0;
int header_flags;
#ifndef HAVE_ZLIB_H
(void)state; /* UNUSED */
#endif
/* Start by looking at the first ten bytes of the header, which
* is all fixed layout. */
@@ -144,7 +160,11 @@ peek_at_header(struct archive_read_filter *filter, int *pbits)
return (0);
bits += 3;
header_flags = p[3];
/* Bytes 4-7 are mod time. */
/* Bytes 4-7 are mod time in little endian. */
#ifdef HAVE_ZLIB_H
if (state)
state->mtime = archive_le32dec(p + 4);
#endif
/* Byte 8 is deflate flags. */
/* XXXX TODO: return deflate flags back to consume_header for use
in initializing the decompressor. */
@@ -161,6 +181,9 @@ peek_at_header(struct archive_read_filter *filter, int *pbits)
/* Null-terminated optional filename. */
if (header_flags & 8) {
#ifdef HAVE_ZLIB_H
ssize_t file_start = len;
#endif
do {
++len;
if (avail < len)
@@ -169,6 +192,14 @@ peek_at_header(struct archive_read_filter *filter, int *pbits)
if (p == NULL)
return (0);
} while (p[len - 1] != 0);
#ifdef HAVE_ZLIB_H
if (state) {
/* Reset the name in case of repeat header reads. */
free(state->name);
state->name = strdup((const char *)&p[file_start]);
}
#endif
}
/* Null-terminated optional comment. */
@@ -214,12 +245,11 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self,
(void)self; /* UNUSED */
if (peek_at_header(filter, &bits_checked))
if (peek_at_header(filter, &bits_checked, NULL))
return (bits_checked);
return (0);
}
#ifndef HAVE_ZLIB_H
/*
@@ -243,6 +273,24 @@ gzip_bidder_init(struct archive_read_filter *self)
#else
static int
gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
{
struct private_data *state;
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);
/* If the name is available, extract it. */
if (state->name)
archive_entry_set_pathname(entry, state->name);
return (ARCHIVE_OK);
}
/*
* Initialize the filter object.
*/
@@ -272,6 +320,9 @@ gzip_bidder_init(struct archive_read_filter *self)
self->read = gzip_filter_read;
self->skip = NULL; /* not supported */
self->close = gzip_filter_close;
#ifdef HAVE_ZLIB_H
self->read_header = gzip_read_header;
#endif
state->in_stream = 0; /* We're not actually within a stream yet. */
@@ -289,7 +340,7 @@ consume_header(struct archive_read_filter *self)
state = (struct private_data *)self->data;
/* If this is a real header, consume it. */
len = peek_at_header(self->upstream, NULL);
len = peek_at_header(self->upstream, NULL, state);
if (len == 0)
return (ARCHIVE_EOF);
__archive_read_filter_consume(self->upstream, len);
@@ -374,7 +425,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
{
struct private_data *state;
size_t decompressed;
ssize_t avail_in;
ssize_t avail_in, max_in;
int ret;
state = (struct private_data *)self->data;
@@ -408,6 +459,12 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
"truncated gzip input");
return (ARCHIVE_FATAL);
}
if (UINT_MAX >= SSIZE_MAX)
max_in = SSIZE_MAX;
else
max_in = UINT_MAX;
if (avail_in > max_in)
avail_in = max_in;
state->stream.avail_in = (uInt)avail_in;
/* Decompress and consume some of that data. */
@@ -469,6 +526,7 @@ gzip_filter_close(struct archive_read_filter *self)
}
}
free(state->name);
free(state->out_block);
free(state);
return (ret);
@@ -460,7 +460,7 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
__archive_read_filter_consume(self->upstream, descriptor_bytes);
/* Make sure we have an enough buffer for uncompressed data. */
/* Make sure we have a large enough buffer for uncompressed data. */
if (lz4_allocate_out_block(self) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
if (state->flags.stream_checksum)
@@ -520,7 +520,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
if (read_buf == NULL)
goto truncated_error;
/* Optional process, checking a block sum. */
/* Optional processing, checking a block sum. */
if (checksum_size) {
unsigned int chsum = __archive_xxhash.XXH32(
read_buf + 4, (int)compressed_size, 0);
@@ -640,7 +640,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
if (ret == 0 && *p == NULL)
state->stage = SELECT_STREAM;
/* Optional process, checking a stream sum. */
/* Optional processing, checking a stream sum. */
if (state->flags.stream_checksum) {
if (state->stage == SELECT_STREAM) {
unsigned int checksum;
@@ -660,7 +660,7 @@ lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
if (checksum != checksum_stream) {
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
"lz4 stream cheksum error");
"lz4 stream checksum error");
return (ARCHIVE_FATAL);
}
} else if (ret > 0)
@@ -674,7 +674,7 @@ static ssize_t
lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
{
struct private_data *state = (struct private_data *)self->data;
int compressed;
uint32_t compressed;
const char *read_buf;
ssize_t ret;
@@ -574,14 +574,13 @@ read_more:
while (l > 0) {
int n = 0;
if (l > 0) {
if (!uuchar[b[0]] || !uuchar[b[1]])
break;
n = UUDECODE(*b++) << 18;
n |= UUDECODE(*b++) << 12;
*out++ = n >> 16; total++;
--l;
}
if (!uuchar[b[0]] || !uuchar[b[1]])
break;
n = UUDECODE(*b++) << 18;
n |= UUDECODE(*b++) << 12;
*out++ = n >> 16; total++;
--l;
if (l > 0) {
if (!uuchar[b[0]])
break;
@@ -626,14 +625,13 @@ read_more:
while (l > 0) {
int n = 0;
if (l > 0) {
if (!base64[b[0]] || !base64[b[1]])
break;
n = base64num[*b++] << 18;
n |= base64num[*b++] << 12;
*out++ = n >> 16; total++;
l -= 2;
}
if (!base64[b[0]] || !base64[b[1]])
break;
n = base64num[*b++] << 18;
n |= base64num[*b++] << 12;
*out++ = n >> 16; total++;
l -= 2;
if (l > 0) {
if (*b == '=')
break;
@@ -1086,10 +1086,17 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
zip->bcj_state = 0;
break;
case _7Z_DELTA:
if (coder2->propertiesSize != 1) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"Invalid Delta parameter");
return (ARCHIVE_FAILED);
}
filters[fi].id = LZMA_FILTER_DELTA;
memset(&delta_opt, 0, sizeof(delta_opt));
delta_opt.type = LZMA_DELTA_TYPE_BYTE;
delta_opt.dist = 1;
delta_opt.dist =
(uint32_t)coder2->properties[0] + 1;
filters[fi].options = &delta_opt;
fi++;
break;
@@ -1787,7 +1794,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
return (0);
}
if (*p != kSize)
if (*p != kCRC)
return (-1);
if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
@@ -2964,13 +2971,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
/* Copy mode. */
/*
* Note: '1' here is a performance optimization.
* Recall that the decompression layer returns a count of
* available bytes; asking for more than that forces the
* decompressor to combine reads by copying data.
*/
*buff = __archive_read_ahead(a, 1, &bytes_avail);
*buff = __archive_read_ahead(a, minimum, &bytes_avail);
if (bytes_avail <= 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@@ -3323,8 +3324,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
* Release the memory which the previous folder used for BCJ2.
*/
for (i = 0; i < 3; i++) {
if (zip->sub_stream_buff[i] != NULL)
free(zip->sub_stream_buff[i]);
free(zip->sub_stream_buff[i]);
zip->sub_stream_buff[i] = NULL;
}
@@ -72,6 +72,7 @@ archive_read_support_format_all(struct archive *a)
archive_read_support_format_7zip(a);
archive_read_support_format_cab(a);
archive_read_support_format_rar(a);
archive_read_support_format_rar5(a);
archive_read_support_format_iso9660(a);
/* Seek is really bad, since it forces the read-ahead
* logic to discard buffered data. */
@@ -138,8 +138,7 @@ archive_read_format_ar_cleanup(struct archive_read *a)
struct ar *ar;
ar = (struct ar *)(a->format->data);
if (ar->strtab)
free(ar->strtab);
free(ar->strtab);
free(ar);
(a->format->data) = NULL;
return (ARCHIVE_OK);
@@ -388,9 +387,10 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
/*
* "/" is the SVR4/GNU archive symbol table.
* "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
*/
if (strcmp(filename, "/") == 0) {
archive_entry_copy_pathname(entry, "/");
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. */
@@ -459,6 +459,7 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
uint64_t n;
/* Copy remaining header */
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_mtime(entry,
(time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
archive_entry_set_uid(entry,
@@ -60,6 +60,9 @@ archive_read_support_format_by_code(struct archive *a, int format_code)
case ARCHIVE_FORMAT_RAR:
return archive_read_support_format_rar(a);
break;
case ARCHIVE_FORMAT_RAR_V5:
return archive_read_support_format_rar5(a);
break;
case ARCHIVE_FORMAT_TAR:
return archive_read_support_format_tar(a);
break;
@@ -1509,8 +1509,8 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
}
if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
goto nomszip;
else if (cab->stream.next_in[0] != 0x43 ||
cab->stream.next_in[1] != 0x4b)
else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||
cab->stream.next_in[1] != 0x4b))
goto nomszip;
cab->stream.next_in += mszip;
cab->stream.avail_in -= mszip;
@@ -955,8 +955,7 @@ archive_read_format_cpio_cleanup(struct archive_read *a)
while (cpio->links_head != NULL) {
struct links_entry *lp = cpio->links_head->next;
if (cpio->links_head->name)
free(cpio->links_head->name);
free(cpio->links_head->name);
free(cpio->links_head);
cpio->links_head = lp;
}
@@ -1724,8 +1724,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a)
free(iso9660->read_ce_req.reqs);
archive_string_free(&iso9660->pathname);
archive_string_free(&iso9660->previous_pathname);
if (iso9660->pending_files.files)
free(iso9660->pending_files.files);
free(iso9660->pending_files.files);
#ifdef HAVE_ZLIB_H
free(iso9660->entry_zisofs.uncompressed_buffer);
free(iso9660->entry_zisofs.block_pointers);
@@ -2102,6 +2101,7 @@ parse_rockridge(struct archive_read *a, struct file_info *file,
const unsigned char *p, const unsigned char *end)
{
struct iso9660 *iso9660;
int entry_seen = 0;
iso9660 = (struct iso9660 *)(a->format->data);
@@ -2257,8 +2257,16 @@ parse_rockridge(struct archive_read *a, struct file_info *file,
}
p += p[2];
entry_seen = 1;
}
if (entry_seen)
return (ARCHIVE_OK);
else {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Tried to parse Rockridge extensions, but none found");
return (ARCHIVE_WARN);
}
return (ARCHIVE_OK);
}
static int
@@ -3029,8 +3037,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap,
if (heap->allocated)
memcpy(new_pending_files, heap->files,
heap->allocated * sizeof(new_pending_files[0]));
if (heap->files != NULL)
free(heap->files);
free(heap->files);
heap->files = new_pending_files;
heap->allocated = new_size;
}
@@ -175,7 +175,9 @@ struct lha {
struct archive_string gname;
uint16_t header_crc;
uint16_t crc;
struct archive_string_conv *sconv;
/* dirname and filename could be in different codepages */
struct archive_string_conv *sconv_dir;
struct archive_string_conv *sconv_fname;
struct archive_string_conv *opt_sconv;
struct archive_string dirname;
@@ -232,8 +234,8 @@ static time_t lha_dos_time(const unsigned char *);
static time_t lha_win_time(uint64_t, long *);
static unsigned char lha_calcsum(unsigned char, const void *,
int, size_t);
static int lha_parse_linkname(struct archive_string *,
struct archive_string *);
static int lha_parse_linkname(struct archive_wstring *,
struct archive_wstring *);
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 **,
@@ -473,13 +475,15 @@ static int
archive_read_format_lha_read_header(struct archive_read *a,
struct archive_entry *entry)
{
struct archive_string linkname;
struct archive_string pathname;
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;
@@ -561,10 +565,13 @@ archive_read_format_lha_read_header(struct archive_read *a,
archive_string_empty(&lha->dirname);
archive_string_empty(&lha->filename);
lha->dos_attr = 0;
if (lha->opt_sconv != NULL)
lha->sconv = lha->opt_sconv;
else
lha->sconv = NULL;
if (lha->opt_sconv != NULL) {
lha->sconv_dir = lha->opt_sconv;
lha->sconv_fname = lha->opt_sconv;
} else {
lha->sconv_dir = NULL;
lha->sconv_fname = NULL;
}
switch (p[H_LEVEL_OFFSET]) {
case 0:
@@ -594,12 +601,54 @@ archive_read_format_lha_read_header(struct archive_read *a,
return (truncated_error(a));
/*
* Make a pathname from a dirname and a filename.
*/
archive_string_concat(&lha->dirname, &lha->filename);
* Make a pathname from a dirname and a filename, after converting to Unicode.
* This is because codepages might differ between dirname and filename.
*/
archive_string_init(&pathname);
archive_string_init(&linkname);
archive_string_copy(&pathname, &lha->dirname);
archive_string_init(&conv_buffer.aes_mbs);
archive_string_init(&conv_buffer.aes_mbs_in_locale);
archive_string_init(&conv_buffer.aes_utf8);
archive_string_init(&conv_buffer.aes_wcs);
if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
"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))
err = ARCHIVE_FATAL;
if (err == ARCHIVE_FATAL) {
archive_mstring_clean(&conv_buffer);
archive_wstring_free(&pathname);
archive_wstring_free(&linkname);
return (err);
}
archive_wstring_copy(&pathname, &conv_buffer.aes_wcs);
archive_string_empty(&conv_buffer.aes_mbs);
archive_string_empty(&conv_buffer.aes_mbs_in_locale);
archive_string_empty(&conv_buffer.aes_utf8);
archive_wstring_empty(&conv_buffer.aes_wcs);
if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
"from %s to Unicode.",
archive_string_conversion_charset_name(lha->sconv_fname));
err = ARCHIVE_FATAL;
}
else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
err = ARCHIVE_FATAL;
if (err == ARCHIVE_FATAL) {
archive_mstring_clean(&conv_buffer);
archive_wstring_free(&pathname);
archive_wstring_free(&linkname);
return (err);
}
archive_wstring_concat(&pathname, &conv_buffer.aes_wcs);
archive_mstring_clean(&conv_buffer);
if ((lha->mode & AE_IFMT) == AE_IFLNK) {
/*
@@ -610,8 +659,8 @@ archive_read_format_lha_read_header(struct archive_read *a,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Unknown symlink-name");
archive_string_free(&pathname);
archive_string_free(&linkname);
archive_wstring_free(&pathname);
archive_wstring_free(&linkname);
return (ARCHIVE_FAILED);
}
} else {
@@ -629,39 +678,13 @@ archive_read_format_lha_read_header(struct archive_read *a,
/*
* Set basic file parameters.
*/
if (archive_entry_copy_pathname_l(entry, pathname.s,
pathname.length, lha->sconv) != 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Pathname cannot be converted "
"from %s to current locale.",
archive_string_conversion_charset_name(lha->sconv));
err = ARCHIVE_WARN;
}
archive_string_free(&pathname);
archive_entry_copy_pathname_w(entry, pathname.s);
archive_wstring_free(&pathname);
if (archive_strlen(&linkname) > 0) {
if (archive_entry_copy_symlink_l(entry, linkname.s,
linkname.length, lha->sconv) != 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Linkname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Linkname cannot be converted "
"from %s to current locale.",
archive_string_conversion_charset_name(lha->sconv));
err = ARCHIVE_WARN;
}
archive_entry_copy_symlink_w(entry, linkname.s);
} else
archive_entry_set_symlink(entry, NULL);
archive_string_free(&linkname);
archive_wstring_free(&linkname);
/*
* When a header level is 0, there is a possibility that
* a pathname and a symlink has '\' character, a directory
@@ -1208,6 +1231,27 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
archive_strncpy(&lha->filename,
(const char *)extdheader, datasize);
break;
case EXT_UTF16_FILENAME:
if (datasize == 0) {
/* maybe directory header */
archive_string_empty(&lha->filename);
break;
} else if (datasize & 1) {
/* UTF-16 characters take always 2 or 4 bytes */
goto invalid;
}
if (extdheader[0] == '\0')
goto invalid;
archive_string_empty(&lha->filename);
archive_array_append(&lha->filename,
(const char *)extdheader, datasize);
/* Setup a string conversion for a filename. */
lha->sconv_fname =
archive_string_conversion_from_charset(&a->archive,
"UTF-16LE", 1);
if (lha->sconv_fname == NULL)
return (ARCHIVE_FATAL);
break;
case EXT_DIRECTORY:
if (datasize == 0 || extdheader[0] == '\0')
/* no directory name data. exit this case. */
@@ -1228,6 +1272,50 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
/* invalid directory data */
goto invalid;
break;
case EXT_UTF16_DIRECTORY:
/* UTF-16 characters take always 2 or 4 bytes */
if (datasize == 0 || (datasize & 1) ||
extdheader[0] == '\0') {
/* no directory name data. exit this case. */
goto invalid;
}
archive_string_empty(&lha->dirname);
archive_array_append(&lha->dirname,
(const char *)extdheader, datasize);
lha->sconv_dir =
archive_string_conversion_from_charset(&a->archive,
"UTF-16LE", 1);
if (lha->sconv_dir == NULL)
return (ARCHIVE_FATAL);
else {
/*
* Convert directory delimiter from 0xFFFF
* to '/' for local system.
*/
uint16_t dirSep;
uint16_t d = 1;
if (archive_be16dec(&d) == 1)
dirSep = 0x2F00;
else
dirSep = 0x002F;
/* UTF-16LE character */
uint16_t *utf16name =
(uint16_t *)lha->dirname.s;
for (i = 0; i < lha->dirname.length / 2; i++) {
if (utf16name[i] == 0xFFFF) {
utf16name[i] = dirSep;
}
}
/* Is last character directory separator? */
if (utf16name[lha->dirname.length / 2 - 1] !=
dirSep) {
/* invalid directory data */
goto invalid;
}
}
break;
case EXT_DOS_ATTR:
if (datasize == 2)
lha->dos_attr = (unsigned char)
@@ -1276,11 +1364,16 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
charset = cp.s;
break;
}
lha->sconv =
lha->sconv_dir =
archive_string_conversion_from_charset(
&(a->archive), charset, 1);
lha->sconv_fname =
archive_string_conversion_from_charset(
&(a->archive), charset, 1);
archive_string_free(&cp);
if (lha->sconv == NULL)
if (lha->sconv_dir == NULL)
return (ARCHIVE_FATAL);
if (lha->sconv_fname == NULL)
return (ARCHIVE_FATAL);
}
break;
@@ -1336,8 +1429,7 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
}
break;
case EXT_TIMEZONE: /* Not supported */
case EXT_UTF16_FILENAME: /* Not supported */
case EXT_UTF16_DIRECTORY: /* Not supported */
break;
default:
break;
}
@@ -1600,19 +1692,19 @@ archive_read_format_lha_cleanup(struct archive_read *a)
* then a archived pathname is 'xxx/bbb|aaa/bb/cc'
*/
static int
lha_parse_linkname(struct archive_string *linkname,
struct archive_string *pathname)
lha_parse_linkname(struct archive_wstring *linkname,
struct archive_wstring *pathname)
{
char * linkptr;
wchar_t * linkptr;
size_t symlen;
linkptr = strchr(pathname->s, '|');
linkptr = wcschr(pathname->s, L'|');
if (linkptr != NULL) {
symlen = strlen(linkptr + 1);
archive_strncpy(linkname, linkptr+1, symlen);
symlen = wcslen(linkptr + 1);
archive_wstrncpy(linkname, linkptr+1, symlen);
*linkptr = 0;
pathname->length = strlen(pathname->s);
pathname->length = wcslen(pathname->s);
return (1);
}
@@ -45,6 +45,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "archive.h"
#include "archive_entry.h"
@@ -255,6 +258,7 @@ archive_read_support_format_mtree(struct archive *_a)
"Can't allocate mtree data");
return (ARCHIVE_FATAL);
}
mtree->checkfs = 0;
mtree->fd = -1;
__archive_rb_tree_init(&mtree->rbtree, &rb_ops);
@@ -1011,7 +1015,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
{
ssize_t len;
uintmax_t counter;
char *p;
char *p, *s;
struct mtree_option *global;
struct mtree_entry *last_entry;
int r, is_form_d;
@@ -1025,6 +1029,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
(void)detect_form(a, &is_form_d);
for (counter = 1; ; ++counter) {
r = ARCHIVE_OK;
len = readline(a, mtree, &p, 65536);
if (len == 0) {
mtree->this_entry = mtree->entries;
@@ -1045,6 +1050,15 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
continue;
if (*p == '\r' || *p == '\n' || *p == '\0')
continue;
/* Non-printable characters are not allowed */
for (s = p;s < p + len - 1; s++) {
if (!isprint(*s)) {
r = ARCHIVE_FATAL;
break;
}
}
if (r != ARCHIVE_OK)
break;
if (*p != '/') {
r = process_add_entry(a, mtree, &global, p, len,
&last_entry, is_form_d);
@@ -148,6 +148,9 @@
#define FILE_ATTRIBUTE_DIRECTORY 0x10
#endif
#undef minimum
#define minimum(a, b) ((a)<(b)?(a):(b))
/* Fields common to all headers */
struct rar_header
{
@@ -258,6 +261,7 @@ struct rar
struct data_block_offsets *dbo;
unsigned int cursor;
unsigned int nodes;
char filename_must_match;
/* LZSS members */
struct huffman_code maincode;
@@ -1023,8 +1027,11 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
case COMPRESS_METHOD_GOOD:
case COMPRESS_METHOD_BEST:
ret = read_data_compressed(a, buff, size, offset);
if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN)
if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
__archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
rar->start_new_table = 1;
rar->ppmd_valid = 0;
}
break;
default:
@@ -1560,6 +1567,12 @@ read_header(struct archive_read *a, struct archive_entry *entry,
}
return ret;
}
else if (rar->filename_must_match)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Mismatch of file parts split across multi-volume archive");
return (ARCHIVE_FATAL);
}
rar->filename_save = (char*)realloc(rar->filename_save,
filename_size + 1);
@@ -1712,6 +1725,13 @@ read_exttime(const char *p, struct rar *rar, const char *endp)
struct tm *tm;
time_t t;
long nsec;
#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
struct tm tmbuf;
#endif
#if defined(HAVE__LOCALTIME64_S)
errno_t terr;
__time64_t tmptime;
#endif
if (p + 2 > endp)
return (-1);
@@ -1743,7 +1763,18 @@ read_exttime(const char *p, struct rar *rar, const char *endp)
rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
p++;
}
#if defined(HAVE_LOCALTIME_R)
tm = localtime_r(&t, &tmbuf);
#elif defined(HAVE__LOCALTIME64_S)
tmptime = t;
terr = _localtime64_s(&tmbuf, &tmptime);
if (terr)
tm = NULL;
else
tm = &tmbuf;
#else
tm = localtime(&t);
#endif
nsec = tm->tm_sec + rem / NS_UNIT;
if (rmode & 4)
{
@@ -2300,6 +2331,11 @@ parse_codes(struct archive_read *a)
new_size = DICTIONARY_MAX_SIZE;
else
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);
}
new_window = realloc(rar->lzss.window, new_size);
if (new_window == NULL) {
archive_set_error(&a->archive, ENOMEM,
@@ -2437,8 +2473,11 @@ create_code(struct archive_read *a, struct huffman_code *code,
if (add_value(a, code, j, codebits, i) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
codebits++;
if (--symbolsleft <= 0) { break; break; }
if (--symbolsleft <= 0)
break;
}
if (symbolsleft <= 0)
break;
codebits <<= 1;
}
return (ARCHIVE_OK);
@@ -2448,7 +2487,8 @@ static int
add_value(struct archive_read *a, struct huffman_code *code, int value,
int codebits, int length)
{
int repeatpos, lastnode, bitpos, bit, repeatnode, nextnode;
int lastnode, bitpos, bit;
/* int repeatpos, repeatnode, nextnode; */
free(code->table);
code->table = NULL;
@@ -2458,6 +2498,9 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
if(length < code->minlength)
code->minlength = length;
/*
* Dead code, repeatpos was is -1
*
repeatpos = -1;
if (repeatpos == 0 || (repeatpos >= 0
&& (((codebits >> (repeatpos - 1)) & 3) == 0
@@ -2467,6 +2510,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
"Invalid repeat position");
return (ARCHIVE_FATAL);
}
*/
lastnode = 0;
for (bitpos = length - 1; bitpos >= 0; bitpos--)
@@ -2482,9 +2526,12 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
return (ARCHIVE_FATAL);
}
/*
* Dead code, repeatpos was -1, bitpos >=0
*
if (bitpos == repeatpos)
{
/* Open branch check */
* Open branch check *
if (!(code->tree[lastnode].branches[bit] < 0))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2503,16 +2550,17 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
return (ARCHIVE_FATAL);
}
/* Set branches */
* Set branches *
code->tree[lastnode].branches[bit] = repeatnode;
code->tree[repeatnode].branches[bit] = repeatnode;
code->tree[repeatnode].branches[bit^1] = nextnode;
lastnode = nextnode;
bitpos++; /* terminating bit already handled, skip it */
bitpos++; * terminating bit already handled, skip it *
}
else
{
*/
/* Open branch check */
if (code->tree[lastnode].branches[bit] < 0)
{
@@ -2526,7 +2574,7 @@ add_value(struct archive_read *a, struct huffman_code *code, int value,
/* set to branch */
lastnode = code->tree[lastnode].branches[bit];
}
/* } */
}
if (!(code->tree[lastnode].branches[0] == -1
@@ -2610,11 +2658,15 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
table[i].value = code->tree[node].branches[0];
}
}
/*
* Dead code, node >= 0
*
else if (node < 0)
{
for(i = 0; i < currtablesize; i++)
table[i].length = -1;
}
*/
else
{
if(depth == maxdepth)
@@ -2646,6 +2698,10 @@ expand(struct archive_read *a, int64_t end)
0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 5 };
static const int lengthb_min = minimum(
(int)(sizeof(lengthbases)/sizeof(lengthbases[0])),
(int)(sizeof(lengthbits)/sizeof(lengthbits[0]))
);
static const unsigned int offsetbases[] =
{ 0, 1, 2, 3, 4, 6,
8, 12, 16, 24, 32, 48,
@@ -2663,6 +2719,10 @@ expand(struct archive_read *a, int64_t end)
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };
static const int offsetb_min = minimum(
(int)(sizeof(offsetbases)/sizeof(offsetbases[0])),
(int)(sizeof(offsetbits)/sizeof(offsetbits[0]))
);
static const unsigned char shortbases[] =
{ 0, 4, 8, 16, 32, 64, 128, 192 };
static const unsigned char shortbits[] =
@@ -2742,9 +2802,7 @@ expand(struct archive_read *a, int64_t end)
if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)
goto bad_data;
if (lensymbol > (int)(sizeof(lengthbases)/sizeof(lengthbases[0])))
goto bad_data;
if (lensymbol > (int)(sizeof(lengthbits)/sizeof(lengthbits[0])))
if (lensymbol > lengthb_min)
goto bad_data;
len = lengthbases[lensymbol] + 2;
if (lengthbits[lensymbol] > 0) {
@@ -2776,9 +2834,7 @@ expand(struct archive_read *a, int64_t end)
}
else
{
if (symbol-271 > (int)(sizeof(lengthbases)/sizeof(lengthbases[0])))
goto bad_data;
if (symbol-271 > (int)(sizeof(lengthbits)/sizeof(lengthbits[0])))
if (symbol-271 > lengthb_min)
goto bad_data;
len = lengthbases[symbol-271]+3;
if(lengthbits[symbol-271] > 0) {
@@ -2790,9 +2846,7 @@ expand(struct archive_read *a, int64_t end)
if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0)
goto bad_data;
if (offssymbol > (int)(sizeof(offsetbases)/sizeof(offsetbases[0])))
goto bad_data;
if (offssymbol > (int)(sizeof(offsetbits)/sizeof(offsetbits[0])))
if (offssymbol > offsetb_min)
goto bad_data;
offs = offsetbases[offssymbol]+1;
if(offsetbits[offssymbol] > 0)
@@ -2928,12 +2982,14 @@ rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&
rar->file_flags & FHD_SPLIT_AFTER)
{
rar->filename_must_match = 1;
ret = archive_read_format_rar_read_header(a, a->entry);
if (ret == (ARCHIVE_EOF))
{
rar->has_endarc_header = 1;
ret = archive_read_format_rar_read_header(a, a->entry);
}
rar->filename_must_match = 0;
if (ret != (ARCHIVE_OK))
return NULL;
return rar_read_ahead(a, min, avail);
@@ -0,0 +1,4103 @@
/*-
* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
#include "archive_endian.h"
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <time.h>
#ifdef HAVE_ZLIB_H
#include <zlib.h> /* crc32 */
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include "archive.h"
#ifndef HAVE_ZLIB_H
#include "archive_crc32.h"
#endif
#include "archive_entry.h"
#include "archive_entry_locale.h"
#include "archive_ppmd7_private.h"
#include "archive_entry_private.h"
#ifdef HAVE_BLAKE2_H
#include <blake2.h>
#else
#include "archive_blake2.h"
#endif
/*#define CHECK_CRC_ON_SOLID_SKIP*/
/*#define DONT_FAIL_ON_CRC_ERROR*/
/*#define DEBUG*/
#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
#if defined DEBUG
#define DEBUG_CODE if(1)
#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
#else
#define DEBUG_CODE if(0)
#endif
/* Real RAR5 magic number is:
*
* 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
* "Rar!→•☺·\x00"
*
* Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
* want to put this magic sequence in each binary that uses libarchive, so
* applications that scan through the file for this marker won't trigger on
* this "false" one.
*
* 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 size_t g_unpack_window_size = 0x20000;
/* These could have been static const's, but they aren't, because of
* Visual Studio. */
#define MAX_NAME_IN_CHARS 2048
#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
struct file_header {
ssize_t bytes_remaining;
ssize_t unpacked_size;
int64_t last_offset; /* Used in sanity checks. */
int64_t last_size; /* Used in sanity checks. */
uint8_t solid : 1; /* Is this a solid stream? */
uint8_t service : 1; /* Is this file a service data? */
uint8_t eof : 1; /* Did we finish unpacking the file? */
uint8_t dir : 1; /* Is this file entry a directory? */
/* Optional time fields. */
uint64_t e_mtime;
uint64_t e_ctime;
uint64_t e_atime;
uint32_t e_unix_ns;
/* Optional hash fields. */
uint32_t stored_crc32;
uint32_t calculated_crc32;
uint8_t blake2sp[32];
blake2sp_state b2state;
char has_blake2;
/* Optional redir fields */
uint64_t redir_type;
uint64_t redir_flags;
ssize_t solid_window_size; /* Used in file format check. */
};
enum EXTRA {
EX_CRYPT = 0x01,
EX_HASH = 0x02,
EX_HTIME = 0x03,
EX_VERSION = 0x04,
EX_REDIR = 0x05,
EX_UOWNER = 0x06,
EX_SUBDATA = 0x07
};
#define REDIR_SYMLINK_IS_DIR 1
enum REDIR_TYPE {
REDIR_TYPE_NONE = 0,
REDIR_TYPE_UNIXSYMLINK = 1,
REDIR_TYPE_WINSYMLINK = 2,
REDIR_TYPE_JUNCTION = 3,
REDIR_TYPE_HARDLINK = 4,
REDIR_TYPE_FILECOPY = 5,
};
#define OWNER_USER_NAME 0x01
#define OWNER_GROUP_NAME 0x02
#define OWNER_USER_UID 0x04
#define OWNER_GROUP_GID 0x08
#define OWNER_MAXNAMELEN 256
enum FILTER_TYPE {
FILTER_DELTA = 0, /* Generic pattern. */
FILTER_E8 = 1, /* Intel x86 code. */
FILTER_E8E9 = 2, /* Intel x86 code. */
FILTER_ARM = 3, /* ARM code. */
FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */
FILTER_RGB = 5, /* Color palette, not used in RARv5. */
FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
FILTER_PPM = 7, /* Predictive pattern matching, not used in
RARv5. */
FILTER_NONE = 8,
};
struct filter_info {
int type;
int channels;
int pos_r;
int64_t block_start;
ssize_t block_length;
uint16_t width;
};
struct data_ready {
char used;
const uint8_t* buf;
size_t size;
int64_t offset;
};
struct cdeque {
uint16_t beg_pos;
uint16_t end_pos;
uint16_t cap_mask;
uint16_t size;
size_t* arr;
};
struct decode_table {
uint32_t size;
int32_t decode_len[16];
uint32_t decode_pos[16];
uint32_t quick_bits;
uint8_t quick_len[1 << 10];
uint16_t quick_num[1 << 10];
uint16_t decode_num[306];
};
struct comp_state {
/* Flag used to specify if unpacker needs to reinitialize the
uncompression context. */
uint8_t initialized : 1;
/* Flag used when applying filters. */
uint8_t all_filters_applied : 1;
/* Flag used to skip file context reinitialization, used when unpacker
is skipping through different multivolume archives. */
uint8_t switch_multivolume : 1;
/* Flag used to specify if unpacker has processed the whole data block
or just a part of it. */
uint8_t block_parsing_finished : 1;
signed int notused : 4;
int flags; /* Uncompression flags. */
int method; /* Uncompression algorithm method. */
int version; /* Uncompression algorithm version. */
ssize_t window_size; /* Size of window_buf. */
uint8_t* window_buf; /* Circular buffer used during
decompression. */
uint8_t* filtered_buf; /* Buffer used when applying filters. */
const uint8_t* block_buf; /* Buffer used when merging blocks. */
size_t window_mask; /* Convenience field; window_size - 1. */
int64_t write_ptr; /* This amount of data has been unpacked
in the window buffer. */
int64_t last_write_ptr; /* This amount of data has been stored in
the output file. */
int64_t last_unstore_ptr; /* Counter of bytes extracted during
unstoring. This is separate from
last_write_ptr because of how SERVICE
base blocks are handled during skipping
in solid multiarchive archives. */
int64_t solid_offset; /* Additional offset inside the window
buffer, used in unpacking solid
archives. */
ssize_t cur_block_size; /* Size of current data block. */
int last_len; /* Flag used in lzss decompression. */
/* Decode tables used during lzss uncompression. */
#define HUFF_BC 20
struct decode_table bd; /* huffman bit lengths */
#define HUFF_NC 306
struct decode_table ld; /* literals */
#define HUFF_DC 64
struct decode_table dd; /* distances */
#define HUFF_LDC 16
struct decode_table ldd; /* lower bits of distances */
#define HUFF_RC 44
struct decode_table rd; /* repeating distances */
#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
/* Circular deque for storing filters. */
struct cdeque filters;
int64_t last_block_start; /* Used for sanity checking. */
ssize_t last_block_length; /* Used for sanity checking. */
/* Distance cache used during lzss uncompression. */
int dist_cache[4];
/* Data buffer stack. */
struct data_ready dready[2];
};
/* Bit reader state. */
struct bit_reader {
int8_t bit_addr; /* Current bit pointer inside current byte. */
int in_addr; /* Current byte pointer. */
};
/* RARv5 block header structure. Use bf_* functions to get values from
* block_flags_u8 field. I.e. bf_byte_count, etc. */
struct compressed_block_header {
/* block_flags_u8 contain fields encoded in little-endian bitfield:
*
* - table present flag (shr 7, and 1),
* - last block flag (shr 6, and 1),
* - byte_count (shr 3, and 7),
* - bit_size (shr 0, and 7).
*/
uint8_t block_flags_u8;
uint8_t block_cksum;
};
/* RARv5 main header structure. */
struct main_header {
/* Does the archive contain solid streams? */
uint8_t solid : 1;
/* If this a multi-file archive? */
uint8_t volume : 1;
uint8_t endarc : 1;
uint8_t notused : 5;
unsigned int vol_no;
};
struct generic_header {
uint8_t split_after : 1;
uint8_t split_before : 1;
uint8_t padding : 6;
int size;
int last_header_id;
};
struct multivolume {
unsigned int expected_vol_no;
uint8_t* push_buf;
};
/* Main context structure. */
struct rar5 {
int header_initialized;
/* Set to 1 if current file is positioned AFTER the magic value
* of the archive file. This is used in header reading functions. */
int skipped_magic;
/* Set to not zero if we're in skip mode (either by calling
* rar5_data_skip function or when skipping over solid streams).
* Set to 0 when in * extraction mode. This is used during checksum
* calculation functions. */
int skip_mode;
/* Set to not zero if we're in block merging mode (i.e. when switching
* to another file in multivolume archive, last block from 1st archive
* needs to be merged with 1st block from 2nd archive). This flag
* guards against recursive use of the merging function, which doesn't
* support recursive calls. */
int merge_mode;
/* An offset to QuickOpen list. This is not supported by this unpacker,
* because we're focusing on streaming interface. QuickOpen is designed
* to make things quicker for non-stream interfaces, so it's not our
* use case. */
uint64_t qlist_offset;
/* An offset to additional Recovery data. This is not supported by this
* unpacker. Recovery data are additional Reed-Solomon codes that could
* be used to calculate bytes that are missing in archive or are
* corrupted. */
uint64_t rr_offset;
/* Various context variables grouped to different structures. */
struct generic_header generic;
struct main_header main;
struct comp_state cstate;
struct file_header file;
struct bit_reader bits;
struct multivolume vol;
/* The header of currently processed RARv5 block. Used in main
* decompression logic loop. */
struct compressed_block_header last_block_hdr;
};
/* Forward function declarations. */
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,
const uint8_t* buf, size_t size, int64_t offset);
/* CDE_xxx = Circular Double Ended (Queue) return values. */
enum CDE_RETURN_VALUES {
CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
};
/* Clears the contents of this circular deque. */
static void cdeque_clear(struct cdeque* d) {
d->size = 0;
d->beg_pos = 0;
d->end_pos = 0;
}
/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
* 64, 256, etc. When the user will add another item above current capacity,
* the circular deque will overwrite the oldest entry. */
static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
if(d == NULL || max_capacity_power_of_2 == 0)
return CDE_PARAM;
d->cap_mask = max_capacity_power_of_2 - 1;
d->arr = NULL;
if((max_capacity_power_of_2 & d->cap_mask) != 0)
return CDE_PARAM;
cdeque_clear(d);
d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
return d->arr ? CDE_OK : CDE_ALLOC;
}
/* Return the current size (not capacity) of circular deque `d`. */
static size_t cdeque_size(struct cdeque* d) {
return d->size;
}
/* Returns the first element of current circular deque. Note that this function
* doesn't perform any bounds checking. If you need bounds checking, use
* `cdeque_front()` function instead. */
static void cdeque_front_fast(struct cdeque* d, void** value) {
*value = (void*) d->arr[d->beg_pos];
}
/* Returns the first element of current circular deque. This function
* performs bounds checking. */
static int cdeque_front(struct cdeque* d, void** value) {
if(d->size > 0) {
cdeque_front_fast(d, value);
return CDE_OK;
} else
return CDE_OUT_OF_BOUNDS;
}
/* Pushes a new element into the end of this circular deque object. If current
* size will exceed capacity, the oldest element will be overwritten. */
static int cdeque_push_back(struct cdeque* d, void* item) {
if(d == NULL)
return CDE_PARAM;
if(d->size == d->cap_mask + 1)
return CDE_OUT_OF_BOUNDS;
d->arr[d->end_pos] = (size_t) item;
d->end_pos = (d->end_pos + 1) & d->cap_mask;
d->size++;
return CDE_OK;
}
/* Pops a front element of this circular deque object and returns its value.
* This function doesn't perform any bounds checking. */
static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
*value = (void*) d->arr[d->beg_pos];
d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
d->size--;
}
/* Pops a front element of this circular deque object and returns its value.
* This function performs bounds checking. */
static int cdeque_pop_front(struct cdeque* d, void** value) {
if(!d || !value)
return CDE_PARAM;
if(d->size == 0)
return CDE_OUT_OF_BOUNDS;
cdeque_pop_front_fast(d, value);
return CDE_OK;
}
/* Convenience function to cast filter_info** to void **. */
static void** cdeque_filter_p(struct filter_info** f) {
return (void**) (size_t) f;
}
/* Convenience function to cast filter_info* to void *. */
static void* cdeque_filter(struct filter_info* f) {
return (void**) (size_t) f;
}
/* Destroys this circular deque object. Deallocates the memory of the
* collection buffer, but doesn't deallocate the memory of any pointer passed
* to this deque as a value. */
static void cdeque_free(struct cdeque* d) {
if(!d)
return;
if(!d->arr)
return;
free(d->arr);
d->arr = NULL;
d->beg_pos = -1;
d->end_pos = -1;
d->cap_mask = 0;
}
static inline
uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
return hdr->block_flags_u8 & 7;
}
static inline
uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
return (hdr->block_flags_u8 >> 3) & 7;
}
static inline
uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
return (hdr->block_flags_u8 >> 7) & 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 uint64_t mask,
int64_t start, int64_t end)
{
if((start & mask) > (end & mask)) {
ssize_t len1 = mask + 1 - (start & mask);
ssize_t len2 = end & mask;
memcpy(dst, &window[start & mask], len1);
memcpy(dst + len1, window, len2);
} else {
memcpy(dst, &window[start & mask], (size_t) (end - start));
}
}
static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
uint8_t linear_buf[4];
circular_memcpy(linear_buf, rar->cstate.window_buf,
rar->cstate.window_mask, offset, offset + 4);
return archive_le32dec(linear_buf);
}
static void write_filter_data(struct rar5* rar, uint32_t offset,
uint32_t value)
{
archive_le32enc(&rar->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) {
struct filter_info* f =
(struct filter_info*) calloc(1, sizeof(struct filter_info));
if(!f) {
return NULL;
}
cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
return f;
}
static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
int i;
ssize_t dest_pos, src_pos = 0;
for(i = 0; i < flt->channels; i++) {
uint8_t prev_byte = 0;
for(dest_pos = i;
dest_pos < flt->block_length;
dest_pos += flt->channels)
{
uint8_t byte;
byte = rar->cstate.window_buf[
(rar->cstate.solid_offset + flt->block_start +
src_pos) & rar->cstate.window_mask];
prev_byte -= byte;
rar->cstate.filtered_buf[dest_pos] = prev_byte;
src_pos++;
}
}
return ARCHIVE_OK;
}
static int run_e8e9_filter(struct rar5* rar, 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);
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];
/*
* 0xE8 = x86's call <relative_addr_uint32> (function call)
* 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
*/
if(b == 0xE8 || (extended && b == 0xE9)) {
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);
if(addr & 0x80000000) {
if(((addr + offset) & 0x80000000) == 0) {
write_filter_data(rar, (uint32_t)i,
addr + file_size);
}
} else {
if((addr - file_size) & 0x80000000) {
uint32_t naddr = addr - offset;
write_filter_data(rar, (uint32_t)i,
naddr);
}
}
i += 4;
}
}
return ARCHIVE_OK;
}
static int run_arm_filter(struct rar5* rar, 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);
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];
if(*b == 0xEB) {
/* 0xEB = ARM's BL (branch + link) instruction. */
offset = read_filter_data(rar,
(rar->cstate.solid_offset + flt->block_start + i) &
rar->cstate.window_mask) & 0x00ffffff;
offset -= (uint32_t) ((i + flt->block_start) / 4);
offset = (offset & 0x00ffffff) | 0xeb000000;
write_filter_data(rar, (uint32_t)i, offset);
}
}
return ARCHIVE_OK;
}
static int run_filter(struct archive_read* a, struct filter_info* flt) {
int ret;
struct rar5* rar = get_context(a);
free(rar->cstate.filtered_buf);
rar->cstate.filtered_buf = malloc(flt->block_length);
if(!rar->cstate.filtered_buf) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for filter data.");
return ARCHIVE_FATAL;
}
switch(flt->type) {
case FILTER_DELTA:
ret = run_delta_filter(rar, flt);
break;
case FILTER_E8:
/* fallthrough */
case FILTER_E8E9:
ret = run_e8e9_filter(rar, flt,
flt->type == FILTER_E8E9);
break;
case FILTER_ARM:
ret = run_arm_filter(rar, flt);
break;
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported filter type: 0x%x", flt->type);
return ARCHIVE_FATAL;
}
if(ret != ARCHIVE_OK) {
/* Filter has failed. */
return ret;
}
if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
flt->block_length, rar->cstate.last_write_ptr))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Stack overflow when submitting unpacked data");
return ARCHIVE_FATAL;
}
rar->cstate.last_write_ptr += flt->block_length;
return ARCHIVE_OK;
}
/* The `push_data` function submits the selected data range to the user.
* 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,
const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
{
const uint64_t wmask = rar->cstate.window_mask;
const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
rar->cstate.last_write_ptr) & wmask;
idx_begin += rar->cstate.solid_offset;
idx_end += rar->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
* a single memcpy, but when it's wrapped, we need to copy the first
* part with one memcpy, and the second part with another memcpy. */
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 -
(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);
/* Copy the second part of the buffer. */
push_data_ready(a, rar, buf, frag2_size,
rar->cstate.last_write_ptr + frag1_size);
rar->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,
buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
rar->cstate.last_write_ptr);
rar->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,
int64_t idx_begin, int64_t idx_end)
{
push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
}
static int apply_filters(struct archive_read* a) {
struct filter_info* flt;
struct rar5* rar = get_context(a);
int ret;
rar->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,
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 +
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) {
/* Run the filter specified by descriptor
* `flt`. */
ret = run_filter(a, flt);
if(ret != ARCHIVE_OK) {
/* Filter failure, return error. */
return ret;
}
/* 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,
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,
flt->block_start);
}
/* Return 'filter applied or not needed' state to the
* caller. */
return ARCHIVE_RETRY;
}
}
rar->cstate.all_filters_applied = 1;
return ARCHIVE_OK;
}
static void dist_cache_push(struct rar5* rar, int value) {
int* q = rar->cstate.dist_cache;
q[3] = q[2];
q[2] = q[1];
q[1] = q[0];
q[0] = value;
}
static int dist_cache_touch(struct rar5* rar, int idx) {
int* q = rar->cstate.dist_cache;
int i, dist = q[idx];
for(i = idx; i > 0; i--)
q[i] = q[i - 1];
q[0] = dist;
return dist;
}
static void free_filters(struct rar5* rar) {
struct cdeque* d = &rar->cstate.filters;
/* Free any remaining filters. All filters should be naturally
* consumed by the unpacking function, so remaining filters after
* unpacking normally mean that unpacking wasn't successful.
* But still of course we shouldn't leak memory in such case. */
/* cdeque_size() is a fast operation, so we can use it as a loop
* expression. */
while(cdeque_size(d) > 0) {
struct filter_info* f = NULL;
/* Pop_front will also decrease the collection's size. */
if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
free(f);
}
cdeque_clear(d);
/* Also clear out the variables needed for sanity checking. */
rar->cstate.last_block_start = 0;
rar->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);
if(rar->main.solid) {
rar->cstate.solid_offset += rar->cstate.write_ptr;
} else {
rar->cstate.solid_offset = 0;
}
rar->cstate.write_ptr = 0;
rar->cstate.last_write_ptr = 0;
rar->cstate.last_unstore_ptr = 0;
rar->file.redir_type = REDIR_TYPE_NONE;
rar->file.redir_flags = 0;
free_filters(rar);
}
static inline int get_archive_read(struct archive* a,
struct archive_read** ar)
{
*ar = (struct archive_read*) a;
archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
"archive_read_support_format_rar5");
return ARCHIVE_OK;
}
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);
if(*ptr == NULL) {
return 0;
}
return 1;
}
static int consume(struct archive_read* a, int64_t how_many) {
int ret;
ret = how_many == __archive_read_consume(a, how_many)
? ARCHIVE_OK
: ARCHIVE_FATAL;
return ret;
}
/**
* Read a RAR5 variable sized numeric value. This value will be stored in
* `pvalue`. The `pvalue_len` argument points to a variable that will receive
* the byte count that was consumed in order to decode the `pvalue` value, plus
* one.
*
* pvalue_len is optional and can be NULL.
*
* NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
* the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
* is NULL, this consuming operation is done automatically.
*
* Returns 1 if *pvalue was successfully read.
* Returns 0 if there was an error. In this case, *pvalue contains an
* invalid value.
*/
static int read_var(struct archive_read* a, uint64_t* pvalue,
uint64_t* pvalue_len)
{
uint64_t result = 0;
size_t shift, i;
const uint8_t* p;
uint8_t b;
/* We will read maximum of 8 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))
return 0;
for(shift = 0, i = 0; i < 8; i++, shift += 7) {
b = p[i];
/* Strip the MSB from the input byte and add the resulting
* number to the `result`. */
result += (b & (uint64_t)0x7F) << shift;
/* MSB set to 1 means we need to continue decoding process.
* MSB set to 0 means we're done.
*
* This conditional checks for the second case. */
if((b & 0x80) == 0) {
if(pvalue) {
*pvalue = result;
}
/* If the caller has passed the `pvalue_len` pointer,
* store the number of consumed bytes in it and do NOT
* consume those bytes, since the caller has all the
* information it needs to perform */
if(pvalue_len) {
*pvalue_len = 1 + i;
} else {
/* If the caller did not provide the
* `pvalue_len` pointer, it will not have the
* possibility to advance the file pointer,
* because it will not know how many bytes it
* needs to consume. This is why we handle
* such situation here automatically. */
if(ARCHIVE_OK != consume(a, 1 + i)) {
return 0;
}
}
/* End of decoding process, return success. */
return 1;
}
}
/* 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;
}
static int read_var_sized(struct archive_read* a, size_t* pvalue,
size_t* pvalue_len)
{
uint64_t v;
uint64_t v_size = 0;
const int ret = pvalue_len ? read_var(a, &v, &v_size)
: read_var(a, &v, NULL);
if(ret == 1 && pvalue) {
*pvalue = (size_t) v;
}
if(pvalue_len) {
/* Possible data truncation should be safe. */
*pvalue_len = (size_t) v_size;
}
return ret;
}
static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {
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);
*value = bits;
return ARCHIVE_OK;
}
static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {
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);
*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;
}
/* n = up to 16 */
static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,
int* value)
{
uint16_t v;
int ret, num;
if(n == 0 || n > 16) {
/* This is a programmer error and should never happen
* in runtime. */
return ARCHIVE_FATAL;
}
ret = read_bits_16(rar, p, &v);
if(ret != ARCHIVE_OK)
return ret;
num = (int) v;
num >>= 16 - n;
skip_bits(rar, n);
if(value)
*value = num;
return ARCHIVE_OK;
}
static int read_u32(struct archive_read* a, uint32_t* pvalue) {
const uint8_t* p;
if(!read_ahead(a, 4, &p))
return 0;
*pvalue = archive_le32dec(p);
return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
}
static int read_u64(struct archive_read* a, uint64_t* pvalue) {
const uint8_t* p;
if(!read_ahead(a, 8, &p))
return 0;
*pvalue = archive_le64dec(p);
return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
}
static int bid_standard(struct archive_read* a) {
const uint8_t* p;
char signature[sizeof(rar5_signature_xor)];
rar5_signature(signature);
if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
return -1;
if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
return 30;
return -1;
}
static int rar5_bid(struct archive_read* a, int best_bid) {
int my_bid;
if(best_bid > 30)
return -1;
my_bid = bid_standard(a);
if(my_bid > -1) {
return my_bid;
}
return -1;
}
static int rar5_options(struct archive_read *a, const char *key,
const char *val) {
(void) a;
(void) key;
(void) val;
/* No options supported in this version. Return the ARCHIVE_WARN code
* to signal the options supervisor that the unpacker didn't handle
* setting this option. */
return ARCHIVE_WARN;
}
static void init_header(struct archive_read* a) {
a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
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;
else
rar->cstate.window_mask = 0;
}
enum HEADER_FLAGS {
HFL_EXTRA_DATA = 0x0001,
HFL_DATA = 0x0002,
HFL_SKIP_IF_UNKNOWN = 0x0004,
HFL_SPLIT_BEFORE = 0x0008,
HFL_SPLIT_AFTER = 0x0010,
HFL_CHILD = 0x0020,
HFL_INHERITED = 0x0040
};
static int process_main_locator_extra_block(struct archive_read* a,
struct rar5* rar)
{
uint64_t locator_flags;
enum LOCATOR_FLAGS {
QLIST = 0x01, RECOVERY = 0x02,
};
if(!read_var(a, &locator_flags, NULL)) {
return ARCHIVE_EOF;
}
if(locator_flags & QLIST) {
if(!read_var(a, &rar->qlist_offset, NULL)) {
return ARCHIVE_EOF;
}
/* qlist is not used */
}
if(locator_flags & RECOVERY) {
if(!read_var(a, &rar->rr_offset, NULL)) {
return ARCHIVE_EOF;
}
/* rr is not used */
}
return ARCHIVE_OK;
}
static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
ssize_t* extra_data_size)
{
size_t hash_type = 0;
size_t value_len;
enum HASH_TYPE {
BLAKE2sp = 0x00
};
if(!read_var_sized(a, &hash_type, &value_len))
return ARCHIVE_EOF;
*extra_data_size -= value_len;
if(ARCHIVE_OK != consume(a, value_len)) {
return ARCHIVE_EOF;
}
/* The file uses BLAKE2sp checksum algorithm instead of plain old
* CRC32. */
if(hash_type == BLAKE2sp) {
const uint8_t* p;
const int hash_size = sizeof(rar->file.blake2sp);
if(!read_ahead(a, hash_size, &p))
return ARCHIVE_EOF;
rar->file.has_blake2 = 1;
memcpy(&rar->file.blake2sp, p, hash_size);
if(ARCHIVE_OK != consume(a, hash_size)) {
return ARCHIVE_EOF;
}
*extra_data_size -= hash_size;
} else {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported hash type (0x%x)", (int) hash_type);
return ARCHIVE_FATAL;
}
return ARCHIVE_OK;
}
static uint64_t time_win_to_unix(uint64_t win_time) {
const size_t ns_in_sec = 10000000;
const uint64_t sec_to_unix = 11644473600LL;
return win_time / ns_in_sec - sec_to_unix;
}
static int parse_htime_item(struct archive_read* a, char unix_time,
uint64_t* where, ssize_t* extra_data_size)
{
if(unix_time) {
uint32_t time_val;
if(!read_u32(a, &time_val))
return ARCHIVE_EOF;
*extra_data_size -= 4;
*where = (uint64_t) time_val;
} else {
uint64_t windows_time;
if(!read_u64(a, &windows_time))
return ARCHIVE_EOF;
*where = time_win_to_unix(windows_time);
*extra_data_size -= 8;
}
return ARCHIVE_OK;
}
static int parse_file_extra_version(struct archive_read* a,
struct archive_entry* e, ssize_t* extra_data_size)
{
size_t flags = 0;
size_t version = 0;
size_t value_len = 0;
struct archive_string version_string;
struct archive_string name_utf8_string;
const char* cur_filename;
/* Flags are ignored. */
if(!read_var_sized(a, &flags, &value_len))
return ARCHIVE_EOF;
*extra_data_size -= value_len;
if(ARCHIVE_OK != consume(a, value_len))
return ARCHIVE_EOF;
if(!read_var_sized(a, &version, &value_len))
return ARCHIVE_EOF;
*extra_data_size -= value_len;
if(ARCHIVE_OK != consume(a, value_len))
return ARCHIVE_EOF;
/* extra_data_size should be zero here. */
cur_filename = archive_entry_pathname_utf8(e);
if(cur_filename == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Version entry without file name");
return ARCHIVE_FATAL;
}
archive_string_init(&version_string);
archive_string_init(&name_utf8_string);
/* Prepare a ;123 suffix for the filename, where '123' is the version
* value of this file. */
archive_string_sprintf(&version_string, ";%zu", version);
/* Build the new filename. */
archive_strcat(&name_utf8_string, cur_filename);
archive_strcat(&name_utf8_string, version_string.s);
/* Apply the new filename into this file's context. */
archive_entry_update_pathname_utf8(e, name_utf8_string.s);
/* Free buffers. */
archive_string_free(&version_string);
archive_string_free(&name_utf8_string);
return ARCHIVE_OK;
}
static int parse_file_extra_htime(struct archive_read* a,
struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
{
char unix_time = 0;
size_t flags = 0;
size_t value_len;
enum HTIME_FLAGS {
IS_UNIX = 0x01,
HAS_MTIME = 0x02,
HAS_CTIME = 0x04,
HAS_ATIME = 0x08,
HAS_UNIX_NS = 0x10,
};
if(!read_var_sized(a, &flags, &value_len))
return ARCHIVE_EOF;
*extra_data_size -= value_len;
if(ARCHIVE_OK != consume(a, value_len)) {
return ARCHIVE_EOF;
}
unix_time = flags & IS_UNIX;
if(flags & HAS_MTIME) {
parse_htime_item(a, unix_time, &rar->file.e_mtime,
extra_data_size);
archive_entry_set_mtime(e, rar->file.e_mtime, 0);
}
if(flags & HAS_CTIME) {
parse_htime_item(a, unix_time, &rar->file.e_ctime,
extra_data_size);
archive_entry_set_ctime(e, rar->file.e_ctime, 0);
}
if(flags & HAS_ATIME) {
parse_htime_item(a, unix_time, &rar->file.e_atime,
extra_data_size);
archive_entry_set_atime(e, rar->file.e_atime, 0);
}
if(flags & HAS_UNIX_NS) {
if(!read_u32(a, &rar->file.e_unix_ns))
return ARCHIVE_EOF;
*extra_data_size -= 4;
}
return ARCHIVE_OK;
}
static int parse_file_extra_redir(struct archive_read* a,
struct archive_entry* e, struct rar5* rar, ssize_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))
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))
return ARCHIVE_EOF;
if(ARCHIVE_OK != consume(a, (int64_t)value_size))
return ARCHIVE_EOF;
*extra_data_size -= value_size;
if(!read_var_sized(a, &target_size, NULL))
return ARCHIVE_EOF;
*extra_data_size -= target_size + 1;
if(!read_ahead(a, target_size, &p))
return ARCHIVE_EOF;
if(target_size > (MAX_NAME_IN_CHARS - 1)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Link target is too long");
return ARCHIVE_FATAL;
}
if(target_size == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"No link target specified");
return ARCHIVE_FATAL;
}
memcpy(target_utf8_buf, p, target_size);
target_utf8_buf[target_size] = 0;
if(ARCHIVE_OK != consume(a, (int64_t)target_size))
return ARCHIVE_EOF;
switch(rar->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) {
archive_entry_set_symlink_type(e,
AE_SYMLINK_TYPE_DIRECTORY);
} else {
archive_entry_set_symlink_type(e,
AE_SYMLINK_TYPE_FILE);
}
break;
case REDIR_TYPE_HARDLINK:
archive_entry_set_filetype(e, AE_IFREG);
archive_entry_update_hardlink_utf8(e, target_utf8_buf);
break;
default:
/* Unknown redir type, skip it. */
break;
}
return ARCHIVE_OK;
}
static int parse_file_extra_owner(struct archive_read* a,
struct archive_entry* e, ssize_t* extra_data_size)
{
uint64_t flags = 0;
uint64_t value_size = 0;
uint64_t id = 0;
size_t name_len = 0;
size_t name_size = 0;
char namebuf[OWNER_MAXNAMELEN];
const uint8_t* p;
if(!read_var(a, &flags, &value_size))
return ARCHIVE_EOF;
if(ARCHIVE_OK != consume(a, (int64_t)value_size))
return ARCHIVE_EOF;
*extra_data_size -= value_size;
if ((flags & OWNER_USER_NAME) != 0) {
if(!read_var_sized(a, &name_size, NULL))
return ARCHIVE_EOF;
*extra_data_size -= name_size + 1;
if(!read_ahead(a, name_size, &p))
return ARCHIVE_EOF;
if (name_size >= OWNER_MAXNAMELEN) {
name_len = OWNER_MAXNAMELEN - 1;
} else {
name_len = name_size;
}
memcpy(namebuf, p, name_len);
namebuf[name_len] = 0;
if(ARCHIVE_OK != consume(a, (int64_t)name_size))
return ARCHIVE_EOF;
archive_entry_set_uname(e, namebuf);
}
if ((flags & OWNER_GROUP_NAME) != 0) {
if(!read_var_sized(a, &name_size, NULL))
return ARCHIVE_EOF;
*extra_data_size -= name_size + 1;
if(!read_ahead(a, name_size, &p))
return ARCHIVE_EOF;
if (name_size >= OWNER_MAXNAMELEN) {
name_len = OWNER_MAXNAMELEN - 1;
} else {
name_len = name_size;
}
memcpy(namebuf, p, name_len);
namebuf[name_len] = 0;
if(ARCHIVE_OK != consume(a, (int64_t)name_size))
return ARCHIVE_EOF;
archive_entry_set_gname(e, namebuf);
}
if ((flags & OWNER_USER_UID) != 0) {
if(!read_var(a, &id, &value_size))
return ARCHIVE_EOF;
if(ARCHIVE_OK != consume(a, (int64_t)value_size))
return ARCHIVE_EOF;
*extra_data_size -= value_size;
archive_entry_set_uid(e, (la_int64_t)id);
}
if ((flags & OWNER_GROUP_GID) != 0) {
if(!read_var(a, &id, &value_size))
return ARCHIVE_EOF;
if(ARCHIVE_OK != consume(a, (int64_t)value_size))
return ARCHIVE_EOF;
*extra_data_size -= value_size;
archive_entry_set_gid(e, (la_int64_t)id);
}
return ARCHIVE_OK;
}
static int process_head_file_extra(struct archive_read* a,
struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)
{
size_t extra_field_size;
size_t extra_field_id = 0;
int ret = ARCHIVE_FATAL;
size_t var_size;
while(extra_data_size > 0) {
if(!read_var_sized(a, &extra_field_size, &var_size))
return ARCHIVE_EOF;
extra_data_size -= var_size;
if(ARCHIVE_OK != consume(a, var_size)) {
return ARCHIVE_EOF;
}
if(!read_var_sized(a, &extra_field_id, &var_size))
return ARCHIVE_EOF;
extra_data_size -= var_size;
if(ARCHIVE_OK != consume(a, var_size)) {
return ARCHIVE_EOF;
}
switch(extra_field_id) {
case EX_HASH:
ret = parse_file_extra_hash(a, rar,
&extra_data_size);
break;
case EX_HTIME:
ret = parse_file_extra_htime(a, e, rar,
&extra_data_size);
break;
case EX_REDIR:
ret = parse_file_extra_redir(a, e, rar,
&extra_data_size);
break;
case EX_UOWNER:
ret = parse_file_extra_owner(a, e,
&extra_data_size);
break;
case EX_VERSION:
ret = parse_file_extra_version(a, e,
&extra_data_size);
break;
case EX_CRYPT:
/* fallthrough */
case EX_SUBDATA:
/* fallthrough */
default:
/* Skip unsupported entry. */
return consume(a, extra_data_size);
}
}
if(ret != ARCHIVE_OK) {
/* Attribute not implemented. */
return ret;
}
return ARCHIVE_OK;
}
static int process_head_file(struct archive_read* a, struct rar5* rar,
struct archive_entry* entry, size_t block_flags)
{
ssize_t extra_data_size = 0;
size_t data_size = 0;
size_t file_flags = 0;
size_t file_attr = 0;
size_t compression_info = 0;
size_t host_os = 0;
size_t name_size = 0;
uint64_t unpacked_size, window_size;
uint32_t mtime = 0, crc = 0;
int c_method = 0, c_version = 0;
char name_utf8_buf[MAX_NAME_IN_BYTES];
const uint8_t* p;
enum FILE_FLAGS {
DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
UNKNOWN_UNPACKED_SIZE = 0x0008,
};
enum FILE_ATTRS {
ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
ATTR_DIRECTORY = 0x10,
};
enum COMP_INFO_FLAGS {
SOLID = 0x0040,
};
enum HOST_OS {
HOST_WINDOWS = 0,
HOST_UNIX = 1,
};
archive_entry_clear(entry);
/* Do not reset file context if we're switching archives. */
if(!rar->cstate.switch_multivolume) {
reset_file_context(rar);
}
if(block_flags & HFL_EXTRA_DATA) {
size_t edata_size = 0;
if(!read_var_sized(a, &edata_size, NULL))
return ARCHIVE_EOF;
/* Intentional type cast from unsigned to signed. */
extra_data_size = (ssize_t) edata_size;
}
if(block_flags & HFL_DATA) {
if(!read_var_sized(a, &data_size, NULL))
return ARCHIVE_EOF;
rar->file.bytes_remaining = data_size;
} else {
rar->file.bytes_remaining = 0;
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"no data found in file/service block");
return ARCHIVE_FATAL;
}
if(!read_var_sized(a, &file_flags, NULL))
return ARCHIVE_EOF;
if(!read_var(a, &unpacked_size, NULL))
return ARCHIVE_EOF;
if(file_flags & UNKNOWN_UNPACKED_SIZE) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Files with unknown unpacked size are not supported");
return ARCHIVE_FATAL;
}
rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
if(!read_var_sized(a, &file_attr, NULL))
return ARCHIVE_EOF;
if(file_flags & UTIME) {
if(!read_u32(a, &mtime))
return ARCHIVE_EOF;
}
if(file_flags & CRC32) {
if(!read_u32(a, &crc))
return ARCHIVE_EOF;
}
if(!read_var_sized(a, &compression_info, NULL))
return ARCHIVE_EOF;
c_method = (int) (compression_info >> 7) & 0x7;
c_version = (int) (compression_info & 0x3f);
/* RAR5 seems to limit the dictionary size to 64MB. */
window_size = (rar->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;
/* Archives which declare solid files without initializing the window
* buffer first are invalid. */
if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Declared solid file, but no window buffer "
"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))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Declared dictionary size is not supported.");
return ARCHIVE_FATAL;
}
if(rar->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)
{
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. ");
return ARCHIVE_FATAL;
}
}
/* If we're currently switching volumes, ignore the new definition of
* window_size. */
if(rar->cstate.switch_multivolume == 0) {
/* Values up to 64M should fit into ssize_t on every
* architecture. */
rar->cstate.window_size = (ssize_t) window_size;
}
if(rar->file.solid > 0 && rar->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;
}
init_window_mask(rar);
rar->file.service = 0;
if(!read_var_sized(a, &host_os, NULL))
return ARCHIVE_EOF;
if(host_os == HOST_WINDOWS) {
/* Host OS is Windows */
__LA_MODE_T mode;
if(file_attr & ATTR_DIRECTORY) {
if (file_attr & ATTR_READONLY) {
mode = 0555 | AE_IFDIR;
} else {
mode = 0755 | AE_IFDIR;
}
} else {
if (file_attr & ATTR_READONLY) {
mode = 0444 | AE_IFREG;
} else {
mode = 0644 | AE_IFREG;
}
}
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(char));
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) {
/* Delete trailing comma */
*(ptr - 1) = '\0';
archive_entry_copy_fflags_text(entry,
fflags_text);
}
free(fflags_text);
}
}
} else if(host_os == HOST_UNIX) {
/* Host OS is Unix */
archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
} else {
/* Unknown host OS */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported Host OS: 0x%x", (int) host_os);
return ARCHIVE_FATAL;
}
if(!read_var_sized(a, &name_size, NULL))
return ARCHIVE_EOF;
if(!read_ahead(a, name_size, &p))
return ARCHIVE_EOF;
if(name_size > (MAX_NAME_IN_CHARS - 1)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Filename is too long");
return ARCHIVE_FATAL;
}
if(name_size == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"No filename specified");
return ARCHIVE_FATAL;
}
memcpy(name_utf8_buf, p, name_size);
name_utf8_buf[name_size] = 0;
if(ARCHIVE_OK != consume(a, name_size)) {
return ARCHIVE_EOF;
}
archive_entry_update_pathname_utf8(entry, name_utf8_buf);
if(extra_data_size > 0) {
int ret = process_head_file_extra(a, entry, rar,
extra_data_size);
/*
* TODO: rewrite or remove useless sanity check
* as extra_data_size is not passed as a pointer
*
if(extra_data_size < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"File extra data size is not zero");
return ARCHIVE_FATAL;
}
*/
if(ret != ARCHIVE_OK)
return ret;
}
if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
rar->file.unpacked_size = (ssize_t) unpacked_size;
if(rar->file.redir_type == REDIR_TYPE_NONE)
archive_entry_set_size(entry, unpacked_size);
}
if(file_flags & UTIME) {
archive_entry_set_mtime(entry, (time_t) mtime, 0);
}
if(file_flags & CRC32) {
rar->file.stored_crc32 = crc;
}
if(!rar->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;
}
if(rar->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
* another file, it must call rar5_read_header() function
* again. */
return ARCHIVE_RETRY;
} else {
return ARCHIVE_OK;
}
}
static int process_head_service(struct archive_read* a, struct rar5* rar,
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);
if(ret != ARCHIVE_OK)
return ret;
rar->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
* properly unpack the file. */
ret = rar5_read_data_skip(a);
if(ret != ARCHIVE_OK)
return ret;
/* After skipping, try parsing another block automatically. */
return ARCHIVE_RETRY;
}
static int process_head_main(struct archive_read* a, struct rar5* rar,
struct archive_entry* entry, size_t block_flags)
{
int ret;
size_t extra_data_size = 0;
size_t extra_field_size = 0;
size_t extra_field_id = 0;
size_t archive_flags = 0;
enum MAIN_FLAGS {
VOLUME = 0x0001, /* multi-volume archive */
VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
* have it */
SOLID = 0x0004, /* solid archive */
PROTECT = 0x0008, /* contains Recovery info */
LOCK = 0x0010, /* readonly flag, not used */
};
enum MAIN_EXTRA {
// Just one attribute here.
LOCATOR = 0x01,
};
(void) entry;
if(block_flags & HFL_EXTRA_DATA) {
if(!read_var_sized(a, &extra_data_size, NULL))
return ARCHIVE_EOF;
} else {
extra_data_size = 0;
}
if(!read_var_sized(a, &archive_flags, NULL)) {
return ARCHIVE_EOF;
}
rar->main.volume = (archive_flags & VOLUME) > 0;
rar->main.solid = (archive_flags & SOLID) > 0;
if(archive_flags & VOLUME_NUMBER) {
size_t v = 0;
if(!read_var_sized(a, &v, NULL)) {
return ARCHIVE_EOF;
}
if (v > UINT_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid volume number");
return ARCHIVE_FATAL;
}
rar->main.vol_no = (unsigned int) v;
} else {
rar->main.vol_no = 0;
}
if(rar->vol.expected_vol_no > 0 &&
rar->main.vol_no != rar->vol.expected_vol_no)
{
/* Returning EOF instead of FATAL because of strange
* libarchive behavior. When opening multiple files via
* archive_read_open_filenames(), after reading up the whole
* last file, the __archive_read_ahead function wraps up to
* the first archive instead of returning EOF. */
return ARCHIVE_EOF;
}
if(extra_data_size == 0) {
/* Early return. */
return ARCHIVE_OK;
}
if(!read_var_sized(a, &extra_field_size, NULL)) {
return ARCHIVE_EOF;
}
if(!read_var_sized(a, &extra_field_id, NULL)) {
return ARCHIVE_EOF;
}
if(extra_field_size == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid extra field size");
return ARCHIVE_FATAL;
}
switch(extra_field_id) {
case LOCATOR:
ret = process_main_locator_extra_block(a, rar);
if(ret != ARCHIVE_OK) {
/* Error while parsing main locator extra
* block. */
return ret;
}
break;
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported extra type (0x%x)",
(int) extra_field_id);
return ARCHIVE_FATAL;
}
return ARCHIVE_OK;
}
static int skip_unprocessed_bytes(struct archive_read* a) {
struct rar5* rar = get_context(a);
int ret;
if(rar->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) {
/* 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);
if(ret != ARCHIVE_OK) {
return ret;
}
rar->file.bytes_remaining = 0;
} else {
/* If we're not in merge mode, use safe skipping code.
* This will ensure we'll handle solid archives
* properly. */
ret = rar5_read_data_skip(a);
if(ret != ARCHIVE_OK) {
return ret;
}
}
}
return ARCHIVE_OK;
}
static int scan_for_signature(struct archive_read* a);
/* Base block processing function. A 'base block' is a RARv5 header block
* that tells the reader what kind of data is stored inside the block.
*
* From the birds-eye view a RAR file looks file this:
*
* <magic><base_block_1><base_block_2>...<base_block_n>
*
* There are a few types of base blocks. Those types are specified inside
* the 'switch' statement in this function. For example purposes, I'll write
* how a standard RARv5 file could look like here:
*
* <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
*
* The structure above could describe an archive file with 3 files in it,
* one service "QuickOpen" block (that is ignored by this parser), and an
* end of file base block marker.
*
* If the file is stored in multiple archive files ("multiarchive"), it might
* look like this:
*
* .part01.rar: <magic><MAIN><FILE><ENDARC>
* .part02.rar: <magic><MAIN><FILE><ENDARC>
* .part03.rar: <magic><MAIN><FILE><ENDARC>
*
* This example could describe 3 RAR files that contain ONE archived file.
* Or it could describe 3 RAR files that contain 3 different files. Or 3
* RAR files than contain 2 files. It all depends what metadata is stored in
* the headers of <FILE> blocks.
*
* Each <FILE> block contains info about its size, the name of the file it's
* storing inside, and whether this FILE block is a continuation block of
* previous archive ('split before'), and is this FILE block should be
* continued in another archive ('split after'). By parsing the 'split before'
* and 'split after' flags, we're able to tell if multiple <FILE> base blocks
* are describing one file, or multiple files (with the same filename, for
* example).
*
* One thing to note is that if we're parsing the first <FILE> block, and
* we see 'split after' flag, then we need to jump over to another <FILE>
* block to be able to decompress rest of the data. To do this, we need
* to skip the <ENDARC> block, then switch to another file, then skip the
* <magic> block, <MAIN> block, and then we're standing on the proper
* <FILE> block.
*/
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);
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;
int ret;
enum HEADER_TYPE {
HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
HEAD_UNKNOWN = 0xff,
};
/* Skip any unprocessed data for this file. */
ret = skip_unprocessed_bytes(a);
if(ret != ARCHIVE_OK)
return ret;
/* Read the expected CRC32 checksum. */
if(!read_u32(a, &hdr_crc)) {
return ARCHIVE_EOF;
}
/* Read header size. */
if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
return ARCHIVE_EOF;
}
hdr_size = raw_hdr_size + hdr_size_len;
/* Sanity check, maximum header size for RAR5 is 2MB. */
if(hdr_size > (2 * 1024 * 1024)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Base block header is too large");
return ARCHIVE_FATAL;
}
/* Additional sanity checks to weed out invalid files. */
if(raw_hdr_size == 0 || hdr_size_len == 0 ||
hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Too small block encountered (%zu bytes)",
raw_hdr_size);
return ARCHIVE_FATAL;
}
/* Read the whole header data into memory, maximum memory use here is
* 2MB. */
if(!read_ahead(a, hdr_size, &p)) {
return ARCHIVE_EOF;
}
/* Verify the CRC32 of the header data. */
computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
if(computed_crc != hdr_crc) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Header CRC error");
return ARCHIVE_FATAL;
}
/* If the checksum is OK, we proceed with parsing. */
if(ARCHIVE_OK != consume(a, hdr_size_len)) {
return ARCHIVE_EOF;
}
if(!read_var_sized(a, &header_id, NULL))
return ARCHIVE_EOF;
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;
/* Those are possible header ids in RARv5. */
switch(header_id) {
case HEAD_MAIN:
ret = process_head_main(a, rar, 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)
return ARCHIVE_RETRY;
return ret;
case HEAD_SERVICE:
ret = process_head_service(a, rar, entry, header_flags);
return ret;
case HEAD_FILE:
ret = process_head_file(a, rar, entry, header_flags);
return ret;
case HEAD_CRYPT:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Encryption is not supported");
return ARCHIVE_FATAL;
case HEAD_ENDARC:
rar->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) {
/* In case there is part02.rar, position the
* read pointer in a proper place, so we can
* resume parsing. */
ret = scan_for_signature(a);
if(ret == ARCHIVE_FATAL) {
return ARCHIVE_EOF;
} else {
if(rar->vol.expected_vol_no ==
UINT_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Header error");
return ARCHIVE_FATAL;
}
rar->vol.expected_vol_no =
rar->main.vol_no + 1;
return ARCHIVE_OK;
}
} else {
return ARCHIVE_EOF;
}
case HEAD_MARK:
return ARCHIVE_EOF;
default:
if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Header type error");
return ARCHIVE_FATAL;
} else {
/* If the block is marked as 'skip if unknown',
* do as the flag says: skip the block
* instead on failing on it. */
return ARCHIVE_RETRY;
}
}
#if !defined WIN32
// Not reached.
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Internal unpacker error");
return ARCHIVE_FATAL;
#endif
}
static int skip_base_block(struct archive_read* a) {
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();
ret = process_base_block(a, entry);
/* Discard operations on this archive_entry structure. */
archive_entry_free(entry);
if(ret == ARCHIVE_FATAL)
return ret;
if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
return ARCHIVE_OK;
if(ret == ARCHIVE_OK)
return ARCHIVE_RETRY;
else
return ret;
}
static int rar5_read_header(struct archive_read *a,
struct archive_entry *entry)
{
struct rar5* rar = get_context(a);
int ret;
if(rar->header_initialized == 0) {
init_header(a);
rar->header_initialized = 1;
}
if(rar->skipped_magic == 0) {
if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
return ARCHIVE_EOF;
}
rar->skipped_magic = 1;
}
do {
ret = process_base_block(a, entry);
} while(ret == ARCHIVE_RETRY ||
(rar->main.endarc > 0 && ret == ARCHIVE_OK));
return ret;
}
static void init_unpack(struct rar5* rar) {
rar->file.calculated_crc32 = 0;
init_window_mask(rar);
free(rar->cstate.window_buf);
free(rar->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;
}
rar->cstate.write_ptr = 0;
rar->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));
}
static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
int verify_crc;
if(rar->skip_mode) {
#if defined CHECK_CRC_ON_SOLID_SKIP
verify_crc = 1;
#else
verify_crc = 0;
#endif
} else
verify_crc = 1;
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, to_read);
}
/* Check if the file uses an optional BLAKE2sp checksum
* algorithm. */
if(rar->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);
}
}
}
static int create_decode_tables(uint8_t* bit_length,
struct decode_table* table, int size)
{
int code, upper_limit = 0, i, lc[16];
uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
ssize_t cur_len, quick_data_size;
memset(&lc, 0, sizeof(lc));
memset(table->decode_num, 0, sizeof(table->decode_num));
table->size = size;
table->quick_bits = size == HUFF_NC ? 10 : 7;
for(i = 0; i < size; i++) {
lc[bit_length[i] & 15]++;
}
lc[0] = 0;
table->decode_pos[0] = 0;
table->decode_len[0] = 0;
for(i = 1; i < 16; i++) {
upper_limit += lc[i];
table->decode_len[i] = upper_limit << (16 - i);
table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
upper_limit <<= 1;
}
memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
for(i = 0; i < size; i++) {
uint8_t clen = bit_length[i] & 15;
if(clen > 0) {
int last_pos = decode_pos_clone[clen];
table->decode_num[last_pos] = i;
decode_pos_clone[clen]++;
}
}
quick_data_size = (int64_t)1 << table->quick_bits;
cur_len = 1;
for(code = 0; code < quick_data_size; code++) {
int bit_field = code << (16 - table->quick_bits);
int dist, pos;
while(cur_len < rar5_countof(table->decode_len) &&
bit_field >= table->decode_len[cur_len]) {
cur_len++;
}
table->quick_len[code] = (uint8_t) cur_len;
dist = bit_field - table->decode_len[cur_len - 1];
dist >>= (16 - cur_len);
pos = table->decode_pos[cur_len & 15] + dist;
if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
table->quick_num[code] = table->decode_num[pos];
} else {
table->quick_num[code] = 0;
}
}
return ARCHIVE_OK;
}
static int decode_number(struct archive_read* a, struct decode_table* table,
const uint8_t* p, uint16_t* num)
{
int i, bits, dist;
uint16_t bitfield;
uint32_t pos;
struct rar5* rar = get_context(a);
if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
return ARCHIVE_EOF;
}
bitfield &= 0xfffe;
if(bitfield < table->decode_len[table->quick_bits]) {
int code = bitfield >> (16 - table->quick_bits);
skip_bits(rar, table->quick_len[code]);
*num = table->quick_num[code];
return ARCHIVE_OK;
}
bits = 15;
for(i = table->quick_bits + 1; i < 15; i++) {
if(bitfield < table->decode_len[i]) {
bits = i;
break;
}
}
skip_bits(rar, bits);
dist = bitfield - table->decode_len[bits - 1];
dist >>= (16 - bits);
pos = table->decode_pos[bits] + dist;
if(pos >= table->size)
pos = 0;
*num = table->decode_num[pos];
return ARCHIVE_OK;
}
/* Reads and parses Huffman tables from the beginning of the block. */
static int parse_tables(struct archive_read* a, struct rar5* rar,
const uint8_t* p)
{
int ret, value, i, w, idx = 0;
uint8_t bit_length[HUFF_BC],
table[HUFF_TABLE_SIZE],
nibble_mask = 0xF0,
nibble_shift = 4;
enum { ESCAPE = 15 };
/* 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) {
/* Truncated data, can't continue. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated data in huffman tables");
return ARCHIVE_FATAL;
}
value = (p[i] & nibble_mask) >> nibble_shift;
if(nibble_mask == 0x0F)
++i;
nibble_mask ^= 0xFF;
nibble_shift ^= 4;
/* Values smaller than 15 is data, so we write it directly.
* Value 15 is a flag telling us that we need to unpack more
* bytes. */
if(value == ESCAPE) {
value = (p[i] & nibble_mask) >> nibble_shift;
if(nibble_mask == 0x0F)
++i;
nibble_mask ^= 0xFF;
nibble_shift ^= 4;
if(value == 0) {
/* We sometimes need to write the actual value
* of 15, so this case handles that. */
bit_length[w++] = ESCAPE;
} else {
int k;
/* Fill zeroes. */
for(k = 0; (k < value + 2) && (w < HUFF_BC);
k++) {
bit_length[w++] = 0;
}
}
} else {
bit_length[w++] = value;
}
}
rar->bits.in_addr = i;
rar->bits.bit_addr = nibble_shift ^ 4;
ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Decoding huffman tables failed");
return ARCHIVE_FATAL;
}
for(i = 0; i < HUFF_TABLE_SIZE;) {
uint16_t num;
if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {
/* Truncated data, can't continue. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated data in huffman tables (#2)");
return ARCHIVE_FATAL;
}
ret = decode_number(a, &rar->cstate.bd, p, &num);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Decoding huffman tables failed");
return ARCHIVE_FATAL;
}
if(num < 16) {
/* 0..15: store directly */
table[i] = (uint8_t) num;
i++;
} else if(num < 18) {
/* 16..17: repeat previous code */
uint16_t n;
if(ARCHIVE_OK != read_bits_16(rar, p, &n))
return ARCHIVE_EOF;
if(num == 16) {
n >>= 13;
n += 3;
skip_bits(rar, 3);
} else {
n >>= 9;
n += 11;
skip_bits(rar, 7);
}
if(i > 0) {
while(n-- > 0 && i < HUFF_TABLE_SIZE) {
table[i] = table[i - 1];
i++;
}
} else {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Unexpected error when decoding "
"huffman tables");
return ARCHIVE_FATAL;
}
} else {
/* other codes: fill with zeroes `n` times */
uint16_t n;
if(ARCHIVE_OK != read_bits_16(rar, p, &n))
return ARCHIVE_EOF;
if(num == 18) {
n >>= 13;
n += 3;
skip_bits(rar, 3);
} else {
n >>= 9;
n += 11;
skip_bits(rar, 7);
}
while(n-- > 0 && i < HUFF_TABLE_SIZE)
table[i++] = 0;
}
}
ret = create_decode_tables(&table[idx], &rar->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;
}
idx += HUFF_NC;
ret = create_decode_tables(&table[idx], &rar->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;
}
idx += HUFF_DC;
ret = create_decode_tables(&table[idx], &rar->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;
}
idx += HUFF_LDC;
ret = create_decode_tables(&table[idx], &rar->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_OK;
}
/* Parses the block header, verifies its CRC byte, and saves the header
* fields inside the `hdr` pointer. */
static int parse_block_header(struct archive_read* a, const uint8_t* p,
ssize_t* block_size, struct compressed_block_header* hdr)
{
uint8_t calculated_cksum;
memcpy(hdr, p, sizeof(struct compressed_block_header));
if(bf_byte_count(hdr) > 2) {
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;
}
/* This should probably use bit reader interface in order to be more
* future-proof. */
*block_size = 0;
switch(bf_byte_count(hdr)) {
/* 1-byte block size */
case 0:
*block_size = *(const uint8_t*) &p[2];
break;
/* 2-byte block size */
case 1:
*block_size = archive_le16dec(&p[2]);
break;
/* 3-byte block size */
case 2:
*block_size = archive_le32dec(&p[2]);
*block_size &= 0x00FFFFFF;
break;
/* Other block sizes are not supported. This case is not
* reached, because we have an 'if' guard before the switch
* that makes sure of it. */
default:
return ARCHIVE_FATAL;
}
/* Verify the block header checksum. 0x5A is a magic value and is
* always * constant. */
calculated_cksum = 0x5A
^ (uint8_t) hdr->block_flags_u8
^ (uint8_t) *block_size
^ (uint8_t) (*block_size >> 8)
^ (uint8_t) (*block_size >> 16);
if(calculated_cksum != hdr->block_cksum) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Block checksum error: got 0x%x, expected 0x%x",
hdr->block_cksum, calculated_cksum);
return ARCHIVE_FATAL;
}
return ARCHIVE_OK;
}
/* Convenience function used during filter processing. */
static int parse_filter_data(struct rar5* rar, const uint8_t* p,
uint32_t* filter_data)
{
int i, bytes;
uint32_t data = 0;
if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
return ARCHIVE_EOF;
bytes++;
for(i = 0; i < bytes; i++) {
uint16_t byte;
if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
return ARCHIVE_EOF;
}
/* 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);
}
*filter_data = data;
return ARCHIVE_OK;
}
/* Function is used during sanity checking. */
static int is_valid_filter_block_start(struct rar5* rar,
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;
if(last_bs == 0 || last_bl == 0) {
/* We didn't have any filters yet, so accept this offset. */
return 1;
}
if(block_start >= last_bs + last_bl) {
/* Current offset is bigger than last block's end offset, so
* accept current offset. */
return 1;
}
/* Any other case is not a normal situation and we should fail. */
return 0;
}
/* 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) {
uint32_t block_start, block_length;
uint16_t filter_type;
struct filter_info* filt = NULL;
struct rar5* rar = get_context(ar);
/* Read the parameters from the input stream. */
if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
return ARCHIVE_EOF;
if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
return ARCHIVE_EOF;
if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
return ARCHIVE_EOF;
filter_type >>= 13;
skip_bits(rar, 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. */
if(block_length < 4 ||
block_length > 0x400000 ||
filter_type > FILTER_ARM ||
!is_valid_filter_block_start(rar, block_start))
{
archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid filter encountered");
return ARCHIVE_FATAL;
}
/* Allocate a new filter. */
filt = add_new_filter(rar);
if(filt == NULL) {
archive_set_error(&ar->archive, ENOMEM,
"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_length = block_length;
rar->cstate.last_block_start = filt->block_start;
rar->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
* read. */
if(filter_type == FILTER_DELTA) {
int channels;
if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
return ARCHIVE_EOF;
filt->channels = channels + 1;
}
return ARCHIVE_OK;
}
static int decode_code_length(struct rar5* rar, const uint8_t* p,
uint16_t code)
{
int lbits, length = 2;
if(code < 8) {
lbits = 0;
length += code;
} else {
lbits = code / 4 - 1;
length += (4 | (code & 3)) << lbits;
}
if(lbits > 0) {
int add;
if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
return -1;
length += add;
}
return length;
}
static int copy_string(struct archive_read* a, int len, int dist) {
struct rar5* rar = get_context(a);
const uint64_t cmask = rar->cstate.window_mask;
const uint64_t write_ptr = rar->cstate.write_ptr +
rar->cstate.solid_offset;
int i;
if (rar->cstate.window_buf == NULL)
return ARCHIVE_FATAL;
/* The unpacker spends most of the time in this function. It would be
* a good idea to introduce some optimizations here.
*
* Just remember that this loop treats buffers that overlap differently
* than buffers that do not overlap. This is why a simple memcpy(3)
* call will not be enough. */
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];
}
rar->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);
uint16_t num;
int ret;
const uint64_t cmask = rar->cstate.window_mask;
const struct compressed_block_header* hdr = &rar->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)) {
/* 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
* from this moment. */
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 the program counter is here, it means the
* function has finished processing the block. */
rar->cstate.block_parsing_finished = 1;
break;
}
/* Decode the next literal. */
if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
return ARCHIVE_EOF;
}
/* Num holds a decompression literal, or 'command code'.
*
* - Values lower than 256 are just bytes. Those codes
* can be stored in the output buffer directly.
*
* - Code 256 defines a new filter, which is later used to
* ransform the data block accordingly to the filter type.
* The data block needs to be fully uncompressed first.
*
* - Code bigger than 257 and smaller than 262 define
* a repetition pattern that should be copied from
* an already uncompressed chunk of data.
*/
if(num < 256) {
/* Directly store the byte. */
int64_t write_idx = rar->cstate.solid_offset +
rar->cstate.write_ptr++;
rar->cstate.window_buf[write_idx & cmask] =
(uint8_t) num;
continue;
} else if(num >= 262) {
uint16_t dist_slot;
int len = decode_code_length(rar, p, num - 262),
dbits,
dist = 1;
if(len == -1) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the code length");
return ARCHIVE_FATAL;
}
if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
&dist_slot))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the distance slot");
return ARCHIVE_FATAL;
}
if(dist_slot < 4) {
dbits = 0;
dist += dist_slot;
} else {
dbits = dist_slot / 2 - 1;
/* Cast to uint32_t will make sure the shift
* left operation won't produce undefined
* result. Then, the uint32_t type will
* be implicitly casted to int. */
dist += (uint32_t) (2 |
(dist_slot & 1)) << dbits;
}
if(dbits > 0) {
if(dbits >= 4) {
uint32_t add = 0;
uint16_t low_dist;
if(dbits > 4) {
if(ARCHIVE_OK != read_bits_32(
rar, p, &add)) {
/* Return EOF if we
* can't read more
* data. */
return ARCHIVE_EOF;
}
skip_bits(rar, dbits - 4);
add = (add >> (
36 - dbits)) << 4;
dist += add;
}
if(ARCHIVE_OK != decode_number(a,
&rar->cstate.ldd, p, &low_dist))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the "
"distance slot");
return ARCHIVE_FATAL;
}
if(dist >= INT_MAX - low_dist - 1) {
/* This only happens in
* invalid archives. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Distance pointer "
"overflow");
return ARCHIVE_FATAL;
}
dist += low_dist;
} else {
/* dbits is one of [0,1,2,3] */
int add;
if(ARCHIVE_OK != read_consume_bits(rar,
p, dbits, &add)) {
/* Return EOF if we can't read
* more data. */
return ARCHIVE_EOF;
}
dist += add;
}
}
if(dist > 0x100) {
len++;
if(dist > 0x2000) {
len++;
if(dist > 0x40000) {
len++;
}
}
}
dist_cache_push(rar, dist);
rar->cstate.last_len = len;
if(ARCHIVE_OK != copy_string(a, len, dist))
return ARCHIVE_FATAL;
continue;
} else if(num == 256) {
/* Create a filter. */
ret = parse_filter(a, p);
if(ret != ARCHIVE_OK)
return ret;
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;
}
}
continue;
} else {
/* num < 262 */
const int idx = num - 258;
const int dist = dist_cache_touch(rar, idx);
uint16_t len_slot;
int len;
if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
&len_slot)) {
return ARCHIVE_FATAL;
}
len = decode_code_length(rar, p, len_slot);
rar->cstate.last_len = len;
if(ARCHIVE_OK != copy_string(a, len, dist))
return ARCHIVE_FATAL;
continue;
}
/* The program counter shouldn't reach here. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported block code: 0x%x", num);
return ARCHIVE_FATAL;
}
return ARCHIVE_OK;
}
/* Binary search for the RARv5 signature. */
static int scan_for_signature(struct archive_read* a) {
const uint8_t* p;
const int chunk_size = 512;
ssize_t i;
char signature[sizeof(rar5_signature_xor)];
/* If we're here, it means we're on an 'unknown territory' data.
* There's no indication what kind of data we're reading here.
* It could be some text comment, any kind of binary data,
* digital sign, dragons, etc.
*
* We want to find a valid RARv5 magic header inside this unknown
* data. */
/* Is it possible in libarchive to just skip everything until the
* end of the file? If so, it would be a better approach than the
* current implementation of this function. */
rar5_signature(signature);
while(1) {
if(!read_ahead(a, chunk_size, &p))
return ARCHIVE_EOF;
for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
i++) {
if(memcmp(&p[i], signature,
sizeof(rar5_signature_xor)) == 0) {
/* Consume the number of bytes we've used to
* search for the signature, as well as the
* number of bytes used by the signature
* itself. After this we should be standing
* on a valid base block header. */
(void) consume(a,
i + sizeof(rar5_signature_xor));
return ARCHIVE_OK;
}
}
consume(a, chunk_size);
}
return ARCHIVE_FATAL;
}
/* 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) {
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
* assumes that current stream pointer points to some base block
* header.
*
* The `endarc` field is being set when the base block parsing
* function encounters the 'end of archive' marker.
*/
while(1) {
if(rar->main.endarc == 1) {
int looping = 1;
rar->main.endarc = 0;
while(looping) {
lret = skip_base_block(a);
switch(lret) {
case ARCHIVE_RETRY:
/* Continue looping. */
break;
case ARCHIVE_OK:
/* Break loop. */
looping = 0;
break;
default:
/* Forward any errors to the
* caller. */
return lret;
}
}
break;
} else {
/* Skip current base block. In order to properly skip
* it, we really need to simply parse it and discard
* the results. */
lret = skip_base_block(a);
if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
return lret;
/* The `skip_base_block` function tells us if we
* should continue with skipping, or we should stop
* skipping. We're trying to skip everything up to
* a base FILE block. */
if(lret != ARCHIVE_RETRY) {
/* If there was an error during skipping, or we
* have just skipped a FILE base block... */
if(rar->main.endarc == 0) {
return lret;
} else {
continue;
}
}
}
}
return ARCHIVE_OK;
}
/* Merges the partial block from the first multivolume archive file, and
* partial block from the second multivolume archive file. The result is
* a chunk of memory containing the whole block, and the stream pointer
* is advanced to the next block in the second multivolume archive file. */
static int merge_block(struct archive_read* a, ssize_t block_size,
const uint8_t** p)
{
struct rar5* rar = get_context(a);
ssize_t cur_block_size, partial_offset = 0;
const uint8_t* lp;
int ret;
if(rar->merge_mode) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Recursive merge is not allowed");
return ARCHIVE_FATAL;
}
/* Set a flag that we're in the switching mode. */
rar->cstate.switch_multivolume = 1;
/* Reallocate the memory which will hold the whole block. */
if(rar->vol.push_buf)
free((void*) rar->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) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for a merge block buffer.");
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);
/* A single block can span across multiple multivolume archive files,
* so we use a loop here. This loop will consume enough multivolume
* archive files until the whole block is read. */
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,
block_size - partial_offset);
if(cur_block_size == 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Encountered block size == 0 during block merge");
return ARCHIVE_FATAL;
}
if(!read_ahead(a, cur_block_size, &lp))
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.");
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);
/* Advance the stream read pointer by this block chunk size. */
if(ARCHIVE_OK != consume(a, cur_block_size))
return ARCHIVE_EOF;
/* 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;
/* If `partial_offset` is the same as `block_size`, this means
* we've merged all block chunks and we have a valid full
* block. */
if(partial_offset == block_size) {
break;
}
/* 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++;
ret = advance_multivolume(a);
rar->merge_mode--;
if(ret != ARCHIVE_OK) {
return ret;
}
}
}
*p = rar->vol.push_buf;
/* If we're here, we can resume unpacking by processing the block
* pointed to by the `*p` memory pointer. */
return ARCHIVE_OK;
}
static int process_block(struct archive_read* a) {
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) {
ret = advance_multivolume(a);
if(ret != ARCHIVE_OK)
return ret;
}
if(rar->cstate.block_parsing_finished) {
ssize_t block_size;
ssize_t to_skip;
ssize_t cur_block_size;
/* The header size won't be bigger than 6 bytes. */
if(!read_ahead(a, 6, &p)) {
/* Failed to prefetch data block header. */
return ARCHIVE_EOF;
}
/*
* Read block_size by parsing block header. Validate the header
* by calculating CRC byte stored inside the header. Size of
* the header is not constant (block size can be stored either
* in 1 or 2 bytes), that's why block size is left out from the
* `compressed_block_header` structure and returned by
* `parse_block_header` as the second argument. */
ret = parse_block_header(a, p, &block_size,
&rar->last_block_hdr);
if(ret != ARCHIVE_OK) {
return ret;
}
/* 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;
if(ARCHIVE_OK != consume(a, to_skip))
return ARCHIVE_EOF;
rar->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
* multi-volume archives. In this case, the block size will be
* bigger than the actual data stored in this file. Remaining
* part of the data will be in another file. */
cur_block_size =
rar5_min(rar->file.bytes_remaining, block_size);
if(block_size > rar->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
* of the file, proceed to next "partXXX.rar" volume,
* find its signature, skip all headers up to the first
* FILE base header, and continue from there.
*
* Note that `merge_block` will update the `rar`
* context structure quite extensively. */
ret = merge_block(a, block_size, &p);
if(ret != ARCHIVE_OK) {
return ret;
}
cur_block_size = block_size;
/* Current stream pointer should be now directly
* *after* the block that spanned through multiple
* archive files. `p` pointer should have the data of
* the *whole* block (merged from partial blocks
* stored in multiple archives files). */
} else {
rar->cstate.switch_multivolume = 0;
/* Read the whole block size into memory. This can take
* up to 8 megabytes of memory in theoretical cases.
* Might be worth to optimize this and use a standard
* chunk of 4kb's. */
if(!read_ahead(a, 4 + cur_block_size, &p)) {
/* Failed to prefetch block data. */
return ARCHIVE_EOF;
}
}
rar->cstate.block_buf = p;
rar->cstate.cur_block_size = cur_block_size;
rar->cstate.block_parsing_finished = 0;
rar->bits.in_addr = 0;
rar->bits.bit_addr = 0;
if(bf_is_table_present(&rar->last_block_hdr)) {
/* Load Huffman tables. */
ret = parse_tables(a, rar, p);
if(ret != ARCHIVE_OK) {
/* Error during decompression of Huffman
* tables. */
return ret;
}
}
} else {
/* Block parsing not finished, reuse previous memory buffer. */
p = rar->cstate.block_buf;
}
/* Uncompress the block, or a part of it, depending on how many bytes
* will be generated by uncompressing the block.
*
* In case too many bytes will be generated, calling this function
* again will resume the uncompression operation. */
ret = do_uncompress_block(a, p);
if(ret != ARCHIVE_OK) {
return ret;
}
if(rar->cstate.block_parsing_finished &&
rar->cstate.switch_multivolume == 0 &&
rar->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))
return ARCHIVE_FATAL;
rar->file.bytes_remaining -= rar->cstate.cur_block_size;
} else if(rar->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;
}
return ARCHIVE_OK;
}
/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
*
* 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,
int64_t* offset)
{
int i;
for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
struct data_ready *d = &rar->cstate.dready[i];
if(d->used) {
if(buf) *buf = d->buf;
if(size) *size = d->size;
if(offset) *offset = d->offset;
d->used = 0;
return ARCHIVE_OK;
}
}
return ARCHIVE_RETRY;
}
/* Pushes the `buf`, `size` and `offset` arguments to the rar->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,
const uint8_t* buf, size_t size, int64_t offset)
{
int i;
/* Don't push if we're in skip mode. This is needed because solid
* streams need full processing even if we're skipping data. After
* fully processing the stream, we need to discard the generated bytes,
* 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)
return ARCHIVE_OK;
/* Sanity check. */
if(offset != rar->file.last_offset + rar->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];
if(!d->used) {
d->used = 1;
d->buf = buf;
d->size = size;
d->offset = offset;
/* These fields are used only in sanity checking. */
rar->file.last_offset = offset;
rar->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);
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. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Error: premature end of data_ready stack");
return ARCHIVE_FATAL;
}
/* This function uncompresses the data that is stored in the <FILE> base
* block.
*
* The FILE base block looks like this:
*
* <header><huffman tables><block_1><block_2>...<block_n>
*
* The <header> is a block header, that is parsed in parse_block_header().
* It's a "compressed_block_header" structure, containing metadata needed
* to know when we should stop looking for more <block_n> blocks.
*
* <huffman tables> contain data needed to set up the huffman tables, needed
* for the actual decompression.
*
* Each <block_n> consists of series of literals:
*
* <literal><literal><literal>...<literal>
*
* Those literals generate the uncompression data. They operate on a circular
* buffer, sometimes writing raw data into it, sometimes referencing
* some previous data inside this buffer, and sometimes declaring a filter
* that will need to be executed on the data stored in the circular buffer.
* It all depends on the literal that is used.
*
* Sometimes blocks produce output data, sometimes they don't. For example, for
* some huge files that use lots of filters, sometimes a block is filled with
* only filter declaration literals. Such blocks won't produce any data in the
* circular buffer.
*
* Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
* because a literal can reference previously decompressed data. For example,
* there can be a literal that says: 'append a byte 0xFE here', and after
* it another literal can say 'append 1 megabyte of data from circular buffer
* offset 0x12345'. This is how RAR format handles compressing repeated
* patterns.
*
* The RAR compressor creates those literals and the actual efficiency of
* compression depends on what those literals are. The literals can also
* be seen as a kind of a non-turing-complete virtual machine that simply
* tells the decompressor what it should do.
* */
static int do_uncompress_file(struct archive_read* a) {
struct rar5* rar = get_context(a);
int ret;
int64_t max_end_pos;
if(!rar->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);
}
rar->cstate.initialized = 1;
}
if(rar->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
* contains only filter definitions (this is common in big
* files). */
while(1) {
ret = process_block(a);
if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
return ret;
if(rar->cstate.last_write_ptr ==
rar->cstate.write_ptr) {
/* The block didn't generate any new data,
* so just process a new block. */
continue;
}
/* The block has generated some new data, so break
* the loop. */
break;
}
}
/* Try to run filters. If filters won't be applied, it means that
* insufficient data was generated. */
ret = apply_filters(a);
if(ret == ARCHIVE_RETRY) {
return ARCHIVE_OK;
} else if(ret == ARCHIVE_FATAL) {
return ARCHIVE_FATAL;
}
/* If apply_filters() will return ARCHIVE_OK, we can continue here. */
if(cdeque_size(&rar->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,
cdeque_filter_p(&flt)))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Can't read first filter");
return ARCHIVE_FATAL;
}
max_end_pos = rar5_min(flt->block_start,
rar->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;
}
if(max_end_pos == rar->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
* stored without filter postprocessing. This means we need to
* wait for more data to be generated, so we can apply the
* filters.
*
* Signal the caller that we need more data to be able to do
* anything.
*/
return ARCHIVE_RETRY;
} else {
/* We can write the data before hitting the first filter.
* 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,
max_end_pos);
rar->cstate.last_write_ptr = max_end_pos;
}
return ARCHIVE_OK;
}
static int uncompress_file(struct archive_read* a) {
int ret;
while(1) {
/* Sometimes the uncompression function will return a
* 'retry' signal. If this will happen, we have to retry
* the function. */
ret = do_uncompress_file(a);
if(ret != ARCHIVE_RETRY)
return ret;
}
}
static int do_unstore_file(struct archive_read* a,
struct rar5* rar, 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)
{
int ret;
rar->cstate.switch_multivolume = 1;
ret = advance_multivolume(a);
rar->cstate.switch_multivolume = 0;
if(ret != ARCHIVE_OK) {
/* Failed to advance to next multivolume archive
* file. */
return ret;
}
}
to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
if(to_read == 0) {
return ARCHIVE_EOF;
}
if(!read_ahead(a, to_read, &p)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"I/O error when unstoring file");
return ARCHIVE_FATAL;
}
if(ARCHIVE_OK != consume(a, to_read)) {
return ARCHIVE_EOF;
}
if(buf) *buf = p;
if(size) *size = to_read;
if(offset) *offset = rar->cstate.last_unstore_ptr;
rar->file.bytes_remaining -= to_read;
rar->cstate.last_unstore_ptr += to_read;
update_crc(rar, p, to_read);
return ARCHIVE_OK;
}
static int do_unpack(struct archive_read* a, struct rar5* rar,
const void** buf, size_t* size, int64_t* offset)
{
enum COMPRESSION_METHOD {
STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
BEST = 5
};
if(rar->file.service > 0) {
return do_unstore_file(a, rar, buf, size, offset);
} else {
switch(rar->cstate.method) {
case STORE:
return do_unstore_file(a, rar, buf, size,
offset);
case FASTEST:
/* fallthrough */
case FAST:
/* fallthrough */
case NORMAL:
/* fallthrough */
case GOOD:
/* fallthrough */
case BEST:
return uncompress_file(a);
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Compression method not supported: 0x%x",
rar->cstate.method);
return ARCHIVE_FATAL;
}
}
#if !defined WIN32
/* Not reached. */
return ARCHIVE_OK;
#endif
}
static int verify_checksums(struct archive_read* a) {
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) {
/* Always check checksums if we're not in skip mode */
verify_crc = 1;
} else {
/* We can override the logic above with a compile-time option
* NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
* and it will check checksums of unpacked data even when
* we're skipping it. */
#if defined CHECK_CRC_ON_SOLID_SKIP
/* Debug case */
verify_crc = 1;
#else
/* Normal case */
verify_crc = 0;
#endif
}
if(verify_crc) {
/* During unpacking, on each unpacked block we're calling the
* update_crc() function. Since we are here, the unpacking
* 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) {
/* Check CRC32 only when the file contains a CRC32
* value for this file. */
if(rar->file.calculated_crc32 !=
rar->file.stored_crc32) {
/* Checksums do not match; the unpacked file
* is corrupted. */
DEBUG_CODE {
printf("Checksum error: CRC32 "
"(was: %08x, expected: %08x)\n",
rar->file.calculated_crc32,
rar->file.stored_crc32);
}
#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Checksum error: CRC32");
return ARCHIVE_FATAL;
#endif
} else {
DEBUG_CODE {
printf("Checksum OK: CRC32 "
"(%08x/%08x)\n",
rar->file.stored_crc32,
rar->file.calculated_crc32);
}
}
}
if(rar->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.
*
* We now finalize the hash calculation by calling the
* `final` function. This will generate the final hash
* value we can use to compare it with the BLAKE2sp
* checksum that is stored in the archive.
*
* The return value of this `final` function is not
* very helpful, as it guards only against improper use.
* This is why we're explicitly ignoring it. */
uint8_t b2_buf[32];
(void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
if(memcmp(&rar->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;
#endif
}
}
}
/* Finalization for this file has been successfully completed. */
return ARCHIVE_OK;
}
static int verify_global_checksums(struct archive_read* a) {
return verify_checksums(a);
}
/*
* Decryption function for the magic signature pattern. Check the comment near
* the `rar5_signature_xor` symbol to read the rationale behind this.
*/
static void rar5_signature(char *buf) {
size_t i;
for(i = 0; i < sizeof(rar5_signature_xor); i++) {
buf[i] = rar5_signature_xor[i] ^ 0xA1;
}
}
static int rar5_read_data(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset) {
int ret;
struct rar5* rar = get_context(a);
if(rar->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
* it's impossible to perform any decompression. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Can't decompress an entry marked as a directory");
return ARCHIVE_FAILED;
}
if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->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);
if(ret == ARCHIVE_OK) {
return ret;
}
if(rar->file.eof == 1) {
return ARCHIVE_EOF;
}
ret = do_unpack(a, rar, 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 all bytes of current file were processed, run
* finalization.
*
* Finalization will check checksum against proper values. If
* some of the checksums will not match, we'll return an error
* value in the last `archive_read_data` call to signal an error
* to the user. */
rar->file.eof = 1;
return verify_global_checksums(a);
}
return ARCHIVE_OK;
}
static int rar5_read_data_skip(struct archive_read *a) {
struct rar5* rar = get_context(a);
if(rar->main.solid) {
/* 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. */
int ret;
/* Make sure to process all blocks in the compressed stream. */
while(rar->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
* it's only slowing things down.
*
* This is incremented instead of setting to 1 because
* this data skipping function can be called
* recursively. */
rar->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--;
if(ret < 0 || ret == ARCHIVE_EOF) {
/* Propagate any potential error conditions
* to the caller. */
return ret;
}
}
} else {
/* In standard archives, we can just jump over the compressed
* stream. Each file in non-solid archives starts from an empty
* window buffer. */
if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
return ARCHIVE_FATAL;
}
rar->file.bytes_remaining = 0;
}
return ARCHIVE_OK;
}
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. */
return ARCHIVE_FATAL;
}
static int rar5_cleanup(struct archive_read *a) {
struct rar5* rar = get_context(a);
free(rar->cstate.window_buf);
free(rar->cstate.filtered_buf);
free(rar->vol.push_buf);
free_filters(rar);
cdeque_free(&rar->cstate.filters);
free(rar);
a->format->data = NULL;
return ARCHIVE_OK;
}
static int rar5_capabilities(struct archive_read * a) {
(void) a;
return 0;
}
static int rar5_has_encrypted_entries(struct archive_read *_a) {
(void) _a;
/* Unsupported for now. */
return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
}
static int rar5_init(struct rar5* rar) {
memset(rar, 0, sizeof(struct rar5));
if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
return ARCHIVE_FATAL;
return ARCHIVE_OK;
}
int archive_read_support_format_rar5(struct archive *_a) {
struct archive_read* ar;
int ret;
struct rar5* rar;
if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
return ret;
rar = malloc(sizeof(*rar));
if(rar == NULL) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate rar5 data");
return ARCHIVE_FATAL;
}
if(ARCHIVE_OK != rar5_init(rar)) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate rar5 filter buffer");
return ARCHIVE_FATAL;
}
ret = __archive_read_register_format(ar,
rar,
"rar5",
rar5_bid,
rar5_options,
rar5_read_header,
rar5_read_data,
rar5_read_data_skip,
rar5_seek_data,
rar5_cleanup,
rar5_capabilities,
rar5_has_encrypted_entries);
if(ret != ARCHIVE_OK) {
(void) rar5_cleanup(ar);
}
return ret;
}
@@ -120,7 +120,9 @@ archive_read_format_raw_read_header(struct archive_read *a,
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_perm(entry, 0644);
/* I'm deliberately leaving most fields unset here. */
return (ARCHIVE_OK);
/* Let the filter fill out any fields it might have. */
return __archive_read_header(a, entry);
}
static int
@@ -694,11 +694,13 @@ tar_read_header(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, size_t *unconsumed)
{
ssize_t bytes;
int err;
int err, eof_vol_header;
const char *h;
const struct archive_entry_header_ustar *header;
const struct archive_entry_header_gnutar *gnuheader;
eof_vol_header = 0;
/* Loop until we find a workable header record. */
for (;;) {
tar_flush_unconsumed(a, unconsumed);
@@ -788,6 +790,8 @@ tar_read_header(struct archive_read *a, struct tar *tar,
break;
case 'V': /* GNU volume header */
err = header_volume(a, tar, entry, h, unconsumed);
if (err == ARCHIVE_EOF)
eof_vol_header = 1;
break;
case 'X': /* Used by SUN tar; same as 'x'. */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
@@ -862,9 +866,17 @@ tar_read_header(struct archive_read *a, struct tar *tar,
}
return (err);
}
if (err == ARCHIVE_EOF)
/* EOF when recursively reading a header is bad. */
archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
if (err == ARCHIVE_EOF) {
if (!eof_vol_header) {
/* EOF when recursively reading a header is bad. */
archive_set_error(&a->archive, EINVAL,
"Damaged tar archive");
} else {
/* If we encounter just a GNU volume header treat
* this situation as an empty archive */
return (ARCHIVE_EOF);
}
}
return (ARCHIVE_FATAL);
}
@@ -1942,6 +1954,15 @@ pax_attribute(struct archive_read *a, struct tar *tar,
pax_time(value, &s, &n);
archive_entry_set_birthtime(entry, s, n);
}
if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {
if (strcmp(value, "file") == 0) {
archive_entry_set_symlink_type(entry,
AE_SYMLINK_TYPE_FILE);
} else if (strcmp(value, "dir") == 0) {
archive_entry_set_symlink_type(entry,
AE_SYMLINK_TYPE_DIRECTORY);
}
}
if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
pax_attribute_xattr(entry, key, value);
break;
@@ -386,6 +386,11 @@ _warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
return (ARCHIVE_EOF);
}
if (w->unconsumed) {
__archive_read_consume(a, w->unconsumed);
w->unconsumed = 0U;
}
rab = __archive_read_ahead(a, 1U, &nrd);
if (nrd < 0) {
*bsz = 0U;
@@ -621,7 +626,8 @@ _warc_rdver(const char *buf, size_t bsz)
if (ver >= 1200U) {
if (memcmp(c, "\r\n", 2U) != 0)
ver = 0U;
} else if (ver < 1200U) {
} else {
/* ver < 1200U */
if (*c != ' ' && *c != '\t')
ver = 0U;
}
@@ -739,8 +745,9 @@ _warc_rdlen(const char *buf, size_t bsz)
/* there must be at least one digit */
if (!isdigit((unsigned char)*val))
return -1;
errno = 0;
len = strtol(val, &on, 10);
if (on != eol) {
if (errno != 0 || on != eol) {
/* line must end here */
return -1;
}
@@ -167,6 +167,9 @@ struct xar_file {
#define HAS_FFLAGS 0x01000
#define HAS_XATTR 0x02000
#define HAS_ACL 0x04000
#define HAS_CTIME 0x08000
#define HAS_MTIME 0x10000
#define HAS_ATIME 0x20000
uint64_t id;
uint64_t length;
@@ -695,9 +698,15 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
*/
file_free(file);
}
archive_entry_set_atime(entry, file->atime, 0);
archive_entry_set_ctime(entry, file->ctime, 0);
archive_entry_set_mtime(entry, file->mtime, 0);
if (file->has & HAS_ATIME) {
archive_entry_set_atime(entry, file->atime, 0);
}
if (file->has & HAS_CTIME) {
archive_entry_set_ctime(entry, file->ctime, 0);
}
if (file->has & HAS_MTIME) {
archive_entry_set_mtime(entry, file->mtime, 0);
}
archive_entry_set_gid(entry, file->gid);
if (file->gname.length > 0 &&
archive_entry_copy_gname_l(entry, file->gname.s,
@@ -789,7 +798,8 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
xattr = file->xattr_list;
while (xattr != NULL) {
const void *d;
size_t outbytes, used;
size_t outbytes = 0;
size_t used = 0;
r = move_reading_point(a, xattr->offset);
if (r != ARCHIVE_OK)
@@ -811,8 +821,18 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
r = checksum_final(a,
xattr->a_sum.val, xattr->a_sum.len,
xattr->e_sum.val, xattr->e_sum.len);
if (r != ARCHIVE_OK)
if (r != ARCHIVE_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"Xattr checksum error");
r = ARCHIVE_WARN;
break;
}
if (xattr->name.s == NULL) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"Xattr name error");
r = ARCHIVE_WARN;
break;
}
archive_entry_xattr_add_entry(entry,
xattr->name.s, d, outbytes);
xattr = xattr->next;
@@ -838,7 +858,7 @@ xar_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
struct xar *xar;
size_t used;
size_t used = 0;
int r;
xar = (struct xar *)(a->format->data);
@@ -967,7 +987,7 @@ move_reading_point(struct archive_read *a, uint64_t offset)
return ((int)step);
xar->offset += step;
} else {
int64_t pos = __archive_read_seek(a, offset, SEEK_SET);
int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET);
if (pos == ARCHIVE_FAILED) {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
@@ -1220,8 +1240,7 @@ heap_add_entry(struct archive_read *a,
}
memcpy(new_pending_files, heap->files,
heap->allocated * sizeof(new_pending_files[0]));
if (heap->files != NULL)
free(heap->files);
free(heap->files);
heap->files = new_pending_files;
heap->allocated = new_size;
}
@@ -1767,8 +1786,8 @@ file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
}
file->parent = xar->file;
file->mode = 0777 | AE_IFREG;
file->atime = time(NULL);
file->mtime = time(NULL);
file->atime = 0;
file->mtime = 0;
xar->file = file;
xar->xattr = NULL;
for (attr = list->first; attr != NULL; attr = attr->next) {
@@ -2594,15 +2613,14 @@ strappend_base64(struct xar *xar,
while (l > 0) {
int n = 0;
if (l > 0) {
if (base64[b[0]] < 0 || base64[b[1]] < 0)
break;
n = base64[*b++] << 18;
n |= base64[*b++] << 12;
*out++ = n >> 16;
len++;
l -= 2;
}
if (base64[b[0]] < 0 || base64[b[1]] < 0)
break;
n = base64[*b++] << 18;
n |= base64[*b++] << 12;
*out++ = n >> 16;
len++;
l -= 2;
if (l > 0) {
if (base64[*b] < 0)
break;
@@ -2751,15 +2769,15 @@ xml_data(void *userData, const char *s, int len)
xar->file->uid = atol10(s, len);
break;
case FILE_CTIME:
xar->file->has |= HAS_TIME;
xar->file->has |= HAS_TIME | HAS_CTIME;
xar->file->ctime = parse_time(s, len);
break;
case FILE_MTIME:
xar->file->has |= HAS_TIME;
xar->file->has |= HAS_TIME | HAS_MTIME;
xar->file->mtime = parse_time(s, len);
break;
case FILE_ATIME:
xar->file->has |= HAS_TIME;
xar->file->has |= HAS_TIME | HAS_ATIME;
xar->file->atime = parse_time(s, len);
break;
case FILE_DATA_LENGTH:
@@ -52,6 +52,12 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
#endif
#ifdef HAVE_LZMA_H
#include <lzma.h>
#endif
#include "archive.h"
#include "archive_digest_private.h"
@@ -63,6 +69,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102
#include "archive_private.h"
#include "archive_rb.h"
#include "archive_read_private.h"
#include "archive_ppmd8_private.h"
#ifndef HAVE_ZLIB_H
#include "archive_crc32.h"
@@ -165,13 +172,30 @@ struct zip {
char decompress_init;
char end_of_entry;
#ifdef HAVE_ZLIB_H
unsigned char *uncompressed_buffer;
size_t uncompressed_buffer_size;
#ifdef HAVE_ZLIB_H
z_stream stream;
char stream_valid;
#endif
#if HAVE_LZMA_H && HAVE_LIBLZMA
lzma_stream zipx_lzma_stream;
char zipx_lzma_valid;
#endif
#ifdef HAVE_BZLIB_H
bz_stream bzstream;
char bzstream_valid;
#endif
IByteIn zipx_ppmd_stream;
ssize_t zipx_ppmd_read_compressed;
CPpmd8 ppmd8;
char ppmd8_valid;
char ppmd8_stream_failed;
struct archive_string_conv *sconv;
struct archive_string_conv *sconv_default;
struct archive_string_conv *sconv_utf8;
@@ -222,6 +246,33 @@ struct zip {
/* Many systems define min or MIN, but not all. */
#define zipmin(a,b) ((a) < (b) ? (a) : (b))
/* 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
* many compressed bytes were read. */
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;
/* Fetch next byte. */
const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail);
if(bytes_avail < 1) {
zip->ppmd8_stream_failed = 1;
return 0;
}
__archive_read_consume(a, 1);
/* Increment the counter. */
++zip->zipx_ppmd_read_compressed;
/* Return the next compressed byte. */
return data[0];
}
/* ------------------------------------------------------------------------ */
/*
@@ -372,6 +423,8 @@ static const struct {
{17, "reserved"}, /* Reserved by PKWARE */
{18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */
{19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */
{95, "xz"}, /* XZ compressed data */
{96, "jpeg"}, /* JPEG compressed data */
{97, "wav-pack"}, /* WavPack compressed data */
{98, "ppmd-1"}, /* PPMd version I, Rev 1 */
{99, "aes"} /* WinZip AES encryption */
@@ -419,27 +472,49 @@ zip_time(const char *p)
* triplets. id and size are 2 bytes each.
*/
static int
process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry)
process_extra(struct archive_read *a, struct archive_entry *entry,
const char *p, size_t extra_length, struct zip_entry* zip_entry)
{
unsigned offset = 0;
struct zip *zip = (struct zip *)(a->format->data);
if (extra_length == 0) {
return ARCHIVE_OK;
}
if (extra_length < 4) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length);
return ARCHIVE_FAILED;
size_t i = 0;
/* Some ZIP files may have trailing 0 bytes. Let's check they
* are all 0 and ignore them instead of returning an error.
*
* This is not technically correct, but some ZIP files look
* like this and other tools support those files - so let's
* also support them.
*/
for (; i < extra_length; i++) {
if (p[i] != 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Too-small extra data: "
"Need at least 4 bytes, "
"but only found %d bytes",
(int)extra_length);
return ARCHIVE_FAILED;
}
}
return ARCHIVE_OK;
}
while (offset <= extra_length - 4) {
unsigned short headerid = archive_le16dec(p + offset);
unsigned short datasize = archive_le16dec(p + offset + 2);
offset += 4;
if (offset + datasize > extra_length) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Extra data overflow: Need %d bytes but only found %d bytes",
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "
"Need %d bytes but only found %d bytes",
(int)datasize, (int)(extra_length - offset));
return ARCHIVE_FAILED;
}
@@ -454,9 +529,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->uncompressed_size == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
|| (t = archive_le64dec(p + offset)) > INT64_MAX) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit uncompressed size");
|| (t = archive_le64dec(p + offset)) >
INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit "
"uncompressed size");
return ARCHIVE_FAILED;
}
zip_entry->uncompressed_size = t;
@@ -466,9 +544,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->compressed_size == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
|| (t = archive_le64dec(p + offset)) > INT64_MAX) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit compressed size");
|| (t = archive_le64dec(p + offset)) >
INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit "
"compressed size");
return ARCHIVE_FAILED;
}
zip_entry->compressed_size = t;
@@ -478,9 +559,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->local_header_offset == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
|| (t = archive_le64dec(p + offset)) > INT64_MAX) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit local header offset");
|| (t = archive_le64dec(p + offset)) >
INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed 64-bit "
"local header offset");
return ARCHIVE_FAILED;
}
zip_entry->local_header_offset = t;
@@ -513,7 +597,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
/* Extended time field "UT". */
int flags;
if (datasize == 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Incomplete extended time field");
return ARCHIVE_FAILED;
}
@@ -595,7 +680,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
* if bitmap & 1, 2 byte "version made by"
* if bitmap & 2, 2 byte "internal file attributes"
* if bitmap & 4, 4 byte "external file attributes"
* if bitmap & 8, 2 byte comment length + n byte comment
* if bitmap & 8, 2 byte comment length + n byte
* comment
*/
int bitmap, bitmap_last;
@@ -646,13 +732,18 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
= external_attributes >> 16;
} else if (zip_entry->system == 0) {
// Interpret MSDOS directory bit
if (0x10 == (external_attributes & 0x10)) {
zip_entry->mode = AE_IFDIR | 0775;
if (0x10 == (external_attributes &
0x10)) {
zip_entry->mode =
AE_IFDIR | 0775;
} else {
zip_entry->mode = AE_IFREG | 0664;
zip_entry->mode =
AE_IFREG | 0664;
}
if (0x01 == (external_attributes & 0x01)) {
// Read-only bit; strip write permissions
if (0x01 == (external_attributes &
0x01)) {
/* Read-only bit;
* strip write permissions */
zip_entry->mode &= 0555;
}
} else {
@@ -679,6 +770,59 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
break;
}
case 0x7075:
{
/* Info-ZIP Unicode Path Extra Field. */
if (datasize < 5 || entry == NULL)
break;
offset += 5;
datasize -= 5;
/* The path name in this field is always encoded
* in UTF-8. */
if (zip->sconv_utf8 == NULL) {
zip->sconv_utf8 =
archive_string_conversion_from_charset(
&a->archive, "UTF-8", 1);
/* If the converter from UTF-8 is not
* available, then the path name from the main
* field will more likely be correct. */
if (zip->sconv_utf8 == NULL)
break;
}
/* Make sure the CRC32 of the filename matches. */
if (!zip->ignore_crc32) {
const char *cp = archive_entry_pathname(entry);
if (cp) {
unsigned long file_crc =
zip->crc32func(0, cp, strlen(cp));
unsigned long utf_crc =
archive_le32dec(p + offset - 4);
if (file_crc != utf_crc) {
#ifdef DEBUG
fprintf(stderr,
"CRC filename mismatch; "
"CDE is %lx, but UTF8 "
"is outdated with %lx\n",
file_crc, utf_crc);
#endif
break;
}
}
}
if (archive_entry_copy_pathname_l(entry,
p + offset, datasize, zip->sconv_utf8) != 0) {
/* Ignore the error, and fallback to the path
* name from the main field. */
#ifdef DEBUG
fprintf(stderr, "Failed to read the ZIP "
"0x7075 extra field path.\n");
#endif
}
break;
}
case 0x7855:
/* Info-ZIP Unix Extra Field (type 2) "Ux". */
#ifdef DEBUG
@@ -713,7 +857,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
if (datasize >= (2 + uidsize + 3)) {
/* get a gid size. */
gidsize = 0xff & (int)p[offset+2+uidsize];
gidsize = 0xff &
(int)p[offset+2+uidsize];
if (gidsize == 2)
zip_entry->gid =
archive_le16dec(
@@ -730,7 +875,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
case 0x9901:
/* WinZip AES extra data field. */
if (datasize < 6) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Incomplete AES field");
return ARCHIVE_FAILED;
}
@@ -750,12 +896,6 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
offset += datasize;
}
if (offset != extra_length) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed extra data: Consumed %d bytes of %d bytes",
(int)offset, (int)extra_length);
return ARCHIVE_FAILED;
}
return ARCHIVE_OK;
}
@@ -875,7 +1015,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) {
if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,
zip_entry)) {
return ARCHIVE_FATAL;
}
__archive_read_consume(a, extra_length);
@@ -892,8 +1033,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
zip_entry->mode |= 0664;
}
/* Windows archivers sometimes use backslash as the directory separator.
Normalize to slash. */
/* Windows archivers sometimes use backslash as the directory
* separator. Normalize to slash. */
if (zip_entry->system == 0 &&
(wp = archive_entry_pathname_w(entry)) != NULL) {
if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
@@ -912,7 +1053,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
/* Make sure that entries with a trailing '/' are marked as directories
* even if the External File Attributes contains bogus values. If this
* is not a directory and there is no type, assume regularfile. */
* is not a directory and there is no type, assume a regular file. */
if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {
int has_slash;
@@ -963,7 +1104,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
}
if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) {
/* If this came from the central dir, it's size info
/* If this came from the central dir, its size info
* is definitive, so ignore the length-at-end flag. */
zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;
/* If local header is missing a value, use the one from
@@ -1202,7 +1343,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff,
zip->entry->crc32 = archive_le32dec(p + 4);
compressed = archive_le64dec(p + 8);
uncompressed = archive_le64dec(p + 16);
if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
if (compressed > INT64_MAX || uncompressed >
INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Overflow of 64-bit file sizes");
@@ -1296,6 +1438,745 @@ zip_read_data_none(struct archive_read *a, const void **_buff,
return (ARCHIVE_OK);
}
static int
consume_optional_marker(struct archive_read *a, struct zip *zip)
{
if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
const char *p;
if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP end-of-file record");
return (ARCHIVE_FATAL);
}
/* Consume the optional PK\007\010 marker. */
if (p[0] == 'P' && p[1] == 'K' &&
p[2] == '\007' && p[3] == '\010') {
p += 4;
zip->unconsumed = 4;
}
if (zip->entry->flags & LA_USED_ZIP64) {
uint64_t compressed, uncompressed;
zip->entry->crc32 = archive_le32dec(p);
compressed = archive_le64dec(p + 4);
uncompressed = archive_le64dec(p + 12);
if (compressed > INT64_MAX ||
uncompressed > INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Overflow of 64-bit file sizes");
return ARCHIVE_FAILED;
}
zip->entry->compressed_size = compressed;
zip->entry->uncompressed_size = uncompressed;
zip->unconsumed += 20;
} else {
zip->entry->crc32 = archive_le32dec(p);
zip->entry->compressed_size = archive_le32dec(p + 4);
zip->entry->uncompressed_size = archive_le32dec(p + 8);
zip->unconsumed += 12;
}
}
return (ARCHIVE_OK);
}
#if HAVE_LZMA_H && HAVE_LIBLZMA
static int
zipx_xz_init(struct archive_read *a, struct zip *zip)
{
lzma_ret r;
if(zip->zipx_lzma_valid) {
lzma_end(&zip->zipx_lzma_stream);
zip->zipx_lzma_valid = 0;
}
memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
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)",
r);
return (ARCHIVE_FAILED);
}
zip->zipx_lzma_valid = 1;
free(zip->uncompressed_buffer);
zip->uncompressed_buffer_size = 256 * 1024;
zip->uncompressed_buffer =
(uint8_t*) malloc(zip->uncompressed_buffer_size);
if (zip->uncompressed_buffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"No memory for xz decompression");
return (ARCHIVE_FATAL);
}
zip->decompress_init = 1;
return (ARCHIVE_OK);
}
static int
zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
{
lzma_ret r;
const uint8_t* p;
#pragma pack(push)
#pragma pack(1)
struct _alone_header {
uint8_t bytes[5];
uint64_t uncompressed_size;
} alone_header;
#pragma pack(pop)
if(zip->zipx_lzma_valid) {
lzma_end(&zip->zipx_lzma_stream);
zip->zipx_lzma_valid = 0;
}
/* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma
* that is a part of XZ Utils. The stream format stored inside ZIPX
* file is a modified "lzma alone" file format, that was used by the
* `lzma` utility which was later deprecated in favour of `xz` utility. * Since those formats are nearly the same, we can use a standard
* "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);
if (r != LZMA_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"lzma initialization failed(%d)", r);
return (ARCHIVE_FAILED);
}
/* Flag the cleanup function that we want our lzma-related structures
* to be freed later. */
zip->zipx_lzma_valid = 1;
/* The "lzma alone" file format and the stream format inside ZIPx are
* almost the same. Here's an example of a structure of "lzma alone"
* format:
*
* $ cat /bin/ls | lzma | xxd | head -n 1
* 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814
*
* 5 bytes 8 bytes n bytes
* <lzma_params><uncompressed_size><data...>
*
* lzma_params is a 5-byte blob that has to be decoded to extract
* parameters of this LZMA stream. The uncompressed_size field is an
* uint64_t value that contains information about the size of the
* uncompressed file, or UINT64_MAX if this value is unknown.
* The <data...> part is the actual lzma-compressed data stream.
*
* Now here's the structure of the stream inside the ZIPX file:
*
* $ cat stream_inside_zipx | xxd | head -n 1
* 00000000: 0914 0500 5d00 8000 0000 2814 .... ....
*
* 2byte 2byte 5 bytes n bytes
* <magic1><magic2><lzma_params><data...>
*
* This means that the ZIPX file contains an additional magic1 and
* magic2 headers, the lzma_params field contains the same parameter
* set as in the "lzma alone" format, and the <data...> field is the
* same as in the "lzma alone" format as well. Note that also the zipx
* format is missing the uncompressed_size field.
*
* So, in order to use the "lzma alone" decoder for the zipx lzma
* stream, we simply need to shuffle around some fields, prepare a new
* lzma alone header, feed it into lzma alone decoder so it will
* initialize itself properly, and then we can start feeding normal
* zipx lzma stream into the decoder.
*/
/* Read magic1,magic2,lzma_params from the ZIPX stream. */
if((p = __archive_read_ahead(a, 9, NULL)) == 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");
return (ARCHIVE_FATAL);
}
/* Prepare an lzma alone header: copy the lzma_params blob into
* a proper place into the lzma alone header. */
memcpy(&alone_header.bytes[0], p + 4, 5);
/* Initialize the 'uncompressed size' field to unknown; we'll manually
* monitor how many bytes there are still to be uncompressed. */
alone_header.uncompressed_size = UINT64_MAX;
if(!zip->uncompressed_buffer) {
zip->uncompressed_buffer_size = 256 * 1024;
zip->uncompressed_buffer =
(uint8_t*) malloc(zip->uncompressed_buffer_size);
if (zip->uncompressed_buffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"No memory for lzma decompression");
return (ARCHIVE_FATAL);
}
}
zip->zipx_lzma_stream.next_in = (void*) &alone_header;
zip->zipx_lzma_stream.avail_in = sizeof(alone_header);
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;
zip->zipx_lzma_stream.total_out = 0;
/* Feed only the header into the lzma alone decoder. This will
* effectively initialize the decoder, and will not produce any
* 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");
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;
zip->decompress_init = 1;
return (ARCHIVE_OK);
}
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);
int ret;
lzma_ret lz_ret;
const void* compressed_buf;
ssize_t bytes_avail, in_bytes, to_consume = 0;
(void) offset; /* UNUSED */
/* Initialize decompressor if not yet initialized. */
if (!zip->decompress_init) {
ret = zipx_xz_init(a, zip);
if (ret != ARCHIVE_OK)
return (ret);
}
compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
if (bytes_avail < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated xz file body");
return (ARCHIVE_FATAL);
}
in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
zip->zipx_lzma_stream.next_in = compressed_buf;
zip->zipx_lzma_stream.avail_in = in_bytes;
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;
zip->zipx_lzma_stream.total_out = 0;
/* Perform the decompression. */
lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
switch(lz_ret) {
case LZMA_DATA_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"xz data error (error %d)", (int) lz_ret);
return (ARCHIVE_FATAL);
case LZMA_NO_CHECK:
case LZMA_OK:
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"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)
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xz premature end of stream");
return (ARCHIVE_FATAL);
}
zip->end_of_entry = 1;
break;
}
to_consume = zip->zipx_lzma_stream.total_in;
__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;
*size = zip->zipx_lzma_stream.total_out;
*buff = zip->uncompressed_buffer;
ret = consume_optional_marker(a, zip);
if (ret != ARCHIVE_OK)
return (ret);
return (ARCHIVE_OK);
}
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);
int ret;
lzma_ret lz_ret;
const void* compressed_buf;
ssize_t bytes_avail, in_bytes, to_consume;
(void) offset; /* UNUSED */
/* Initialize decompressor if not yet initialized. */
if (!zip->decompress_init) {
ret = zipx_lzma_alone_init(a, zip);
if (ret != ARCHIVE_OK)
return (ret);
}
/* Fetch more compressed data. The same note as in deflate handler
* applies here as well:
*
* Note: '1' here is a performance optimization. Recall that the
* decompression layer returns a count of available bytes; asking for
* more than that forces the decompressor to combine reads by copying
* data.
*/
compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
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 = zipmin(zip->entry_bytes_remaining, bytes_avail);
zip->zipx_lzma_stream.next_in = compressed_buf;
zip->zipx_lzma_stream.avail_in = in_bytes;
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. */
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. */
lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
switch(lz_ret) {
case LZMA_DATA_ERROR:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"lzma data error (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:
lzma_end(&zip->zipx_lzma_stream);
zip->zipx_lzma_valid = 0;
if((int64_t) zip->zipx_lzma_stream.total_in !=
zip->entry_bytes_remaining)
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"lzma alone premature end of stream");
return (ARCHIVE_FATAL);
}
zip->end_of_entry = 1;
break;
case LZMA_OK:
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"lzma unknown error %d", (int) lz_ret);
return (ARCHIVE_FATAL);
}
to_consume = zip->zipx_lzma_stream.total_in;
/* 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;
}
/* Return values. */
*size = zip->zipx_lzma_stream.total_out;
*buff = zip->uncompressed_buffer;
/* Behave the same way as during deflate decompression. */
ret = consume_optional_marker(a, zip);
if (ret != ARCHIVE_OK)
return (ret);
/* Free lzma decoder handle because we'll no longer need it. */
if(zip->end_of_entry) {
lzma_end(&zip->zipx_lzma_stream);
zip->zipx_lzma_valid = 0;
}
/* If we're here, then we're good! */
return (ARCHIVE_OK);
}
#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */
static int
zipx_ppmd8_init(struct archive_read *a, struct zip *zip)
{
const void* p;
uint32_t val;
uint32_t order;
uint32_t mem;
uint32_t restore_method;
/* Remove previous decompression context if it exists. */
if(zip->ppmd8_valid) {
__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
zip->ppmd8_valid = 0;
}
/* Create a new decompression context. */
__archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8);
zip->ppmd8_stream_failed = 0;
/* Setup function pointers required by Ppmd8 decompressor. The
* 'ppmd_read' function will feed new bytes to the decompressor,
* and will increment the 'zip->zipx_ppmd_read_compressed' counter. */
zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream;
zip->zipx_ppmd_stream.a = a;
zip->zipx_ppmd_stream.Read = &ppmd_read;
/* Reset number of read bytes to 0. */
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);
}
__archive_read_consume(a, 2);
/* Decode the stream's compression parameters. */
val = archive_le16dec(p);
order = (val & 15) + 1;
mem = ((val >> 4) & 0xff) + 1;
restore_method = (val >> 12);
if(order < 2 || restore_method > 2) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid parameter set in PPMd8 stream (order=%d, "
"restore=%d)", order, restore_method);
return (ARCHIVE_FAILED);
}
/* Allocate the memory needed to properly decompress the file. */
if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {
archive_set_error(&a->archive, ENOMEM,
"Unable to allocate memory for PPMd8 stream: %d bytes",
mem << 20);
return (ARCHIVE_FATAL);
}
/* Signal the cleanup function to release Ppmd8 context in the
* cleanup phase. */
zip->ppmd8_valid = 1;
/* Perform further Ppmd8 initialization. */
if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"PPMd8 stream range decoder initialization error");
return (ARCHIVE_FATAL);
}
__archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,
restore_method);
/* Allocate the buffer that will hold uncompressed data. */
free(zip->uncompressed_buffer);
zip->uncompressed_buffer_size = 256 * 1024;
zip->uncompressed_buffer =
(uint8_t*) malloc(zip->uncompressed_buffer_size);
if(zip->uncompressed_buffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"No memory for PPMd8 decompression");
return ARCHIVE_FATAL;
}
/* Ppmd8 initialization is done. */
zip->decompress_init = 1;
/* We've already read 2 bytes in the output stream. Additionally,
* Ppmd8 initialization code could read some data as well. So we
* are advancing the stream by 2 bytes plus whatever number of
* bytes Ppmd8 init function used. */
zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed;
return ARCHIVE_OK;
}
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);
int ret;
size_t consumed_bytes = 0;
ssize_t bytes_avail = 0;
(void) offset; /* UNUSED */
/* If we're here for the first time, initialize Ppmd8 decompression
* context first. */
if(!zip->decompress_init) {
ret = zipx_ppmd8_init(a, zip);
if(ret != ARCHIVE_OK)
return ret;
}
/* 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) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated PPMd8 file body");
return (ARCHIVE_FATAL);
}
/* This counter will be updated inside ppmd_read(), which at one
* point will be called by Ppmd8_DecodeSymbol. */
zip->zipx_ppmd_read_compressed = 0;
/* Decompression loop. */
do {
int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(
&zip->ppmd8);
if(sym < 0) {
zip->end_of_entry = 1;
break;
}
/* This field is set by ppmd_read() when there was no more data
* to be read. */
if(zip->ppmd8_stream_failed) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated PPMd8 file body");
return (ARCHIVE_FATAL);
}
zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym;
++consumed_bytes;
} while(consumed_bytes < zip->uncompressed_buffer_size);
/* Update pointers for libarchive. */
*buff = zip->uncompressed_buffer;
*size = consumed_bytes;
/* Update pointers so we can continue decompression in another call. */
zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed;
zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed;
zip->entry_uncompressed_bytes_read += consumed_bytes;
/* If we're at the end of stream, deinitialize Ppmd8 context. */
if(zip->end_of_entry) {
__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
zip->ppmd8_valid = 0;
}
/* Seek for optional marker, same way as in each zip entry. */
ret = consume_optional_marker(a, zip);
if (ret != ARCHIVE_OK)
return ret;
return ARCHIVE_OK;
}
#ifdef HAVE_BZLIB_H
static int
zipx_bzip2_init(struct archive_read *a, struct zip *zip)
{
int r;
/* Deallocate already existing BZ2 decompression context if it
* exists. */
if(zip->bzstream_valid) {
BZ2_bzDecompressEnd(&zip->bzstream);
zip->bzstream_valid = 0;
}
/* Allocate a new BZ2 decompression context. */
memset(&zip->bzstream, 0, sizeof(bz_stream));
r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);
if(r != BZ_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
"bzip2 initialization failed(%d)",
r);
return ARCHIVE_FAILED;
}
/* Mark the bzstream field to be released in cleanup phase. */
zip->bzstream_valid = 1;
/* (Re)allocate the buffer that will contain decompressed bytes. */
free(zip->uncompressed_buffer);
zip->uncompressed_buffer_size = 256 * 1024;
zip->uncompressed_buffer =
(uint8_t*) malloc(zip->uncompressed_buffer_size);
if (zip->uncompressed_buffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"No memory for bzip2 decompression");
return ARCHIVE_FATAL;
}
/* Initialization done. */
zip->decompress_init = 1;
return ARCHIVE_OK;
}
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;
const void *compressed_buff;
int r;
uint64_t total_out;
(void) offset; /* UNUSED */
/* Initialize decompression context if we're here for the first time. */
if(!zip->decompress_init) {
r = zipx_bzip2_init(a, zip);
if(r != ARCHIVE_OK)
return r;
}
/* 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);
}
in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
if(in_bytes < 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);
}
/* Setup buffer boundaries. */
zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
zip->bzstream.avail_in = in_bytes;
zip->bzstream.total_in_hi32 = 0;
zip->bzstream.total_in_lo32 = 0;
zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
zip->bzstream.avail_out = zip->uncompressed_buffer_size;
zip->bzstream.total_out_hi32 = 0;
zip->bzstream.total_out_lo32 = 0;
/* Perform the decompression. */
r = BZ2_bzDecompress(&zip->bzstream);
switch(r) {
case BZ_STREAM_END:
/* If we're at the end of the stream, deinitialize the
* decompression context now. */
switch(BZ2_bzDecompressEnd(&zip->bzstream)) {
case BZ_OK:
break;
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"Failed to clean up bzip2 "
"decompressor");
return ARCHIVE_FATAL;
}
zip->end_of_entry = 1;
break;
case BZ_OK:
/* The decompressor has successfully decoded this
* chunk of data, but more data is still in queue. */
break;
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"bzip2 decompression failed");
return ARCHIVE_FATAL;
}
/* Update the pointers so decompressor can continue decoding. */
to_consume = zip->bzstream.total_in_lo32;
__archive_read_consume(a, to_consume);
total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) +
zip->bzstream.total_out_lo32;
zip->entry_bytes_remaining -= to_consume;
zip->entry_compressed_bytes_read += to_consume;
zip->entry_uncompressed_bytes_read += total_out;
/* Give libarchive its due. */
*size = total_out;
*buff = zip->uncompressed_buffer;
/* Seek for optional marker, like in other entries. */
r = consume_optional_marker(a, zip);
if(r != ARCHIVE_OK)
return r;
return ARCHIVE_OK;
}
#endif
#ifdef HAVE_ZLIB_H
static int
zip_deflate_init(struct archive_read *a, struct zip *zip)
@@ -1371,8 +2252,10 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
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);
(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;
@@ -1383,12 +2266,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
+ buff_remaining)
> zip->entry_bytes_remaining) {
if (zip->entry_bytes_remaining <
(int64_t)zip->decrypted_bytes_remaining)
(int64_t)zip->decrypted_bytes_remaining)
buff_remaining = 0;
else
buff_remaining =
(size_t)zip->entry_bytes_remaining
- zip->decrypted_bytes_remaining;
- zip->decrypted_bytes_remaining;
}
}
if (buff_remaining > 0) {
@@ -1407,7 +2290,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
+ zip->decrypted_bytes_remaining,
&dsize);
}
zip->decrypted_bytes_remaining += buff_remaining;
zip->decrypted_bytes_remaining +=
buff_remaining;
}
}
bytes_avail = zip->decrypted_bytes_remaining;
@@ -1470,42 +2354,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
return (r);
}
if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
const char *p;
if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated ZIP end-of-file record");
return (ARCHIVE_FATAL);
}
/* Consume the optional PK\007\010 marker. */
if (p[0] == 'P' && p[1] == 'K' &&
p[2] == '\007' && p[3] == '\010') {
p += 4;
zip->unconsumed = 4;
}
if (zip->entry->flags & LA_USED_ZIP64) {
uint64_t compressed, uncompressed;
zip->entry->crc32 = archive_le32dec(p);
compressed = archive_le64dec(p + 4);
uncompressed = archive_le64dec(p + 12);
if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Overflow of 64-bit file sizes");
return ARCHIVE_FAILED;
}
zip->entry->compressed_size = compressed;
zip->entry->uncompressed_size = uncompressed;
zip->unconsumed += 20;
} else {
zip->entry->crc32 = archive_le32dec(p);
zip->entry->compressed_size = archive_le32dec(p + 4);
zip->entry->uncompressed_size = archive_le32dec(p + 8);
zip->unconsumed += 12;
}
}
r = consume_optional_marker(a, zip);
if (r != ARCHIVE_OK)
return (r);
return (ARCHIVE_OK);
}
@@ -1933,6 +2784,24 @@ archive_read_format_zip_read_data(struct archive_read *a,
case 0: /* No compression. */
r = zip_read_data_none(a, buff, size, offset);
break;
#ifdef HAVE_BZLIB_H
case 12: /* ZIPx bzip2 compression. */
r = zip_read_data_zipx_bzip2(a, buff, size, offset);
break;
#endif
#if HAVE_LZMA_H && HAVE_LIBLZMA
case 14: /* ZIPx LZMA compression. */
r = zip_read_data_zipx_lzma_alone(a, buff, size, offset);
break;
case 95: /* ZIPx XZ compression. */
r = zip_read_data_zipx_xz(a, buff, size, offset);
break;
#endif
/* PPMd support is built-in, so we don't need any #if guards. */
case 98: /* ZIPx PPMd compression. */
r = zip_read_data_zipx_ppmd(a, buff, size, offset);
break;
#ifdef HAVE_ZLIB_H
case 8: /* Deflate compression. */
r = zip_read_data_deflate(a, buff, size, offset);
@@ -1941,8 +2810,8 @@ archive_read_format_zip_read_data(struct archive_read *a,
default: /* Unsupported compression. */
/* Return a warning. */
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Unsupported ZIP compression method (%s)",
compression_name(zip->entry->compression));
"Unsupported ZIP compression method (%d: %s)",
zip->entry->compression, compression_name(zip->entry->compression));
/* We can't decompress this entry, but we will
* be able to skip() it and try the next entry. */
return (ARCHIVE_FAILED);
@@ -2000,11 +2869,29 @@ archive_read_format_zip_cleanup(struct archive_read *a)
struct zip_entry *zip_entry, *next_zip_entry;
zip = (struct zip *)(a->format->data);
#ifdef HAVE_ZLIB_H
if (zip->stream_valid)
inflateEnd(&zip->stream);
free(zip->uncompressed_buffer);
#endif
#if HAVE_LZMA_H && HAVE_LIBLZMA
if (zip->zipx_lzma_valid) {
lzma_end(&zip->zipx_lzma_stream);
}
#endif
#ifdef HAVE_BZLIB_H
if (zip->bzstream_valid) {
BZ2_bzDecompressEnd(&zip->bzstream);
}
#endif
free(zip->uncompressed_buffer);
if (zip->ppmd8_valid)
__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
if (zip->zip_entries) {
zip_entry = zip->zip_entries;
while (zip_entry != NULL) {
@@ -2628,7 +3515,8 @@ expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)
}
static int
slurp_central_directory(struct archive_read *a, struct zip *zip)
slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
struct zip *zip)
{
ssize_t i;
unsigned found;
@@ -2708,6 +3596,11 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
return ARCHIVE_FATAL;
zip_entry = calloc(1, sizeof(struct zip_entry));
if (zip_entry == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate zip entry");
return ARCHIVE_FATAL;
}
zip_entry->next = zip->zip_entries;
zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
zip->zip_entries = zip_entry;
@@ -2733,8 +3626,10 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
filename_length = archive_le16dec(p + 28);
extra_length = archive_le16dec(p + 30);
comment_length = archive_le16dec(p + 32);
/* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */
/* internal_attributes = archive_le16dec(p + 36); */ /* text bit */
/* disk_start = archive_le16dec(p + 34);
* Better be zero.
* internal_attributes = archive_le16dec(p + 36);
* text bit */
external_attributes = archive_le32dec(p + 38);
zip_entry->local_header_offset =
archive_le32dec(p + 42) + correction;
@@ -2770,7 +3665,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
"Truncated ZIP file header");
return ARCHIVE_FATAL;
}
if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) {
if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,
extra_length, zip_entry)) {
return ARCHIVE_FATAL;
}
@@ -2792,7 +3688,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
* 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] != '_')) {
(r - name < 3 || r[0] != '.' ||
r[1] != '_')) {
__archive_rb_tree_insert_node(
&zip->tree, &zip_entry->node);
/* Expose its parent directories. */
@@ -2869,8 +3766,10 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
switch(rsrc->compression) {
case 0: /* No compression. */
if (rsrc->uncompressed_size != rsrc->compressed_size) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed OS X metadata entry: inconsistent size");
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Malformed OS X metadata entry: "
"inconsistent size");
return (ARCHIVE_FATAL);
}
#ifdef HAVE_ZLIB_H
@@ -3029,7 +3928,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a,
a->archive.archive_format_name = "ZIP";
if (zip->zip_entries == NULL) {
r = slurp_central_directory(a, zip);
r = slurp_central_directory(a, entry, zip);
if (r != ARCHIVE_OK)
return r;
/* Get first entry whose local header offset is lower than
@@ -3059,8 +3958,8 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a,
__archive_read_reset_passphrase(a);
/* File entries are sorted by the header offset, we should mostly
* use __archive_read_consume to advance a read point to avoid redundant
* data reading. */
* use __archive_read_consume to advance a read point to avoid
* redundant data reading. */
offset = archive_filter_bytes(&a->archive, 0);
if (offset < zip->entry->local_header_offset)
__archive_read_consume(a,
@@ -3141,3 +4040,5 @@ archive_read_support_format_zip_seekable(struct archive *_a)
free(zip);
return (ARCHIVE_OK);
}
/*# vim:set noet:*/
@@ -75,6 +75,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33
#define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))
#endif
#undef max
#define max(a, b) ((a)>(b)?(a):(b))
struct archive_string_conv {
struct archive_string_conv *next;
char *from_charset;
@@ -458,7 +461,7 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
if (from_cp == CP_C_LOCALE) {
/*
* "C" locale special process.
* "C" locale special processing.
*/
wchar_t *ws;
const unsigned char *mp;
@@ -591,7 +594,7 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest,
* No single byte will be more than one wide character,
* so this length estimate will always be big enough.
*/
size_t wcs_length = len;
// size_t wcs_length = len;
size_t mbs_length = len;
const char *mbs = p;
wchar_t *wcs;
@@ -600,7 +603,11 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest,
memset(&shift_state, 0, sizeof(shift_state));
#endif
if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1))
/*
* As we decided to have wcs_length == mbs_length == len
* we can use len here instead of wcs_length
*/
if (NULL == archive_wstring_ensure(dest, dest->length + len + 1))
return (-1);
wcs = dest->s + dest->length;
/*
@@ -609,6 +616,12 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest,
* multi bytes.
*/
while (*mbs && mbs_length > 0) {
/*
* The buffer we allocated is always big enough.
* Keep this code path in a comment if we decide to choose
* smaller wcs_length in the future
*/
/*
if (wcs_length == 0) {
dest->length = wcs - dest->s;
dest->s[dest->length] = L'\0';
@@ -618,24 +631,20 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest,
return (-1);
wcs = dest->s + dest->length;
}
*/
#if HAVE_MBRTOWC
r = mbrtowc(wcs, mbs, wcs_length, &shift_state);
r = mbrtowc(wcs, mbs, mbs_length, &shift_state);
#else
r = mbtowc(wcs, mbs, wcs_length);
r = mbtowc(wcs, mbs, mbs_length);
#endif
if (r == (size_t)-1 || r == (size_t)-2) {
ret_val = -1;
if (errno == EILSEQ) {
++mbs;
--mbs_length;
continue;
} else
break;
break;
}
if (r == 0 || r > mbs_length)
break;
wcs++;
wcs_length--;
// wcs_length--;
mbs += r;
mbs_length -= r;
}
@@ -680,7 +689,7 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as,
if (to_cp == CP_C_LOCALE) {
/*
* "C" locale special process.
* "C" locale special processing.
*/
const wchar_t *wp = ws;
char *p;
@@ -799,7 +808,8 @@ archive_string_append_from_wcs(struct archive_string *as,
as->s[as->length] = '\0';
/* Re-allocate buffer for MBS. */
if (archive_string_ensure(as,
as->length + len * 2 + 1) == NULL)
as->length + max(len * 2,
(size_t)MB_CUR_MAX) + 1) == NULL)
return (-1);
p = as->s + as->length;
end = as->s + as->buffer_length - MB_CUR_MAX -1;
@@ -890,7 +900,7 @@ add_converter(struct archive_string_conv *sc, int (*converter)
struct archive_string_conv *))
{
if (sc == NULL || sc->nconverter >= 2)
__archive_errx(1, "Programing error");
__archive_errx(1, "Programming error");
sc->converter[sc->nconverter++] = converter;
}
@@ -3441,7 +3451,8 @@ strncat_from_utf8_libarchive2(struct archive_string *as,
as->length = p - as->s;
/* Re-allocate buffer for MBS. */
if (archive_string_ensure(as,
as->length + len * 2 + 1) == NULL)
as->length + max(len * 2,
(size_t)MB_CUR_MAX) + 1) == NULL)
return (-1);
p = as->s + as->length;
end = as->s + as->buffer_length - MB_CUR_MAX -1;
@@ -4053,6 +4064,7 @@ archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)
{
if (utf8 == NULL) {
aes->aes_set = 0;
return (0);
}
aes->aes_set = AES_SET_UTF8;
archive_string_empty(&(aes->aes_mbs));
@@ -4067,6 +4079,7 @@ archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
{
if (wcs == NULL) {
aes->aes_set = 0;
return (0);
}
aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
archive_string_empty(&(aes->aes_mbs));
@@ -26,15 +26,15 @@
*
*/
#ifndef ARCHIVE_STRING_H_INCLUDED
#define ARCHIVE_STRING_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#ifndef __LIBARCHIVE_TEST
#error This header is only to be used internally to libarchive.
#endif
#endif
#ifndef ARCHIVE_STRING_H_INCLUDED
#define ARCHIVE_STRING_H_INCLUDED
#include <stdarg.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h> /* required for wchar_t on some systems */
@@ -34,13 +34,13 @@
* See also http://unicode.org/report/tr15/
*/
#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED
#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED
#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED
struct unicode_composition_table {
uint32_t cp1;
uint32_t cp2;
@@ -92,10 +92,10 @@ Clears any error information left over from a previous call.
Not generally used in client code.
.It Fn archive_compression
Synonym for
.Fn archive_filter_code(a, 0) .
.Fn archive_filter_code a 0 .
.It Fn archive_compression_name
Synonym for
.Fn archive_filter_name(a, 0) .
.Fn archive_filter_name a 0 .
.It Fn archive_copy_error
Copies error information from one archive to another.
.It Fn archive_errno
@@ -142,13 +142,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_position a -1
would be a synonym for
.Fn archive_position(a, 2)
.Fn archive_position a 2
which would return the number of bytes currently read from the archive, while
.Fn archive_position(a, 1)
.Fn archive_position a 1
would return the number of bytes after uudecoding, and
.Fn archive_position(a, 0)
.Fn archive_position a 0
would return the number of bytes after decompression.
.It Fn archive_filter_name
Returns a textual name identifying the indicated filter.
@@ -170,9 +170,9 @@ A textual description of the format of the current entry.
.It Fn archive_position
Returns the number of bytes read from or written to the indicated filter.
In particular,
.Fn archive_position(a, 0)
.Fn archive_position a 0
returns the number of bytes read or written by the format handler, while
.Fn archive_position(a, -1)
.Fn archive_position a -1
returns the number of bytes read or written to the archive.
See
.Fn archive_filter_count
+147 -83
View File
@@ -218,8 +218,8 @@ __archive_errx(int retvalue, const char *msg)
* Also Windows version of mktemp family including _mktemp_s
* are not secure.
*/
int
__archive_mktemp(const char *tmpdir)
static int
__archive_mktempx(const char *tmpdir, wchar_t *template)
{
static const wchar_t prefix[] = L"libarchive_";
static const wchar_t suffix[] = L"XXXXXXXXXX";
@@ -243,64 +243,76 @@ __archive_mktemp(const char *tmpdir)
hProv = (HCRYPTPROV)NULL;
fd = -1;
ws = NULL;
archive_string_init(&temp_name);
/* Get a temporary directory. */
if (tmpdir == NULL) {
size_t l;
wchar_t *tmp;
if (template == NULL) {
archive_string_init(&temp_name);
l = GetTempPathW(0, NULL);
if (l == 0) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
tmp = malloc(l*sizeof(wchar_t));
if (tmp == NULL) {
errno = ENOMEM;
goto exit_tmpfile;
}
GetTempPathW((DWORD)l, tmp);
archive_wstrcpy(&temp_name, tmp);
free(tmp);
} else {
if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
strlen(tmpdir)) < 0)
goto exit_tmpfile;
if (temp_name.s[temp_name.length-1] != L'/')
archive_wstrappend_wchar(&temp_name, L'/');
}
/* Get a temporary directory. */
if (tmpdir == NULL) {
size_t l;
wchar_t *tmp;
/* Check if temp_name is a directory. */
attr = GetFileAttributesW(temp_name.s);
if (attr == (DWORD)-1) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
l = GetTempPathW(0, NULL);
if (l == 0) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
tmp = malloc(l*sizeof(wchar_t));
if (tmp == NULL) {
errno = ENOMEM;
goto exit_tmpfile;
}
GetTempPathW((DWORD)l, tmp);
archive_wstrcpy(&temp_name, tmp);
free(tmp);
} else {
if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
strlen(tmpdir)) < 0)
goto exit_tmpfile;
if (temp_name.s[temp_name.length-1] != L'/')
archive_wstrappend_wchar(&temp_name, L'/');
}
ws = __la_win_permissive_name_w(temp_name.s);
if (ws == NULL) {
errno = EINVAL;
goto exit_tmpfile;
}
attr = GetFileAttributesW(ws);
/* Check if temp_name is a directory. */
attr = GetFileAttributesW(temp_name.s);
if (attr == (DWORD)-1) {
la_dosmaperr(GetLastError());
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
ws = __la_win_permissive_name_w(temp_name.s);
if (ws == NULL) {
errno = EINVAL;
goto exit_tmpfile;
}
attr = GetFileAttributesW(ws);
if (attr == (DWORD)-1) {
la_dosmaperr(GetLastError());
goto exit_tmpfile;
}
}
if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
errno = ENOTDIR;
goto exit_tmpfile;
}
}
if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
errno = ENOTDIR;
goto exit_tmpfile;
}
/*
* Create a temporary file.
*/
archive_wstrcat(&temp_name, prefix);
archive_wstrcat(&temp_name, suffix);
ep = temp_name.s + archive_strlen(&temp_name);
xp = ep - wcslen(suffix);
/*
* Create a temporary file.
*/
archive_wstrcat(&temp_name, prefix);
archive_wstrcat(&temp_name, suffix);
ep = temp_name.s + archive_strlen(&temp_name);
xp = ep - wcslen(suffix);
template = temp_name.s;
} else {
xp = wcschr(template, L'X');
if (xp == NULL) /* No X, programming error */
abort();
for (ep = xp; *ep == L'X'; ep++)
continue;
if (*ep) /* X followed by non X, programming error */
abort();
}
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
@@ -323,20 +335,24 @@ __archive_mktemp(const char *tmpdir)
*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
free(ws);
ws = __la_win_permissive_name_w(temp_name.s);
ws = __la_win_permissive_name_w(template);
if (ws == NULL) {
errno = EINVAL;
goto exit_tmpfile;
}
/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
* delete this temporary file immediately when this
* file closed. */
if (template == temp_name.s) {
attr = FILE_ATTRIBUTE_TEMPORARY |
FILE_FLAG_DELETE_ON_CLOSE;
} else {
/* mkstemp */
attr = FILE_ATTRIBUTE_NORMAL;
}
h = CreateFileW(ws,
GENERIC_READ | GENERIC_WRITE | DELETE,
0,/* Not share */
NULL,
CREATE_NEW,/* Create a new file only */
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
attr,
NULL);
if (h == INVALID_HANDLE_VALUE) {
/* The same file already exists. retry with
@@ -358,10 +374,23 @@ exit_tmpfile:
if (hProv != (HCRYPTPROV)NULL)
CryptReleaseContext(hProv, 0);
free(ws);
archive_wstring_free(&temp_name);
if (template == temp_name.s)
archive_wstring_free(&temp_name);
return (fd);
}
int
__archive_mktemp(const char *tmpdir)
{
return __archive_mktempx(tmpdir, NULL);
}
int
__archive_mkstemp(wchar_t *template)
{
return __archive_mktempx(NULL, template);
}
#else
static int
@@ -414,14 +443,24 @@ exit_tmpfile:
return (fd);
}
#else
int
__archive_mkstemp(char *template)
{
int fd = -1;
fd = mkstemp(template);
if (fd >= 0)
__archive_ensure_cloexec_flag(fd);
return (fd);
}
#else /* !HAVE_MKSTEMP */
/*
* We use a private routine.
*/
int
__archive_mktemp(const char *tmpdir)
static int
__archive_mktempx(const char *tmpdir, char *template)
{
static const char num[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -439,26 +478,37 @@ __archive_mktemp(const char *tmpdir)
char *tp, *ep;
fd = -1;
archive_string_init(&temp_name);
if (tmpdir == NULL) {
if (get_tempdir(&temp_name) != ARCHIVE_OK)
if (template == NULL) {
archive_string_init(&temp_name);
if (tmpdir == NULL) {
if (get_tempdir(&temp_name) != ARCHIVE_OK)
goto exit_tmpfile;
} else
archive_strcpy(&temp_name, tmpdir);
if (temp_name.s[temp_name.length-1] == '/') {
temp_name.s[temp_name.length-1] = '\0';
temp_name.length --;
}
if (la_stat(temp_name.s, &st) < 0)
goto exit_tmpfile;
} else
archive_strcpy(&temp_name, tmpdir);
if (temp_name.s[temp_name.length-1] == '/') {
temp_name.s[temp_name.length-1] = '\0';
temp_name.length --;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
goto exit_tmpfile;
}
archive_strcat(&temp_name, "/libarchive_");
tp = temp_name.s + archive_strlen(&temp_name);
archive_strcat(&temp_name, "XXXXXXXXXX");
ep = temp_name.s + archive_strlen(&temp_name);
template = temp_name.s;
} else {
tp = strchr(template, 'X');
if (tp == NULL) /* No X, programming error */
abort();
for (ep = tp; *ep == 'X'; ep++)
continue;
if (*ep) /* X followed by non X, programming error */
abort();
}
if (stat(temp_name.s, &st) < 0)
goto exit_tmpfile;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
goto exit_tmpfile;
}
archive_strcat(&temp_name, "/libarchive_");
tp = temp_name.s + archive_strlen(&temp_name);
archive_strcat(&temp_name, "XXXXXXXXXX");
ep = temp_name.s + archive_strlen(&temp_name);
do {
char *p;
@@ -469,19 +519,33 @@ __archive_mktemp(const char *tmpdir)
int d = *((unsigned char *)p) % sizeof(num);
*p++ = num[d];
}
fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
0600);
} while (fd < 0 && errno == EEXIST);
if (fd < 0)
goto exit_tmpfile;
__archive_ensure_cloexec_flag(fd);
unlink(temp_name.s);
if (template == temp_name.s)
unlink(temp_name.s);
exit_tmpfile:
archive_string_free(&temp_name);
if (template == temp_name.s)
archive_string_free(&temp_name);
return (fd);
}
#endif /* HAVE_MKSTEMP */
int
__archive_mktemp(const char *tmpdir)
{
return __archive_mktempx(tmpdir, NULL);
}
int
__archive_mkstemp(char *template)
{
return __archive_mktempx(NULL, template);
}
#endif /* !HAVE_MKSTEMP */
#endif /* !_WIN32 || __CYGWIN__ */
/*
@@ -445,7 +445,8 @@ fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
* Windows' stat() does not accept the path added "\\?\" especially "?"
* character.
* It means we cannot access the long name path longer than MAX_PATH.
* So I've implemented simular Windows' stat() to access the long name path.
* So I've implemented a function similar to Windows' stat() to access the
* long name path.
* And I've added some feature.
* 1. set st_ino by nFileIndexHigh and nFileIndexLow of
* BY_HANDLE_FILE_INFORMATION.
@@ -27,10 +27,6 @@
* $FreeBSD$
*/
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
/*
* TODO: A lot of stuff in here isn't actually used by libarchive and
* can be trimmed out. Note that this file is used by libarchive and
@@ -48,6 +44,10 @@
#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
#define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
#error This header is only to be used internally to libarchive.
#endif
/* Start of configuration for native Win32 */
#ifndef MINGW_HAS_SECURE_API
#define MINGW_HAS_SECURE_API 1
@@ -117,10 +117,7 @@
#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
#define setmode _setmode
#endif
#ifdef stat
#undef stat
#endif
#define stat(path,stref) __la_stat(path,stref)
#define la_stat(path,stref) __la_stat(path,stref)
#if !defined(__WATCOMC__)
#if !defined(__BORLANDC__)
#define strdup _strdup
@@ -118,7 +118,7 @@ After all entries have been written, use the
.Fn archive_write_free
function to release all resources.
.\"
.Sh EXAMPLE
.Sh EXAMPLES
The following sketch illustrates basic usage of the library.
In this example,
the callback functions are simply wrappers around the standard
@@ -192,7 +192,7 @@ write_archive(const char *outname, const char **filename)
if (archive_write_set_format_filter_by_ext(a, outname) != ARCHIVE_OK) {
archive_write_add_filter_gzip(a);
archive_write_set_format_ustar(a);
}
}
archive_write_open(a, mydata, myopen, mywrite, myclose);
while (*filename) {
stat(*filename, &st);
@@ -225,8 +225,8 @@ int main(int argc, const char **argv)
.Ed
.Sh SEE ALSO
.Xr tar 1 ,
.Xr libarchive 3 ,
.Xr archive_write_set_options 3 ,
.Xr libarchive 3 ,
.Xr cpio 5 ,
.Xr mtree 5 ,
.Xr tar 5

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