mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-27 04:09:36 +03:00
LibArchive 2020-02-11 (3288ebb0)
Code extracted from:
https://github.com/libarchive/libarchive.git
at commit 3288ebb0353beb51dfb09d444dedbe9235ead53d (v3.4.2).
This commit is contained in:
committed by
Brad King
parent
2aaed7a050
commit
8cce62295a
@@ -0,0 +1 @@
|
||||
* -whitespace
|
||||
+117
-18
@@ -1,5 +1,8 @@
|
||||
#
|
||||
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()
|
||||
#
|
||||
PROJECT(libarchive C)
|
||||
#
|
||||
@@ -84,6 +87,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 ()
|
||||
|
||||
# Especially for early development, we want to be a little
|
||||
# aggressive about diagnosing build problems; this can get
|
||||
@@ -93,10 +101,12 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$")
|
||||
#################################################################
|
||||
# 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} -Werror")
|
||||
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")
|
||||
@@ -108,11 +118,13 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^Clang$")
|
||||
#################################################################
|
||||
# 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} -Werror")
|
||||
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")
|
||||
@@ -125,21 +137,27 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^XL$")
|
||||
#################################################################
|
||||
# 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} -qhalt=w")
|
||||
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 C4061: The enumerate has no associated handler in a switch
|
||||
# statement.
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4061")
|
||||
# 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")
|
||||
@@ -165,8 +183,6 @@ IF (MSVC)
|
||||
# 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")
|
||||
# /WX option is the same as gcc's -Werror option.
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /WX")
|
||||
# /Oi option enables built-in functions.
|
||||
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi")
|
||||
#################################################################
|
||||
@@ -177,11 +193,14 @@ ENDIF (MSVC)
|
||||
# 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)
|
||||
@@ -268,6 +287,10 @@ 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)
|
||||
@@ -457,12 +480,13 @@ MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES)
|
||||
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})
|
||||
@@ -476,9 +500,13 @@ IF(LIBLZMA_FOUND)
|
||||
IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
|
||||
ADD_DEFINITIONS(-DLZMA_API_STATIC)
|
||||
ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
|
||||
CMAKE_POP_CHECK_STATE()
|
||||
ELSE(LIBLZMA_FOUND)
|
||||
# LZMA not found and will not be used.
|
||||
ENDIF(LIBLZMA_FOUND)
|
||||
MARK_AS_ADVANCED(CLEAR LIBLZMA_INCLUDE_DIR)
|
||||
MARK_AS_ADVANCED(CLEAR LIBLZMA_LIBRARY)
|
||||
|
||||
#
|
||||
# Find LZO2
|
||||
#
|
||||
@@ -508,6 +536,35 @@ ENDIF(LZO2_FOUND)
|
||||
MARK_AS_ADVANCED(CLEAR LZO2_INCLUDE_DIR)
|
||||
MARK_AS_ADVANCED(CLEAR LZO2_LIBRARY)
|
||||
#
|
||||
# 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)
|
||||
@@ -541,29 +598,36 @@ MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY)
|
||||
#
|
||||
# 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})
|
||||
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(ZSTD_FOUND)
|
||||
MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR)
|
||||
MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY)
|
||||
|
||||
|
||||
#
|
||||
# Check headers
|
||||
#
|
||||
@@ -679,6 +743,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
|
||||
#
|
||||
@@ -741,6 +825,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)
|
||||
@@ -756,6 +842,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}")
|
||||
@@ -1291,6 +1382,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)
|
||||
@@ -1304,6 +1396,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)
|
||||
|
||||
@@ -1572,6 +1665,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)
|
||||
@@ -1895,6 +1993,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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -680,6 +680,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
|
||||
|
||||
@@ -704,6 +710,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
|
||||
|
||||
@@ -1105,6 +1114,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
|
||||
|
||||
@@ -1198,6 +1210,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
@@ -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)
|
||||
@@ -227,6 +237,7 @@ ENDIF()
|
||||
|
||||
# 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})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -155,7 +155,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);
|
||||
|
||||
@@ -340,6 +340,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().
|
||||
@@ -449,6 +450,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 *);
|
||||
@@ -691,6 +693,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);
|
||||
@@ -1093,6 +1097,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
@@ -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
@@ -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
|
||||
@@ -190,6 +190,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
|
||||
*/
|
||||
@@ -275,6 +282,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 *);
|
||||
@@ -350,6 +358,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 *);
|
||||
@@ -515,9 +524,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
|
||||
@@ -692,7 +698,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
|
||||
|
||||
+138
-11
@@ -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
|
||||
|
||||
@@ -57,8 +57,12 @@ __RCSID("$NetBSD$");
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#if MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
#define HAVE_MAJOR
|
||||
#elif MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
#define HAVE_MAJOR
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -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
@@ -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 <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:*/
|
||||
|
||||
+34
-18
@@ -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;
|
||||
@@ -735,7 +744,8 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as,
|
||||
else
|
||||
dp = &defchar_used;
|
||||
count = WideCharToMultiByte(to_cp, 0, ws, wslen,
|
||||
as->s + as->length, (int)as->buffer_length-1, NULL, dp);
|
||||
as->s + as->length,
|
||||
(int)as->buffer_length - as->length - 1, NULL, dp);
|
||||
if (count == 0 &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
/* Expand the MBS buffer and retry. */
|
||||
@@ -798,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;
|
||||
@@ -889,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;
|
||||
}
|
||||
|
||||
@@ -1512,8 +1523,10 @@ get_current_codepage(void)
|
||||
p = strrchr(locale, '.');
|
||||
if (p == NULL)
|
||||
return (GetACP());
|
||||
if (strcmp(p+1, "utf8") == 0)
|
||||
return CP_UTF8;
|
||||
cp = my_atoi(p+1);
|
||||
if (cp <= 0)
|
||||
if ((int)cp <= 0)
|
||||
return (GetACP());
|
||||
return (cp);
|
||||
}
|
||||
@@ -3438,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;
|
||||
@@ -4050,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));
|
||||
@@ -4064,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
@@ -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.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user