diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d8796999f6b..16b210e9b2de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,18 +6,67 @@ permissions: contents: read jobs: + debian-i386: + name: Debian (cmake, i386) + runs-on: ubuntu-latest + container: + image: i386/debian:bookworm + steps: + - name: Install dependencies + run: | + apt-get update + apt-get install -y --no-install-recommends \ + build-essential ca-certificates cmake git pkg-config \ + libacl1-dev libbz2-dev liblz4-dev liblzma-dev \ + libssl-dev libzstd-dev zlib1g-dev + # Plain git instead of the checkout action: the runner executes + # actions with its own 64-bit node, which cannot run inside a + # 32-bit container. + - name: Check out + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + git init -q + git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" + git fetch -q --depth 1 origin "$GITHUB_SHA" + git checkout -q FETCH_HEAD + - name: Configure + run: ./build/ci/build.sh -a configure + env: + BS: cmake + - name: Build + run: ./build/ci/build.sh -a build + env: + BS: cmake + MAKE_ARGS: -j4 + - name: Test + run: ./build/ci/build.sh -a test + env: + BS: cmake + CTEST_OUTPUT_ON_FAILURE: ON + - name: Install + run: ./build/ci/build.sh -a install + env: + BS: cmake + # The artifact is built like in the other jobs, but not uploaded: + # the upload-artifact action cannot run in this container either, + # for the same reason as the checkout action above. + - name: Artifact + run: ./build/ci/build.sh -a artifact + env: + BS: cmake + FreeBSD: runs-on: ubuntu-latest strategy: matrix: bs: [autotools, cmake] steps: - - name: Build and test on a FreeBSD 15 firecracker VM - uses: acj/freebsd-firecracker-action@e04896d2dd91f7ec7381e5f363c6fbe83ee445c3 # v0.11.0 + - name: Build and test on a FreeBSD 15.1 firecracker VM + uses: acj/freebsd-firecracker-action@ba4f70408ef1d979acdd7bd36fc4b27e06fe5abe # v0.12.0 with: run-in-vm: | chflags -R noschg / && \ - fetch -o - https://download.freebsd.org/releases/amd64/15.0-RELEASE/base.txz | bsdtar -x -C / -f - && \ + fetch -o - https://download.freebsd.org/releases/amd64/15.1-RELEASE/base.txz | bsdtar -x -C / -f - && \ pkg bootstrap -y && \ cd firecracker-freebsd && \ env BS=${{ matrix.bs }} ./build/ci/github_actions/freebsd.sh prepare && \ @@ -68,7 +117,7 @@ jobs: run: ./build/ci/build.sh -a artifact env: BS: ${{ matrix.bs }} - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: libarchive-macos-${{ matrix.bs }}-${{ github.sha }} path: libarchive.tar.xz @@ -114,7 +163,7 @@ jobs: run: ./build/ci/build.sh -a artifact env: BS: ${{ matrix.bs }} - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: libarchive-ubuntu-${{ matrix.bs }}-${{ matrix.crypto }}-${{ github.sha }} path: libarchive.tar.xz @@ -133,10 +182,11 @@ jobs: - name: Distcheck run: ./build/ci/build.sh -a distcheck env: + CTEST_OUTPUT_ON_FAILURE: ON SKIP_OPEN_FD_ERR_TEST: 1 - name: Dist-Artifact run: ./build/ci/build.sh -a dist-artifact - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: libarchive-${{ github.sha }} path: libarchive-dist.tar @@ -146,7 +196,13 @@ jobs: strategy: fail-fast: false matrix: - be: [mingw-gcc, cygwin-gcc, msvc] + be: [cygwin-gcc, msvc] + include: + - { be: mingw, msystem: mingw64 } + - { be: mingw, msystem: ucrt64 } + - { be: mingw, msystem: clang64 } + env: + MSYSTEM: ${{ matrix.msystem }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install Cygwin @@ -161,21 +217,23 @@ jobs: liblz4-devel libiconv-devel libxml2-devel libzstd-devel libssl-devel - name: Install MinGW (msys2) - uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 - if: ${{ matrix.be=='mingw-gcc' }} + uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2.33.0 + if: ${{ matrix.be=='mingw' }} with: location: "D:\\" - install: |- - mingw-w64-x86_64-gcc - mingw-w64-x86_64-autotools - mingw-w64-x86_64-binutils - mingw-w64-x86_64-pkgconf - mingw-w64-x86_64-cmake - mingw-w64-x86_64-make - mingw-w64-x86_64-zlib - mingw-w64-x86_64-bzip2 - mingw-w64-x86_64-xz - mingw-w64-x86_64-zstd + suppress-deprecation-warnings: msystem-mingw64 + msystem: ${{ matrix.msystem }} + pacboy: |- + gcc:p + autotools:p + binutils:p + pkgconf:p + cmake:p + make:p + zlib:p + bzip2:p + xz:p + zstd:p - name: Configure run: ./build/ci/github_actions/ci.cmd configure shell: cmd @@ -202,7 +260,9 @@ jobs: shell: cmd env: BE: ${{ matrix.be }} - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: - name: libarchive-windows-${{ matrix.be }}-${{ github.sha }} + # for msvc and cygwin, libarchive-windows-backend-arch + # for mingw, libarchive-windows-mingw-msystem-arch + name: libarchive-windows-${{ matrix.be }}${{ matrix.be=='mingw' && format('-{0}', matrix.msystem) || '' }}-${{ github.sha }} path: ${{ matrix.be=='cygwin-gcc' && 'libarchive.tar.xz' || 'libarchive.zip' }} diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index bb37ce89efe3..79e44b318722 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -21,7 +21,7 @@ jobs: fuzz-seconds: 600 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9387827285cd..4eb9698a4398 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,15 +29,15 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/autobuild@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 69ee68f8fc63..a194c907e4d6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -26,7 +26,7 @@ jobs: - name: Configure run: ./build/ci/build.sh -a configure env: - CMAKE_ARGS: -GNinja -DENABLE_CLANG_TIDY=YES -DCLANG_TIDY_PATH=la-lint + CMAKE_ARGS: -GNinja -DENABLE_CLANG_TIDY=YES -DCLANG_TIDY_PATH=la-lint;--extra-arg=-Werror=newline-eof - name: Build run: ./build/ci/build.sh -a build env: diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 5751f9cc30da..dbe2f333740f 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -52,7 +52,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: SARIF file path: results.sarif @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 + uses: github/codeql-action/upload-sarif@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3 with: sarif_file: results.sarif diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cb92764676a..7ff01a2bfa0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,6 +223,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" 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_MD "Enable use of Message Digest" 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) @@ -232,13 +233,14 @@ 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) OPTION(ENABLE_LIBXML2 "Enable the use of the system libxml2 library if found" ON) +OPTION(ENABLE_BSDXML "Enable the use of the system bsdxml library if found" ON) OPTION(ENABLE_EXPAT "Enable the use of the system EXPAT library if found" ON) OPTION(ENABLE_WIN32_XMLLITE "Enable the use of the Windows XmlLite library if found" ON) OPTION(ENABLE_PCREPOSIX "Enable the use of the system PCREPOSIX library if found" ON) OPTION(ENABLE_PCRE2POSIX "Enable the use of the system PCRE2POSIX library if found" ON) OPTION(ENABLE_LIBGCC "Enable the use of the system LibGCC library if found" ON) # CNG is used for encrypt/decrypt Zip archives on Windows. -OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON) +OPTION(ENABLE_CNG "Enable the use of Windows CNG (Crypto Next Generation) if found" ON) OPTION(ENABLE_TAR "Enable tar building" ON) OPTION(ENABLE_TAR_SHARED "Enable dynamic build of tar" FALSE) @@ -316,8 +318,8 @@ IF("${CMAKE_C_PLATFORM_ID}" MATCHES "^(HP-UX)$") ENDIF() IF(MINGW) - ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO) - ADD_DEFINITIONS(-D__MINGW_USE_VC2005_COMPAT) + ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1) + ADD_DEFINITIONS(-D__MINGW_USE_VC2005_COMPAT=1) ENDIF() # @@ -801,9 +803,11 @@ LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H) LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H) LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H) LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) +LA_CHECK_INCLUDE_FILE("sys/single_threaded.h" HAVE_SYS_SINGLE_THREADED_H) LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) LA_CHECK_INCLUDE_FILE("sys/statfs.h" HAVE_SYS_STATFS_H) LA_CHECK_INCLUDE_FILE("sys/statvfs.h" HAVE_SYS_STATVFS_H) +LA_CHECK_INCLUDE_FILE("sys/sysctl.h" HAVE_SYS_SYSCTL_H) LA_CHECK_INCLUDE_FILE("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) LA_CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H) LA_CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H) @@ -817,10 +821,11 @@ LA_CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H) LA_CHECK_INCLUDE_FILE("wchar.h" HAVE_WCHAR_H) LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H) LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H) -IF(ENABLE_CNG) - LA_CHECK_INCLUDE_FILE("bcrypt.h" HAVE_BCRYPT_H) - IF(HAVE_BCRYPT_H) - LIST(APPEND ADDITIONAL_LIBS "bcrypt") +LA_CHECK_INCLUDE_FILE("bcrypt.h" HAVE_BCRYPT_H) +IF(HAVE_BCRYPT_H) + LIST(APPEND ADDITIONAL_LIBS "bcrypt") + IF(ENABLE_CNG) + SET(ARCHIVE_CRYPTO_WIN 1) # bcrypt supports these algorithms on all available versions SET(ARCHIVE_CRYPTO_MD5 1) SET(ARCHIVE_CRYPTO_MD5_WIN 1) @@ -832,10 +837,8 @@ IF(ENABLE_CNG) SET(ARCHIVE_CRYPTO_SHA384_WIN 1) SET(ARCHIVE_CRYPTO_SHA512 1) SET(ARCHIVE_CRYPTO_SHA512_WIN 1) - ENDIF(HAVE_BCRYPT_H) -ELSE(ENABLE_CNG) - UNSET(HAVE_BCRYPT_H CACHE) -ENDIF(ENABLE_CNG) + ENDIF(ENABLE_CNG) +ENDIF(HAVE_BCRYPT_H) # Following files need windows.h, so we should test it after windows.h test. LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H) @@ -895,6 +898,8 @@ IF(ENABLE_NETTLE) LA_CHECK_INCLUDE_FILE("nettle/pbkdf2.h" HAVE_NETTLE_PBKDF2_H) LA_CHECK_INCLUDE_FILE("nettle/ripemd160.h" HAVE_NETTLE_RIPEMD160_H) LA_CHECK_INCLUDE_FILE("nettle/sha.h" HAVE_NETTLE_SHA_H) + LA_CHECK_INCLUDE_FILE("nettle/sha1.h" HAVE_NETTLE_SHA1_H) + LA_CHECK_INCLUDE_FILE("nettle/sha2.h" HAVE_NETTLE_SHA2_H) LA_CHECK_INCLUDE_FILE("nettle/version.h" HAVE_NETTLE_VERSION_H) CMAKE_POP_CHECK_STATE() @@ -928,7 +933,7 @@ ELSE() ENDIF() # FreeBSD libmd -IF(NOT OPENSSL_FOUND) +IF(ENABLE_MD AND NOT OPENSSL_FOUND) CHECK_LIBRARY_EXISTS(md "MD5Init" "" LIBMD_FOUND) IF(LIBMD_FOUND) CMAKE_PUSH_CHECK_STATE() # Save the state of the variables @@ -937,7 +942,7 @@ IF(NOT OPENSSL_FOUND) LIST(APPEND ADDITIONAL_LIBS ${LIBMD_LIBRARY}) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ENDIF(LIBMD_FOUND) -ENDIF(NOT OPENSSL_FOUND) +ENDIF(ENABLE_MD AND NOT OPENSSL_FOUND) # libbsd for readpassphrase on Haiku IF("${CMAKE_SYSTEM_NAME}" MATCHES "Haiku") @@ -1145,7 +1150,6 @@ IF(ENABLE_ICONV) CHECK_ICONV("libiconv" "const") CHECK_ICONV("libiconv" "") IF (HAVE_ICONV) - SET(HAVE_LIBICONV 1) LIST(APPEND ADDITIONAL_LIBS ${LIBICONV_PATH}) ENDIF(HAVE_ICONV) ENDIF(NOT HAVE_ICONV AND LIBICONV_PATH) @@ -1189,7 +1193,6 @@ ELSE(ENABLE_ICONV) # (once enabled). UNSET(HAVE_LOCALE_CHARSET CACHE) UNSET(HAVE_ICONV CACHE) - UNSET(HAVE_LIBICONV CACHE) UNSET(HAVE_ICONV_libc_ CACHE) UNSET(HAVE_ICONV_libc_const CACHE) UNSET(HAVE_ICONV_libiconv_ CACHE) @@ -1206,7 +1209,7 @@ ENDIF(ENABLE_ICONV) # Find Libxml2 # IF(ENABLE_LIBXML2 AND HAVE_ICONV) - FIND_PACKAGE(LibXml2) + FIND_PACKAGE(LibXml2 2.7.3) ELSE() SET(LIBXML2_FOUND FALSE) ENDIF() @@ -1233,40 +1236,61 @@ IF(LIBXML2_FOUND) CMAKE_POP_CHECK_STATE() # Restore the state of the variables ELSE(LIBXML2_FOUND) # - # Find Expat + # Find XmlLite # - IF(ENABLE_EXPAT) - FIND_PACKAGE(EXPAT) - ELSE() - SET(EXPAT_FOUND FALSE) - ENDIF() - IF(EXPAT_FOUND) - CMAKE_PUSH_CHECK_STATE() # Save the state of the variables - INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR}) - LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES}) - SET(HAVE_LIBEXPAT 1) - LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H) - CMAKE_POP_CHECK_STATE() # Restore the state of the variables - ELSE(EXPAT_FOUND) - IF(WIN32 AND ENABLE_WIN32_XMLLITE) - # Check linkage as well; versions of mingw-w64 before v11.0.0 - # do not contain an import library for xmllite. - cmake_push_check_state() - SET(CMAKE_REQUIRED_LIBRARIES "xmllite" "uuid") - check_c_source_compiles(" - #include - #include - int main() { - return CreateXmlReader(&IID_IXmlReader, NULL, NULL); - } - " HAVE_XMLLITE_H) - cmake_pop_check_state() - IF(HAVE_XMLLITE_H) - SET(XMLLITE_FOUND TRUE) - LIST(APPEND ADDITIONAL_LIBS "xmllite" "uuid") - ENDIF() - ENDIF() - ENDIF(EXPAT_FOUND) + IF(WIN32 AND ENABLE_WIN32_XMLLITE) + # Check linkage as well; versions of mingw-w64 before v11.0.0 + # do not contain an import library for XmlLite. + cmake_push_check_state() + SET(CMAKE_REQUIRED_LIBRARIES "xmllite" "uuid") + check_c_source_compiles(" + #include + #include + int main() { + return CreateXmlReader(&IID_IXmlReader, NULL, NULL); + } + " HAVE_XMLLITE_H) + cmake_pop_check_state() + IF(HAVE_XMLLITE_H) + SET(HAVE_LIBXMLLITE 1) + SET(XMLLITE_FOUND TRUE) + LIST(APPEND ADDITIONAL_LIBS "xmllite" "uuid") + ENDIF(HAVE_XMLLITE_H) + ENDIF(WIN32 AND ENABLE_WIN32_XMLLITE) + IF(NOT XMLLITE_FOUND) + # + # Find Expat + # + IF(ENABLE_EXPAT) + FIND_PACKAGE(EXPAT) + ELSE(ENABLE_EXPAT) + SET(EXPAT_FOUND FALSE) + ENDIF(ENABLE_EXPAT) + IF(EXPAT_FOUND) + CMAKE_PUSH_CHECK_STATE() # Save the state of the variables + INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIRS}) + LIST(APPEND CMAKE_REQUIRED_INCLUDES ${EXPAT_INCLUDE_DIR}) + LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES}) + SET(HAVE_LIBEXPAT 1) + LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H) + CMAKE_POP_CHECK_STATE() # Restore the state of the variables + ENDIF(EXPAT_FOUND) + # + # Find bsdxml + # + IF(NOT EXPAT_FOUND) + IF(ENABLE_BSDXML) + FIND_PATH(BSDXML_INCLUDE_DIR NAMES bsdxml.h) + FIND_LIBRARY(BSDXML_LIBRARY NAMES bsdxml) + IF(BSDXML_INCLUDE_DIR AND BSDXML_LIBRARY) + INCLUDE_DIRECTORIES(${BSDXML_INCLUDE_DIR}) + LIST(APPEND ADDITIONAL_LIBS ${BSDXML_LIBRARY}) + SET(HAVE_LIBBSDXML 1) + LA_CHECK_INCLUDE_FILE("bsdxml.h" HAVE_BSDXML_H) + ENDIF(BSDXML_INCLUDE_DIR AND BSDXML_LIBRARY) + ENDIF(ENABLE_BSDXML) + ENDIF(NOT EXPAT_FOUND) + ENDIF(NOT XMLLITE_FOUND) ENDIF(LIBXML2_FOUND) MARK_AS_ADVANCED(CLEAR LIBXML2_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR LIBXML2_LIBRARIES) @@ -1462,6 +1486,17 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin") ENDIF (CMAKE_C_COMPILER_ID MATCHES "^GNU$" OR CMAKE_C_COMPILER_ID MATCHES "^Clang$") +IF (MSVC) + # + # The Windows CRT marks a number of standard functions (e.g. wcscpy) as + # deprecated. All source files in this project are compiled with those + # deprecations suppressed, so suppress these warnings for the following + # symbol checks as well. Otherwise, the deprecation warnings get turned into + # errors by ENABLE_WERROR's /WX flag and the check fails even when the + # function exists. + # + LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS) +ENDIF (MSVC) CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode) CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_ARC4RANDOM_BUF) CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS) @@ -1470,6 +1505,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT) CHECK_FUNCTION_EXISTS_GLIBC(closefrom HAVE_CLOSEFROM) CHECK_FUNCTION_EXISTS_GLIBC(close_range HAVE_CLOSE_RANGE) CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R) +CHECK_FUNCTION_EXISTS_GLIBC(explicit_bzero HAVE_EXPLICIT_BZERO) CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR) CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD) @@ -1510,6 +1546,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(lstat HAVE_LSTAT) CHECK_FUNCTION_EXISTS_GLIBC(lutimes HAVE_LUTIMES) CHECK_FUNCTION_EXISTS_GLIBC(mbrtowc HAVE_MBRTOWC) CHECK_FUNCTION_EXISTS_GLIBC(memmove HAVE_MEMMOVE) +CHECK_FUNCTION_EXISTS_GLIBC(memset_explicit HAVE_MEMSET_EXPLICIT) CHECK_FUNCTION_EXISTS_GLIBC(mkdir HAVE_MKDIR) CHECK_FUNCTION_EXISTS_GLIBC(mkfifo HAVE_MKFIFO) CHECK_FUNCTION_EXISTS_GLIBC(mknod HAVE_MKNOD) @@ -1564,6 +1601,7 @@ CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO) CHECK_FUNCTION_EXISTS(strerror_r HAVE_STRERROR_R) CHECK_FUNCTION_EXISTS(strftime HAVE_STRFTIME) CHECK_FUNCTION_EXISTS(vprintf HAVE_VPRINTF) +CHECK_FUNCTION_EXISTS(waitpid HAVE_WAITPID) CHECK_FUNCTION_EXISTS(wmemcmp HAVE_WMEMCMP) CHECK_FUNCTION_EXISTS(wmemcpy HAVE_WMEMCPY) CHECK_FUNCTION_EXISTS(wmemmove HAVE_WMEMMOVE) @@ -2218,7 +2256,10 @@ ENDIF(WIN32 AND NOT CYGWIN AND ENABLE_INSTALL) INCLUDE_DIRECTORIES(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/libarchive) # IF(MSVC) - ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) + ADD_DEFINITIONS( + -D_CRT_NONSTDC_NO_WARNINGS + -D_CRT_SECURE_NO_DEPRECATE + ) ENDIF(MSVC) OPTION(DONT_FAIL_ON_CRC_ERROR "Ignore CRC errors during parsing (For fuzzing)" OFF) diff --git a/Makefile.am b/Makefile.am index 2fb4fdf4b1e2..8bc70c5f752a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -108,7 +108,6 @@ include_HEADERS= libarchive/archive.h libarchive/archive_entry.h noinst_HEADERS= \ libarchive/archive_acl_private.h \ libarchive/archive_cmdline_private.h \ - libarchive/archive_crc32.h \ libarchive/archive_cryptor_private.h \ libarchive/archive_digest_private.h \ libarchive/archive_endian.h \ @@ -137,9 +136,11 @@ noinst_HEADERS= \ libarchive/archive_string.h \ libarchive/archive_string_composition.h \ libarchive/archive_time_private.h \ + libarchive/archive_umask_private.h \ libarchive/archive_write_disk_private.h \ libarchive/archive_write_private.h \ libarchive/archive_write_set_format_private.h \ + libarchive/archive_xml_private.h \ libarchive/archive_xxhash.h \ libarchive/config_freebsd.h \ libarchive/filter_fork.h @@ -148,6 +149,7 @@ libarchive_la_SOURCES= \ libarchive/archive_acl.c \ libarchive/archive_check_magic.c \ libarchive/archive_cmdline.c \ + libarchive/archive_crc32.c \ libarchive/archive_cryptor.c \ libarchive/archive_digest.c \ libarchive/archive_entry.c \ @@ -217,6 +219,7 @@ libarchive_la_SOURCES= \ libarchive/archive_string.c \ libarchive/archive_string_sprintf.c \ libarchive/archive_time.c \ + libarchive/archive_umask.c \ libarchive/archive_util.c \ libarchive/archive_version_details.c \ libarchive/archive_virtual.c \ @@ -302,7 +305,10 @@ endif # -no-undefined marks that libarchive doesn't rely on symbols # defined in the application. This is mandatory for cygwin. -libarchive_la_LDFLAGS= -no-undefined -version-info $(ARCHIVE_LIBTOOL_VERSION) $(DEAD_CODE_REMOVAL) +# For Windows, set libtool bindir. +libarchive_la_LDFLAGS= -no-undefined -version-info $(ARCHIVE_LIBTOOL_VERSION) \ + $(DEAD_CODE_REMOVAL) $(DLL_BINDIR) + libarchive_la_LIBADD= $(LTLIBICONV) # Manpages to install @@ -382,6 +388,7 @@ libarchive_test_SOURCES= \ libarchive/test/read_open_memory.c \ libarchive/test/test_7zip_filename_encoding.c \ libarchive/test/test_acl_from_text_overread.c \ + libarchive/test/test_archive_crc32.c \ libarchive/test/test_acl_nfs4.c \ libarchive/test/test_acl_nfs4_null_id_overflow.c \ libarchive/test/test_acl_nfs4_text.c \ @@ -399,15 +406,18 @@ libarchive_test_SOURCES= \ libarchive/test/test_archive_match_owner.c \ libarchive/test/test_archive_match_path.c \ libarchive/test/test_archive_match_time.c \ + libarchive/test/test_archive_ntfs_time.c \ libarchive/test/test_archive_parse_date.c \ libarchive/test/test_archive_pathmatch.c \ libarchive/test/test_archive_pathmatch_oob.c \ libarchive/test/test_archive_read.c \ libarchive/test/test_archive_read_add_passphrase.c \ + libarchive/test/test_archive_read_append_filter.c \ libarchive/test/test_archive_read_close_twice.c \ libarchive/test/test_archive_read_multiple_data_objects.c \ libarchive/test/test_archive_read_next_header_empty.c \ libarchive/test/test_archive_read_next_header_raw.c \ + libarchive/test/test_archive_read_open_filenames.c \ libarchive/test/test_archive_read_open2.c \ libarchive/test/test_archive_read_set_options.c \ libarchive/test/test_archive_read_support.c \ @@ -445,6 +455,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_compat_xz.c \ libarchive/test/test_compat_zip.c \ libarchive/test/test_compat_zstd.c \ + libarchive/test/test_create_child_environment.c \ libarchive/test/test_empty_write.c \ libarchive/test/test_entry.c \ libarchive/test/test_entry_strmode.c \ @@ -459,6 +470,8 @@ libarchive_test_SOURCES= \ libarchive/test/test_open_filename.c \ libarchive/test/test_pax_filename_encoding.c \ libarchive/test/test_pax_xattr_header.c \ + libarchive/test/test_read_data_block_offset_at_eof.c \ + libarchive/test/test_read_data_into_fd_no_offset_reported.c \ libarchive/test/test_read_data_into_fd_size_exceeds_declared.c \ libarchive/test/test_read_data_large.c \ libarchive/test/test_read_disk.c \ @@ -478,6 +491,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_filter_program_signature.c \ libarchive/test/test_read_filter_zstd_raw.c \ libarchive/test/test_read_filter_uudecode.c \ + libarchive/test/test_read_filter_uudecode_newlines.c \ libarchive/test/test_read_filter_uudecode_raw.c \ libarchive/test/test_read_format_7zip.c \ libarchive/test/test_read_format_7zip_encryption_data.c \ @@ -493,9 +507,11 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_cab_filename.c \ libarchive/test/test_read_format_cab_lzx_16bit.c \ libarchive/test/test_read_format_cab_lzx_e8.c \ + libarchive/test/test_read_format_cab_lzx_invalid_history.c \ libarchive/test/test_read_format_cab_lzx_oob.c \ libarchive/test/test_read_format_cab_mszip_oob.c \ libarchive/test/test_read_format_cab_multi.c \ + libarchive/test/test_read_format_cab_skip_across_folders.c \ libarchive/test/test_read_format_cab_skip_malformed.c \ libarchive/test/test_read_format_cpio_afio.c \ libarchive/test/test_read_format_cpio_bin.c \ @@ -520,6 +536,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_gtar_lzma.c \ libarchive/test/test_read_format_gtar_redundant_L.c \ libarchive/test/test_read_format_gtar_sparse.c \ + libarchive/test/test_read_format_gtar_sparse_exceeds_length.c \ libarchive/test/test_read_format_gtar_sparse_length.c \ libarchive/test/test_read_format_gtar_sparse_skip_entry.c \ libarchive/test/test_read_format_huge_rpm.c \ @@ -537,9 +554,12 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_isorr_new_compress.c \ libarchive/test/test_read_format_isorr_rr_moved.c \ libarchive/test/test_read_format_isozisofs_compress.c \ + libarchive/test/test_read_format_iso_rockridge_ce_overflow.c \ + libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.c \ libarchive/test/test_read_format_iso_rockridge_zf_overflow.c \ libarchive/test/test_read_format_iso_zisofs_overflow.c \ libarchive/test/test_read_format_lha.c \ + libarchive/test/test_read_format_lha_amiga_script.c \ libarchive/test/test_read_format_lha_bugfix_0.c \ libarchive/test/test_read_format_lha_filename.c \ libarchive/test/test_read_format_lha_filename_utf16.c \ @@ -547,6 +567,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_lha_symlink_missing_target.c \ libarchive/test/test_read_format_mtree.c \ libarchive/test/test_read_format_mtree_crash747.c \ + libarchive/test/test_read_format_mtree_size.c \ libarchive/test/test_read_format_pax_bz2.c \ libarchive/test/test_read_format_rar.c \ libarchive/test/test_read_format_rar_encryption.c \ @@ -554,13 +575,19 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_rar_encryption_partially.c \ libarchive/test/test_read_format_rar_encryption_header.c \ libarchive/test/test_read_format_rar_filter.c \ + libarchive/test/test_read_format_rar_filter_incomplete_block.c \ libarchive/test/test_read_format_rar_invalid1.c \ libarchive/test/test_read_format_rar_overflow.c \ libarchive/test/test_read_format_rar5.c \ libarchive/test/test_read_format_rar5_bad_filter.c \ libarchive/test/test_read_format_rar5_bad_tables.c \ libarchive/test/test_read_format_rar5_block_hdr_fail_loop.c \ + libarchive/test/test_read_format_rar5_extra_field_underflow.c \ + libarchive/test/test_read_format_rar5_header_size_wraparound.c \ libarchive/test/test_read_format_rar5_loop_bug.c \ + libarchive/test/test_read_format_rar5_redir_overflow.c \ + libarchive/test/test_read_format_rar5_truncated_htime.c \ + libarchive/test/test_read_format_rar5_varint_near_eof.c \ libarchive/test/test_read_format_raw.c \ libarchive/test/test_read_format_tar.c \ libarchive/test/test_read_format_tar_acl_oob_read.c \ @@ -573,6 +600,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_tar_invalid_pax_size.c \ libarchive/test/test_read_format_tar_mac_metadata.c \ libarchive/test/test_read_format_tar_pax_g_large.c \ + libarchive/test/test_read_format_tar_pax_global_header.c \ libarchive/test/test_read_format_tar_pax_large_attr.c \ libarchive/test/test_read_format_tar_pax_negative_time.c \ libarchive/test/test_read_format_tar_pax_sun_holesdata.c \ @@ -589,7 +617,10 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_xar_doublelink.c \ libarchive/test/test_read_format_zip.c \ libarchive/test/test_read_format_zip_7075_utf8_paths.c \ + libarchive/test/test_read_format_zip_ambiguous_timestamps.c \ libarchive/test/test_read_format_zip_comment_stored.c \ + libarchive/test/test_read_format_zip_dos_mtime_precedence.c \ + libarchive/test/test_read_format_zip_empty.c \ libarchive/test/test_read_format_zip_encryption_data.c \ libarchive/test/test_read_format_zip_encryption_partially.c \ libarchive/test/test_read_format_zip_encryption_header.c \ @@ -597,17 +628,22 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_zip_filename.c \ libarchive/test/test_read_format_zip_high_compression.c \ libarchive/test/test_read_format_zip_jar.c \ + libarchive/test/test_read_format_zip_lzma_alone_large_size.c \ libarchive/test/test_read_format_zip_mac_metadata.c \ libarchive/test/test_read_format_zip_malformed.c \ libarchive/test/test_read_format_zip_msdos.c \ + libarchive/test/test_read_format_zip_negative_timestamps.c \ libarchive/test/test_read_format_zip_nested.c \ libarchive/test/test_read_format_zip_nofiletype.c \ + libarchive/test/test_read_format_zip_ntfs_precedence.c \ + libarchive/test/test_read_format_zip_ntfs_timestamp.c \ libarchive/test/test_read_format_zip_padded.c \ libarchive/test/test_read_format_zip_sfx.c \ libarchive/test/test_read_format_zip_size_exceeds_declared.c \ libarchive/test/test_read_format_zip_symlink_unsupported_compression.c \ libarchive/test/test_read_format_zip_traditional_encryption_data.c \ libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c \ + libarchive/test/test_read_format_zip_unset_times.c \ libarchive/test/test_read_format_zip_winzip_aes.c \ libarchive/test/test_read_format_zip_winzip_aes_large.c \ libarchive/test/test_read_format_zip_zip64.c \ @@ -644,6 +680,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_write_disk_perms.c \ libarchive/test/test_write_disk_secure.c \ libarchive/test/test_write_disk_secure_noabsolutepaths.c \ + libarchive/test/test_write_disk_secure_physicaldrive.c \ libarchive/test/test_write_disk_secure744.c \ libarchive/test/test_write_disk_secure745.c \ libarchive/test/test_write_disk_secure746.c \ @@ -683,6 +720,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_write_format_iso9660_joliet_id.c \ libarchive/test/test_write_format_iso9660_rockridge.c \ libarchive/test/test_write_format_iso9660_rockridge_overflow.c \ + libarchive/test/test_write_format_iso9660_rr_moved_notdir.c \ libarchive/test/test_write_format_iso9660_zisofs.c \ libarchive/test/test_write_format_iso9660_zisofs_overflow.c \ libarchive/test/test_write_format_mtree.c \ @@ -747,8 +785,8 @@ libarchive_test_LDADD= $(LTLIBICONV) # Building it automatically provides a sanity-check on libarchive_test_SOURCES # above. libarchive/test/list.h: $(libarchive_test_SOURCES) - $(MKDIR_P) libarchive/test - grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ + @$(MKDIR_P) libarchive/test + $(AM_V_GEN)grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ libarchive_TESTS_ENVIRONMENT= LIBARCHIVE_TEST_FILES=`cd $(top_srcdir);/bin/pwd`/libarchive/test LRZIP=NOCONFIG @@ -757,6 +795,8 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_fuzz_consumer.h \ libarchive/test/test_acl_pax_posix1e.tar.uu \ libarchive/test/test_acl_pax_nfs4.tar.uu \ + libarchive/test/test_archive_read_open_filenames_split_uaf_1.tar.uu \ + libarchive/test/test_archive_read_open_filenames_split_uaf_2.tar.uu \ libarchive/test/test_archive_string_conversion.txt.Z.uu \ libarchive/test/test_archive_pathmatch_oob_bracket.bin.uu \ libarchive/test/test_archive_pathmatch_oob_strchr.bin.uu \ @@ -813,6 +853,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_compat_zip_8.zip.uu \ libarchive/test/test_compat_zstd_1.tar.zst.uu \ libarchive/test/test_compat_zstd_2.tar.zst.uu \ + libarchive/test/test_compat_zstd_3.tar.zst.uu \ libarchive/test/test_fuzz.cab.uu \ libarchive/test/test_fuzz.lzh.uu \ libarchive/test/test_fuzz_1.iso.Z.uu \ @@ -873,7 +914,13 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_7zip_copy_2.7z.uu \ libarchive/test/test_read_format_7zip_deflate.7z.uu \ libarchive/test/test_read_format_7zip_deflate_arm64.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_arm64_large.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_armthumb.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_delta.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_ia64.7z.uu \ libarchive/test/test_read_format_7zip_deflate_powerpc.7z.uu \ + libarchive/test/test_read_format_7zip_deflate_riscv.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_delta4_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_delta_lzma2.7z.uu \ @@ -891,6 +938,13 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_7zip_lzma1.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_2.7z.uu \ libarchive/test/test_read_format_7zip_lzma1_lzma2.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_arm.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_arm64.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_armthumb.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_ia64.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_powerpc.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_riscv.7z.uu \ + libarchive/test/test_read_format_7zip_lzma1_sparc.7z.uu \ libarchive/test/test_read_format_7zip_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_lzma2_arm64.7z.uu \ libarchive/test/test_read_format_7zip_lzma2_arm.7z.uu \ @@ -902,8 +956,12 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_7zip_malformed3.7z.uu \ libarchive/test/test_read_format_7zip_malformed4.7z.uu \ libarchive/test/test_read_format_7zip_malformed_numfiles_oom.7z.uu \ + libarchive/test/test_read_format_7zip_null_client_next.7z.uu \ libarchive/test/test_read_format_7zip_packinfo_digests.7z.uu \ libarchive/test/test_read_format_7zip_ppmd.7z.uu \ + libarchive/test/test_read_format_7zip_ppmd_arm64.7z.uu \ + libarchive/test/test_read_format_7zip_ppmd_bcj.7z.uu \ + libarchive/test/test_read_format_7zip_ppmd_small_block.7z.uu \ libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu \ libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu \ libarchive/test/test_read_format_7zip_sfx_elf.elf.uu \ @@ -925,9 +983,12 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_cab_filename_cp932.cab.uu \ libarchive/test/test_read_format_cab_lzx_16bit.cab.uu \ libarchive/test/test_read_format_cab_lzx_e8.cab.uu \ + libarchive/test/test_read_format_cab_lzx_invalid_history.cab.uu \ + libarchive/test/test_read_format_cab_lzx_literal_control.cab.uu \ libarchive/test/test_read_format_cab_lzx_oob.cab.uu \ libarchive/test/test_read_format_cab_multi.p1.cab.uu \ libarchive/test/test_read_format_cab_multi.p2.cab.uu \ + libarchive/test/test_read_format_cab_skip_across_folders.cab.uu \ libarchive/test/test_read_format_cab_skip_malformed.cab.uu \ libarchive/test/test_read_format_cpio_afio_header.part1.cpio.uu \ libarchive/test/test_read_format_cpio_afio_header.part2.cpio.uu \ @@ -951,6 +1012,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tar.uu \ libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \ + libarchive/test/test_read_format_gtar_sparse_exceeds_length.tar.Z.uu \ libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu \ libarchive/test/test_read_format_gtar_sparse_length.tar.Z.uu \ libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu \ @@ -967,12 +1029,16 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_iso_rockridge.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_ce.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_ce_loop.iso.Z.uu \ + libarchive/test/test_read_format_iso_rockridge_ce_overflow.iso.Z.uu \ + libarchive/test/test_read_format_iso_rockridge_cl_re_control.iso.Z.uu \ + libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_new.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_rr_moved.iso.Z.uu \ libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu \ libarchive/test/test_read_format_iso_xorriso.iso.Z.uu \ libarchive/test/test_read_format_iso_zisofs.iso.Z.uu \ libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu \ + libarchive/test/test_read_format_lha_amiga_script.lha.uu \ libarchive/test/test_read_format_lha_bugfix_0.lzh.uu \ libarchive/test/test_read_format_lha_filename_cp932.lzh.uu \ libarchive/test/test_read_format_lha_filename_utf16.lzh.uu \ @@ -999,6 +1065,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar_encryption_partially.rar.uu \ libarchive/test/test_read_format_rar_endarc_huge.rar.uu \ libarchive/test/test_read_format_rar_filter.rar.uu \ + libarchive/test/test_read_format_rar_filter_incomplete_block.rar.uu \ libarchive/test/test_read_format_rar_invalid1.rar.uu \ libarchive/test/test_read_format_rar_multi_lzss_blocks.rar.uu \ libarchive/test/test_read_format_rar3_lowdist_reset.rar.uu \ @@ -1040,13 +1107,17 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_different_window_size.rar.uu \ libarchive/test/test_read_format_rar5_different_solid_window_size.rar.uu \ libarchive/test/test_read_format_rar5_distance_overflow.rar.uu \ + libarchive/test/test_read_format_rar5_extra_data_size_toolong.rar.Z.uu \ + libarchive/test/test_read_format_rar5_extra_field_underflow.rar.uu \ libarchive/test/test_read_format_rar5_extra_field_version.rar.uu \ libarchive/test/test_read_format_rar5_fileattr.rar.uu \ + libarchive/test/test_read_format_rar5_group_name_toolong.rar.uu \ libarchive/test/test_read_format_rar5_hardlink.rar.uu \ libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu \ libarchive/test/test_read_format_rar5_leftshift1.rar.uu \ libarchive/test/test_read_format_rar5_leftshift2.rar.uu \ libarchive/test/test_read_format_rar5_loop_bug.rar.uu \ + libarchive/test/test_read_format_rar5_redir_overflow.rar.uu \ libarchive/test/test_read_format_rar5_multiarchive.part01.rar.uu \ libarchive/test/test_read_format_rar5_multiarchive.part02.rar.uu \ libarchive/test/test_read_format_rar5_multiarchive.part03.rar.uu \ @@ -1064,7 +1135,9 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu \ libarchive/test/test_read_format_rar5_dirdata.rar.uu \ libarchive/test/test_read_format_rar5_owner.rar.uu \ + libarchive/test/test_read_format_rar5_owner_name_toolong.rar.uu \ libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu \ + libarchive/test/test_read_format_rar5_redir_varint_2byte.rar.uu \ libarchive/test/test_read_format_rar5_sfx.exe.uu \ libarchive/test/test_read_format_rar5_solid.rar.uu \ libarchive/test/test_read_format_rar5_stored.rar.uu \ @@ -1082,6 +1155,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_bad_tables.rar.uu \ libarchive/test/test_read_format_rar5_bad_window_sz_in_mltarc_file.rar.uu \ libarchive/test/test_read_format_rar5_data_ready_pointer_leak.rar.uu \ + libarchive/test/test_read_format_rar5_zip_in_rar.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.gz.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ @@ -1096,6 +1170,8 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_tar_invalid_pax_size.tar.uu \ libarchive/test/test_read_format_tar_mac_metadata_1.tar.uu \ libarchive/test/test_read_format_tar_pax_g_large.tar.uu \ + libarchive/test/test_read_format_tar_pax_global_header.tar.uu \ + libarchive/test/test_read_format_tar_pax_global_header_ignorable.tar.uu \ libarchive/test/test_read_format_tar_pax_large_attr.tar.Z.uu \ libarchive/test/test_read_format_tar_pax_negative_time.tar.uu \ libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu \ @@ -1104,14 +1180,18 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_ustar_filename_cp866.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_eucjp.tar.Z.uu \ libarchive/test/test_read_format_ustar_filename_koi8r.tar.Z.uu \ - libarchive/test/test_read_format_warc.warc.uu \ + libarchive/test/test_read_format_warc_uri_nul.warc.uu \ libarchive/test/test_read_format_warc_incomplete.warc.uu \ + libarchive/test/test_read_format_warc.warc.uu \ libarchive/test/test_read_format_xar_atou64_overread.xar.uu \ libarchive/test/test_read_format_xar_base64_oob.xar.uu \ libarchive/test/test_read_format_xar_doublelink.xar.uu \ libarchive/test/test_read_format_xar_duplicate_filename_node.xar.uu \ + libarchive/test/test_read_format_xar_empty_xattr.xar.uu \ libarchive/test/test_read_format_xar_large_mode.xar.uu \ libarchive/test/test_read_format_xar_parse_time_overread.xar.uu \ + libarchive/test/test_read_format_xar_toc_premature_stream_end.xar.uu \ + libarchive/test/test_read_format_xar_toc_premature_stream_end_control.xar.uu \ libarchive/test/test_read_format_zip.zip.uu \ libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu \ libarchive/test/test_read_format_zip_7z_deflate.zip.uu \ @@ -1134,6 +1214,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_high_compression.zip.uu \ libarchive/test/test_read_format_zip_length_at_end.zip.uu \ libarchive/test/test_read_format_zip_lzma.zipx.uu \ + libarchive/test/test_read_format_zip_lzma_alone_large_size.zip.uu \ libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu \ libarchive/test/test_read_format_zip_lzma_multi.zipx.uu \ libarchive/test/test_read_format_zip_lzma_stream_end.zipx.uu \ @@ -1211,15 +1292,19 @@ libarchive_test_EXTRA_DIST=\ # noinst_HEADERS+= \ libarchive_fe/lafe_err.h \ + libarchive_fe/lafe_explicit_bzero.h \ libarchive_fe/lafe_fnmatch.h \ libarchive_fe/lafe_getline.h \ libarchive_fe/lafe_platform.h \ + libarchive_fe/lafe_setmode.h \ libarchive_fe/line_reader.h \ libarchive_fe/passphrase.h libarchive_fe_la_SOURCES= \ libarchive_fe/lafe_err.c \ + libarchive_fe/lafe_explicit_bzero.c \ libarchive_fe/lafe_fnmatch.c \ libarchive_fe/lafe_getline.c \ + libarchive_fe/lafe_setmode.c \ libarchive_fe/line_reader.c \ libarchive_fe/passphrase.c @@ -1315,6 +1400,7 @@ bsdtar_test_SOURCES= \ tar/test/test_extract_tar_lzo.c \ tar/test/test_extract_tar_xz.c \ tar/test/test_extract_tar_zstd.c \ + tar/test/test_edit_pathname_hardlink.c \ tar/test/test_format_newc.c \ tar/test/test_help.c \ tar/test/test_leading_slash.c \ @@ -1347,6 +1433,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_lz4.c \ tar/test/test_option_lzma.c \ tar/test/test_option_lzop.c \ + tar/test/test_option_mode.c \ tar/test/test_option_mtime.c \ tar/test/test_option_n.c \ tar/test/test_option_newer_than.c \ @@ -1381,8 +1468,8 @@ bsdtar_test_CPPFLAGS=\ $(PLATFORMCPPFLAGS) tar/test/list.h: $(bsdtar_test_SOURCES) - $(MKDIR_P) tar/test - grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ + @$(MKDIR_P) tar/test + $(AM_V_GEN)grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ if BUILD_BSDTAR bsdtar_test_programs= bsdtar_test @@ -1406,6 +1493,7 @@ bsdtar_test_EXTRA_DIST= \ tar/test/test_extract.tar.lzo.uu \ tar/test/test_extract.tar.xz.uu \ tar/test/test_leading_slash.tar.uu \ + tar/test/test_edit_pathname_hardlink.tar.Z.uu \ tar/test/test_list_item.tar.uu \ tar/test/test_option_keep_newer_files.tar.Z.uu \ tar/test/test_option_passphrase.zip.uu \ @@ -1539,8 +1627,8 @@ bsdcpio_test_CPPFLAGS= \ bsdcpio_test_LDADD=libarchive_fe.la cpio/test/list.h: $(bsdcpio_test_SOURCES) - $(MKDIR_P) cpio/test - grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ + @$(MKDIR_P) cpio/test + $(AM_V_GEN)grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ if BUILD_BSDCPIO bsdcpio_test_programs= bsdcpio_test @@ -1662,8 +1750,8 @@ bsdcat_test_CPPFLAGS= \ bsdcat_test_LDADD=libarchive_fe.la cat/test/list.h: $(bsdcat_test_SOURCES) - $(MKDIR_P) cat/test - grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ + @$(MKDIR_P) cat/test + $(AM_V_GEN)grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ if BUILD_BSDCAT bsdcat_test_programs= bsdcat_test @@ -1780,6 +1868,7 @@ bsdunzip_test_SOURCES= \ unzip/test/test_t_bad.c \ unzip/test/test_u.c \ unzip/test/test_version.c \ + unzip/test/test_windows_paths.c \ unzip/test/test_x.c \ unzip/test/test_Z1.c \ unzip/test/test_P_encryption.c \ @@ -1794,8 +1883,8 @@ bsdunzip_test_CPPFLAGS= \ bsdunzip_test_LDADD=libarchive_fe.la unzip/test/list.h: $(bsdunzip_test_SOURCES) - $(MKDIR_P) unzip/test - grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ + @$(MKDIR_P) unzip/test + $(AM_V_GEN)grep -h '^DEFINE_TEST(' $^ | LC_COLLATE=C sort > $@ if BUILD_BSDUNZIP bsdunzip_test_programs= bsdunzip_test @@ -1815,6 +1904,7 @@ bsdunzip_test_EXTRA_DIST= \ unzip/test/test_symlink_1.zip.uu \ unzip/test/test_symlink_2.zip.uu \ unzip/test/test_symlink_dotdot.zip.uu \ + unzip/test/test_windows_paths.zip.uu \ unzip/test/test_t_bad.zip.uu \ unzip/test/test_u_new.zip.uu \ unzip/test/test_u_old.zip.uu \ diff --git a/NEWS b/NEWS index a46b69aaf3e9..e0dc5f5c110a 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +Oct 14, 2026: libarchive 3.8.10 released + Jul 28, 2026: libarchive 3.8.9 released Jun 23, 2026: libarchive 3.8.8 released diff --git a/build/autoconf/visibility.m4 b/build/autoconf/visibility.m4 new file mode 100644 index 000000000000..55ceb67219be --- /dev/null +++ b/build/autoconf/visibility.m4 @@ -0,0 +1,84 @@ +# visibility.m4 +# serial 9 +dnl Copyright (C) 2005, 2008, 2010-2026 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl This file is offered as-is, without any warranty. + +dnl From Bruno Haible. + +dnl Tests whether the compiler supports the command-line option +dnl -fvisibility=hidden and the function and variable attributes +dnl __attribute__((__visibility__("hidden"))) and +dnl __attribute__((__visibility__("default"))). +dnl Does *not* test for __visibility__("protected") - which has tricky +dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on +dnl Mac OS X. +dnl Does *not* test for __visibility__("internal") - which has processor +dnl dependent semantics. +dnl Does *not* test for #pragma GCC visibility push(hidden) - which is +dnl "really only recommended for legacy code". +dnl Set the variable CFLAG_VISIBILITY. +dnl Defines and sets the variable HAVE_VISIBILITY. + +AC_DEFUN([gl_VISIBILITY], +[ + AC_REQUIRE([AC_PROG_CC]) + CFLAG_VISIBILITY= + HAVE_VISIBILITY=0 + if test -n "$GCC"; then + dnl First, check whether -Werror can be added to the command line, or + dnl whether it leads to an error because of some other option that the + dnl user has put into $CC $CFLAGS $CPPFLAGS. + AC_CACHE_CHECK([whether the -Werror option is usable], + [gl_cv_cc_vis_werror], + [gl_saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]], [[]])], + [gl_cv_cc_vis_werror=yes], + [gl_cv_cc_vis_werror=no]) + CFLAGS="$gl_saved_CFLAGS" + ]) + dnl Now check whether visibility declarations are supported. + AC_CACHE_CHECK([for simple visibility declarations], + [gl_cv_cc_visibility], + [gl_saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fvisibility=hidden" + dnl We use the option -Werror and a function dummyfunc, because on some + dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning + dnl "visibility attribute not supported in this configuration; ignored" + dnl at the first function definition in every compilation unit, and we + dnl don't want to use the option in this case. + if test $gl_cv_cc_vis_werror = yes; then + CFLAGS="$CFLAGS -Werror" + fi + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[extern __attribute__((__visibility__("hidden"))) int hiddenvar; + extern __attribute__((__visibility__("default"))) int exportedvar; + extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); + extern __attribute__((__visibility__("default"))) int exportedfunc (void); + void dummyfunc (void); + int hiddenvar; + int exportedvar; + int hiddenfunc (void) { return 51; } + int exportedfunc (void) { return 1225736919; } + void dummyfunc (void) {} + ]], + [[]])], + [gl_cv_cc_visibility=yes], + [gl_cv_cc_visibility=no]) + CFLAGS="$gl_saved_CFLAGS" + ]) + if test $gl_cv_cc_visibility = yes; then + CFLAG_VISIBILITY="-fvisibility=hidden" + HAVE_VISIBILITY=1 + fi + fi + AC_SUBST([CFLAG_VISIBILITY]) + AC_SUBST([HAVE_VISIBILITY]) + AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], + [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) +]) diff --git a/build/ci/build.sh b/build/ci/build.sh index 9b983f64af8c..675368e8ba5b 100755 --- a/build/ci/build.sh +++ b/build/ci/build.sh @@ -4,6 +4,7 @@ # # Variables that can be passed via environment: # BS= # build system (autotools or cmake) +# BUILD_SYSTEM= # alias for BS, overrides BS if set # CRYPTO= # cryptography provider (openssl, nettle or mbedtls) # BUILDDIR= # build directory # SRCDIR= # source directory @@ -26,10 +27,9 @@ CMAKE_ARGS="${CMAKE_ARGS:-}" CONFIGURE_ARGS="${CONFIGURE_ARGS:-}" CURDIR=`pwd` SRCDIR="${SRCDIR:-`pwd`}" -RET=0 usage () { - echo "Usage: $0 [-b autotools|cmake] [-a autogen|configure|build|test|install|distcheck ] [ -a ... ] [ -d builddir ] [-c openssl|nettle|mbedtls] [-s srcdir ]" + echo "Usage: $0 [-b autotools|cmake] [-a autogen|configure|build|test|install|distcheck|artifact|dist-artifact ] [ -a ... ] [ -d builddir ] [-c openssl|nettle|mbedtls] [-s srcdir ]" } inputerror () { echo $1 @@ -117,6 +117,15 @@ fi if [ -z "${BUILDDIR:-}" ]; then BUILDDIR="${CURDIR}/build_ci/${BS}" fi +# The loop below cd's around, so make these absolute. +case "${SRCDIR}" in + /*) ;; + *) SRCDIR="${CURDIR}/${SRCDIR}" ;; +esac +case "${BUILDDIR}" in + /*) ;; + *) BUILDDIR="${CURDIR}/${BUILDDIR}" ;; +esac mkdir -p "${BUILDDIR}" for action in ${ACTIONS}; do cd "${BUILDDIR}" @@ -126,7 +135,6 @@ for action in ${ACTIONS}; do autotools) cd "${SRCDIR}" sh build/autogen.sh - RET="$?" ;; esac ;; @@ -135,38 +143,27 @@ for action in ${ACTIONS}; do autotools) "${SRCDIR}/configure" ${CONFIGURE_ARGS} ;; cmake) ${CMAKE} ${CMAKE_ARGS} "${SRCDIR}" ;; esac - RET="$?" ;; build) ${MAKE} ${MAKE_ARGS} - RET="$?" ;; test) case "${BS}" in autotools) - ${MAKE} ${MAKE_ARGS} check LOG_DRIVER="${SRCDIR}/build/ci/test_driver" + ${MAKE} ${MAKE_ARGS} check LOG_DRIVER="${SRCDIR}/build/ci/test_driver" _VERBOSITY_LEVEL=2 ;; cmake) - ${MAKE} ${MAKE_ARGS} test + ${MAKE} ${MAKE_ARGS} test _VERBOSITY_LEVEL=2 ;; esac - RET="$?" - find ${TMPDIR:-/tmp} -path '*_test.*' -name '*.log' -print -exec cat {} \; 2>/dev/null || /bin/true ;; install) ${MAKE} ${MAKE_ARGS} install DESTDIR="${BUILDDIR}/destdir" - RET="$?" cd "${BUILDDIR}/destdir" && ls -lR . ./usr/local/bin/bsdtar --version ;; distcheck) - ${MAKE} ${MAKE_ARGS} distcheck || ( - RET="$?" - find . -name 'test-suite.log' -print -exec cat {} \; - find ${TMPDIR:-/tmp} -path '*_test.*' -name '*.log' -print -exec cat {} \; 2>/dev/null || /bin/true - exit "${RET}" - ) - RET="$?" + ${MAKE} ${MAKE_ARGS} distcheck _VERBOSITY_LEVEL=2 ;; artifact) tar -c -J -C "${BUILDDIR}/destdir" -f "${CURDIR}/libarchive.tar.xz" usr @@ -178,9 +175,5 @@ for action in ${ACTIONS}; do ls -l "${CURDIR}/libarchive-dist.tar" ;; esac - if [ "${RET}" != "0" ]; then - exit "${RET}" - fi cd "${CURDIR}" done -exit "${RET}" diff --git a/build/ci/github_actions/ci.cmd b/build/ci/github_actions/ci.cmd index 47600aa4bdf0..e5b188e56c16 100755 --- a/build/ci/github_actions/ci.cmd +++ b/build/ci/github_actions/ci.cmd @@ -1,20 +1,20 @@ @ECHO OFF -IF NOT "%BE%"=="mingw-gcc" ( +IF NOT "%BE%"=="mingw" ( IF NOT "%BE%"=="msvc" ( IF NOT "%BE%"=="cygwin-gcc" ( - ECHO Environment variable BE must be cygwin-gcc, mingw-gcc or msvc + ECHO Environment variable BE must be cygwin-gcc, mingw or msvc EXIT /b 1 ) ) ) SET ORIGPATH=%PATH% -IF "%BE%"=="mingw-gcc" ( - SET MINGWPATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\msys64\mingw64\bin;C:\Program Files\cmake\bin +IF "%BE%"=="mingw" ( + SET MINGWPATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\msys64\%MSYSTEM%\bin;C:\Program Files\cmake\bin ) IF "%1%"=="configure" ( - IF "%BE%"=="mingw-gcc" ( + IF "%BE%"=="mingw" ( SET PATH=%MINGWPATH% MKDIR build_ci\cmake CD build_ci\cmake @@ -29,7 +29,7 @@ IF "%1%"=="configure" ( D:\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a configure" || EXIT /b 1 ) ) ELSE IF "%1%"=="build" ( - IF "%BE%"=="mingw-gcc" ( + IF "%BE%"=="mingw" ( SET PATH=%MINGWPATH% CD build_ci\cmake mingw32-make -j %NUMBER_OF_PROCESSORS% VERBOSE=1 || EXIT /b 1 @@ -38,12 +38,13 @@ IF "%1%"=="configure" ( cmake --build . --target ALL_BUILD --config Release || EXIT /b 1 ) ELSE IF "%BE%"=="cygwin-gcc" ( SET BS=cmake - SET MAKE_ARGS=-j + SET MAKE_ARGS=-j %NUMBER_OF_PROCESSORS% SET CYGWIN_NOWINPATH=1 D:\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a build" || EXIT /b 1 ) ) ELSE IF "%1%"=="test" ( - IF "%BE%"=="mingw-gcc" ( + SET _VERBOSITY_LEVEL=2 + IF "%BE%"=="mingw" ( SET PATH=%MINGWPATH% CD build_ci\cmake SET SKIP_TEST_SPARSE=1 @@ -60,7 +61,7 @@ IF "%1%"=="configure" ( EXIT /b 0 ) ) ELSE IF "%1%"=="install" ( - IF "%BE%"=="mingw-gcc" ( + IF "%BE%"=="mingw" ( SET PATH=%MINGWPATH% CD build_ci\cmake mingw32-make install || EXIT /b 1 diff --git a/build/ci/github_actions/freebsd.sh b/build/ci/github_actions/freebsd.sh index fff74f6a6f0c..623898ec30e2 100755 --- a/build/ci/github_actions/freebsd.sh +++ b/build/ci/github_actions/freebsd.sh @@ -34,7 +34,7 @@ then fi BUILDDIR="${CURDIR}/build_ci/${BS}" cd "$BUILDDIR" - TMPDIR=/tmp_acl_nfsv4 ${BIN_SUBDIR}/libarchive_test -r "${CURDIR}/libarchive/test" -v test_acl_platform_nfs4 + _VERBOSITY_LEVEL=2 TMPDIR=/tmp_acl_nfsv4 ${BIN_SUBDIR}/libarchive_test -r "${CURDIR}/libarchive/test" -v test_acl_platform_nfs4 else echo "Usage: $0 prepare | test" exit 1 diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index 76864a909e44..00482d18123c 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -204,6 +204,9 @@ typedef uint64_t uintmax_t; /* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_LIBC 1 +/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */ +#cmakedefine ARCHIVE_CRYPTO_MD5_LIBMD 1 + /* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1 @@ -222,18 +225,24 @@ typedef uint64_t uintmax_t; /* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_LIBC 1 -/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */ -#cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */ +#cmakedefine ARCHIVE_CRYPTO_RMD160_LIBMD 1 /* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_MBEDTLS 1 +/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */ +#cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1 + /* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */ #cmakedefine ARCHIVE_CRYPTO_RMD160_OPENSSL 1 /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBC 1 +/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA1_LIBMD 1 + /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1 @@ -258,6 +267,9 @@ typedef uint64_t uintmax_t; /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBC3 1 +/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA256_LIBMD 1 + /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1 @@ -306,6 +318,9 @@ typedef uint64_t uintmax_t; /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBC3 1 +/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */ +#cmakedefine ARCHIVE_CRYPTO_SHA512_LIBMD 1 + /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1 @@ -321,6 +336,9 @@ typedef uint64_t uintmax_t; /* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */ #cmakedefine ARCHIVE_CRYPTO_SHA512_WIN 1 +/* Use Windows CNG(Crypto Next Generation) */ +#cmakedefine ARCHIVE_CRYPTO_WIN 1 + /* AIX xattr support */ #cmakedefine ARCHIVE_XATTR_AIX 1 @@ -345,43 +363,43 @@ typedef uint64_t uintmax_t; /* Version number of bsdunzip */ #cmakedefine BSDUNZIP_VERSION_STRING "@BSDUNZIP_VERSION_STRING@" -/* Define to 1 if you have the `acl_create_entry' function. */ +/* Define to 1 if you have the 'acl_create_entry' function. */ #cmakedefine HAVE_ACL_CREATE_ENTRY 1 -/* Define to 1 if you have the `acl_get_fd_np' function. */ +/* Define to 1 if you have the 'acl_get_fd_np' function. */ #cmakedefine HAVE_ACL_GET_FD_NP 1 -/* Define to 1 if you have the `acl_get_link' function. */ +/* Define to 1 if you have the 'acl_get_link' function. */ #cmakedefine HAVE_ACL_GET_LINK 1 -/* Define to 1 if you have the `acl_get_link_np' function. */ +/* Define to 1 if you have the 'acl_get_link_np' function. */ #cmakedefine HAVE_ACL_GET_LINK_NP 1 -/* Define to 1 if you have the `acl_get_perm' function. */ +/* Define to 1 if you have the 'acl_get_perm' function. */ #cmakedefine HAVE_ACL_GET_PERM 1 -/* Define to 1 if you have the `acl_get_perm_np' function. */ +/* Define to 1 if you have the 'acl_get_perm_np' function. */ #cmakedefine HAVE_ACL_GET_PERM_NP 1 -/* Define to 1 if you have the `acl_init' function. */ +/* Define to 1 if you have the 'acl_init' function. */ #cmakedefine HAVE_ACL_INIT 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ACL_LIBACL_H 1 -/* Define to 1 if the system has the type `acl_permset_t'. */ +/* Define to 1 if the system has the type 'acl_permset_t'. */ #cmakedefine HAVE_ACL_PERMSET_T 1 -/* Define to 1 if you have the `acl_set_fd' function. */ +/* Define to 1 if you have the 'acl_set_fd' function. */ #cmakedefine HAVE_ACL_SET_FD 1 -/* Define to 1 if you have the `acl_set_fd_np' function. */ +/* Define to 1 if you have the 'acl_set_fd_np' function. */ #cmakedefine HAVE_ACL_SET_FD_NP 1 -/* Define to 1 if you have the `acl_set_file' function. */ +/* Define to 1 if you have the 'acl_set_file' function. */ #cmakedefine HAVE_ACL_SET_FILE 1 -/* Define to 1 if you have the `arc4random_buf' function. */ +/* Define to 1 if you have the 'arc4random_buf' function. */ #cmakedefine HAVE_ARC4RANDOM_BUF 1 /* Define to 1 if you have the header file. */ @@ -396,131 +414,131 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BZLIB_H 1 -/* Define to 1 if you have the `chflags' function. */ +/* Define to 1 if you have the 'chflags' function. */ #cmakedefine HAVE_CHFLAGS 1 -/* Define to 1 if you have the `chown' function. */ +/* Define to 1 if you have the 'chown' function. */ #cmakedefine HAVE_CHOWN 1 -/* Define to 1 if you have the `chroot' function. */ +/* Define to 1 if you have the 'chroot' function. */ #cmakedefine HAVE_CHROOT 1 -/* Define to 1 if you have the `closefrom' function. */ +/* Define to 1 if you have the 'closefrom' function. */ #cmakedefine HAVE_CLOSEFROM 1 -/* Define to 1 if you have the `close_range' function. */ +/* Define to 1 if you have the 'close_range' function. */ #cmakedefine HAVE_CLOSE_RANGE 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_COPYFILE_H 1 -/* Define to 1 if you have the `ctime_r' function. */ +/* Define to 1 if you have the 'ctime_r' function. */ #cmakedefine HAVE_CTIME_R 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_CTYPE_H 1 -/* Define to 1 if you have the `cygwin_conv_path' function. */ +/* Define to 1 if you have the 'cygwin_conv_path' function. */ #cmakedefine HAVE_CYGWIN_CONV_PATH 1 -/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you +/* Define to 1 if you have the declaration of 'ACE_GETACL', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACE_GETACL 1 -/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you +/* Define to 1 if you have the declaration of 'ACE_GETACLCNT', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACE_GETACLCNT 1 -/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you +/* Define to 1 if you have the declaration of 'ACE_SETACL', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACE_SETACL 1 -/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if +/* Define to 1 if you have the declaration of 'ACL_SYNCHRONIZE', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACL_SYNCHRONIZE 1 -/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if +/* Define to 1 if you have the declaration of 'ACL_TYPE_EXTENDED', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACL_TYPE_EXTENDED 1 -/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you +/* Define to 1 if you have the declaration of 'ACL_TYPE_NFS4', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACL_TYPE_NFS4 1 -/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you +/* Define to 1 if you have the declaration of 'ACL_USER', and to 0 if you don't. */ #cmakedefine HAVE_DECL_ACL_USER 1 -/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'INT32_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT32_MAX 1 -/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you +/* Define to 1 if you have the declaration of 'INT32_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT32_MIN 1 -/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'INT64_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT64_MAX 1 -/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you +/* Define to 1 if you have the declaration of 'INT64_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INT64_MIN 1 -/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'INTMAX_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INTMAX_MAX 1 -/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you +/* Define to 1 if you have the declaration of 'INTMAX_MIN', and to 0 if you don't. */ #cmakedefine HAVE_DECL_INTMAX_MIN 1 -/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't. +/* Define to 1 if you have the declaration of 'SETACL', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SETACL 1 -/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'SIZE_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SIZE_MAX 1 -/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'SSIZE_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_SSIZE_MAX 1 -/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you +/* Define to 1 if you have the declaration of 'strerror_r', and to 0 if you don't. */ #cmakedefine HAVE_DECL_STRERROR_R 1 -/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'UINT32_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINT32_MAX 1 -/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'UINT64_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINT64_MAX 1 -/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you +/* Define to 1 if you have the declaration of 'UINTMAX_MAX', and to 0 if you don't. */ #cmakedefine HAVE_DECL_UINTMAX_MAX 1 -/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if +/* Define to 1 if you have the declaration of 'XATTR_NOFOLLOW', and to 0 if you don't. */ #cmakedefine HAVE_DECL_XATTR_NOFOLLOW 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DIRECT_H 1 -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #cmakedefine HAVE_DIRENT_H 1 -/* Define to 1 if you have the `dirfd' function. */ +/* Define to 1 if you have the 'dirfd' function. */ #cmakedefine HAVE_DIRFD 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* Define to 1 if you don't have 'vprintf' but do have '_doprnt.' */ #cmakedefine HAVE_DOPRNT 1 /* Define to 1 if nl_langinfo supports D_MD_ORDER */ @@ -538,150 +556,153 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_EXPAT_H 1 +/* Define to 1 if you have the 'explicit_bzero' function. */ +#cmakedefine HAVE_EXPLICIT_BZERO 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_EXT2FS_EXT2_FS_H 1 -/* Define to 1 if you have the `extattr_get_file' function. */ +/* Define to 1 if you have the 'extattr_get_file' function. */ #cmakedefine HAVE_EXTATTR_GET_FILE 1 -/* Define to 1 if you have the `extattr_list_file' function. */ +/* Define to 1 if you have the 'extattr_list_file' function. */ #cmakedefine HAVE_EXTATTR_LIST_FILE 1 -/* Define to 1 if you have the `extattr_set_fd' function. */ +/* Define to 1 if you have the 'extattr_set_fd' function. */ #cmakedefine HAVE_EXTATTR_SET_FD 1 -/* Define to 1 if you have the `extattr_set_file' function. */ +/* Define to 1 if you have the 'extattr_set_file' function. */ #cmakedefine HAVE_EXTATTR_SET_FILE 1 /* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */ #cmakedefine HAVE_DECL_EXTATTR_NAMESPACE_USER 1 -/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't. +/* Define to 1 if you have the declaration of 'GETACL', and to 0 if you don't. */ #cmakedefine HAVE_DECL_GETACL 1 -/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you +/* Define to 1 if you have the declaration of 'GETACLCNT', and to 0 if you don't. */ #cmakedefine HAVE_DECL_GETACLCNT 1 -/* Define to 1 if you have the `fchdir' function. */ +/* Define to 1 if you have the 'fchdir' function. */ #cmakedefine HAVE_FCHDIR 1 -/* Define to 1 if you have the `fchflags' function. */ +/* Define to 1 if you have the 'fchflags' function. */ #cmakedefine HAVE_FCHFLAGS 1 -/* Define to 1 if you have the `fchmod' function. */ +/* Define to 1 if you have the 'fchmod' function. */ #cmakedefine HAVE_FCHMOD 1 -/* Define to 1 if you have the `fchown' function. */ +/* Define to 1 if you have the 'fchown' function. */ #cmakedefine HAVE_FCHOWN 1 -/* Define to 1 if you have the `fcntl' function. */ +/* Define to 1 if you have the 'fcntl' function. */ #cmakedefine HAVE_FCNTL 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 -/* Define to 1 if you have the `fdopendir' function. */ +/* Define to 1 if you have the 'fdopendir' function. */ #cmakedefine HAVE_FDOPENDIR 1 -/* Define to 1 if you have the `fgetea' function. */ +/* Define to 1 if you have the 'fgetea' function. */ #cmakedefine HAVE_FGETEA 1 -/* Define to 1 if you have the `fgetxattr' function. */ +/* Define to 1 if you have the 'fgetxattr' function. */ #cmakedefine HAVE_FGETXATTR 1 -/* Define to 1 if you have the `flistea' function. */ +/* Define to 1 if you have the 'flistea' function. */ #cmakedefine HAVE_FLISTEA 1 -/* Define to 1 if you have the `flistxattr' function. */ +/* Define to 1 if you have the 'flistxattr' function. */ #cmakedefine HAVE_FLISTXATTR 1 -/* Define to 1 if you have the `fnmatch' function. */ +/* Define to 1 if you have the 'fnmatch' function. */ #cmakedefine HAVE_FNMATCH 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FNMATCH_H 1 -/* Define to 1 if you have the `fork' function. */ +/* Define to 1 if you have the 'fork' function. */ #cmakedefine HAVE_FORK 1 /* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ #cmakedefine HAVE_FSEEKO 1 -/* Define to 1 if you have the `fsetea' function. */ +/* Define to 1 if you have the 'fsetea' function. */ #cmakedefine HAVE_FSETEA 1 -/* Define to 1 if you have the `fsetxattr' function. */ +/* Define to 1 if you have the 'fsetxattr' function. */ #cmakedefine HAVE_FSETXATTR 1 -/* Define to 1 if you have the `fstat' function. */ +/* Define to 1 if you have the 'fstat' function. */ #cmakedefine HAVE_FSTAT 1 -/* Define to 1 if you have the `fstatat' function. */ +/* Define to 1 if you have the 'fstatat' function. */ #cmakedefine HAVE_FSTATAT 1 -/* Define to 1 if you have the `fstatfs' function. */ +/* Define to 1 if you have the 'fstatfs' function. */ #cmakedefine HAVE_FSTATFS 1 -/* Define to 1 if you have the `fstatvfs' function. */ +/* Define to 1 if you have the 'fstatvfs' function. */ #cmakedefine HAVE_FSTATVFS 1 -/* Define to 1 if you have the `ftruncate' function. */ +/* Define to 1 if you have the 'ftruncate' function. */ #cmakedefine HAVE_FTRUNCATE 1 -/* Define to 1 if you have the `futimens' function. */ +/* Define to 1 if you have the 'futimens' function. */ #cmakedefine HAVE_FUTIMENS 1 -/* Define to 1 if you have the `futimes' function. */ +/* Define to 1 if you have the 'futimes' function. */ #cmakedefine HAVE_FUTIMES 1 -/* Define to 1 if you have the `futimesat' function. */ +/* Define to 1 if you have the 'futimesat' function. */ #cmakedefine HAVE_FUTIMESAT 1 -/* Define to 1 if you have the `getea' function. */ +/* Define to 1 if you have the 'getea' function. */ #cmakedefine HAVE_GETEA 1 -/* Define to 1 if you have the `getegid' function. */ +/* Define to 1 if you have the 'getegid' function. */ #cmakedefine HAVE_GETEGID 1 -/* Define to 1 if you have the `geteuid' function. */ +/* Define to 1 if you have the 'geteuid' function. */ #cmakedefine HAVE_GETEUID 1 -/* Define to 1 if you have the `getgrgid_r' function. */ +/* Define to 1 if you have the 'getgrgid_r' function. */ #cmakedefine HAVE_GETGRGID_R 1 -/* Define to 1 if you have the `getgrnam_r' function. */ +/* Define to 1 if you have the 'getgrnam_r' function. */ #cmakedefine HAVE_GETGRNAM_R 1 -/* Define to 1 if you have the `getline' function. */ +/* Define to 1 if you have the 'getline' function. */ #cmakedefine HAVE_GETLINE 1 -/* Define to 1 if you have the `getpid' function. */ +/* Define to 1 if you have the 'getpid' function. */ #cmakedefine HAVE_GETPID 1 -/* Define to 1 if you have the `getpwnam_r' function. */ +/* Define to 1 if you have the 'getpwnam_r' function. */ #cmakedefine HAVE_GETPWNAM_R 1 -/* Define to 1 if you have the `getpwuid_r' function. */ +/* Define to 1 if you have the 'getpwuid_r' function. */ #cmakedefine HAVE_GETPWUID_R 1 -/* Define to 1 if you have the `gettimeofday' function. */ +/* Define to 1 if you have the 'gettimeofday' function. */ #cmakedefine HAVE_GETTIMEOFDAY 1 -/* Define to 1 if you have the `getvfsbyname' function. */ +/* Define to 1 if you have the 'getvfsbyname' function. */ #cmakedefine HAVE_GETVFSBYNAME 1 -/* Define to 1 if you have the `getxattr' function. */ +/* Define to 1 if you have the 'getxattr' function. */ #cmakedefine HAVE_GETXATTR 1 -/* Define to 1 if you have the `gmtime_r' function. */ +/* Define to 1 if you have the 'gmtime_r' function. */ #cmakedefine HAVE_GMTIME_R 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_GRP_H 1 -/* Define to 1 if you have the `iconv' function. */ +/* Define to 1 if you have the 'iconv' function. */ #cmakedefine HAVE_ICONV 1 /* Define to 1 if you have the header file. */ @@ -699,84 +720,84 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LANGINFO_H 1 -/* Define to 1 if you have the `lchflags' function. */ +/* Define to 1 if you have the 'lchflags' function. */ #cmakedefine HAVE_LCHFLAGS 1 -/* Define to 1 if you have the `lchmod' function. */ +/* Define to 1 if you have the 'lchmod' function. */ #cmakedefine HAVE_LCHMOD 1 -/* Define to 1 if you have the `lchown' function. */ +/* Define to 1 if you have the 'lchown' function. */ #cmakedefine HAVE_LCHOWN 1 -/* Define to 1 if you have the `lgetea' function. */ +/* Define to 1 if you have the 'lgetea' function. */ #cmakedefine HAVE_LGETEA 1 -/* Define to 1 if you have the `lgetxattr' function. */ +/* Define to 1 if you have the 'lgetxattr' function. */ #cmakedefine HAVE_LGETXATTR 1 -/* Define to 1 if you have the `acl' library (-lacl). */ +/* Define to 1 if you have the 'acl' library (-lacl). */ #cmakedefine HAVE_LIBACL 1 -/* Define to 1 if you have the `attr' library (-lattr). */ +/* Define to 1 if you have the 'attr' library (-lattr). */ #cmakedefine HAVE_LIBATTR 1 -/* Define to 1 if you have the `bsdxml' library (-lbsdxml). */ +/* Define to 1 if you have the 'bsdxml' library (-lbsdxml). */ #cmakedefine HAVE_LIBBSDXML 1 -/* Define to 1 if you have the `bz2' library (-lbz2). */ +/* Define to 1 if you have the 'bz2' library (-lbz2). */ #cmakedefine HAVE_LIBBZ2 1 -/* Define to 1 if you have the `b2' library (-lb2). */ +/* Define to 1 if you have the 'b2' library (-lb2). */ #cmakedefine HAVE_LIBB2 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BLAKE2_H 1 -/* Define to 1 if you have the `charset' library (-lcharset). */ +/* Define to 1 if you have the 'charset' library (-lcharset). */ #cmakedefine HAVE_LIBCHARSET 1 -/* Define to 1 if you have the `crypto' library (-lcrypto). */ +/* Define to 1 if you have the 'crypto' library (-lcrypto). */ #cmakedefine HAVE_LIBCRYPTO 1 -/* Define to 1 if you have the `expat' library (-lexpat). */ +/* Define to 1 if you have the 'expat' library (-lexpat). */ #cmakedefine HAVE_LIBEXPAT 1 -/* Define to 1 if you have the `gcc' library (-lgcc). */ +/* Define to 1 if you have the 'gcc' library (-lgcc). */ #cmakedefine HAVE_LIBGCC 1 -/* Define to 1 if you have the `iconv' library (-liconv). */ -#cmakedefine HAVE_LIBICONV 1 - -/* Define to 1 if you have the `lz4' library (-llz4). */ +/* Define to 1 if you have the 'lz4' library (-llz4). */ #cmakedefine HAVE_LIBLZ4 1 -/* Define to 1 if you have the `lzma' library (-llzma). */ +/* Define to 1 if you have the 'lzma' library (-llzma). */ #cmakedefine HAVE_LIBLZMA 1 -/* Define to 1 if you have the `lzo2' library (-llzo2). */ +/* Define to 1 if you have the 'lzo2' library (-llzo2). */ #cmakedefine HAVE_LIBLZO2 1 -/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */ +/* Define to 1 if you have the 'mbedcrypto' library (-lmbedcrypto). */ #cmakedefine HAVE_LIBMBEDCRYPTO 1 -/* Define to 1 if you have the `nettle' library (-lnettle). */ +/* Define to 1 if you have the 'nettle' library (-lnettle). */ #cmakedefine HAVE_LIBNETTLE 1 -/* Define to 1 if you have the `pcre' library (-lpcre). */ +/* Define to 1 if you have the 'pcre' library (-lpcre). */ #cmakedefine HAVE_LIBPCRE 1 -/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */ +/* Define to 1 if you have the 'pcreposix' library (-lpcreposix). */ #cmakedefine HAVE_LIBPCREPOSIX 1 -/* Define to 1 if you have the `pcre2-8' library (-lpcre2-8). */ +/* Define to 1 if you have the 'pcre2-8' library (-lpcre2-8). */ #cmakedefine HAVE_LIBPCRE2 1 -/* Define to 1 if you have the `pcreposix' library (-lpcre2posix). */ +/* Define to 1 if you have the 'pcreposix' library (-lpcre2posix). */ #cmakedefine HAVE_LIBPCRE2POSIX 1 -/* Define to 1 if you have the `xml2' library (-lxml2). */ +/* Define to 1 if you have the 'xml2' library (-lxml2). */ #cmakedefine HAVE_LIBXML2 1 +/* Define to 1 if you have the 'xmllite' library. */ +#cmakedefine HAVE_LIBXMLLITE 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBXML_XMLREADER_H 1 @@ -786,10 +807,10 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIBXML_XMLVERSION_H 1 -/* Define to 1 if you have the `z' library (-lz). */ +/* Define to 1 if you have the 'z' library (-lz). */ #cmakedefine HAVE_LIBZ 1 -/* Define to 1 if you have the `zstd' library (-lzstd). */ +/* Define to 1 if you have the 'zstd' library (-lzstd). */ #cmakedefine HAVE_LIBZSTD 1 /* Define to 1 if you have the ZSTD_compressStream function. */ @@ -801,10 +822,10 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LIMITS_H 1 -/* Define to 1 if you have the `link' function. */ +/* Define to 1 if you have the 'link' function. */ #cmakedefine HAVE_LINK 1 -/* Define to 1 if you have the `linkat' function. */ +/* Define to 1 if you have the 'linkat' function. */ #cmakedefine HAVE_LINKAT 1 /* Define to 1 if you have the header file. */ @@ -819,47 +840,47 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LINUX_TYPES_H 1 -/* Define to 1 if you have the `listea' function. */ +/* Define to 1 if you have the 'listea' function. */ #cmakedefine HAVE_LISTEA 1 -/* Define to 1 if you have the `listxattr' function. */ +/* Define to 1 if you have the 'listxattr' function. */ #cmakedefine HAVE_LISTXATTR 1 -/* Define to 1 if you have the `llistea' function. */ +/* Define to 1 if you have the 'llistea' function. */ #cmakedefine HAVE_LLISTEA 1 -/* Define to 1 if you have the `llistxattr' function. */ +/* Define to 1 if you have the 'llistxattr' function. */ #cmakedefine HAVE_LLISTXATTR 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALCHARSET_H 1 -/* Define to 1 if you have the `locale_charset' function. */ +/* Define to 1 if you have the 'locale_charset' function. */ #cmakedefine HAVE_LOCALE_CHARSET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALE_H 1 -/* Define to 1 if you have the `localtime_r' function. */ +/* Define to 1 if you have the 'localtime_r' function. */ #cmakedefine HAVE_LOCALTIME_R 1 -/* Define to 1 if the system has the type `long long int'. */ +/* Define to 1 if the system has the type 'long long int'. */ #cmakedefine HAVE_LONG_LONG_INT 1 -/* Define to 1 if you have the `lsetea' function. */ +/* Define to 1 if you have the 'lsetea' function. */ #cmakedefine HAVE_LSETEA 1 -/* Define to 1 if you have the `lsetxattr' function. */ +/* Define to 1 if you have the 'lsetxattr' function. */ #cmakedefine HAVE_LSETXATTR 1 -/* Define to 1 if you have the `lstat' function. */ +/* Define to 1 if you have the 'lstat' function. */ #cmakedefine HAVE_LSTAT 1 -/* Define to 1 if `lstat' has the bug that it succeeds when given the +/* Define to 1 if 'lstat' has the bug that it succeeds when given the zero-length file name argument. */ #cmakedefine HAVE_LSTAT_EMPTY_STRING_BUG 1 -/* Define to 1 if you have the `lutimes' function. */ +/* Define to 1 if you have the 'lutimes' function. */ #cmakedefine HAVE_LUTIMES 1 /* Define to 1 if you have the header file. */ @@ -871,7 +892,7 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LZMA_H 1 -/* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */ +/* Define to 1 if you have a working 'lzma_stream_encoder_mt' function. */ #cmakedefine HAVE_LZMA_STREAM_ENCODER_MT 1 /* Define to 1 if you have the header file. */ @@ -892,31 +913,34 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MBEDTLS_VERSION_H 1 -/* Define to 1 if you have the `mbrtowc' function. */ +/* Define to 1 if you have the 'mbrtowc' function. */ #cmakedefine HAVE_MBRTOWC 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMBERSHIP_H 1 -/* Define to 1 if you have the `memmove' function. */ +/* Define to 1 if you have the 'memmove' function. */ #cmakedefine HAVE_MEMMOVE 1 +/* Define to 1 if you have the 'memset_explicit' function. */ +#cmakedefine HAVE_MEMSET_EXPLICIT 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H 1 -/* Define to 1 if you have the `mkdir' function. */ +/* Define to 1 if you have the 'mkdir' function. */ #cmakedefine HAVE_MKDIR 1 -/* Define to 1 if you have the `mkfifo' function. */ +/* Define to 1 if you have the 'mkfifo' function. */ #cmakedefine HAVE_MKFIFO 1 -/* Define to 1 if you have the `mknod' function. */ +/* Define to 1 if you have the 'mknod' function. */ #cmakedefine HAVE_MKNOD 1 -/* Define to 1 if you have the `mkstemp' function. */ +/* Define to 1 if you have the 'mkstemp' function. */ #cmakedefine HAVE_MKSTEMP 1 -/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #cmakedefine HAVE_NDIR_H 1 /* Define to 1 if you have the header file. */ @@ -937,13 +961,19 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_SHA_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETTLE_SHA1_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETTLE_SHA2_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETTLE_VERSION_H 1 -/* Define to 1 if you have the `nl_langinfo' function. */ +/* Define to 1 if you have the 'nl_langinfo' function. */ #cmakedefine HAVE_NL_LANGINFO 1 -/* Define to 1 if you have the `openat' function. */ +/* Define to 1 if you have the 'openat' function. */ #cmakedefine HAVE_OPENAT 1 /* Define to 1 if you have the header file. */ @@ -961,19 +991,19 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PCRE2POSIX_H 1 -/* Define to 1 if you have the `pipe' function. */ +/* Define to 1 if you have the 'pipe' function. */ #cmakedefine HAVE_PIPE 1 -/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */ +/* Define to 1 if you have the 'PKCS5_PBKDF2_HMAC_SHA1' function. */ #cmakedefine HAVE_PKCS5_PBKDF2_HMAC_SHA1 1 -/* Define to 1 if you have the `poll' function. */ +/* Define to 1 if you have the 'poll' function. */ #cmakedefine HAVE_POLL 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_POLL_H 1 -/* Define to 1 if you have the `posix_spawnp' function. */ +/* Define to 1 if you have the 'posix_spawnp' function. */ #cmakedefine HAVE_POSIX_SPAWNP 1 /* Define to 1 if you have the header file. */ @@ -985,13 +1015,13 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H 1 -/* Define to 1 if you have the `readlink' function. */ +/* Define to 1 if you have the 'readlink' function. */ #cmakedefine HAVE_READLINK 1 -/* Define to 1 if you have the `readlinkat' function. */ +/* Define to 1 if you have the 'readlinkat' function. */ #cmakedefine HAVE_READLINKAT 1 -/* Define to 1 if you have the `readpassphrase' function. */ +/* Define to 1 if you have the 'readpassphrase' function. */ #cmakedefine HAVE_READPASSPHRASE 1 /* Define to 1 if you have the header file. */ @@ -1000,16 +1030,16 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_REGEX_H 1 -/* Define to 1 if you have the `select' function. */ +/* Define to 1 if you have the 'select' function. */ #cmakedefine HAVE_SELECT 1 -/* Define to 1 if you have the `setenv' function. */ +/* Define to 1 if you have the 'setenv' function. */ #cmakedefine HAVE_SETENV 1 -/* Define to 1 if you have the `setlocale' function. */ +/* Define to 1 if you have the 'setlocale' function. */ #cmakedefine HAVE_SETLOCALE 1 -/* Define to 1 if you have the `sigaction' function. */ +/* Define to 1 if you have the 'sigaction' function. */ #cmakedefine HAVE_SIGACTION 1 /* Define to 1 if you have the header file. */ @@ -1018,13 +1048,13 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SPAWN_H 1 -/* Define to 1 if you have the `statfs' function. */ +/* Define to 1 if you have the 'statfs' function. */ #cmakedefine HAVE_STATFS 1 -/* Define to 1 if you have the `statvfs' function. */ +/* Define to 1 if you have the 'statvfs' function. */ #cmakedefine HAVE_STATVFS 1 -/* Define to 1 if `stat' has the bug that it succeeds when given the +/* Define to 1 if 'stat' has the bug that it succeeds when given the zero-length file name argument. */ #cmakedefine HAVE_STAT_EMPTY_STRING_BUG 1 @@ -1043,22 +1073,22 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 -/* Define to 1 if you have the `strchr' function. */ +/* Define to 1 if you have the 'strchr' function. */ #cmakedefine HAVE_STRCHR 1 -/* Define to 1 if you have the `strnlen' function. */ +/* Define to 1 if you have the 'strnlen' function. */ #cmakedefine HAVE_STRNLEN 1 -/* Define to 1 if you have the `strdup' function. */ +/* Define to 1 if you have the 'strdup' function. */ #cmakedefine HAVE_STRDUP 1 -/* Define to 1 if you have the `strerror' function. */ +/* Define to 1 if you have the 'strerror' function. */ #cmakedefine HAVE_STRERROR 1 -/* Define to 1 if you have the `strerror_r' function. */ +/* Define to 1 if you have the 'strerror_r' function. */ #cmakedefine HAVE_STRERROR_R 1 -/* Define to 1 if you have the `strftime' function. */ +/* Define to 1 if you have the 'strftime' function. */ #cmakedefine HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ @@ -1067,64 +1097,64 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H 1 -/* Define to 1 if you have the `strrchr' function. */ +/* Define to 1 if you have the 'strrchr' function. */ #cmakedefine HAVE_STRRCHR 1 -/* Define to 1 if the system has the type `struct statfs'. */ +/* Define to 1 if the system has the type 'struct statfs'. */ #cmakedefine HAVE_STRUCT_STATFS 1 -/* Define to 1 if `f_iosize' is a member of `struct statfs'. */ +/* Define to 1 if 'f_iosize' is a member of 'struct statfs'. */ #cmakedefine HAVE_STRUCT_STATFS_F_IOSIZE 1 -/* Define to 1 if `f_namemax' is a member of `struct statfs'. */ +/* Define to 1 if 'f_namemax' is a member of 'struct statfs'. */ #cmakedefine HAVE_STRUCT_STATFS_F_NAMEMAX 1 -/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */ +/* Define to 1 if 'f_iosize' is a member of 'struct statvfs'. */ #cmakedefine HAVE_STRUCT_STATVFS_F_IOSIZE 1 -/* Define to 1 if `st_birthtime' is a member of `struct stat'. */ +/* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIME 1 -/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */ +/* Define to 1 if 'st_birthtimespec.tv_nsec' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1 -/* Define to 1 if `st_blksize' is a member of `struct stat'. */ +/* Define to 1 if 'st_blksize' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_BLKSIZE 1 -/* Define to 1 if `st_flags' is a member of `struct stat'. */ +/* Define to 1 if 'st_flags' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_FLAGS 1 -/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */ +/* Define to 1 if 'st_mtimespec.tv_nsec' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 -/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */ +/* Define to 1 if 'st_mtime_n' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIME_N 1 -/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */ +/* Define to 1 if 'st_mtime_usec' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIME_USEC 1 -/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ +/* Define to 1 if 'st_mtim.tv_nsec' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 -/* Define to 1 if `st_umtime' is a member of `struct stat'. */ +/* Define to 1 if 'st_umtime' is a member of 'struct stat'. */ #cmakedefine HAVE_STRUCT_STAT_ST_UMTIME 1 -/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */ +/* Define to 1 if 'tm_gmtoff' is a member of 'struct tm'. */ #cmakedefine HAVE_STRUCT_TM_TM_GMTOFF 1 -/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */ +/* Define to 1 if '__tm_gmtoff' is a member of 'struct tm'. */ #cmakedefine HAVE_STRUCT_TM___TM_GMTOFF 1 -/* Define to 1 if you have `struct vfsconf'. */ +/* Define to 1 if you have 'struct vfsconf'. */ #cmakedefine HAVE_STRUCT_VFSCONF 1 -/* Define to 1 if you have `struct xvfsconf'. */ +/* Define to 1 if you have 'struct xvfsconf'. */ #cmakedefine HAVE_STRUCT_XVFSCONF 1 -/* Define to 1 if you have the `symlink' function. */ +/* Define to 1 if you have the 'symlink' function. */ #cmakedefine HAVE_SYMLINK 1 -/* Define to 1 if you have the `sysconf' function. */ +/* Define to 1 if you have the 'sysconf' function. */ #cmakedefine HAVE_SYSCONF 1 /* Define to 1 if you have the header file. */ @@ -1133,7 +1163,7 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_CDEFS_H 1 -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #cmakedefine HAVE_SYS_DIR_H 1 @@ -1152,7 +1182,7 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_MOUNT_H 1 -/* Define to 1 if you have the header file, and it defines `DIR'. +/* Define to 1 if you have the header file, and it defines 'DIR'. */ #cmakedefine HAVE_SYS_NDIR_H 1 @@ -1168,6 +1198,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SELECT_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SINGLE_THREADED_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STATFS_H 1 @@ -1201,73 +1234,76 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_XATTR_H 1 -/* Define to 1 if you have the `tcgetattr' function. */ +/* Define to 1 if you have the 'tcgetattr' function. */ #cmakedefine HAVE_TCGETATTR 1 -/* Define to 1 if you have the `tcsetattr' function. */ +/* Define to 1 if you have the 'tcsetattr' function. */ #cmakedefine HAVE_TCSETATTR 1 -/* Define to 1 if you have the `timegm' function. */ +/* Define to 1 if you have the 'timegm' function. */ #cmakedefine HAVE_TIMEGM 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TIME_H 1 -/* Define to 1 if you have the `tzset' function. */ +/* Define to 1 if you have the 'tzset' function. */ #cmakedefine HAVE_TZSET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 -/* Define to 1 if you have the `unlinkat' function. */ +/* Define to 1 if you have the 'unlinkat' function. */ #cmakedefine HAVE_UNLINKAT 1 -/* Define to 1 if you have the `unsetenv' function. */ +/* Define to 1 if you have the 'unsetenv' function. */ #cmakedefine HAVE_UNSETENV 1 -/* Define to 1 if the system has the type `unsigned long long'. */ +/* Define to 1 if the system has the type 'unsigned long long'. */ #cmakedefine HAVE_UNSIGNED_LONG_LONG 1 -/* Define to 1 if the system has the type `unsigned long long int'. */ +/* Define to 1 if the system has the type 'unsigned long long int'. */ #cmakedefine HAVE_UNSIGNED_LONG_LONG_INT 1 -/* Define to 1 if you have the `utime' function. */ +/* Define to 1 if you have the 'utime' function. */ #cmakedefine HAVE_UTIME 1 -/* Define to 1 if you have the `utimensat' function. */ +/* Define to 1 if you have the 'utimensat' function. */ #cmakedefine HAVE_UTIMENSAT 1 -/* Define to 1 if you have the `utimes' function. */ +/* Define to 1 if you have the 'utimes' function. */ #cmakedefine HAVE_UTIMES 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UTIME_H 1 -/* Define to 1 if you have the `vfork' function. */ +/* Define to 1 if you have the 'vfork' function. */ #cmakedefine HAVE_VFORK 1 -/* Define to 1 if you have the `vprintf' function. */ +/* Define to 1 if you have the 'vprintf' function. */ #cmakedefine HAVE_VPRINTF 1 +/* Define to 1 if you have the 'waitpid' function. */ +#cmakedefine HAVE_WAITPID 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCHAR_H 1 -/* Define to 1 if the system has the type `wchar_t'. */ +/* Define to 1 if the system has the type 'wchar_t'. */ #cmakedefine HAVE_WCHAR_T 1 -/* Define to 1 if you have the `wcrtomb' function. */ +/* Define to 1 if you have the 'wcrtomb' function. */ #cmakedefine HAVE_WCRTOMB 1 -/* Define to 1 if you have the `wcscmp' function. */ +/* Define to 1 if you have the 'wcscmp' function. */ #cmakedefine HAVE_WCSCMP 1 -/* Define to 1 if you have the `wcscpy' function. */ +/* Define to 1 if you have the 'wcscpy' function. */ #cmakedefine HAVE_WCSCPY 1 -/* Define to 1 if you have the `wcslen' function. */ +/* Define to 1 if you have the 'wcslen' function. */ #cmakedefine HAVE_WCSLEN 1 -/* Define to 1 if you have the `wctomb' function. */ +/* Define to 1 if you have the 'wctomb' function. */ #cmakedefine HAVE_WCTOMB 1 /* Define to 1 if you have the header file. */ @@ -1285,13 +1321,13 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have _CrtSetReportMode in */ #cmakedefine HAVE__CrtSetReportMode 1 -/* Define to 1 if you have the `wmemcmp' function. */ +/* Define to 1 if you have the 'wmemcmp' function. */ #cmakedefine HAVE_WMEMCMP 1 -/* Define to 1 if you have the `wmemcpy' function. */ +/* Define to 1 if you have the 'wmemcpy' function. */ #cmakedefine HAVE_WMEMCPY 1 -/* Define to 1 if you have the `wmemmove' function. */ +/* Define to 1 if you have the 'wmemmove' function. */ #cmakedefine HAVE_WMEMMOVE 1 /* Define to 1 if you have a working EXT2_IOC_GETFLAGS */ @@ -1300,7 +1336,7 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have a working FS_IOC_GETFLAGS */ #cmakedefine HAVE_WORKING_FS_IOC_GETFLAGS 1 -/* Define to 1 if you have the Windows `xmllite' library (-lxmllite). */ +/* Define to 1 if you have the Windows 'xmllite' library (-lxmllite). */ #cmakedefine HAVE_XMLLITE_H 1 /* Define to 1 if you have the header file. */ @@ -1309,22 +1345,22 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ZSTD_H 1 -/* Define to 1 if you have the `ctime_s' function. */ +/* Define to 1 if you have the 'ctime_s' function. */ #cmakedefine HAVE_CTIME_S 1 -/* Define to 1 if you have the `_fseeki64' function. */ +/* Define to 1 if you have the '_fseeki64' function. */ #cmakedefine HAVE__FSEEKI64 1 -/* Define to 1 if you have the `_get_timezone' function. */ +/* Define to 1 if you have the '_get_timezone' function. */ #cmakedefine HAVE__GET_TIMEZONE 1 -/* Define to 1 if you have the `gmtime_s' function. */ +/* Define to 1 if you have the 'gmtime_s' function. */ #cmakedefine HAVE_GMTIME_S 1 -/* Define to 1 if you have the `localtime_s' function. */ +/* Define to 1 if you have the 'localtime_s' function. */ #cmakedefine HAVE_LOCALTIME_S 1 -/* Define to 1 if you have the `_mkgmtime' function. */ +/* Define to 1 if you have the '_mkgmtime' function. */ #cmakedefine HAVE__MKGMTIME 1 /* Define as const if the declaration of iconv() needs const. */ @@ -1336,22 +1372,22 @@ typedef uint64_t uintmax_t; /* Version number of libarchive */ #cmakedefine LIBARCHIVE_VERSION_STRING "@LIBARCHIVE_VERSION_STRING@" -/* Define to 1 if `lstat' dereferences a symlink specified with a trailing +/* Define to 1 if 'lstat' dereferences a symlink specified with a trailing slash. */ #cmakedefine LSTAT_FOLLOWS_SLASHED_SYMLINK 1 -/* Define to 1 if `major', `minor', and `makedev' are declared in . +/* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #cmakedefine MAJOR_IN_MKDEV 1 -/* Define to 1 if `major', `minor', and `makedev' are declared in +/* Define to 1 if 'major', 'minor', and 'makedev' are declared in . */ #cmakedefine MAJOR_IN_SYSMACROS 1 /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #cmakedefine NO_MINUS_C_MINUS_O 1 -/* The size of `wchar_t', as computed by sizeof. */ +/* The size of 'wchar_t', as computed by sizeof. */ #cmakedefine SIZEOF_WCHAR_T @SIZEOF_WCHAR_T@ /* Define to 1 if strerror_r returns char *. */ @@ -1421,35 +1457,35 @@ typedef uint64_t uintmax_t; #cmakedefine WINVER @WINVER@ #endif // WINVER -/* Define to empty if `const' does not conform to ANSI C. */ +/* Define to empty if 'const' does not conform to ANSI C. */ #cmakedefine const @const@ -/* Define to `int' if doesn't define. */ +/* Define to 'int' if doesn't define. */ #cmakedefine gid_t @gid_t@ -/* Define to `unsigned long' if does not define. */ +/* Define to 'unsigned long' if does not define. */ #cmakedefine id_t @id_t@ -/* Define to `int' if does not define. */ +/* Define to 'int' if does not define. */ #cmakedefine mode_t @mode_t@ -/* Define to `long long' if does not define. */ +/* Define to 'long long' if does not define. */ #cmakedefine off_t @off_t@ -/* Define to `int' if doesn't define. */ +/* Define to 'int' if doesn't define. */ #cmakedefine pid_t @pid_t@ -/* Define to `unsigned int' if does not define. */ +/* Define to 'unsigned int' if does not define. */ #cmakedefine size_t @size_t@ -/* Define to `int' if does not define. */ +/* Define to 'int' if does not define. */ #cmakedefine ssize_t @ssize_t@ -/* Define to `int' if doesn't define. */ +/* Define to 'int' if doesn't define. */ #cmakedefine uid_t @uid_t@ -/* Define to `int' if does not define. */ +/* Define to 'int' if does not define. */ #cmakedefine intptr_t @intptr_t@ -/* Define to `unsigned int' if does not define. */ +/* Define to 'unsigned int' if does not define. */ #cmakedefine uintptr_t @uintptr_t@ diff --git a/build/version b/build/version index c9c7ecb809f5..87f0e2bca59e 100644 --- a/build/version +++ b/build/version @@ -1 +1 @@ -3008009 +3008010 diff --git a/configure.ac b/configure.ac index a2a6d7c213ae..b964e36191ba 100644 --- a/configure.ac +++ b/configure.ac @@ -4,8 +4,8 @@ dnl First, define all of the version numbers up front. dnl In particular, this allows the version macro to be used in AC_INIT dnl These first two version numbers are updated automatically on each release. -m4_define([LIBARCHIVE_VERSION_S],[3.8.9]) -m4_define([LIBARCHIVE_VERSION_N],[3008009]) +m4_define([LIBARCHIVE_VERSION_S],[3.8.10]) +m4_define([LIBARCHIVE_VERSION_N],[3008010]) dnl bsdtar and bsdcpio versioning tracks libarchive m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S()) @@ -15,39 +15,39 @@ m4_define([BSDUNZIP_VERSION_S],LIBARCHIVE_VERSION_S()) AC_PREREQ([2.71]) -# -# Now starts the "real" configure script. -# +dnl +dnl Now starts the "real" configure script. +dnl AC_INIT([libarchive],[LIBARCHIVE_VERSION_S()],[libarchive-discuss@googlegroups.com]) -# Make sure the srcdir contains "libarchive" directory +dnl Make sure the srcdir contains "libarchive" directory AC_CONFIG_SRCDIR([libarchive]) -# Use auxiliary subscripts from this subdirectory (cleans up root) +dnl Use auxiliary subscripts from this subdirectory (cleans up root) AC_CONFIG_AUX_DIR([build/autoconf]) -# M4 scripts +dnl M4 scripts AC_CONFIG_MACRO_DIR([build/autoconf]) -# Must follow AC_CONFIG macros above... +dnl Must follow AC_CONFIG macros above... AM_INIT_AUTOMAKE([1.11 dist-xz dist-zip]) AM_MAINTAINER_MODE([enable]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -# Libtool's "interface version" can be computed from the libarchive version. +dnl Libtool's "interface version" can be computed from the libarchive version. -# Libtool interface version bumps on any API change, so increments -# whenever libarchive minor version does. +dnl Libtool interface version bumps on any API change, so increments +dnl whenever libarchive minor version does. ARCHIVE_MINOR=$(( (LIBARCHIVE_VERSION_N() / 1000) % 1000 )) -# Libarchive 2.7 == libtool interface 9 = 2 + 7 -# Libarchive 2.8 == libtool interface 10 = 2 + 8 -# Libarchive 2.9 == libtool interface 11 = 2 + 9 -# Libarchive 3.0 == libtool interface 12 -# Libarchive 3.1 == libtool interface 13 +dnl Libarchive 2.7 == libtool interface 9 = 2 + 7 +dnl Libarchive 2.8 == libtool interface 10 = 2 + 8 +dnl Libarchive 2.9 == libtool interface 11 = 2 + 9 +dnl Libarchive 3.0 == libtool interface 12 +dnl Libarchive 3.1 == libtool interface 13 ARCHIVE_INTERFACE=`echo $((13 + ${ARCHIVE_MINOR}))` -# Libarchive revision is bumped on any source change === libtool revision +dnl Libarchive revision is bumped on any source change === libtool revision ARCHIVE_REVISION=$(( LIBARCHIVE_VERSION_N() % 1000 )) -# Libarchive minor is bumped on any interface addition === libtool age +dnl Libarchive minor is bumped on any interface addition === libtool age ARCHIVE_LIBTOOL_VERSION=$ARCHIVE_INTERFACE:$ARCHIVE_REVISION:$ARCHIVE_MINOR -# Stick the version numbers into config.h +dnl Stick the version numbers into config.h AC_DEFINE([__LIBARCHIVE_CONFIG_H_INCLUDED], [1], [Internal macro for sanity checks]) AC_DEFINE([LIBARCHIVE_VERSION_STRING],"LIBARCHIVE_VERSION_S()", @@ -63,9 +63,9 @@ AC_DEFINE([BSDCAT_VERSION_STRING],"BSDTAR_VERSION_S()", AC_DEFINE([BSDUNZIP_VERSION_STRING],"BSDUNZIP_VERSION_S()", [Version number of bsdunzip]) -# The shell variables here must be the same as the AC_SUBST() variables -# below, but the shell variable names apparently cannot be the same as -# the m4 macro names above. Why? Ask autoconf. +dnl The shell variables here must be the same as the AC_SUBST() variables +dnl below, but the shell variable names apparently cannot be the same as +dnl the m4 macro names above. Why? Ask autoconf. BSDCPIO_VERSION_STRING=BSDCPIO_VERSION_S() BSDTAR_VERSION_STRING=BSDTAR_VERSION_S() BSDCAT_VERSION_STRING=BSDCAT_VERSION_S() @@ -73,9 +73,9 @@ BSDUNZIP_VERSION_STRING=BSDUNZIP_VERSION_S() LIBARCHIVE_VERSION_STRING=LIBARCHIVE_VERSION_S() LIBARCHIVE_VERSION_NUMBER=LIBARCHIVE_VERSION_N() -# Substitute the above version numbers into the various files below. -# Yes, I believe this is the fourth time we define what are essentially -# the same symbols. Why? Ask autoconf. +dnl Substitute the above version numbers into the various files below. +dnl Yes, I believe this is the fourth time we define what are essentially +dnl the same symbols. Why? Ask autoconf. AC_SUBST(ARCHIVE_LIBTOOL_VERSION) AC_SUBST(BSDCPIO_VERSION_STRING) AC_SUBST(BSDTAR_VERSION_STRING) @@ -88,7 +88,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/pkgconfig/libarchive.pc]) -# Check for host type +dnl Check for host type AC_CANONICAL_HOST dnl Compilation on mingw and Cygwin needs special Makefile rules @@ -107,7 +107,7 @@ AS_IF([test $inc_windows_files = yes], [AC_CHECK_TOOL([WINDRES], [windres])]) dnl Defines that are required for specific platforms (e.g. -D_POSIX_SOURCE, etc) PLATFORMCPPFLAGS= case "$host_os" in - *mingw* ) PLATFORMCPPFLAGS=-D__USE_MINGW_ANSI_STDIO -D__MINGW_USE_VC2005_COMPAT ;; + *mingw* ) PLATFORMCPPFLAGS="-D__USE_MINGW_ANSI_STDIO=1 -D__MINGW_USE_VC2005_COMPAT=1" ;; esac AC_SUBST(PLATFORMCPPFLAGS) @@ -116,7 +116,7 @@ case "$host_os" in haiku*) LIBS="-lbsd $LIBS" ;; esac -# Checks for programs. +dnl Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CPP @@ -126,11 +126,11 @@ LT_INIT([win32-dll]) AC_CHECK_TOOL([STRIP],[strip]) AC_PROG_MKDIR_P -# -# Options for building bsdtar. -# -# Default is to build bsdtar, but allow people to override that. -# +dnl +dnl Options for building bsdtar. +dnl +dnl Default is to build bsdtar, but allow people to override that. +dnl AC_ARG_ENABLE([bsdtar], [AS_HELP_STRING([--enable-bsdtar], [enable build of bsdtar (default)]) AS_HELP_STRING([--enable-bsdtar=static], [force static build of bsdtar]) @@ -170,11 +170,11 @@ esac AM_CONDITIONAL([BUILD_BSDTAR], [ test "$build_bsdtar" = yes ]) AM_CONDITIONAL([STATIC_BSDTAR], [ test "$static_bsdtar" = yes ]) -# -# Options for building bsdcat. -# -# Default is to build bsdcat, but allow people to override that. -# +dnl +dnl Options for building bsdcat. +dnl +dnl Default is to build bsdcat, but allow people to override that. +dnl AC_ARG_ENABLE([bsdcat], [AS_HELP_STRING([--enable-bsdcat], [enable build of bsdcat (default)]) AS_HELP_STRING([--enable-bsdcat=static], [force static build of bsdcat]) @@ -214,11 +214,11 @@ esac AM_CONDITIONAL([BUILD_BSDCAT], [ test "$build_bsdcat" = yes ]) AM_CONDITIONAL([STATIC_BSDCAT], [ test "$static_bsdcat" = yes ]) -# -# Options for building bsdcpio. -# -# Default is not to build bsdcpio, but that can be overridden. -# +dnl +dnl Options for building bsdcpio. +dnl +dnl Default is to build bsdcpio, but allow people to override that. +dnl AC_ARG_ENABLE([bsdcpio], [AS_HELP_STRING([--enable-bsdcpio], [enable build of bsdcpio (default)]) AS_HELP_STRING([--enable-bsdcpio=static], [static build of bsdcpio]) @@ -257,50 +257,17 @@ esac AM_CONDITIONAL([BUILD_BSDCPIO], [ test "$build_bsdcpio" = yes ]) AM_CONDITIONAL([STATIC_BSDCPIO], [ test "$static_bsdcpio" = yes ]) -# Set up defines needed before including any headers -case $host in - *mingw* | *cygwin* | *msys* ) - AC_PREPROC_IFELSE([AC_LANG_PROGRAM( - [[#include - #ifdef _WIN32_WINNT - # error _WIN32_WINNT already defined - #endif - ]],[[;]]) - ],[ - AC_DEFINE([_WIN32_WINNT], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) - AC_DEFINE([NTDDI_VERSION], 0x05020000, [Define to '0x05020000' for Windows Server 2003 APIs.]) - ]) - AC_PREPROC_IFELSE([AC_LANG_PROGRAM( - [[#include - #ifdef WINVER - # error WINVER already defined - #endif - ]],[[;]]) - ],[ - AC_DEFINE([WINVER], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) - ]) - ;; -esac - -# -# Options for building bsdunzip. -# -# Default is to build bsdunzip, but allow people to override that. -# Bsdunzip has not yet been ported for Windows -# -case "$host_os" in - *mingw* | *msys*) - enable_bsdunzip=no - ;; - *) - AC_ARG_ENABLE([bsdunzip], - [AS_HELP_STRING([--enable-bsdunzip], [enable build of bsdunzip (default)]) - AS_HELP_STRING([--enable-bsdunzip=static], [force static build of bsdunzip]) - AS_HELP_STRING([--enable-bsdunzip=shared], [force dynamic build of bsdunzip]) - AS_HELP_STRING([--disable-bsdunzip], [disable build of bsdunzip])], - [], [enable_bsdunzip=yes]) - ;; -esac +dnl +dnl Options for building bsdunzip. +dnl +dnl Default is to build bsdunzip, but allow people to override that. +dnl +AC_ARG_ENABLE([bsdunzip], + [AS_HELP_STRING([--enable-bsdunzip], [enable build of bsdunzip (default)]) + AS_HELP_STRING([--enable-bsdunzip=static], [force static build of bsdunzip]) + AS_HELP_STRING([--enable-bsdunzip=shared], [force dynamic build of bsdunzip]) + AS_HELP_STRING([--disable-bsdunzip], [disable build of bsdunzip])], + [], [enable_bsdunzip=yes]) case "$enable_bsdunzip" in yes) @@ -334,12 +301,53 @@ esac AM_CONDITIONAL([BUILD_BSDUNZIP], [ test "$build_bsdunzip" = yes ]) AM_CONDITIONAL([STATIC_BSDUNZIP], [ test "$static_bsdunzip" = yes ]) -# Checks for header files. +dnl Set up libtool bindir for dll generation on Windows +AC_MSG_CHECKING([if Windows libtool -bindir is needed]) +case "$host_os" in + *cygwin* | *mingw* | *msys*) + AC_MSG_RESULT([yes]) + DLL_BINDIR="-bindir \$(bindir)" + ;; + *) + AC_MSG_RESULT([no]) + DLL_BINDIR="" + ;; +esac +AC_SUBST([DLL_BINDIR]) + +dnl Set up defines needed before including any headers +case $host in + *mingw* | *cygwin* | *msys* ) + AC_PREPROC_IFELSE([AC_LANG_PROGRAM( + [[#include + #ifdef _WIN32_WINNT + # error _WIN32_WINNT already defined + #endif + ]],[[;]]) + ],[ + AC_DEFINE([_WIN32_WINNT], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) + AC_DEFINE([NTDDI_VERSION], 0x05020000, [Define to '0x05020000' for Windows Server 2003 APIs.]) + ]) + AC_PREPROC_IFELSE([AC_LANG_PROGRAM( + [[#include + #ifdef WINVER + # error WINVER already defined + #endif + ]],[[;]]) + ],[ + AC_DEFINE([WINVER], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.]) + ]) + ;; +esac + +dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h]) -AC_CHECK_HEADERS([copyfile.h ctype.h]) -AC_CHECK_HEADERS([errno.h ext2fs/ext2_fs.h fcntl.h fnmatch.h grp.h]) +AC_CHECK_HEADERS(m4_flatten([ + acl/libacl.h attr/xattr.h + copyfile.h ctype.h direct.h + errno.h ext2fs/ext2_fs.h fcntl.h fnmatch.h grp.h +])) AC_CACHE_CHECK([whether EXT2_IOC_GETFLAGS is usable], [ac_cv_have_decl_EXT2_IOC_GETFLAGS], @@ -353,8 +361,10 @@ AS_VAR_IF([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes], [AC_DEFINE_UNQUOTED([HAVE_WORKING_EXT2_IOC_GETFLAGS], [1], [Define to 1 if you have a working EXT2_IOC_GETFLAGS])]) -AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h]) -AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h]) +AC_CHECK_HEADERS(m4_flatten([ + inttypes.h io.h langinfo.h limits.h + linux/fiemap.h linux/fs.h linux/magic.h linux/types.h +])) AC_CACHE_CHECK([whether FS_IOC_GETFLAGS is usable], [ac_cv_have_decl_FS_IOC_GETFLAGS], @@ -368,25 +378,29 @@ AS_VAR_IF([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes], [AC_DEFINE_UNQUOTED([HAVE_WORKING_FS_IOC_GETFLAGS], [1], [Define to 1 if you have a working FS_IOC_GETFLAGS])]) -AC_CHECK_HEADERS([intsafe.h locale.h membership.h paths.h poll.h pthread.h]) -AC_CHECK_HEADERS([pwd.h readpassphrase.h signal.h spawn.h]) -AC_CHECK_HEADERS([stdarg.h stdckdint.h stdint.h stdlib.h string.h]) -AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/ea.h sys/extattr.h]) -AC_CHECK_HEADERS([sys/ioctl.h sys/mkdev.h sys/mount.h]) -AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/richacl.h]) -AC_CHECK_HEADERS([sys/select.h sys/statfs.h sys/statvfs.h sys/sysmacros.h]) -AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h sys/xattr.h]) -AC_CHECK_HEADERS([time.h unistd.h utime.h wchar.h wctype.h]) +AC_CHECK_HEADERS(m4_flatten([ + intsafe.h locale.h membership.h paths.h poll.h pthread.h + pwd.h readpassphrase.h signal.h spawn.h + stdarg.h stdckdint.h stdint.h stdlib.h string.h + sys/acl.h sys/cdefs.h sys/ea.h sys/extattr.h + sys/ioctl.h sys/mkdev.h sys/mount.h + sys/param.h sys/poll.h sys/richacl.h + sys/select.h sys/single_threaded.h + sys/statfs.h sys/statvfs.h sys/sysctl.h + sys/sysmacros.h sys/time.h sys/utime.h sys/utsname.h + sys/vfs.h sys/xattr.h time.h unistd.h utime.h wchar.h + wctype.h +])) AC_CHECK_TYPE([suseconds_t]) AC_CHECK_HEADERS([windows.h]) -# check windows.h first; the other headers require it. +dnl check windows.h first; the other headers require it. AC_CHECK_HEADERS([winioctl.h],[],[], [[#ifdef HAVE_WINDOWS_H # include #endif ]]) -# Checks for libraries. +dnl Checks for libraries. AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib], [Don't build support for gzip through zlib])) @@ -431,7 +445,7 @@ if test "x$with_bz2lib" != "xno"; then LIBS="$old_LIBS" AC_MSG_RESULT($ac_cv_lib_bz2_BZ2_bzDecompressInit) if test "x$ac_cv_lib_bz2_BZ2_bzDecompressInit" = xyes; then - AC_DEFINE([HAVE_LIBBZ2], [1], [Define to 1 if you have the `bz2' library (-lbz2).]) + AC_DEFINE([HAVE_LIBBZ2], [1], [Define to 1 if you have the 'bz2' library (-lbz2).]) LIBS="-lbz2 $LIBS" fi ;; @@ -467,17 +481,15 @@ if test "x$with_iconv" != "xno"; then AC_CHECK_HEADERS([localcharset.h]) LIBS="${LIBS} ${LIBICONV}" if test -n "$LIBICONV"; then - AC_DEFINE([HAVE_LIBICONV], [1], [Define to 1 if you have the `iconv' library (-liconv).]) - - # Most platforms do not provide iconv.pc, but MSYS2 MinGW does. - # We add it to our Requires.private only if it exists. + dnl Most platforms do not provide iconv.pc, but MSYS2 MinGW does. + dnl We add it to our Requires.private only if it exists. PKG_CHECK_MODULES(ICONV_PC, [iconv], [ LIBSREQUIRED="$LIBSREQUIRED${LIBSREQUIRED:+ }iconv" ], [true]) fi AC_CHECK_FUNCS([locale_charset]) if test "x$ac_cv_func_locale_charset" != "xyes"; then - # If locale_charset() is not in libiconv, we have to find libcharset. + dnl If locale_charset() is not in libiconv, we have to find libcharset. AC_CHECK_LIB(charset,locale_charset) fi fi @@ -498,9 +510,9 @@ if test "x$with_zstd" != "xno"; then AC_CHECK_HEADERS([zstd.h]) AC_CHECK_LIB(zstd,ZSTD_decompressStream) AC_CHECK_LIB(zstd,ZSTD_compressStream, - AC_DEFINE([HAVE_ZSTD_compressStream], [1], [Define to 1 if you have the `zstd' library (-lzstd) with compression support.])) + AC_DEFINE([HAVE_ZSTD_compressStream], [1], [Define to 1 if you have the 'zstd' library (-lzstd) with compression support.])) AC_CHECK_LIB(zstd,ZSTD_minCLevel, - AC_DEFINE([HAVE_ZSTD_minCLevel], [1], [Define to 1 if you have a `zstd' library version with ZSTD_minCLevel().])) + AC_DEFINE([HAVE_ZSTD_minCLevel], [1], [Define to 1 if you have a 'zstd' library version with ZSTD_minCLevel().])) fi AC_ARG_WITH([lzma], @@ -511,16 +523,16 @@ if test "x$with_lzma" != "xno"; then PKG_CHECK_MODULES(LZMA_PC, [liblzma], [ CPPFLAGS="${CPPFLAGS} ${LZMA_PC_CFLAGS}" LIBS="${LIBS} ${LZMA_PC_LIBS}" - AC_DEFINE(HAVE_LIBLZMA, [1], [Define to 1 if you have the `lzma' library.]) + AC_DEFINE(HAVE_LIBLZMA, [1], [Define to 1 if you have the 'lzma' library.]) ], [ AC_CHECK_LIB(lzma,lzma_stream_decoder) ]) AC_CHECK_HEADERS([lzma.h]) - # Some pre-release (but widely distributed) versions of liblzma - # included a disabled version of lzma_stream_encoder_mt that - # fools a naive AC_CHECK_LIB or AC_CHECK_FUNC, so we need - # to do something more complex here: + dnl Some pre-release (but widely distributed) versions of liblzma + dnl included a disabled version of lzma_stream_encoder_mt that + dnl fools a naive AC_CHECK_LIB or AC_CHECK_FUNC, so we need + dnl to do something more complex here: AC_CACHE_CHECK( [whether we have multithread support in lzma], ac_cv_lzma_has_mt, @@ -532,7 +544,7 @@ if test "x$with_lzma" != "xno"; then [[int ignored __attribute__((unused)); ignored = lzma_stream_encoder_mt(0, 0);]])], [ac_cv_lzma_has_mt=yes], [ac_cv_lzma_has_mt=no])]) if test "x$ac_cv_lzma_has_mt" != xno; then - AC_DEFINE([HAVE_LZMA_STREAM_ENCODER_MT], [1], [Define to 1 if you have the `lzma_stream_encoder_mt' function.]) + AC_DEFINE([HAVE_LZMA_STREAM_ENCODER_MT], [1], [Define to 1 if you have the 'lzma_stream_encoder_mt' function.]) fi fi @@ -545,7 +557,7 @@ if test "x$with_lzo2" = "xyes"; then fi AC_ARG_WITH([cng], - AS_HELP_STRING([--without-cng], [Don't build support of CNG(Crypto Next Generation)])) + AS_HELP_STRING([--without-cng], [Don't build with crypto support from Windows CNG (Crypto Next Generation)])) AC_ARG_WITH([mbedtls], AS_HELP_STRING([--with-mbedtls], [Build with crypto support from mbed TLS])) @@ -553,18 +565,24 @@ AC_ARG_WITH([nettle], AS_HELP_STRING([--with-nettle], [Build with crypto support from Nettle])) AC_ARG_WITH([openssl], AS_HELP_STRING([--without-openssl], [Don't build support for mtree and xar hashes through openssl])) +AC_ARG_WITH([md], + AS_HELP_STRING([--without-md], [Don't build with crypto support from Message Digest])) case "$host_os" in *darwin* ) with_openssl=no ;; esac AC_ARG_WITH([xml2], AS_HELP_STRING([--without-xml2], [Don't build support for xar through libxml2])) +AC_ARG_WITH([xmllite], + AS_HELP_STRING([--without-xmllite], [Don't build support for xar through XmlLite])) +AC_ARG_WITH([bsdxml], + AS_HELP_STRING([--without-bsdxml], [Don't build support for xar through bsdxml])) AC_ARG_WITH([expat], AS_HELP_STRING([--without-expat], [Don't build support for xar through expat])) if test "x$with_xml2" != "xno"; then PKG_PROG_PKG_CONFIG - PKG_CHECK_MODULES(LIBXML2_PC, [libxml-2.0], [ + PKG_CHECK_MODULES(LIBXML2_PC, [libxml-2.0 >= 2.7.3], [ CPPFLAGS="${CPPFLAGS} ${LIBXML2_PC_CFLAGS}" LIBS="${LIBS} ${LIBXML2_PC_LIBS}" AC_CHECK_LIB(xml2,xmlInitParser,[true],AC_MSG_FAILURE(Missing xml2 library)) @@ -574,9 +592,49 @@ if test "x$with_xml2" != "xno"; then AC_CHECK_HEADERS([libxml/xmlreader.h libxml/xmlwriter.h libxml/xmlversion.h]) fi if test "x$ac_cv_header_libxml_xmlreader_h" != "xyes"; then - if test "x$with_expat" != "xno"; then - AC_CHECK_HEADERS([expat.h]) - AC_CHECK_LIB(expat,XML_ParserCreate) + if test "x$with_xmllite" != "xno"; then + AC_CHECK_HEADERS([xmllite.h]) + if test "x$ac_cv_header_xmllite_h" == "xyes"; then + AC_MSG_CHECKING([for xmllite]) + saved_LIBS=$LIBS + LIBS="$LIBS -lxmllite -luuid -lole32" + AC_LINK_IFELSE([AC_LANG_SOURCE([ + #include + #include + int main() { + return CreateXmlReader(&IID_IXmlReader, NULL, NULL); + } + ])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_LIBXMLLITE], [1], [Define to 1 if you have the 'xmllite' library.]) + found_xmllite=yes + ], + [ AC_MSG_RESULT([no]) + LIBS=$saved_LIBS + ]) + fi + fi + if test "x$found_xmllite" != "xyes"; then + if test "x$with_expat" != "xno"; then + PKG_CHECK_MODULES(EXPAT_PC, [expat], [ + CPPFLAGS="${CPPFLAGS} ${EXPAT_PC_CFLAGS}" + LIBS="${LIBS} ${EXPAT_PC_LIBS}" + AC_DEFINE(HAVE_LIBEXPAT, [1], [Define to 1 if you have the 'expat' library.]) + have_libexpat="yes" + ], [ + AC_CHECK_LIB(expat,XML_ParserCreate) + if test "x$ac_cv_lib_expat_XML_ParserCreate" != "xyes"; then + have_libexpat="yes" + fi + ]) + AC_CHECK_HEADERS([expat.h]) + fi + if test "x$ac_cv_header_expat_h" != "xyes" || test "x$have_libexpat" != "xyes"; then + if test "x$with_bsdxml" != "xno"; then + AC_CHECK_HEADERS([bsdxml.h]) + AC_CHECK_LIB(bsdxml,XML_ParserCreate) + fi + fi fi fi @@ -672,18 +730,18 @@ if test -z $posix_regex_lib_found && (test "$enable_posix_regex_lib" = "auto" || fi fi -# TODO: Give the user the option of using a pre-existing system -# libarchive. This will define HAVE_LIBARCHIVE which will cause -# bsdtar_platform.h to use #include <...> for the libarchive headers. -# Need to include Makefile.am magic to link against system -# -larchive in that case. -#AC_CHECK_LIB(archive,archive_version) +dnl TODO: Give the user the option of using a pre-existing system +dnl libarchive. This will define HAVE_LIBARCHIVE which will cause +dnl bsdtar_platform.h to use #include <...> for the libarchive headers. +dnl Need to include Makefile.am magic to link against system +dnl -larchive in that case. +dnl AC_CHECK_LIB(archive,archive_version) -# Checks for supported compiler flags +dnl Checks for supported compiler flags AX_APPEND_COMPILE_FLAGS([-Wall -Wformat -Wformat-security]) -# Place the functions and data into separate sections, allowing GNU style -# linkers to garbage collect the unused ones. +dnl Place the functions and data into separate sections, allowing GNU style +dnl linkers to garbage collect the unused ones. save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,--gc-sections" AC_MSG_CHECKING([whether ld supports --gc-sections]) @@ -697,7 +755,7 @@ AC_LINK_IFELSE( LDFLAGS=$save_LDFLAGS if test "$DEAD_CODE_REMOVAL" = ""; then - # Macos linkers have a -dead_strip flag, which is similar to --gc-sections. + dnl Macos linkers have a -dead_strip flag, which is similar to --gc-sections. save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-dead_strip" AC_MSG_CHECKING([whether ld supports -dead_strip]) @@ -712,67 +770,67 @@ fi AC_SUBST(DEAD_CODE_REMOVAL) -# Checks for typedefs, structures, and compiler characteristics. +dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST -# la_TYPE_UID_T defaults to "int", which is incorrect for MinGW -# and MSVC. Use a customized version. +dnl la_TYPE_UID_T defaults to "int", which is incorrect for MinGW +dnl and MSVC. Use a customized version. la_TYPE_UID_T AC_TYPE_MODE_T -# AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on -# most systems... default to "long long" instead. +dnl AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on +dnl most systems... default to "long long" instead. AC_CHECK_TYPE(off_t, [long long]) AC_TYPE_SIZE_T AC_CHECK_TYPE(id_t, [unsigned long]) AC_CHECK_TYPE(uintptr_t, [unsigned int]) -# Check for tm_gmtoff in struct tm +dnl Check for tm_gmtoff in struct tm AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,, [ #include ]) -# Check for f_namemax in struct statfs +dnl Check for f_namemax in struct statfs AC_CHECK_MEMBERS([struct statfs.f_namemax],,, [ #include #include ]) -# Check for f_iosize in struct statfs +dnl Check for f_iosize in struct statfs AC_CHECK_MEMBERS([struct statfs.f_iosize],,, [ #include #include ]) -# Check for f_iosize in struct statvfs +dnl Check for f_iosize in struct statvfs AC_CHECK_MEMBERS([struct statvfs.f_iosize],,, [ #include ]) -# Check for birthtime in struct stat +dnl Check for birthtime in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtime]) -# Check for high-resolution timestamps in struct stat +dnl Check for high-resolution timestamps in struct stat AC_CHECK_MEMBERS([struct stat.st_birthtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec]) AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec]) -AC_CHECK_MEMBERS([struct stat.st_mtime_n]) # AIX -AC_CHECK_MEMBERS([struct stat.st_umtime]) # Tru64 -AC_CHECK_MEMBERS([struct stat.st_mtime_usec]) # Hurd -# Check for block size support in struct stat +AC_CHECK_MEMBERS([struct stat.st_mtime_n]) dnl AIX +AC_CHECK_MEMBERS([struct stat.st_umtime]) dnl Tru64 +AC_CHECK_MEMBERS([struct stat.st_mtime_usec]) dnl Hurd +dnl Check for block size support in struct stat AC_CHECK_MEMBERS([struct stat.st_blksize]) -# Check for st_flags in struct stat (BSD fflags) +dnl Check for st_flags in struct stat (BSD fflags) AC_CHECK_MEMBERS([struct stat.st_flags]) -# If you have uintmax_t, we assume printf supports %ju -# If you have unsigned long long, we assume printf supports %llu -# TODO: Check for %ju and %llu support directly. +dnl If you have uintmax_t, we assume printf supports %ju +dnl If you have unsigned long long, we assume printf supports %llu +dnl TODO: Check for %ju and %llu support directly. AC_CHECK_TYPES([uintmax_t, unsigned long long]) -# We use C99-style integer types -# Declare them if the local platform doesn't already do so. +dnl We use C99-style integer types +dnl Declare them if the local platform doesn't already do so. AC_TYPE_INTMAX_T AC_TYPE_UINTMAX_T AC_TYPE_INT64_T @@ -788,7 +846,7 @@ AC_CHECK_DECLS([INT64_MAX, INT64_MIN, UINT64_MAX, UINT32_MAX]) AC_CHECK_DECLS([INTMAX_MAX, INTMAX_MIN, UINTMAX_MAX]) AC_CHECK_DECL([SSIZE_MAX], - [AC_DEFINE(HAVE_DECL_SSIZE_MAX, 1, [Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't.])], + [AC_DEFINE(HAVE_DECL_SSIZE_MAX, 1, [Define to 1 if you have the declaration of 'SSIZE_MAX', and to 0 if you don't.])], [], [#include ]) @@ -801,7 +859,7 @@ AC_CHECK_DECL([EILSEQ], [], [#include ]) AC_CHECK_TYPE([wchar_t], - [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]wchar_t), 1, [Define to 1 if the system has the type `wchar_t'.])dnl + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]wchar_t), 1, [Define to 1 if the system has the type 'wchar_t'.])dnl AC_CHECK_SIZEOF([wchar_t])], []) @@ -810,7 +868,7 @@ AX_COMPILE_CHECK_SIZEOF(long) AC_CHECK_HEADERS_ONCE([sys/time.h]) -# Checks for library functions. +dnl Checks for library functions. AC_HEADER_MAJOR AC_FUNC_FSEEKO AC_FUNC_MEMCMP @@ -819,35 +877,39 @@ AC_FUNC_STAT AC_FUNC_STRERROR_R AC_FUNC_STRFTIME AC_FUNC_VPRINTF -# check for: -# CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES) -# To avoid necessity for including windows.h or special forward declaration -# workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *' +dnl check for: +dnl CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES) +dnl To avoid necessity for including windows.h or special forward declaration +dnl workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *' AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *]) -AC_CHECK_FUNCS([arc4random_buf chflags chown chroot]) -AC_CHECK_FUNCS([closefrom close_range ctime_r]) -AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fnmatch fork]) -AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate]) -AC_CHECK_FUNCS([futimens futimes futimesat]) -AC_CHECK_FUNCS([getegid geteuid getline getpid getresgid getresuid]) -AC_CHECK_FUNCS([getgrgid_r getgrnam_r getpwnam_r getpwuid_r]) -AC_CHECK_FUNCS([gettimeofday getvfsbyname gmtime_r]) -AC_CHECK_FUNCS([issetugid]) -AC_CHECK_FUNCS([lchflags lchmod lchown link linkat localtime_r lstat lutimes]) -AC_CHECK_FUNCS([mbrtowc memmove memset]) -AC_CHECK_FUNCS([mkdir mkfifo mknod mkstemp]) -AC_CHECK_FUNCS([nl_langinfo openat pipe poll posix_spawn posix_spawnp]) -AC_CHECK_FUNCS([readlink readlinkat]) -AC_CHECK_FUNCS([readpassphrase]) -AC_CHECK_FUNCS([select setenv setlocale sigaction statfs statvfs]) -AC_CHECK_FUNCS([strchr strdup strerror strncpy_s strnlen strrchr symlink]) -AC_CHECK_FUNCS([sysconf]) -AC_CHECK_FUNCS([tcgetattr tcsetattr]) -AC_CHECK_FUNCS([timegm tzset unlinkat unsetenv utime utimensat utimes vfork]) -AC_CHECK_FUNCS([wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove]) -AC_CHECK_FUNCS([_fseeki64 _get_timezone]) -AC_CHECK_DECL([cmtime_s], - [AC_DEFINE(HAVE_CMTIME_S, 1, [cmtime_s function])], +AC_CHECK_FUNCS(m4_flatten([ + arc4random_buf + chflags chown chroot closefrom close_range ctime_r + explicit_bzero + fchdir fchflags fchmod fchown fcntl fdopendir fnmatch fork + fstat fstatat fstatfs fstatvfs ftruncate + futimens futimes futimesat + getegid geteuid getline getpid getresgid getresuid + getgrgid_r getgrnam_r getpwnam_r getpwuid_r + gettimeofday getvfsbyname gmtime_r + issetugid + lchflags lchmod lchown link linkat localtime_r lstat lutimes + mbrtowc memmove memset memset_explicit mkdir mkfifo mknod mkstemp + nl_langinfo + openat + pipe poll posix_spawn posix_spawnp + readlink readlinkat readpassphrase + select setenv setlocale sigaction statfs statvfs + strchr strdup strerror strncpy_s strnlen strrchr symlink + sysconf + tcgetattr tcsetattr timegm tzset + unlinkat unsetenv utime utimensat utimes + vfork + waitpid wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove + _fseeki64 _get_timezone +])) +AC_CHECK_DECL([ctime_s], + [AC_DEFINE(HAVE_CTIME_S, 1, [ctime_s function])], [], [#include ]) AC_CHECK_DECL([gmtime_s], @@ -863,10 +925,10 @@ AC_CHECK_DECL([_mkgmtime], [], [#include ]) -# detects cygwin-1.7, as opposed to older versions +dnl detects cygwin-1.7, as opposed to older versions AC_CHECK_FUNCS([cygwin_conv_path]) -# DragonFly uses vfsconf, FreeBSD xvfsconf. +dnl DragonFly uses vfsconf, FreeBSD xvfsconf. AC_CHECK_TYPES(struct vfsconf,,, [#if HAVE_SYS_TYPES_H #include @@ -888,7 +950,7 @@ AC_CHECK_TYPES(struct statfs,,, #include ]) -# dirfd can be either a function or a macro. +dnl dirfd can be either a function or a macro. AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include DIR *dir;]], @@ -896,9 +958,9 @@ AC_LINK_IFELSE( [AC_DEFINE(HAVE_DIRFD,1,[Define to 1 if you have a dirfd function or macro])] ) -# FreeBSD's nl_langinfo supports an option to specify whether the -# current locale uses month/day or day/month ordering. It makes the -# output a little prettier... +dnl FreeBSD's nl_langinfo supports an option to specify whether the +dnl current locale uses month/day or day/month ordering. It makes the +dnl output a little prettier... AC_CHECK_DECL([D_MD_ORDER], [AC_DEFINE(HAVE_D_MD_ORDER, 1, [Define to 1 if nl_langinfo supports D_MD_ORDER])], [], @@ -907,16 +969,16 @@ AC_CHECK_DECL([D_MD_ORDER], #endif ]) -# Check for dirent.d_namlen field explicitly -# (This is a bit more straightforward than, if not quite as portable as, -# the recipe given by the autoconf maintainers.) +dnl Check for dirent.d_namlen field explicitly +dnl (This is a bit more straightforward than, if not quite as portable as, +dnl the recipe given by the autoconf maintainers.) AC_CHECK_MEMBER(struct dirent.d_namlen,,, [#if HAVE_DIRENT_H #include #endif ]) -# Check for Extended Attributes support +dnl Check for Extended Attributes support AC_ARG_ENABLE([xattr], AS_HELP_STRING([--disable-xattr], [Disable Extended Attributes support (default: check)])) @@ -934,7 +996,7 @@ if test "x$enable_xattr" != "xno"; then fi if test "x$ac_cv_header_sys_xattr_h" = "xyes" \ -a "x$ac_cv_have_decl_XATTR_NOFOLLOW" = "xyes"; then - # Darwin extended attributes support + dnl Darwin extended attributes support AC_CACHE_VAL([ac_cv_archive_xattr_darwin], [AC_CHECK_FUNCS(fgetxattr \ flistxattr \ @@ -948,7 +1010,7 @@ if test "x$enable_xattr" != "xno"; then ) elif test "x$ac_cv_header_sys_extattr_h" = "xyes" \ -a "x$ac_cv_have_decl_EXTATTR_NAMESPACE_USER" = "xyes"; then - # FreeBSD extended attributes support + dnl FreeBSD extended attributes support AC_CACHE_VAL([ac_cv_archive_xattr_freebsd], [AC_CHECK_FUNCS(extattr_get_fd \ extattr_get_file \ @@ -964,7 +1026,7 @@ if test "x$enable_xattr" != "xno"; then ) elif test "x$ac_cv_header_sys_xattr_h" = "xyes" \ -o "x$ac_cv_header_attr_xattr_h" = "xyes"; then - # Linux extended attributes support + dnl Linux extended attributes support AC_CACHE_VAL([ac_cv_archive_xattr_linux], [AC_CHECK_FUNCS(fgetxattr \ flistxattr \ @@ -979,7 +1041,7 @@ if test "x$enable_xattr" != "xno"; then ] ) elif test "x$ac_cv_header_sys_ea_h" = "xyes"; then - # AIX extended attributes support + dnl AIX extended attributes support AC_CACHE_VAL([ac_cv_archive_xattr_aix], [AC_CHECK_FUNCS(fgetea \ flistea \ @@ -1013,19 +1075,19 @@ if test "x$enable_xattr" != "xno"; then fi fi -# Check for ACL support -# -# The ACL support in libarchive is written against the POSIX1e draft, -# which was never officially approved and varies quite a bit across -# platforms. Worse, some systems have completely non-POSIX acl functions, -# which makes the following checks rather more complex than I would like. -# +dnl Check for ACL support +dnl +dnl The ACL support in libarchive is written against the POSIX1e draft, +dnl which was never officially approved and varies quite a bit across +dnl platforms. Worse, some systems have completely non-POSIX acl functions, +dnl which makes the following checks rather more complex than I would like. +dnl AC_ARG_ENABLE([acl], AS_HELP_STRING([--disable-acl], [Disable ACL support (default: check)])) if test "x$enable_acl" != "xno"; then - # Libacl + dnl Libacl AC_CHECK_LIB([acl], [acl_get_file]) ACL_PC_VER=`$PKG_CONFIG --modversion libacl` if test "x$ACL_PC_VER" != "x"; then @@ -1051,7 +1113,7 @@ if test "x$enable_acl" != "xno"; then #endif ]) - # Solaris and derivates ACLs + dnl Solaris and derivates ACLs AC_CHECK_FUNCS(acl facl) if test "x$ac_cv_lib_richacl_richacl_get_file" = "xyes" \ @@ -1089,7 +1151,7 @@ if test "x$enable_acl" != "xno"; then -a "x$ac_cv_type_acl_entry_t" = "xyes" \ -a "x$ac_cv_type_acl_permset_t" = "xyes" \ -a "x$ac_cv_type_acl_tag_t" = "xyes"; then - # POSIX.1e ACL functions + dnl POSIX.1e ACL functions AC_CACHE_VAL([ac_cv_posix_acl_funcs], [AC_CHECK_FUNCS(acl_add_perm \ acl_clear_perms \ @@ -1121,7 +1183,7 @@ if test "x$enable_acl" != "xno"; then AC_DEFINE([ARCHIVE_ACL_LIBACL], [1], [POSIX.1e ACL support via libacl]) else - # FreeBSD/Darwin + dnl FreeBSD/Darwin AC_CHECK_FUNCS(acl_add_flag_np \ acl_clear_flags_np \ acl_get_brand_np \ @@ -1227,7 +1289,7 @@ AM_CONDITIONAL([INC_DARWIN_ACL], AM_CONDITIONAL([INC_FREEBSD_ACL], [test "x$ac_cv_archive_acl_freebsd" = "xyes"]) -# Additional requirements +dnl Additional requirements AC_SYS_LARGEFILE dnl NOTE: Crypto checks must run last. @@ -1265,32 +1327,6 @@ main(int argc, char **argv) fi ]) -AC_DEFUN([CRYPTO_CHECK_WIN], [ - if test "$found_$1" != yes; then - AC_MSG_CHECKING([support for ARCHIVE_CRYPTO_$1_WIN]) - AC_LINK_IFELSE([AC_LANG_SOURCE([ -#define ARCHIVE_$1_COMPILE_TEST -#include -#include - -int -main(int argc, char **argv) -{ - (void)argc; - (void)argv; - - return ($2); -} -])], - [ AC_MSG_RESULT([yes]) - found_$1=yes - found_WIN=yes - AC_DEFINE(ARCHIVE_CRYPTO_$1_WIN, 1, [ $1 via ARCHIVE_CRYPTO_$1_WIN supported.]) - ], - [ AC_MSG_RESULT([no])]) - fi -]) - case "$host_os" in *mingw* | *cygwin* | *msys*) ;; @@ -1317,15 +1353,34 @@ case "$host_os" in ;; esac -if test "x$with_cng" != "xno"; then - AC_CHECK_HEADERS([bcrypt.h],[ - LIBS="$LIBS -lbcrypt" - ],[], - [[#ifdef HAVE_WINDOWS_H - # include - #endif - ]]) -fi +AC_CHECK_HEADERS([bcrypt.h],[ + LIBS="$LIBS -lbcrypt" + if test "x$with_cng" != "xno"; then + dnl bcrypt supports these algorithms on all available versions + found_WIN=yes + found_MD5=yes + found_SHA1=yes + found_SHA256=yes + found_SHA384=yes + found_SHA512=yes + AC_DEFINE(ARCHIVE_CRYPTO_WIN, 1, + [ Use Windows CNG (Crypto Next Generation)]) + AC_DEFINE(ARCHIVE_CRYPTO_MD5_WIN, 1, + [ MD5 via ARCHIVE_CRYPTO_MD5_WIN supported.]) + AC_DEFINE(ARCHIVE_CRYPTO_SHA1_WIN, 1, + [ SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported.]) + AC_DEFINE(ARCHIVE_CRYPTO_SHA256_WIN, 1, + [ SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported.]) + AC_DEFINE(ARCHIVE_CRYPTO_SHA384_WIN, 1, + [ SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported.]) + AC_DEFINE(ARCHIVE_CRYPTO_SHA512_WIN, 1, + [ SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported.]) + fi +],[], +[[#ifdef HAVE_WINDOWS_H +# include +#endif +]]) if test "x$with_mbedtls" = "xyes"; then AC_CHECK_HEADERS([mbedtls/aes.h mbedtls/md.h mbedtls/pkcs5.h]) @@ -1345,6 +1400,7 @@ fi if test "x$with_nettle" = "xyes"; then AC_CHECK_HEADERS([nettle/md5.h nettle/ripemd160.h nettle/sha.h]) + AC_CHECK_HEADERS([nettle/sha1.h nettle/sha2.h]) AC_CHECK_HEADERS([nettle/pbkdf2.h nettle/aes.h nettle/hmac.h]) AC_CHECK_HEADERS([nettle/version.h]) saved_LIBS=$LIBS @@ -1362,7 +1418,6 @@ fi if test "x$with_openssl" != "xno"; then AC_CHECK_HEADERS([openssl/evp.h openssl/opensslv.h]) - saved_LIBS=$LIBS LIBSREQUIRED="$LIBSREQUIRED${LIBSREQUIRED:+ }libcrypto" AC_CHECK_LIB(crypto,OPENSSL_config) CRYPTO_CHECK(MD5, OPENSSL, md5) @@ -1376,42 +1431,27 @@ fi AC_SUBST(LIBSREQUIRED) -# Probe libmd AFTER OpenSSL/libcrypto. -# The two are incompatible and OpenSSL is more complete. -saved_LIBS=$LIBS -AC_CHECK_LIB(md, MD5Init) -CRYPTO_CHECK(MD5, LIBMD, md5) -CRYPTO_CHECK(RMD160, LIBMD, rmd160) -CRYPTO_CHECK(SHA1, LIBMD, sha1) -CRYPTO_CHECK(SHA256, LIBMD, sha256) -CRYPTO_CHECK(SHA512, LIBMD, sha512) -if test "x$found_LIBMD" != "xyes"; then - LIBS=$saved_LIBS +if test "x$with_md" != "xno"; then + dnl Probe libmd AFTER OpenSSL/libcrypto. + dnl The two are incompatible and OpenSSL is more complete. + saved_LIBS=$LIBS + AC_CHECK_LIB(md, MD5Init) + CRYPTO_CHECK(MD5, LIBMD, md5) + CRYPTO_CHECK(RMD160, LIBMD, rmd160) + CRYPTO_CHECK(SHA1, LIBMD, sha1) + CRYPTO_CHECK(SHA256, LIBMD, sha256) + CRYPTO_CHECK(SHA512, LIBMD, sha512) + if test "x$found_LIBMD" != "xyes"; then + LIBS=$saved_LIBS + fi fi -case "$host_os" in - *mingw* | *cygwin* | *msys*) - CRYPTO_CHECK_WIN(MD5, CALG_MD5) - CRYPTO_CHECK_WIN(SHA1, CALG_SHA1) - CRYPTO_CHECK_WIN(SHA256, CALG_SHA_256) - CRYPTO_CHECK_WIN(SHA384, CALG_SHA_384) - CRYPTO_CHECK_WIN(SHA512, CALG_SHA_512) - ;; -esac - dnl Visibility annotations... -saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -fvisibility=hidden -Werror" -AC_MSG_CHECKING(whether compiler supports visibility annotations) -AC_LINK_IFELSE([AC_LANG_PROGRAM([ - int foo( void ) __attribute__((visibility("default"))); - ])], - [CFLAGS="$saved_CFLAGS -fvisibility=hidden -D__LIBARCHIVE_ENABLE_VISIBILITY"; - AC_MSG_RESULT(yes)], - [CFLAGS="$saved_CFLAGS" - AC_MSG_RESULT(no)]) +gl_VISIBILITY +AX_APPEND_FLAG([$CFLAG_VISIBILITY]) +AS_IF([test "$HAVE_VISIBILITY" = "1"], [AX_APPEND_FLAG([-D__LIBARCHIVE_ENABLE_VISIBILITY])]) -# Ensure test directories are present if building out-of-tree +dnl Ensure test directories are present if building out-of-tree AC_CONFIG_COMMANDS([mkdirs], [mkdir -p libarchive/test tar/test cat/test cpio/test]) diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 9e1c72f6d021..f81dcb020456 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -98,6 +98,7 @@ libarchive_src_files := libarchive/archive_acl.c \ libarchive/archive_string.c \ libarchive/archive_string_sprintf.c \ libarchive/archive_time.c \ + libarchive/archive_umask.c \ libarchive/archive_util.c \ libarchive/archive_version_details.c \ libarchive/archive_virtual.c \ diff --git a/contrib/android/config/windows_host.h b/contrib/android/config/windows_host.h index 2eb9a8df66bf..c6da3605a4cb 100644 --- a/contrib/android/config/windows_host.h +++ b/contrib/android/config/windows_host.h @@ -118,6 +118,9 @@ /* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */ #define ARCHIVE_CRYPTO_SHA512_WIN 1 +/* Use Windows CNG (Crypto Next Generation) */ +#define ARCHIVE_CRYPTO_WIN 1 + /* Define to 1 if you have the `acl_create_entry' function. */ /* #undef HAVE_ACL_CREATE_ENTRY */ diff --git a/contrib/libarchive_autodetect-st_lib_archive.m4 b/contrib/libarchive_autodetect-st_lib_archive.m4 index 4419e888f240..9fcc60634ec7 100644 --- a/contrib/libarchive_autodetect-st_lib_archive.m4 +++ b/contrib/libarchive_autodetect-st_lib_archive.m4 @@ -3,7 +3,7 @@ dnl @synopsis ST_LIB_ARCHIVE([ENABLED-DEFAULT]) dnl dnl This macro figures out what's necessary to link a program against an dnl instance of the BSD libarchive package by Tim Kientzle. -dnl +dnl dnl See http://people.freebsd.org/~kientzle/libarchive/ for more info. dnl dnl It exports and substitutes the variables LIBARCHIVE_LIBS, LIBARCHIVE_LDFLAGS, @@ -29,26 +29,25 @@ dnl @author Andre Stechert dnl @version 2006-04-20 dnl @license GPLWithACException -AC_DEFUN([ST_LIB_ARCHIVE], -[ -# -# Handle input from the configurer and blend with the requirements from the maintainer. -# We go through the trouble of creating a second set of variables other than the with_foo -# variables in order to be sure that error/corner cases have been cleaned up. -# -# After this statement, three trusted variable are defined. -# -# st_lib_archive_ENABLED will be either "yes" or "no". its value determines whether -# or not we bother with the rest of the checks and whether or not we export a -# bunch of variables. -# -# st_lib_archive_LOCATION will be either "auto" or "defined". if it is "auto", then -# we try a bunch of standard locations. if it is "defined", then we just try the value -# provided in st_lib_archive_DIR. -# -# st_lib_archive_DIR will contain the string provided by the user, provided that it's -# actually a directory. -# +AC_DEFUN([ST_LIB_ARCHIVE], [dnl +dnl +dnl Handle input from the configurer and blend with the requirements from the maintainer. +dnl We go through the trouble of creating a second set of variables other than the with_foo +dnl variables in order to be sure that error/corner cases have been cleaned up. +dnl +dnl After this statement, three trusted variable are defined. +dnl +dnl st_lib_archive_ENABLED will be either "yes" or "no". its value determines whether +dnl or not we bother with the rest of the checks and whether or not we export a +dnl bunch of variables. +dnl +dnl st_lib_archive_LOCATION will be either "auto" or "defined". if it is "auto", then +dnl we try a bunch of standard locations. if it is "defined", then we just try the value +dnl provided in st_lib_archive_DIR. +dnl +dnl st_lib_archive_DIR will contain the string provided by the user, provided that it's +dnl actually a directory. +dnl AC_MSG_CHECKING([if libarchive is wanted]) AC_ARG_WITH([libarchive], AS_HELP_STRING([--with-libarchive=DIR], [libarchive installation directory]), @@ -76,12 +75,12 @@ AC_ARG_WITH([libarchive], if test "$st_lib_archive_ENABLED" = "yes" ; then AC_MSG_RESULT([yes]) -# -# After this statement, one trusted variable is defined. -# -# st_lib_archive_LIB will be either "lib" or "lib64", depending on whether the configurer -# specified 32, 64. The default is "lib". -# +dnl +dnl After this statement, one trusted variable is defined. +dnl +dnl st_lib_archive_LIB will be either "lib" or "lib64", depending on whether the configurer +dnl specified 32, 64. The default is "lib". +dnl AC_MSG_CHECKING([whether to use lib or lib64]) AC_ARG_WITH([libarchive-bits], AS_HELP_STRING([--with-libarchive-bits=32/64], [if 64, look in /lib64 on hybrid systems]), @@ -94,9 +93,9 @@ if test "$st_lib_archive_ENABLED" = "yes" ; then fi], [st_lib_archive_LIB=lib]) AC_MSG_RESULT($st_lib_archive_LIB) -# -# Save the environment before verifying libarchive availability -# +dnl +dnl Save the environment before verifying libarchive availability +dnl st_lib_archive_SAVECPPFLAGS="$CPPFLAGS" st_lib_archive_SAVELDFLAGS="$LDFLAGS" AC_LANG_SAVE @@ -114,9 +113,9 @@ if test "$st_lib_archive_ENABLED" = "yes" ; then AC_MSG_ERROR([could not find libarchive in the requested location]) fi else - # - # These are the common install directories for Linux, FreeBSD, Solaris, and Mac. - # + dnl + dnl These are the common install directories for Linux, FreeBSD, Solaris, and Mac. + dnl for dir in /usr /usr/local /usr/sfw /opt/csw /opt/local /sw do if test -d "$dir" ; then @@ -141,9 +140,9 @@ if test "$st_lib_archive_ENABLED" = "yes" ; then AC_SUBST(LIBARCHIVE_LDFLAGS) fi -# -# Restore the environment now that we're done. -# +dnl +dnl Restore the environment now that we're done. +dnl AC_LANG_RESTORE CPPFLAGS="$st_lib_archive_SAVECPPFLAGS" LDFLAGS="$st_lib_archive_SAVELDFLAGS" diff --git a/contrib/oss-fuzz/oss-fuzz-build.sh b/contrib/oss-fuzz/oss-fuzz-build.sh index 16850fe38eff..5cc8473d309f 100755 --- a/contrib/oss-fuzz/oss-fuzz-build.sh +++ b/contrib/oss-fuzz/oss-fuzz-build.sh @@ -67,17 +67,20 @@ echo "Building seed corpora..." cp "$FUZZ_DIR/corpus.zip" "$OUT/libarchive_fuzzer_seed_corpus.zip" # Function to create corpus from test files +# Usage: create_corpus [...] create_corpus() { local name=$1 - local pattern=$2 + shift local dir="/tmp/${name}_corpus" mkdir -p "$dir" - for f in $TEST_DIR/$pattern; do - if [ -f "$f" ]; then - base=$(basename "$f" .uu) - uudecode -o "$dir/$base" "$f" 2>/dev/null || true - fi + for pattern in "$@"; do + for f in $TEST_DIR/$pattern; do + if [ -f "$f" ]; then + base=$(basename "$f" .uu) + uudecode -o "$dir/$base" "$f" 2>/dev/null || true + fi + done done if [ "$(ls -A $dir 2>/dev/null)" ]; then @@ -88,23 +91,26 @@ create_corpus() { } # Create format-specific corpora -create_corpus "libarchive_tar_fuzzer" "test_compat_*tar*.uu" +create_corpus "libarchive_tar_fuzzer" "test_compat_*tar*.uu" "*.tar.uu" "*.tar.*.uu" create_corpus "libarchive_zip_fuzzer" "test_*zip*.uu" create_corpus "libarchive_7zip_fuzzer" "test_read_format_7zip*.uu" -create_corpus "libarchive_rar_fuzzer" "test_read_format_rar_*.uu" +create_corpus "libarchive_rar_fuzzer" "test_read_format_rar[!5]*.uu" create_corpus "libarchive_rar5_fuzzer" "test_read_format_rar5*.uu" create_corpus "libarchive_xar_fuzzer" "test_read_format_xar*.uu" create_corpus "libarchive_cab_fuzzer" "test_read_format_cab*.uu" create_corpus "libarchive_lha_fuzzer" "test_read_format_lha*.uu" create_corpus "libarchive_iso9660_fuzzer" "test_read_format_iso*.uu" -create_corpus "libarchive_cpio_fuzzer" "test_compat_cpio*.uu" +create_corpus "libarchive_cpio_fuzzer" "*cpio*.uu" create_corpus "libarchive_warc_fuzzer" "test_read_format_warc*.uu" create_corpus "libarchive_mtree_fuzzer" "test_read_format_mtree*.uu" create_corpus "libarchive_ar_fuzzer" "test_read_format_ar*.uu" # Filter corpus - use compressed test files mkdir -p /tmp/filter_corpus -for f in $TEST_DIR/*.gz.uu $TEST_DIR/*.bz2.uu $TEST_DIR/*.xz.uu $TEST_DIR/*.lz4.uu $TEST_DIR/*.zst.uu $TEST_DIR/*.Z.uu; do +for f in $TEST_DIR/*.gz.uu $TEST_DIR/*.bz2.uu $TEST_DIR/*.xz.uu $TEST_DIR/*.lz4.uu \ + $TEST_DIR/*.zst.uu $TEST_DIR/*.Z.uu $TEST_DIR/*.lz.uu $TEST_DIR/*.rpm.uu \ + $TEST_DIR/test_read_filter_uudecode*.uu \ + $TEST_DIR/*.lrz.uu $TEST_DIR/*.grz.uu $TEST_DIR/*.lzo.uu; do if [ -f "$f" ]; then base=$(basename "$f" .uu) uudecode -o "/tmp/filter_corpus/$base" "$f" 2>/dev/null || true diff --git a/cpio/CMakeLists.txt b/cpio/CMakeLists.txt index 683f542d70f2..a84258acfb7d 100644 --- a/cpio/CMakeLists.txt +++ b/cpio/CMakeLists.txt @@ -15,6 +15,8 @@ IF(ENABLE_CPIO) cpio_platform.h ../libarchive_fe/lafe_err.c ../libarchive_fe/lafe_err.h + ../libarchive_fe/lafe_explicit_bzero.c + ../libarchive_fe/lafe_explicit_bzero.h ../libarchive_fe/lafe_getline.c ../libarchive_fe/lafe_getline.h ../libarchive_fe/lafe_platform.h diff --git a/cpio/bsdcpio.1 b/cpio/bsdcpio.1 index 9dc7d660b448..e4e663f1a55e 100644 --- a/cpio/bsdcpio.1 +++ b/cpio/bsdcpio.1 @@ -79,6 +79,14 @@ Reset access times on files after they are read. .It Fl B (o mode only) Block output to records of 5120 bytes. +.It Fl Fl b64encode +(o mode only) +Encode the archive with base64, after any compression. +In input mode, this option is ignored; base64-encoded archives are +recognized automatically on input. +If both this option and +.Fl Fl uuencode +are given, the last one specified is used. .It Fl C Ar size (o mode only) Block output to records of @@ -154,7 +162,7 @@ automatically on input. .It Fl j Synonym for .Fl y . -.It Fl L +.It Fl L , Fl Fl dereference (o and p modes) All symbolic links will be followed. Normally, symbolic links are archived and copied as symbolic links. @@ -163,6 +171,11 @@ With this option, the target of the link will be archived or copied instead. (p mode only) Create links from the target directory to the original files, instead of copying. +.It Fl Fl grzip +(o mode only) +Compress the resulting archive with +.Xr grzip 1 . +In input mode, this option is ignored. .It Fl Fl lrzip (o mode only) Compress the resulting archive with @@ -265,6 +278,16 @@ do not restore the contents to disk. (i and p modes) Unconditionally overwrite existing files. Ordinarily, an older file will not overwrite a newer file on disk. +.It Fl Fl uuencode +(o mode only) +Encode the archive with +.Xr uuencode 1 , +after any compression. +In input mode, this option is ignored; uuencoded archives are +recognized automatically on input. +If both this option and +.Fl Fl b64encode +are given, the last one specified is used. .It Fl V , Fl Fl dot Print a dot to stderr for each file as it is processed. Superseded by diff --git a/cpio/cpio.c b/cpio/cpio.c index ebdace394cea..9ba153d82770 100644 --- a/cpio/cpio.c +++ b/cpio/cpio.c @@ -66,6 +66,7 @@ #include "cpio.h" #include "lafe_err.h" +#include "lafe_explicit_bzero.h" #include "line_reader.h" #include "passphrase.h" @@ -77,8 +78,6 @@ #endif struct name_cache { - int probes; - int hits; size_t size; struct { id_t id; @@ -87,7 +86,6 @@ struct name_cache { }; static int extract_data(struct archive *, struct archive *); -const char * cpio_i64toa(int64_t); static void cpio_rename(struct archive_entry *); static int entry_to_archive(struct cpio *, struct archive_entry *); static int file_to_archive(struct cpio *, const char *); @@ -409,6 +407,10 @@ main(int argc, char *argv[]) else cpio->format = "cpio"; } + if (cpio->argc > 0) + lafe_errc(1, 0, + "Too many arguments for -o mode (got %d, expected 0)", + cpio->argc); mode_out(cpio); break; case 'i': @@ -429,6 +431,10 @@ main(int argc, char *argv[]) if (*cpio->argv == NULL || **cpio->argv == '\0') lafe_errc(1, 0, "-p mode requires a target directory"); + if (cpio->argc > 1) + lafe_errc(1, 0, + "Too many arguments for -p mode (got %d, expected 1)", + cpio->argc); mode_pass(cpio, *cpio->argv); break; default: @@ -728,12 +734,16 @@ file_to_archive(struct cpio *cpio, const char *srcpath) return (r); } - if (cpio->uid_override >= 0) + if (cpio->uid_override >= 0) { archive_entry_set_uid(entry, cpio->uid_override); + archive_entry_set_uname(entry, NULL); + } if (cpio->uname_override != NULL) archive_entry_set_uname(entry, cpio->uname_override); - if (cpio->gid_override >= 0) + if (cpio->gid_override >= 0) { archive_entry_set_gid(entry, cpio->gid_override); + archive_entry_set_gname(entry, NULL); + } if (cpio->gname_override != NULL) archive_entry_set_gname(entry, cpio->gname_override); @@ -842,7 +852,11 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) lafe_warnc(0, "Copying file instead"); } else #endif - return (0); + { + if (r != ARCHIVE_OK) + cpio->return_value = 1; + return (0); + } } /* @@ -1160,6 +1174,7 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) const char *uname, *gname; FILE *out = stdout; const char *fmt; + double age; time_t mtime; static time_t now; struct tm *ltime; @@ -1172,9 +1187,11 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) if (cpio->option_numeric_uid_gid) { /* Format numeric uid/gid for display. */ - strcpy(uids, cpio_i64toa(archive_entry_uid(entry))); + snprintf(uids, sizeof(uids), "%lld", + (long long)archive_entry_uid(entry)); uname = uids; - strcpy(gids, cpio_i64toa(archive_entry_gid(entry))); + snprintf(gids, sizeof(gids), "%lld", + (long long)archive_entry_gid(entry)); gname = gids; } else { /* Use uname if it's present, else lookup name from uid. */ @@ -1194,21 +1211,21 @@ list_item_verbose(struct cpio *cpio, struct archive_entry *entry) (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { - strcpy(size, cpio_i64toa(archive_entry_size(entry))); + snprintf(size, sizeof(size), "%lld", + (long long)archive_entry_size(entry)); } /* Format the time using 'ls -l' conventions. */ mtime = archive_entry_mtime(entry); + age = difftime(mtime, now); #if defined(_WIN32) && !defined(__CYGWIN__) /* Windows' strftime function does not support %e format. */ - if (mtime - now > 365*86400/2 - || mtime - now < -365*86400/2) + if (age > 365.0 * 86400 / 2 || age < -365.0 * 86400 / 2) fmt = cpio->day_first ? "%d %b %Y" : "%b %d %Y"; else fmt = cpio->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; #else - if (mtime - now > 365*86400/2 - || mtime - now < -365*86400/2) + if (age > 365.0 * 86400 / 2 || age < -365.0 * 86400 / 2) fmt = cpio->day_first ? "%e %b %Y" : "%b %e %Y"; else fmt = cpio->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; @@ -1392,12 +1409,10 @@ lookup_name(struct cpio *cpio, struct name_cache **name_cache_variable, } cache = *name_cache_variable; - cache->probes++; slot = id % cache->size; if (cache->cache[slot].name != NULL) { if (cache->cache[slot].id == id) { - cache->hits++; return (cache->cache[slot].name); } free(cache->cache[slot].name); @@ -1442,8 +1457,8 @@ lookup_uname_helper(struct cpio *cpio, const char **name, id_t id) pwent = getpwuid((uid_t)id); if (pwent == NULL) { if (errno && errno != ENOENT) - lafe_warnc(errno, "getpwuid(%s) failed", - cpio_i64toa((int64_t)id)); + lafe_warnc(errno, "getpwuid(%lld) failed", + (long long)id); return 1; } @@ -1469,8 +1484,8 @@ lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) grent = getgrgid((gid_t)id); if (grent == NULL) { if (errno && errno != ENOENT) - lafe_warnc(errno, "getgrgid(%s) failed", - cpio_i64toa((int64_t)id)); + lafe_warnc(errno, "getgrgid(%lld) failed", + (long long)id); return 1; } @@ -1478,31 +1493,6 @@ lookup_gname_helper(struct cpio *cpio, const char **name, id_t id) return 0; } -/* - * It would be nice to just use printf() for formatting large numbers, - * but the compatibility problems are a big headache. Hence the - * following simple utility function. - */ -const char * -cpio_i64toa(int64_t n0) -{ - /* 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice. - * We also need 1 byte for '-' and 1 for '\0'. - */ - static char buff[22]; - int64_t n = n0 < 0 ? -n0 : n0; - char *p = buff + sizeof(buff); - - *--p = '\0'; - do { - *--p = '0' + (int)(n % 10); - n /= 10; - } while (n > 0); - if (n0 < 0) - *--p = '-'; - return p; -} - #define PPBUFF_SIZE 1024 static const char * passphrase_callback(struct archive *a, void *_client_data) @@ -1523,7 +1513,7 @@ static void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { - memset(ppbuff, 0, PPBUFF_SIZE); + explicit_bzero(ppbuff, PPBUFF_SIZE); free(ppbuff); } } diff --git a/cpio/cpio.h b/cpio/cpio.h index 2621a4c3dda3..8607f1d119b8 100644 --- a/cpio/cpio.h +++ b/cpio/cpio.h @@ -34,7 +34,6 @@ struct cpio { int dot; /* -V */ int quiet; /* --quiet */ int extract_flags; /* Flags for extract operation */ - const char *compress_program; int option_append; /* -A, only relevant for -o */ int option_atime_restore; /* -a */ int option_follow_links; /* -L */ diff --git a/cpio/test/test_basic.c b/cpio/test/test_basic.c index 8b8e8bb0f0ec..96f6f99d521a 100644 --- a/cpio/test/test_basic.c +++ b/cpio/test/test_basic.c @@ -45,7 +45,7 @@ static void basic_cpio(const char *target, const char *pack_options, const char *unpack_options, - const char *se, const char *se2) + const char *se) { int r; @@ -74,7 +74,7 @@ basic_cpio(const char *target, /* Verify stderr. */ failure("Error invoking %s -i %s in dir %s", testprog, unpack_options, target); - assertTextFileContents(se2, "unpack.err"); + assertTextFileContents(se, "unpack.err"); verify_files(pack_options); @@ -112,7 +112,6 @@ DEFINE_TEST(test_basic) { FILE *filelist; const char *msg; - char result[1024]; assertUmask(0); @@ -120,85 +119,28 @@ DEFINE_TEST(test_basic) * Create an assortment of files on disk. */ filelist = fopen("filelist", "w"); - memset(result, 0, sizeof(result)); /* File with 10 bytes content. */ assertMakeFile("file", 0644, "1234567890"); fprintf(filelist, "file\n"); - if (is_LargeInode("file")) { - strncat(result, - "bsdcpio: file: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, - strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, - "\n", - sizeof(result) - strlen(result) -1); - } /* hardlink to above file. */ assertMakeHardlink("linkfile", "file"); fprintf(filelist, "linkfile\n"); - if (is_LargeInode("linkfile")) { - strncat(result, - "bsdcpio: linkfile: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, - strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, - "\n", - sizeof(result) - strlen(result) -1); - } /* Symlink to above file. */ if (canSymlink()) { assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); - if (is_LargeInode("symlink")) { - strncat(result, - "bsdcpio: symlink: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, - strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, - "\n", - sizeof(result) - strlen(result) -1); - } } /* Another file with different permissions. */ assertMakeFile("file2", 0777, "1234567890"); fprintf(filelist, "file2\n"); - if (is_LargeInode("file2")) { - strncat(result, - "bsdcpio: file2: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, - strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, - "\n", - sizeof(result) - strlen(result) -1); - } /* Directory. */ assertMakeDir("dir", 0775); fprintf(filelist, "dir\n"); - if (is_LargeInode("dir")) { - strncat(result, - "bsdcpio: dir: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, - strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, - "\n", - sizeof(result) - strlen(result) -1); - } - strncat(result, "2 blocks\n", sizeof(result) - strlen(result) -1); /* All done. */ fclose(filelist); @@ -207,14 +149,14 @@ DEFINE_TEST(test_basic) /* Archive/dearchive with a variety of options. */ msg = canSymlink() ? "2 blocks\n" : "1 block\n"; - basic_cpio("copy", "", "", msg, msg); - basic_cpio("copy_odc", "--format=odc", "", msg, msg); - basic_cpio("copy_newc", "-H newc", "", result, "2 blocks\n"); - basic_cpio("copy_cpio", "-H odc", "", msg, msg); + basic_cpio("copy", "", "", msg); + basic_cpio("copy_odc", "--format=odc", "", msg); + basic_cpio("copy_newc", "-H newc", "", "2 blocks\n"); + basic_cpio("copy_cpio", "-H odc", "", msg); msg = "1 block\n"; - basic_cpio("copy_bin", "-H bin", "", msg, msg); + basic_cpio("copy_bin", "-H bin", "", msg); msg = canSymlink() ? "9 blocks\n" : "8 blocks\n"; - basic_cpio("copy_ustar", "-H ustar", "", msg, msg); + basic_cpio("copy_ustar", "-H ustar", "", msg); /* Copy in one step using -p */ passthrough("passthrough"); diff --git a/cpio/test/test_cmdline.c b/cpio/test/test_cmdline.c index e4397b14e7f6..7e29be69564d 100644 --- a/cpio/test/test_cmdline.c +++ b/cpio/test/test_cmdline.c @@ -82,7 +82,15 @@ DEFINE_TEST(test_cmdline) assert(0 != systemf("%s -p -o 16.out 2>16.err", testprog)); assertEmptyFile("16.out"); - failure("-p with empty input should fail"); + failure("-p with missing directory should fail"); assert(0 != systemf("%s -p 17.out 2>17.err", testprog)); assertEmptyFile("17.out"); + + failure("-p with more than one argument should fail"); + assert(0 != systemf("%s -p out abc 18.out 2>18.err", testprog)); + assertEmptyFile("18.out"); + + failure("-o with arguments should fail"); + assert(0 != systemf("%s -o abc 19.out 2>19.err", testprog)); + assertEmptyFile("19.out"); } diff --git a/cpio/test/test_format_newc.c b/cpio/test/test_format_newc.c index 7363fc148f16..54e04225eef5 100644 --- a/cpio/test/test_format_newc.c +++ b/cpio/test/test_format_newc.c @@ -113,42 +113,6 @@ DEFINE_TEST(test_format_newc) /* Setup result message. */ memset(result, 0, sizeof(result)); - if (is_LargeInode("file1")) { - strncat(result, - "bsdcpio: file1: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, "\n", - sizeof(result) - strlen(result) -1); - } - if (canSymlink() && is_LargeInode("symlink")) { - strncat(result, - "bsdcpio: symlink: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, "\n", - sizeof(result) - strlen(result) -1); - } - if (is_LargeInode("dir")) { - strncat(result, - "bsdcpio: dir: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, "\n", - sizeof(result) - strlen(result) -1); - } - if (is_LargeInode("hardlink")) { - strncat(result, - "bsdcpio: hardlink: large inode number truncated: ", - sizeof(result) - strlen(result) -1); - strncat(result, strerror(ERANGE), - sizeof(result) - strlen(result) -1); - strncat(result, "\n", - sizeof(result) - strlen(result) -1); - } /* Record some facts about what we just created: */ now = time(NULL); /* They were all created w/in last two seconds. */ @@ -225,7 +189,7 @@ DEFINE_TEST(test_format_newc) /* "symlink" pointing to "file1" */ assert(is_hex(e, 110)); assertEqualMem(e + 0, "070701", 6); /* Magic */ - assert(is_hex(e + 6, 8)); /* ino */ + assert(ino != from_hex(e + 6, 8)); /* ino */ #if defined(_WIN32) && !defined(__CYGWIN__) /* Mode: Group members bits and others bits do not work. */ assertEqualInt(0xa180, from_hex(e + 14, 8) & 0xffc0); @@ -258,7 +222,7 @@ DEFINE_TEST(test_format_newc) /* "dir" */ assert(is_hex(e, 110)); assertEqualMem(e + 0, "070701", 6); /* Magic */ - assert(is_hex(e + 6, 8)); /* ino */ + assert(ino != from_hex(e + 6, 8)); /* ino */ #if defined(_WIN32) && !defined(__CYGWIN__) /* Group members bits and others bits do not work. */ assertEqualInt(0x41c0, from_hex(e + 14, 8) & 0xffc0); /* Mode */ diff --git a/cpio/test/test_option_l.c b/cpio/test/test_option_l.c index bbd54c7bc972..e49fc7ca6fe4 100644 --- a/cpio/test/test_option_l.c +++ b/cpio/test/test_option_l.c @@ -28,4 +28,11 @@ DEFINE_TEST(test_option_l) /* Check that this is a link and not a copy. */ assertIsHardlink("f", "link/f"); + + /* A failed hard link must make pass mode fail. */ + r = systemf("echo f | %s -pl missing/parent >invalid.out 2>invalid.err", + testprog); + assert(r != 0); + assertFileNotExists("missing/parent/f"); + assertNonEmptyFile("invalid.err"); } diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index b29663742fd6..ac039c192dc5 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -22,7 +22,7 @@ SET(libarchive_SOURCES archive_check_magic.c archive_cmdline.c archive_cmdline_private.h - archive_crc32.h + archive_crc32.c archive_cryptor.c archive_cryptor_private.h archive_digest.c @@ -120,6 +120,8 @@ SET(libarchive_SOURCES archive_string_sprintf.c archive_time.c archive_time_private.h + archive_umask.c + archive_umask_private.h archive_util.c archive_version_details.c archive_virtual.c @@ -170,6 +172,7 @@ SET(libarchive_SOURCES archive_write_set_format_zip.c archive_write_set_options.c archive_write_set_passphrase.c + archive_xml_private.h archive_xxhash.h filter_fork_posix.c filter_fork.h diff --git a/libarchive/archive.h b/libarchive/archive.h index a7de6f6dd85b..19b316a40b73 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -34,7 +34,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3008009 +#define ARCHIVE_VERSION_NUMBER 3008010 #include #include /* for wchar_t */ @@ -177,7 +177,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.8.9" +#define ARCHIVE_VERSION_ONLY_STRING "3.8.10" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -204,6 +204,7 @@ __LA_DECL const char * archive_liblzo2_version(void); __LA_DECL const char * archive_libexpat_version(void); __LA_DECL const char * archive_libbsdxml_version(void); __LA_DECL const char * archive_libxml2_version(void); +__LA_DECL const char * archive_xmllite_version(void); __LA_DECL const char * archive_mbedtls_version(void); __LA_DECL const char * archive_nettle_version(void); __LA_DECL const char * archive_openssl_version(void); @@ -273,7 +274,8 @@ typedef la_int64_t archive_skip_callback(struct archive *, /* Seeks to specified location in the file and returns the position. * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h. - * Return ARCHIVE_FATAL if the seek fails for any reason. + * Return ARCHIVE_FAILED if the seek failed in a way that keeps its + * current read position, ARCHIVE_FATAL for any other error reason. */ typedef la_int64_t archive_seek_callback(struct archive *, void *_client_data, la_int64_t offset, int whence); @@ -647,7 +649,7 @@ __LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int); * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify * the desired size of the block. The API does guarantee that offsets will - * be strictly increasing and that returned blocks will not overlap. + * be increasing and that returned blocks will not overlap. */ __LA_DECL int archive_read_data_block(struct archive *a, const void **buff, size_t *size, la_int64_t *offset); diff --git a/libarchive/archive_blake2.h b/libarchive/archive_blake2.h index 8f6b5e9221f4..b8cf2e8e7153 100644 --- a/libarchive/archive_blake2.h +++ b/libarchive/archive_blake2.h @@ -19,6 +19,19 @@ #include #include +// Rename functions for libarchive namespacing. +#define blake2s __archive_blake2s +#define blake2s_final __archive_blake2s_final +#define blake2s_init __archive_blake2s_init +#define blake2s_init_key __archive_blake2s_init_key +#define blake2s_init_param __archive_blake2s_init_param +#define blake2sp __archive_blake2sp +#define blake2sp_final __archive_blake2sp_final +#define blake2sp_init __archive_blake2sp_init +#define blake2sp_init_key __archive_blake2sp_init_key +#define blake2sp_update __archive_blake2sp_update +#define blake2s_update __archive_blake2s_update + #if defined(_MSC_VER) #define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) #elif defined(__GNUC__) diff --git a/libarchive/archive_check_magic.c b/libarchive/archive_check_magic.c index 788a76070b7a..9d925118704a 100644 --- a/libarchive/archive_check_magic.c +++ b/libarchive/archive_check_magic.c @@ -83,6 +83,7 @@ state_name(unsigned s) { switch (s) { case ARCHIVE_STATE_NEW: return ("new"); + case ARCHIVE_STATE_OPEN: return ("open"); case ARCHIVE_STATE_HEADER: return ("header"); case ARCHIVE_STATE_DATA: return ("data"); case ARCHIVE_STATE_DATA_RECOVERY: return ("data_recovery"); diff --git a/libarchive/archive_crc32.c b/libarchive/archive_crc32.c new file mode 100644 index 000000000000..0b0055784943 --- /dev/null +++ b/libarchive/archive_crc32.c @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 2009 Joerg Sonnenberger + * 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_private.h" + +#if defined(HAVE_ZLIB_H) +#include +#endif + +#if (defined(__aarch64__) || defined(_M_ARM64)) && \ + defined(__ARM_FEATURE_CRC32) && !defined(__ARM_BIG_ENDIAN) && \ + !defined(__AARCH64EB__) +/* + * Implementation 1: Hardware-accelerated CRC32 using ARMv8 ACLE instructions. + * Only use this when the compiler target guarantees CRC extension support. + * The extension is optional in ARMv8.0, so enabling it here without runtime + * detection would make generic AArch64 binaries unsafe on older CPUs. + */ + +#if defined(_MSC_VER) +#include +#elif defined(__GNUC__) || defined(__clang__) +#include +#endif +#include + +unsigned long +__archive_crc32(unsigned long crc, const void *_p, size_t len) +{ + const unsigned char *p = (const unsigned char *)_p; + uint32_t c = (uint32_t)crc ^ 0xffffffffU; + + if (p == NULL) + return (0); + + /* Align to 8 bytes */ + while (len > 0 && ((uintptr_t)p & 7)) { + c = __crc32b(c, *p++); + len--; + } + + /* 8-way unrolled 64-bit loop */ + while (len >= 64) { + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + c = __crc32d(c, *(const uint64_t *)p); p += 8; + len -= 64; + } + + while (len >= 8) { + c = __crc32d(c, *(const uint64_t *)p); + p += 8; + len -= 8; + } + + /* Trailing bytes */ + while (len > 0) { + c = __crc32b(c, *p++); + len--; + } + + return (c ^ 0xffffffffU); +} + +#elif defined(HAVE_ZLIB_H) +/* + * Implementation 2: zlib crc32(). + * zlib provides a highly-optimized CRC32 implementation across many platforms. + */ + +unsigned long +__archive_crc32(unsigned long crc, const void *_p, size_t len) +{ + if (_p == NULL) + return (0); + return crc32(crc, _p, (uInt)len); +} + +#else +/* + * Implementation 3: Portable 256-entry table fallback. + * Used when neither hardware CRC32 nor zlib is available. + * Key feature: Reasonably fast and requires no platform-specific support. + */ + +unsigned long +__archive_crc32(unsigned long crc, const void *_p, size_t len) +{ + const unsigned char *p = (const unsigned char *)_p; + static const unsigned long crc_tbl[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d + }; + + if (_p == NULL) + return (0); + + crc = crc ^ 0xffffffffUL; + for (;len >= 8; len -= 8) { + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + } + while (len--) + crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return (crc ^ 0xffffffffUL); +} + +#endif diff --git a/libarchive/archive_cryptor.c b/libarchive/archive_cryptor.c index 85b20ac9acc1..bece2b46408e 100644 --- a/libarchive/archive_cryptor.c +++ b/libarchive/archive_cryptor.c @@ -31,19 +31,6 @@ #include "archive.h" #include "archive_cryptor_private.h" -/* - * On systems that do not support any recognized crypto libraries, - * this file will normally define no usable symbols. - * - * But some compilers and linkers choke on empty object files, so - * define a public symbol that will always exist. This could - * be removed someday if this file gains another always-present - * symbol definition. - */ -int __libarchive_cryptor_build_hack(void) { - return 0; -} - #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto static int @@ -57,7 +44,7 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(ARCHIVE_CRYPTO_WIN) #ifdef _MSC_VER #pragma comment(lib, "Bcrypt.lib") #endif @@ -197,7 +184,7 @@ aes_ctr_release(archive_crypto_ctx *ctx) return 0; } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(ARCHIVE_CRYPTO_WIN) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) diff --git a/libarchive/archive_cryptor_private.h b/libarchive/archive_cryptor_private.h index 069eccd994bf..8a0139425d32 100644 --- a/libarchive/archive_cryptor_private.h +++ b/libarchive/archive_cryptor_private.h @@ -29,16 +29,6 @@ #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. - * - * But some compilers and linkers choke on empty object files, so - * define a public symbol that will always exist. This could - * be removed someday if this file gains another always-present - * symbol definition. - */ -int __libarchive_cryptor_build_hack(void); #ifdef __APPLE__ # include @@ -62,7 +52,7 @@ typedef struct { size_t encr_pos; } archive_crypto_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(ARCHIVE_CRYPTO_WIN) #include #define ARCHIVE_CRYPTOR_USE_CNG 1 diff --git a/libarchive/archive_digest.c b/libarchive/archive_digest.c index ab6db12bebe2..6282563e4e49 100644 --- a/libarchive/archive_digest.c +++ b/libarchive/archive_digest.c @@ -44,7 +44,7 @@ /* * Message digest functions for Windows platform. */ -#if defined(HAVE_BCRYPT_H) +#if defined(ARCHIVE_CRYPTO_WIN) /* * Initialize a Message digest. @@ -97,7 +97,7 @@ win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) return (ARCHIVE_OK); } -#endif /* defined(HAVE_BCRYPT_H) */ +#endif /* defined(ARCHIVE_CRYPTO_WIN) */ /* MD5 implementations */ @@ -211,7 +211,7 @@ static int __archive_md5init(archive_md5_ctx *ctx) { mbedtls_md5_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_md5_starts(ctx) == 0) #else if (mbedtls_md5_starts_ret(ctx) == 0) @@ -225,7 +225,7 @@ static int __archive_md5update(archive_md5_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_md5_update(ctx, indata, insize) == 0) #else if (mbedtls_md5_update_ret(ctx, indata, insize) == 0) @@ -238,7 +238,7 @@ __archive_md5update(archive_md5_ctx *ctx, const void *indata, static int __archive_md5final(archive_md5_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_md5_finish(ctx, md) == 0) { #else if (mbedtls_md5_finish_ret(ctx, md) == 0) { @@ -271,7 +271,11 @@ __archive_md5update(archive_md5_ctx *ctx, const void *indata, static int __archive_md5final(archive_md5_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 md5_digest(ctx, MD5_DIGEST_SIZE, md); +#else + md5_digest(ctx, md); +#endif return (ARCHIVE_OK); } @@ -394,7 +398,7 @@ static int __archive_ripemd160init(archive_rmd160_ctx *ctx) { mbedtls_ripemd160_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_ripemd160_starts(ctx) == 0) #else if (mbedtls_ripemd160_starts_ret(ctx) == 0) @@ -408,7 +412,7 @@ static int __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_ripemd160_update(ctx, indata, insize) == 0) #else if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0) @@ -421,7 +425,7 @@ __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_ripemd160_finish(ctx, md) == 0) { #else if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) { @@ -454,7 +458,11 @@ __archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata, static int __archive_ripemd160final(archive_rmd160_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md); +#else + ripemd160_digest(ctx, md); +#endif return (ARCHIVE_OK); } @@ -618,7 +626,7 @@ static int __archive_sha1init(archive_sha1_ctx *ctx) { mbedtls_sha1_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha1_starts(ctx) == 0) #else if (mbedtls_sha1_starts_ret(ctx) == 0) @@ -632,7 +640,7 @@ static int __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha1_update(ctx, indata, insize) == 0) #else if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0) @@ -645,7 +653,7 @@ __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha1_finish(ctx, md) == 0) { #else if (mbedtls_sha1_finish_ret(ctx, md) == 0) { @@ -678,7 +686,11 @@ __archive_sha1update(archive_sha1_ctx *ctx, const void *indata, static int __archive_sha1final(archive_sha1_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 sha1_digest(ctx, SHA1_DIGEST_SIZE, md); +#else + sha1_digest(ctx, md); +#endif return (ARCHIVE_OK); } @@ -894,7 +906,7 @@ static int __archive_sha256init(archive_sha256_ctx *ctx) { mbedtls_sha256_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha256_starts(ctx, 0) == 0) #else if (mbedtls_sha256_starts_ret(ctx, 0) == 0) @@ -908,7 +920,7 @@ static int __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha256_update(ctx, indata, insize) == 0) #else if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0) @@ -921,7 +933,7 @@ __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha256_finish(ctx, md) == 0) { #else if (mbedtls_sha256_finish_ret(ctx, md) == 0) { @@ -954,7 +966,11 @@ __archive_sha256update(archive_sha256_ctx *ctx, const void *indata, static int __archive_sha256final(archive_sha256_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 sha256_digest(ctx, SHA256_DIGEST_SIZE, md); +#else + sha256_digest(ctx, md); +#endif return (ARCHIVE_OK); } @@ -1142,7 +1158,7 @@ static int __archive_sha384init(archive_sha384_ctx *ctx) { mbedtls_sha512_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_starts(ctx, 1) == 0) #else if (mbedtls_sha512_starts_ret(ctx, 1) == 0) @@ -1156,7 +1172,7 @@ static int __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_update(ctx, indata, insize) == 0) #else if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) @@ -1169,7 +1185,7 @@ __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_finish(ctx, md) == 0) { #else if (mbedtls_sha512_finish_ret(ctx, md) == 0) { @@ -1202,7 +1218,11 @@ __archive_sha384update(archive_sha384_ctx *ctx, const void *indata, static int __archive_sha384final(archive_sha384_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 sha384_digest(ctx, SHA384_DIGEST_SIZE, md); +#else + sha384_digest(ctx, md); +#endif return (ARCHIVE_OK); } @@ -1414,7 +1434,7 @@ static int __archive_sha512init(archive_sha512_ctx *ctx) { mbedtls_sha512_init(ctx); -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_starts(ctx, 0) == 0) #else if (mbedtls_sha512_starts_ret(ctx, 0) == 0) @@ -1428,7 +1448,7 @@ static int __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, size_t insize) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_update(ctx, indata, insize) == 0) #else if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0) @@ -1441,7 +1461,7 @@ __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { -#if MBEDTLS_VERSION_NUMBER > 0x03000000 +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 if (mbedtls_sha512_finish(ctx, md) == 0) { #else if (mbedtls_sha512_finish_ret(ctx, md) == 0) { @@ -1474,7 +1494,11 @@ __archive_sha512update(archive_sha512_ctx *ctx, const void *indata, static int __archive_sha512final(archive_sha512_ctx *ctx, void *md) { +#if NETTLE_VERSION_MAJOR < 4 sha512_digest(ctx, SHA512_DIGEST_SIZE, md); +#else + sha512_digest(ctx, md); +#endif return (ARCHIVE_OK); } diff --git a/libarchive/archive_digest_private.h b/libarchive/archive_digest_private.h index 7db999450e70..032bd8c20427 100644 --- a/libarchive/archive_digest_private.h +++ b/libarchive/archive_digest_private.h @@ -135,6 +135,9 @@ #endif /* Nettle crypto headers */ +#if defined(HAVE_NETTLE_VERSION_H) +#include +#endif #if defined(ARCHIVE_CRYPTO_MD5_NETTLE) #include #endif @@ -145,8 +148,19 @@ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) +#if NETTLE_VERSION_MAJOR < 4 +#if defined(HAVE_NETTLE_SHA_H) #include #endif +#else +#if defined(HAVE_NETTLE_SHA1_H) +#include +#endif +#if defined(HAVE_NETTLE_SHA2_H) +#include +#endif +#endif +#endif /* OpenSSL crypto headers */ #if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\ @@ -165,7 +179,6 @@ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\ defined(ARCHIVE_CRYPTO_SHA512_WIN) -#if defined(HAVE_BCRYPT_H) #include #define ARCHIVE_CRYPTO_CNG 1 typedef struct { @@ -174,7 +187,6 @@ typedef struct { BCRYPT_HASH_HANDLE hHash; } Digest_CTX; #endif -#endif /* typedefs */ #if defined(ARCHIVE_CRYPTO_MD5_LIBC) diff --git a/libarchive/archive_entry.3 b/libarchive/archive_entry.3 index 0fc0f8cc2fe6..e7ac97b3903c 100644 --- a/libarchive/archive_entry.3 +++ b/libarchive/archive_entry.3 @@ -80,7 +80,7 @@ Allocate and return a blank object. .El .Ss Function groups -Due to high number of functions, the accessor functions can be found in +Due to the high number of functions, the accessor functions can be found in man pages grouped by the purpose. .Bl -tag -width ".Xr archive_entry_perms 3" .It Xr archive_entry_acl 3 diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 5b8351b4a190..758d76616959 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -121,15 +121,6 @@ static const wchar_t *ae_wcstofflags(const wchar_t *stringp, static const char *ae_strtofflags(const char *stringp, size_t length, unsigned long *setp, unsigned long *clrp); -#ifndef HAVE_WCSCPY -static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) -{ - wchar_t *dest = s1; - while ((*s1 = *s2) != L'\0') - ++s1, ++s2; - return dest; -} -#endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { @@ -469,7 +460,7 @@ archive_entry_gname_w(struct archive_entry *entry) } int -_archive_entry_gname_l(struct archive_entry *entry, +archive_entry_gname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc)); @@ -533,7 +524,7 @@ archive_entry_hardlink_is_set(struct archive_entry *entry) } int -_archive_entry_hardlink_l(struct archive_entry *entry, +archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_HARDLINK) == 0) { @@ -643,7 +634,7 @@ archive_entry_pathname_w(struct archive_entry *entry) } int -_archive_entry_pathname_l(struct archive_entry *entry, +archive_entry_pathname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_pathname, p, len, sc)); @@ -799,7 +790,7 @@ archive_entry_symlink_w(struct archive_entry *entry) } int -_archive_entry_symlink_l(struct archive_entry *entry, +archive_entry_symlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_SYMLINK) == 0) { @@ -856,7 +847,7 @@ archive_entry_uname_w(struct archive_entry *entry) } int -_archive_entry_uname_l(struct archive_entry *entry, +archive_entry_uname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_uname, p, len, sc)); @@ -974,7 +965,7 @@ archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) } int -_archive_entry_copy_gname_l(struct archive_entry *entry, +archive_entry_copy_gname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc)); @@ -1075,7 +1066,7 @@ archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *targ } int -_archive_entry_copy_hardlink_l(struct archive_entry *entry, +archive_entry_copy_hardlink_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; @@ -1225,7 +1216,7 @@ archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) } int -_archive_entry_copy_link_l(struct archive_entry *entry, +archive_entry_copy_link_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; @@ -1306,7 +1297,7 @@ archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name } int -_archive_entry_copy_pathname_l(struct archive_entry *entry, +archive_entry_copy_pathname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname, @@ -1460,7 +1451,7 @@ archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkn } int -_archive_entry_copy_symlink_l(struct archive_entry *entry, +archive_entry_copy_symlink_l(struct archive_entry *entry, const char *linkname, size_t len, struct archive_string_conv *sc) { int r; @@ -1544,7 +1535,7 @@ archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_enc } int -_archive_entry_copy_uname_l(struct archive_entry *entry, +archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_uname, @@ -1748,7 +1739,7 @@ archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len, } char * -_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len, +archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len, int flags, struct archive_string_conv *sc) { return (archive_acl_to_text_l(&entry->acl, len, flags, sc)); @@ -1772,7 +1763,7 @@ archive_entry_acl_from_text(struct archive_entry *entry, } int -_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text, +archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text, int type, struct archive_string_conv *sc) { return (archive_acl_from_text_l(&entry->acl, text, type, sc)); @@ -1825,7 +1816,7 @@ archive_entry_acl_text(struct archive_entry *entry, int flags) /* Deprecated */ int -_archive_entry_acl_text_l(struct archive_entry *entry, int flags, +archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { free(entry->acl.acl_text); diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 9c859a44e340..50e10a1e33a2 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -28,7 +28,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3008009 +#define ARCHIVE_VERSION_NUMBER 3008010 /* * Note: archive_entry.h is for use outside of libarchive; the diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3 index 4d0d8b50ed07..8d6fc4f7f7d9 100644 --- a/libarchive/archive_entry_acl.3 +++ b/libarchive/archive_entry_acl.3 @@ -161,11 +161,11 @@ Directories have an additional ACL .Pq Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT , 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 +An NFSv4 ACL consists of multiple individual entries called Access Control Entries (ACEs). .Pp -There are four possible types of a NFSv4 ACE: -.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYE_ALLOW" +There are four possible types of an NFSv4 ACE: +.Bl -hang -offset indent -compact -width "ARCHIVE_ENTRY_ACL_TYPE_ALLOW" .It Dv ARCHIVE_ENTRY_ACL_TYPE_ALLOW Allow principal to perform actions requiring given permissions. .It Dv ARCHIVE_ENTRY_ACL_TYPE_DENY @@ -338,13 +338,13 @@ character are skipped. .Pp .Fn archive_entry_acl_next -return the next entry of the ACL list. -This functions may only be called after +returns the next entry of the ACL list. +This function may only be called after .Fn archive_entry_acl_reset has indicated the presence of extended ACL entries. .Pp .Fn archive_entry_acl_reset -prepare reading the list of ACL entries with +prepares reading the list of ACL entries with .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 @@ -380,7 +380,7 @@ Output POSIX.1e default ACLs. Prefix each default ACL entry with the word .Dq default: . .It Dv ARCHIVE_ENTRY_ACL_STYLE_SOLARIS -The mask and other ACLs don not contain a double colon. +The mask and other ACLs do not contain a double colon. .El .Pp The following flags are effective only on NFSv4 ACL: @@ -399,7 +399,7 @@ Separate ACL entries with comma instead of newline. .El .Pp If the archive entry contains NFSv4 ACLs, all types of NFSv4 ACLs are returned. -It the entry contains POSIX.1e ACLs and none of the flags +If the entry contains POSIX.1e ACLs and none of the flags .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS or .Dv ARCHIVE_ENTRY_ACL_TYPE_DEFAULT @@ -408,7 +408,7 @@ are prefixed with .Dq default: . .Pp .Fn archive_entry_acl_types -get ACL entry types contained in an archive entry's ACL. +gets 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. @@ -416,7 +416,7 @@ an ACL already contains POSIX.1e or NFSv4 ACL entries. .Fn archive_entry_acl_count and .Fn archive_entry_acl_reset -returns the number of ACL entries that match the given type mask. +return the number of ACL entries that match the given type mask. For POSIX.1e ACLS if the type mask includes .Dv ARCHIVE_ENTRY_ACL_TYPE_ACCESS and at least one extended ACL entry exists, the three classic Unix diff --git a/libarchive/archive_entry_copy_bhfi.c b/libarchive/archive_entry_copy_bhfi.c index 0708838caee6..aa371c12292a 100644 --- a/libarchive/archive_entry_copy_bhfi.c +++ b/libarchive/archive_entry_copy_bhfi.c @@ -37,11 +37,11 @@ archive_entry_copy_bhfi(struct archive_entry *entry, int64_t secs; uint32_t nsecs; - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs); archive_entry_set_atime(entry, secs, nsecs); - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs); archive_entry_set_mtime(entry, secs, nsecs); - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftCreationTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftCreationTime), &secs, &nsecs); archive_entry_set_birthtime(entry, secs, nsecs); archive_entry_set_ctime(entry, secs, nsecs); archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber); diff --git a/libarchive/archive_entry_linkify.3 b/libarchive/archive_entry_linkify.3 index 8c19fddb6ea1..4fef223e96bb 100644 --- a/libarchive/archive_entry_linkify.3 +++ b/libarchive/archive_entry_linkify.3 @@ -69,15 +69,15 @@ Store the body the last time an inode is seen (new cpio). The .Nm functions help by providing a unified interface and handling the complexity -behind the scene. +behind the scenes. .Pp The .Nm functions assume that .Vt archive_entry instances have valid nlinks, inode and device values. -The inode and device value is used to match entries. -The nlinks value is used to determined if all references have been found and +The inode and device values are used to match entries. +The nlinks value is used to determine if all references have been found and if the internal references can be recycled. .Pp The @@ -100,7 +100,7 @@ The function is the core of .Nm . The -.Fn entry +.Fa entry argument points to the .Vt archive_entry that should be written. diff --git a/libarchive/archive_entry_locale.h b/libarchive/archive_entry_locale.h index e31a0f2ff1fa..570ee90f78fc 100644 --- a/libarchive/archive_entry_locale.h +++ b/libarchive/archive_entry_locale.h @@ -44,47 +44,47 @@ struct archive_string_conv; * Return -1 if the string conversion failed. */ -#define archive_entry_gname_l _archive_entry_gname_l -int _archive_entry_gname_l(struct archive_entry *, +#define archive_entry_gname_l __archive_entry_gname_l +int archive_entry_gname_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); -#define archive_entry_hardlink_l _archive_entry_hardlink_l -int _archive_entry_hardlink_l(struct archive_entry *, +#define archive_entry_hardlink_l __archive_entry_hardlink_l +int archive_entry_hardlink_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); -#define archive_entry_pathname_l _archive_entry_pathname_l -int _archive_entry_pathname_l(struct archive_entry *, +#define archive_entry_pathname_l __archive_entry_pathname_l +int archive_entry_pathname_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); -#define archive_entry_symlink_l _archive_entry_symlink_l -int _archive_entry_symlink_l(struct archive_entry *, +#define archive_entry_symlink_l __archive_entry_symlink_l +int archive_entry_symlink_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); -#define archive_entry_uname_l _archive_entry_uname_l -int _archive_entry_uname_l(struct archive_entry *, +#define archive_entry_uname_l __archive_entry_uname_l +int archive_entry_uname_l(struct archive_entry *, const char **, size_t *, struct archive_string_conv *); -#define archive_entry_acl_text_l _archive_entry_acl_text_l -int _archive_entry_acl_text_l(struct archive_entry *, int, -const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED; -#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l -char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int, +#define archive_entry_acl_text_l __archive_entry_acl_text_l +int archive_entry_acl_text_l(struct archive_entry *, int, + const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED; +#define archive_entry_acl_to_text_l __archive_entry_acl_to_text_l +char *archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int, struct archive_string_conv *); -#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l -int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text, +#define archive_entry_acl_from_text_l __archive_entry_acl_from_text_l +int archive_entry_acl_from_text_l(struct archive_entry *, const char* text, int type, struct archive_string_conv *); -#define archive_entry_copy_gname_l _archive_entry_copy_gname_l -int _archive_entry_copy_gname_l(struct archive_entry *, +#define archive_entry_copy_gname_l __archive_entry_copy_gname_l +int archive_entry_copy_gname_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); -#define archive_entry_copy_hardlink_l _archive_entry_copy_hardlink_l -int _archive_entry_copy_hardlink_l(struct archive_entry *, +#define archive_entry_copy_hardlink_l __archive_entry_copy_hardlink_l +int archive_entry_copy_hardlink_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); -#define archive_entry_copy_link_l _archive_entry_copy_link_l -int _archive_entry_copy_link_l(struct archive_entry *, +#define archive_entry_copy_link_l __archive_entry_copy_link_l +int archive_entry_copy_link_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); -#define archive_entry_copy_pathname_l _archive_entry_copy_pathname_l -int _archive_entry_copy_pathname_l(struct archive_entry *, +#define archive_entry_copy_pathname_l __archive_entry_copy_pathname_l +int archive_entry_copy_pathname_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); -#define archive_entry_copy_symlink_l _archive_entry_copy_symlink_l -int _archive_entry_copy_symlink_l(struct archive_entry *, +#define archive_entry_copy_symlink_l __archive_entry_copy_symlink_l +int archive_entry_copy_symlink_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); -#define archive_entry_copy_uname_l _archive_entry_copy_uname_l -int _archive_entry_copy_uname_l(struct archive_entry *, +#define archive_entry_copy_uname_l __archive_entry_copy_uname_l +int archive_entry_copy_uname_l(struct archive_entry *, const char *, size_t, struct archive_string_conv *); #endif /* !ARCHIVE_ENTRY_LOCALE_H_INCLUDED */ diff --git a/libarchive/archive_entry_perms.3 b/libarchive/archive_entry_perms.3 index 4bfbfc3c7815..ca3d018b7d8f 100644 --- a/libarchive/archive_entry_perms.3 +++ b/libarchive/archive_entry_perms.3 @@ -205,6 +205,6 @@ The platform types .Vt uid_t and .Vt gid_t -are often 16 or 32 bit wide. +are often 16 or 32 bits wide. In this case it is possible that the ids can not be correctly restored from archives and get truncated. diff --git a/libarchive/archive_entry_stat.3 b/libarchive/archive_entry_stat.3 index 2f4a1920eb64..ccd3af2ca68d 100644 --- a/libarchive/archive_entry_stat.3 +++ b/libarchive/archive_entry_stat.3 @@ -251,18 +251,18 @@ is the preferred interface. Block and character devices are characterised either using a device number or a pair of major and minor number. The combined device number can be obtained with -.Fn archive_device_rdev +.Fn archive_entry_rdev and set with -.Fn archive_device_set_rdev . +.Fn archive_entry_set_rdev . The major and minor numbers are accessed by -.Fn archive_device_rdevmajor , -.Fn archive_device_rdevminor -.Fn archive_device_set_rdevmajor +.Fn archive_entry_rdevmajor , +.Fn archive_entry_rdevminor +.Fn archive_entry_set_rdevmajor and -.Fn archive_device_set_rdevminor . +.Fn archive_entry_set_rdevminor . .Pp The process of splitting the combined device number into major and -minor number and the reverse process of combing them differs between +minor number and the reverse process of combining them differs between platforms. Some archive formats use the combined form, while other formats use the split form. diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c index e1e8f05ff0d3..9b38465698c1 100644 --- a/libarchive/archive_hmac.c +++ b/libarchive/archive_hmac.c @@ -31,20 +31,6 @@ #include "archive.h" #include "archive_hmac_private.h" -/* - * On systems that do not support any recognized crypto libraries, - * the archive_hmac.c file is expected to define no usable symbols. - * - * But some compilers and linkers choke on empty object files, so - * define a public symbol that will always exist. This could - * be removed someday if this file gains another always-present - * symbol definition. - */ -int __libarchive_hmac_build_hack(void) { - return 0; -} - - #ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto static int @@ -74,7 +60,7 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) memset(ctx, 0, sizeof(*ctx)); } -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(ARCHIVE_CRYPTO_WIN) #ifndef BCRYPT_HASH_REUSABLE_FLAG # define BCRYPT_HASH_REUSABLE_FLAG 0x00000020 diff --git a/libarchive/archive_hmac_private.h b/libarchive/archive_hmac_private.h index af00beadb588..e8f5e5293e6b 100644 --- a/libarchive/archive_hmac_private.h +++ b/libarchive/archive_hmac_private.h @@ -29,16 +29,6 @@ #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. - * - * But some compilers and linkers choke on empty object files, so - * define a public symbol that will always exist. This could - * be removed someday if this file gains another always-present - * symbol definition. - */ -int __libarchive_hmac_build_hack(void); #ifdef __APPLE__ # include @@ -52,7 +42,7 @@ int __libarchive_hmac_build_hack(void); typedef CCHmacContext archive_hmac_sha1_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(ARCHIVE_CRYPTO_WIN) #include typedef struct { diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index ecd470bd15dd..832e2a7120f9 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -269,6 +269,7 @@ archive_match_free(struct archive *_a) free(a->inclusion_gids.ids); match_list_free(&(a->inclusion_unames)); match_list_free(&(a->inclusion_gnames)); + archive_string_free(&a->archive.error_string); free(a); return (ARCHIVE_OK); } @@ -1172,8 +1173,8 @@ set_timefilter_find_data(struct archive_match *a, int timetype, time_t ctime_sec, mtime_sec; uint32_t ctime_ns, mtime_ns; - ntfs_to_unix(FILETIME_to_ntfs(ftLastWriteTime), &mtime_sec, &mtime_ns); - ntfs_to_unix(FILETIME_to_ntfs(ftCreationTime), &ctime_sec, &ctime_ns); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(ftLastWriteTime), &mtime_sec, &mtime_ns); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(ftCreationTime), &ctime_sec, &ctime_ns); return set_timefilter(a, timetype, mtime_sec, mtime_ns, ctime_sec, ctime_ns); } diff --git a/libarchive/archive_pack_dev.c b/libarchive/archive_pack_dev.c index e29351292895..a08dcfaa1edc 100644 --- a/libarchive/archive_pack_dev.c +++ b/libarchive/archive_pack_dev.c @@ -101,8 +101,7 @@ static const char tooManyFields[] = "too many fields for format"; #define apd_makedev(maj, min) makedev((maj), (min)) #endif -/* exported */ -dev_t +static dev_t pack_native(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; @@ -317,7 +316,7 @@ compare_format(const void *key, const void *element) pack_t * -pack_find(const char *name) +__archive_pack_find(const char *name) { const struct format *format; diff --git a/libarchive/archive_pack_dev.h b/libarchive/archive_pack_dev.h index 953af21da3ee..56c13c79b994 100644 --- a/libarchive/archive_pack_dev.h +++ b/libarchive/archive_pack_dev.h @@ -36,8 +36,7 @@ typedef dev_t pack_t(int, unsigned long [], const char **); -pack_t *pack_find(const char *); -pack_t pack_native; +pack_t *__archive_pack_find(const char *); #define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) #define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index 0f068639b9cc..ca1bad94484b 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -72,23 +72,25 @@ * Generally checked via `__archive_check_magic()`. */ -/* Newly created archive object, not yet opened */ -#define ARCHIVE_STATE_NEW 1U +/* Newly created archive object, not yet opened. */ +#define ARCHIVE_STATE_NEW 0x01U +/* Archive is opened. */ +#define ARCHIVE_STATE_OPEN 0x02U /* Archive is ready to read a header. */ -#define ARCHIVE_STATE_HEADER 2U +#define ARCHIVE_STATE_HEADER 0x04U /* A header has been read: client can ask for data * or they can ask for the next header and * we'll automatically skip the remaining data. */ -#define ARCHIVE_STATE_DATA 4U +#define ARCHIVE_STATE_DATA 0x08U /* Similar to STATE_DATA but after a FAILED header: * the client may not read data, but they may ask * for the next header and we'll recover. */ -#define ARCHIVE_STATE_DATA_RECOVERY 8U +#define ARCHIVE_STATE_DATA_RECOVERY 0x10U /* End-of-archive has been reached. Client can only * close or free the archive. */ -#define ARCHIVE_STATE_EOF 0x10U +#define ARCHIVE_STATE_EOF 0x20U /* Archive is closed; client is only allowed to free the archive. */ -#define ARCHIVE_STATE_CLOSED 0x20U +#define ARCHIVE_STATE_CLOSED 0x40U /* Archive is in a FATAL error state: a close request * is permitted but ignored. */ #define ARCHIVE_STATE_FATAL 0x8000U @@ -206,4 +208,6 @@ void __archive_reset_read_data(struct archive *); # define ARCHIVE_LITERAL_ULL(x) x##ull #endif +unsigned long __archive_crc32(unsigned long crc, const void *p, size_t len); + #endif diff --git a/libarchive/archive_random.c b/libarchive/archive_random.c index 28cba00bd3cd..998a00014a0e 100644 --- a/libarchive/archive_random.c +++ b/libarchive/archive_random.c @@ -83,12 +83,16 @@ archive_random(void *buf, size_t nbytes) BCRYPT_ALG_HANDLE hAlg; status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0); - if (!BCRYPT_SUCCESS(status)) + if (!BCRYPT_SUCCESS(status)) { + la_dosmaperr(GetLastError()); return ARCHIVE_FAILED; + } status = BCryptGenRandom(hAlg, buf, (ULONG)nbytes, 0); BCryptCloseAlgorithmProvider(hAlg, 0); - if (!BCRYPT_SUCCESS(status)) + if (!BCRYPT_SUCCESS(status)) { + la_dosmaperr(GetLastError()); return ARCHIVE_FAILED; + } return ARCHIVE_OK; #elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__)) diff --git a/libarchive/archive_rb.c b/libarchive/archive_rb.c index cf58ac3354d0..d0f0128f1c38 100644 --- a/libarchive/archive_rb.c +++ b/libarchive/archive_rb.c @@ -152,7 +152,7 @@ __archive_rb_tree_find_node_leq(struct archive_rb_tree *rbt, const void *key) return last; } - + int __archive_rb_tree_insert_node(struct archive_rb_tree *rbt, struct archive_rb_node *self) @@ -218,7 +218,7 @@ __archive_rb_tree_insert_node(struct archive_rb_tree *rbt, return T; } - + /* * Swap the location and colors of 'self' and its child @ which. The child * can not be a sentinel node. This is our rotation function. However, @@ -268,7 +268,7 @@ __archive_rb_tree_reparent_nodes( } } - + static void __archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt, struct archive_rb_node *self) @@ -343,7 +343,7 @@ __archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt, */ RB_MARK_BLACK(rbt->rbt_root); } - + static void __archive_rb_tree_prune_node(struct archive_rb_tree *rbt, struct archive_rb_node *self, int rebalance) @@ -363,7 +363,7 @@ __archive_rb_tree_prune_node(struct archive_rb_tree *rbt, if (rebalance) __archive_rb_tree_removal_rebalance(rbt, father, which); } - + /* * When deleting an interior node */ diff --git a/libarchive/archive_read.3 b/libarchive/archive_read.3 index c81c98be2759..ff3328286a05 100644 --- a/libarchive/archive_read.3 +++ b/libarchive/archive_read.3 @@ -170,7 +170,7 @@ list_archive(const char *name) struct archive *a; struct archive_entry *entry; - mydata = malloc(sizeof(struct mydata)); + mydata = malloc(sizeof(*mydata)); a = archive_read_new(); mydata->name = name; archive_read_support_filter_all(a); diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index e844e42d1ac9..2f006fb96da9 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -58,6 +58,7 @@ #define minimum(a, b) (a < b ? a : b) +static int bid_format(struct archive_read *, int); static int choose_filters(struct archive_read *); static int choose_format(struct archive_read *); static int close_filters(struct archive_read *); @@ -175,100 +176,6 @@ archive_read_open2(struct archive *a, void *client_data, return archive_read_open1(a); } -static ssize_t -client_read_proxy(struct archive_read_filter *f, const void **buff) -{ - ssize_t r; - r = (f->archive->client.reader)(&f->archive->archive, - f->data, buff); - return (r); -} - -static int64_t -client_skip_proxy(struct archive_read_filter *f, int64_t request) -{ - if (request < 0) - __archive_errx(1, "Negative skip requested"); - if (request == 0) - return 0; - - if (f->archive->client.skipper != NULL) { - int64_t total = 0; - for (;;) { - int64_t get, ask = request; - get = (f->archive->client.skipper) - (&f->archive->archive, f->data, ask); - total += get; - if (get == 0 || get == request) - return (total); - if (get > request) - return ARCHIVE_FATAL; - request -= get; - } - } else if (f->archive->client.seeker != NULL - && request > 64 * 1024) { - /* If the client provided a seeker but not a skipper, - * we can use the seeker to skip forward. - * - * Note: This isn't always a good idea. The client - * skipper is allowed to skip by less than requested - * if it needs to maintain block alignment. The - * seeker is not allowed to play such games, so using - * the seeker here may be a performance loss compared - * to just reading and discarding. That's why we - * only do this for skips of over 64k. - */ - int64_t before = f->position; - int64_t after = (f->archive->client.seeker) - (&f->archive->archive, f->data, request, SEEK_CUR); - if (after != before + request) - return ARCHIVE_FATAL; - return after - before; - } - return 0; -} - -static int64_t -client_seek_proxy(struct archive_read_filter *f, int64_t offset, int whence) -{ - /* DO NOT use the skipper here! If we transparently handled - * forward seek here by using the skipper, that will break - * other libarchive code that assumes a successful forward - * seek means it can also seek backwards. - */ - if (f->archive->client.seeker == NULL) { - archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, - "Current client reader does not support seeking a device"); - return (ARCHIVE_FAILED); - } - return (f->archive->client.seeker)(&f->archive->archive, - f->data, offset, whence); -} - -static int -read_client_close_proxy(struct archive_read *a) -{ - int r = ARCHIVE_OK, r2; - unsigned int i; - - if (a->client.closer == NULL) - return (r); - for (i = 0; i < a->client.nodes; i++) - { - r2 = (a->client.closer) - ((struct archive *)a, a->client.dataset[i].data); - if (r > r2) - r = r2; - } - return (r); -} - -static int -client_close_proxy(struct archive_read_filter *f) -{ - return read_client_close_proxy(f->archive); -} - static int client_switch_proxy(struct archive_read_filter *f, unsigned int iindex) { @@ -276,8 +183,6 @@ client_switch_proxy(struct archive_read_filter *f, unsigned int iindex) int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; void *data2; - while (f->upstream != NULL) - f = f->upstream; a = f->archive; /* Don't do anything if already in the specified data node */ @@ -303,9 +208,76 @@ client_switch_proxy(struct archive_read_filter *f, unsigned int iindex) r2 = (a->client.opener) ((struct archive *)a, f->data); } + f->client_total = f->client_avail = 0; return (r1 < r2) ? r1 : r2; } +/* + * Reads data from client. + * + * Automatically switches clients if end of file of a client is reached. + * This simplifies handling of multi volume archives for readers. + */ +static ssize_t +client_read_proxy(struct archive_read_filter *f, const void **buff) +{ + ssize_t bytes_read = 0; + + for (;;) { + bytes_read = (f->archive->client.reader)(&f->archive->archive, + f->data, buff); + if (bytes_read == 0) { + unsigned int cursor = f->archive->client.cursor; + + /* Continue with next client object if available. */ + if (cursor != f->archive->client.nodes - 1 && + client_switch_proxy(f, cursor + 1) == ARCHIVE_OK) + continue; + } + break; + } + + return (bytes_read); +} + +static int64_t +client_skip_proxy(struct archive_read_filter *f, int64_t request) +{ + return (f->archive->client.skipper)(&f->archive->archive, + f->data, request); +} + +static int64_t +client_seek_proxy(struct archive_read_filter *f, int64_t offset, int whence) +{ + /* DO NOT use the skipper here! If we transparently handled + * forward seek here by using the skipper, that will break + * other libarchive code that assumes a successful forward + * seek means it can also seek backwards. + */ + return (f->archive->client.seeker)(&f->archive->archive, + f->data, offset, whence); +} + +static int +client_close_proxy(struct archive_read_filter *f) +{ + struct archive_read *a = f->archive; + int r = ARCHIVE_OK, r2; + unsigned int i; + + if (a->client.closer == NULL) + return (r); + for (i = 0; i < a->client.nodes; i++) + { + r2 = (a->client.closer) + ((struct archive *)a, a->client.dataset[i].data); + if (r > r2) + r = r2; + } + return (r); +} + int archive_read_set_open_callback(struct archive *_a, archive_open_callback *client_opener) @@ -496,19 +468,13 @@ archive_read_open1(struct archive *_a) return (ARCHIVE_FATAL); } - /* Open data source. */ - if (a->client.opener != NULL) { - e = (a->client.opener)(&a->archive, a->client.dataset[0].data); - if (e != 0) { - /* If the open failed, call the closer to clean up. */ - read_client_close_proxy(a); - return (e); - } - } + a->archive.state = ARCHIVE_STATE_OPEN; f = calloc(1, sizeof(*f)); - if (f == NULL) + if (f == NULL) { + a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); + } f->bidder = NULL; f->upstream = NULL; f->archive = a; @@ -516,8 +482,20 @@ archive_read_open1(struct archive *_a) f->vtable = &none_reader_vtable; f->name = "none"; f->code = ARCHIVE_FILTER_NONE; - f->can_skip = 1; - f->can_seek = 1; + f->can_skip = a->client.skipper != NULL; + f->can_seek = a->client.seeker != NULL; + + /* Open data source. */ + if (a->client.opener != NULL) { + e = (a->client.opener)(&a->archive, a->client.dataset[0].data); + if (e != ARCHIVE_OK) { + /* If the open failed, call the closer to clean up. */ + client_close_proxy(f); + free(f); + a->archive.state = ARCHIVE_STATE_FATAL; + return (e); + } + } a->client.dataset[0].begin_position = 0; if (!a->filter || !a->bypass_filter_bidding) @@ -549,11 +527,22 @@ archive_read_open1(struct archive *_a) } a->format = &(a->formats[slot]); } + else if (a->format->bid != NULL) + { + if (bid_format(a, -1) < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Registered format does not match"); + close_filters(a); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + } a->archive.state = ARCHIVE_STATE_HEADER; /* Ensure libarchive starts from the first node in a multivolume set */ - client_switch_proxy(a->filter, 0); + client_switch_proxy(f, 0); return (e); } @@ -714,7 +703,6 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) __archive_reset_read_data(&a->archive); - a->data_start_node = a->client.cursor; /* EOF always wins; otherwise return the worst error. */ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; } @@ -730,6 +718,21 @@ _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) return ret; } +static int +bid_format(struct archive_read *a, int best_bid) +{ + int bid; + + bid = (a->format->bid)(a, best_bid); + if (bid == ARCHIVE_FATAL) + return (ARCHIVE_FATAL); + if (a->filter->position != 0 && + __archive_read_seek(a, 0, SEEK_SET) < 0) + return (ARCHIVE_FATAL); + + return (bid); +} + /* * Allow each registered format to bid on whether it wants to handle * the next entry. Return index of winning bidder. @@ -750,11 +753,9 @@ choose_format(struct archive_read *a) a->format = &(a->formats[0]); for (i = 0; i < slots; i++, a->format++) { if (a->format->bid) { - bid = (a->format->bid)(a, best_bid); + bid = bid_format(a, best_bid); if (bid == ARCHIVE_FATAL) return (ARCHIVE_FATAL); - if (a->filter->position != 0) - __archive_read_seek(a, 0, SEEK_SET); if ((bid > best_bid) || (best_bid_slot < 0)) { best_bid = bid; best_bid_slot = i; @@ -1290,7 +1291,6 @@ __archive_read_register_format(struct archive_read *a, int __archive_read_register_bidder(struct archive_read *a, void *bidder_data, - const char *name, const struct archive_read_filter_bidder_vtable *vtable) { struct archive_read_filter_bidder *bidder; @@ -1307,7 +1307,6 @@ __archive_read_register_bidder(struct archive_read *a, memset(a->bidders + i, 0, sizeof(a->bidders[0])); bidder = (a->bidders + i); bidder->data = bidder_data; - bidder->name = name; bidder->vtable = vtable; if (bidder->vtable->bid == NULL || bidder->vtable->init == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, @@ -1358,6 +1357,38 @@ __archive_read_register_bidder(struct archive_read *a, * code that has uncertain look-ahead needs. */ +/* + * Reads data into client buffer. + * + * In case of error or end of file, filter flags are set. + * Client buffer related fields are updated. + */ +static ssize_t +read_client_data(struct archive_read_filter *f) +{ + ssize_t bytes_read; + + if (f->end_of_file) + return (0); + + bytes_read = (f->vtable->read)(f, &f->client_buff); + if (bytes_read < 0) { + f->client_total = f->client_avail = 0; + f->client_next = f->client_buff = NULL; + f->fatal = 1; + } else if (bytes_read == 0) { + f->client_total = f->client_avail = 0; + f->client_next = f->client_buff = NULL; + f->end_of_file = 1; + } else { + f->client_total = bytes_read; + f->client_avail = f->client_total; + f->client_next = f->client_buff; + } + + return (bytes_read); +} + /* * Looks ahead in the input stream: * * If 'avail' pointer is provided, that returns number of bytes available @@ -1383,34 +1414,24 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) const void * __archive_read_filter_ahead(struct archive_read_filter *f, - size_t min, ssize_t *avail) + size_t request, ssize_t *avail) { ssize_t bytes_read; - size_t tocopy; + size_t min, tocopy; if (f->fatal) { if (avail) *avail = ARCHIVE_FATAL; return (NULL); } + /* request == 0 is perfectly well-defined.*/ + min = request == 0 ? 1 : request; /* * Keep pulling more data until we can satisfy the request. */ for (;;) { - /* - * If we can satisfy from the copy buffer (and the - * copy buffer isn't empty), we're done. In particular, - * note that min == 0 is a perfectly well-defined - * request. - */ - if (f->avail >= min && f->avail > 0) { - if (avail != NULL) - *avail = f->avail; - return (f->next); - } - /* * We can satisfy directly from client buffer if everything * currently in the copy buffer is still in the client buffer. @@ -1429,55 +1450,36 @@ __archive_read_filter_ahead(struct archive_read_filter *f, return (f->client_next); } - /* Move data forward in copy buffer if necessary. */ - if (f->next > f->buffer && - min > f->buffer_size - (f->next - f->buffer)) { - if (f->avail > 0) - memmove(f->buffer, f->next, - f->avail); - f->next = f->buffer; + /* If we can satisfy from the copy buffer, we're done. */ + if (f->avail >= min) { + if (avail != NULL) + *avail = f->avail; + return (f->next); } /* If we've used up the client data, get more. */ if (f->client_avail <= 0) { - if (f->end_of_file) { - if (avail != NULL) - *avail = f->avail; - return (NULL); - } - bytes_read = (f->vtable->read)(f, - &f->client_buff); - if (bytes_read < 0) { /* Read error. */ - f->client_total = f->client_avail = 0; - f->client_next = - f->client_buff = NULL; - f->fatal = 1; + bytes_read = read_client_data(f); + if (bytes_read < 0) { + /* Read error. */ if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_read == 0) { - /* Check for another client object first */ - if (f->archive->client.cursor != - f->archive->client.nodes - 1) { - if (client_switch_proxy(f, - f->archive->client.cursor + 1) - == ARCHIVE_OK) - continue; - } - /* Premature end-of-file. */ - f->client_total = f->client_avail = 0; - f->client_next = - f->client_buff = NULL; - f->end_of_file = 1; - /* Return whatever we do have. */ + static const char *empty = ""; + const char *eof; + + /* Premature end-of-file. + * Return whatever we do have. */ if (avail != NULL) *avail = f->avail; - return (NULL); + if (request == 0) + eof = f->avail == 0 ? empty : f->next; + else + eof = NULL; + return (eof); } - f->client_total = bytes_read; - f->client_avail = f->client_total; - f->client_next = f->client_buff; } else { /* * We can't satisfy the request from the copy @@ -1523,19 +1525,23 @@ __archive_read_filter_ahead(struct archive_read_filter *f, } /* Move data into newly-enlarged buffer. */ if (f->avail > 0) - memmove(p, f->next, f->avail); + memcpy(p, f->next, f->avail); free(f->buffer); f->next = f->buffer = p; f->buffer_size = s; + } else if (f->next > f->buffer && + min > f->buffer_size - (f->next - f->buffer)) { + /* Move data forward in copy buffer + * if necessary. */ + if (f->avail > 0) + memmove(f->buffer, f->next, + f->avail); + f->next = f->buffer; } /* We can add client data to copy buffer. */ - /* First estimate: copy to fill rest of buffer. */ - tocopy = (f->buffer + f->buffer_size) - - (f->next + f->avail); /* Don't waste time buffering more than we need to. */ - if (tocopy + f->avail > min) - tocopy = min - f->avail; + tocopy = min - f->avail; /* Don't copy more than is available. */ if (tocopy > f->client_avail) tocopy = f->client_avail; @@ -1593,16 +1599,14 @@ __archive_read_filter_consume(struct archive_read_filter *f, static int64_t advance_file_pointer(struct archive_read_filter *f, int64_t request) { - int64_t bytes_skipped, total_bytes_skipped = 0; - ssize_t bytes_read; - size_t min; + int64_t total_bytes_skipped = 0; if (f->fatal) return (-1); /* Use up the copy buffer first. */ if (f->avail > 0) { - min = (size_t)minimum(request, (int64_t)f->avail); + size_t min = (size_t)minimum(request, (int64_t)f->avail); f->next += min; f->avail -= min; request -= min; @@ -1612,7 +1616,7 @@ advance_file_pointer(struct archive_read_filter *f, int64_t request) /* Then use up the client buffer. */ if (f->client_avail > 0) { - min = (size_t)minimum(request, (int64_t)f->client_avail); + size_t min = (size_t)minimum(request, (int64_t)f->client_avail); f->client_next += min; f->client_avail -= min; request -= min; @@ -1624,12 +1628,41 @@ advance_file_pointer(struct archive_read_filter *f, int64_t request) /* If there's an optimized skip function, use it. */ if (f->can_skip != 0) { - bytes_skipped = client_skip_proxy(f, request); - if (bytes_skipped < 0) { /* error */ - f->fatal = 1; - return (bytes_skipped); + while (request > 0) { + int64_t get = client_skip_proxy(f, request); + if (get < 0 || get > request) { + f->fatal = 1; + return (ARCHIVE_FATAL); + } + if (get == 0) + break; + f->position += get; + total_bytes_skipped += get; + request -= get; } - f->position += bytes_skipped; + if (request == 0) + return (total_bytes_skipped); + } else if (f->can_seek != 0 && request > 64 * 1024) { + /* If the client provided a seeker but not a skipper, + * we can use the seeker to skip forward. + * + * Note: This isn't always a good idea. The client + * skipper is allowed to skip by less than requested + * if it needs to maintain block alignment. The + * seeker is not allowed to play such games, so using + * the seeker here may be a performance loss compared + * to just reading and discarding. That's why we + * only do this for skips of over 64k. + */ + int64_t bytes_skipped; + int64_t before = f->position; + int64_t after = __archive_read_filter_seek(f, + request, SEEK_CUR); + if (after != before + request) { + f->fatal = 1; + return (ARCHIVE_FATAL); + } + bytes_skipped = after - before; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; if (request == 0) @@ -1638,31 +1671,16 @@ advance_file_pointer(struct archive_read_filter *f, int64_t request) /* Use ordinary reads as necessary to complete the request. */ for (;;) { - bytes_read = (f->vtable->read)(f, &f->client_buff); - if (bytes_read < 0) { - f->client_buff = NULL; - f->fatal = 1; + ssize_t bytes_read = read_client_data(f); + if (bytes_read < 0) return (bytes_read); - } - if (bytes_read == 0) { - if (f->archive->client.cursor != - f->archive->client.nodes - 1) { - if (client_switch_proxy(f, - f->archive->client.cursor + 1) - == ARCHIVE_OK) - continue; - } - f->client_buff = NULL; - f->end_of_file = 1; + if (bytes_read == 0) return (total_bytes_skipped); - } if (bytes_read >= request) { - f->client_next = - ((const char *)f->client_buff) + request; - f->client_avail = (size_t)(bytes_read - request); - f->client_total = bytes_read; + f->client_next += request; + f->client_avail -= request; total_bytes_skipped += request; f->position += request; return (total_bytes_skipped); @@ -1689,7 +1707,8 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, { struct archive_read_client *client; int64_t r; - unsigned int cursor; + unsigned int cursor, old_cursor; + int sought = 0; if (f->closed || f->fatal) return (ARCHIVE_FATAL); @@ -1697,6 +1716,8 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, return (ARCHIVE_FAILED); client = &(f->archive->client); + old_cursor = client->cursor; + switch (whence) { case SEEK_CUR: /* Adjust the offset and use SEEK_SET instead */ @@ -1709,7 +1730,7 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, if (client->dataset[cursor].begin_position < 0 || client->dataset[cursor].total_size < 0 || client->dataset[cursor].begin_position + - client->dataset[cursor].total_size - 1 > offset || + client->dataset[cursor].total_size > offset || cursor + 1 >= client->nodes) break; r = client->dataset[cursor].begin_position + @@ -1719,12 +1740,13 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, while (1) { r = client_switch_proxy(f, cursor); if (r != ARCHIVE_OK) - return r; + goto clear_buffer; if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0) - return r; + goto clear_buffer; + sought = 1; client->dataset[cursor].total_size = r; if (client->dataset[cursor].begin_position + - client->dataset[cursor].total_size - 1 > offset || + client->dataset[cursor].total_size > offset || cursor + 1 >= client->nodes) break; r = client->dataset[cursor].begin_position + @@ -1733,10 +1755,13 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, } offset -= client->dataset[cursor].begin_position; if (offset < 0 - || offset > client->dataset[cursor].total_size) - return ARCHIVE_FATAL; + || offset > client->dataset[cursor].total_size) { + r = ARCHIVE_FATAL; + goto clear_buffer; + } if ((r = client_seek_proxy(f, offset, SEEK_SET)) < 0) - return r; + goto clear_buffer; + sought = 1; break; case SEEK_END: @@ -1753,9 +1778,10 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, while (1) { r = client_switch_proxy(f, cursor); if (r != ARCHIVE_OK) - return r; + goto clear_buffer; if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0) - return r; + goto clear_buffer; + sought = 1; client->dataset[cursor].total_size = r; r = client->dataset[cursor].begin_position + client->dataset[cursor].total_size; @@ -1776,10 +1802,11 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, } offset = (r + offset) - client->dataset[cursor].begin_position; if ((r = client_switch_proxy(f, cursor)) != ARCHIVE_OK) - return r; + goto clear_buffer; r = client_seek_proxy(f, offset, SEEK_SET); if (r < ARCHIVE_OK) - return r; + goto clear_buffer; + sought = 1; break; default: @@ -1787,28 +1814,36 @@ __archive_read_filter_seek(struct archive_read_filter *f, int64_t offset, } r += client->dataset[cursor].begin_position; +clear_buffer: + if (r == ARCHIVE_FAILED && !sought && old_cursor == client->cursor) { + /* Seek failed without moving position: keep buffer */ + return (r); + } + + /* + * Ouch. Clearing the buffer like this hurts, especially + * at bid time. A lot of our efficiency at bid time comes + * from having bidders reuse the data we've already read. + * + * TODO: If the seek request is in data we already + * have, then don't call the seek callback. + * + * TODO: Zip seeks to end-of-file at bid time. If + * other formats also start doing this, we may need to + * find a way for clients to fudge the seek offset to + * a block boundary. + * + * Hmmm... If whence was SEEK_END, we know the file + * size is (r - offset). Can we use that to simplify + * the TODO items above? + */ + f->avail = f->client_avail = 0; + f->next = f->buffer; if (r >= 0) { - /* - * Ouch. Clearing the buffer like this hurts, especially - * at bid time. A lot of our efficiency at bid time comes - * from having bidders reuse the data we've already read. - * - * TODO: If the seek request is in data we already - * have, then don't call the seek callback. - * - * TODO: Zip seeks to end-of-file at bid time. If - * other formats also start doing this, we may need to - * find a way for clients to fudge the seek offset to - * a block boundary. - * - * Hmmm... If whence was SEEK_END, we know the file - * size is (r - offset). Can we use that to simplify - * the TODO items above? - */ - f->avail = f->client_avail = 0; - f->next = f->buffer; f->position = r; f->end_of_file = 0; - } + } else + f->fatal = 1; + return r; } diff --git a/libarchive/archive_read_add_passphrase.3 b/libarchive/archive_read_add_passphrase.3 index c35cfeb34cd5..d5eb7fda246b 100644 --- a/libarchive/archive_read_add_passphrase.3 +++ b/libarchive/archive_read_add_passphrase.3 @@ -47,7 +47,7 @@ Streaming Archive Library (libarchive, -larchive) .Sh DESCRIPTION .Bl -tag -width indent .It Fn archive_read_add_passphrase -Register passphrases for reading an encryption archive. +Register passphrases for reading an encrypted archive. If .Ar passphrase is @@ -59,7 +59,7 @@ Otherwise, .Cm ARCHIVE_OK will be returned. .It Fn archive_read_set_passphrase_callback -Register a callback function that will be invoked to get a passphrase +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. diff --git a/libarchive/archive_read_append_filter.c b/libarchive/archive_read_append_filter.c index 8de1b2ea8fba..5cc8ff55d6b0 100644 --- a/libarchive/archive_read_append_filter.c +++ b/libarchive/archive_read_append_filter.c @@ -33,14 +33,25 @@ #include "archive_private.h" #include "archive_read_private.h" +static struct archive_read_filter_bidder * +get_last_bidder(struct archive_read *a) +{ + size_t i; + + for (i = 0; i < sizeof(a->bidders) / sizeof(a->bidders[0]); i++) + { + if (a->bidders[i].vtable == NULL) + break; + } + + return a->bidders + (i - 1); +} + int archive_read_append_filter(struct archive *_a, int code) { - int r1, r2, number_bidders, i; - const char *str; - struct archive_read_filter_bidder *b; - struct archive_read_filter *f; struct archive_read *a = (struct archive_read *)_a; + int r1, r2; r2 = (ARCHIVE_OK); switch (code) @@ -53,15 +64,12 @@ archive_read_append_filter(struct archive *_a, int code) r1 = (ARCHIVE_OK); break; case ARCHIVE_FILTER_GZIP: - str = "gzip"; r1 = archive_read_support_filter_gzip(_a); break; case ARCHIVE_FILTER_BZIP2: - str = "bzip2"; r1 = archive_read_support_filter_bzip2(_a); break; case ARCHIVE_FILTER_COMPRESS: - str = "compress (.Z)"; r1 = archive_read_support_filter_compress(_a); break; case ARCHIVE_FILTER_PROGRAM: @@ -69,43 +77,33 @@ archive_read_append_filter(struct archive *_a, int code) "Cannot append program filter using archive_read_append_filter"); return (ARCHIVE_FATAL); case ARCHIVE_FILTER_LZMA: - str = "lzma"; r1 = archive_read_support_filter_lzma(_a); break; case ARCHIVE_FILTER_XZ: - str = "xz"; r1 = archive_read_support_filter_xz(_a); break; case ARCHIVE_FILTER_UU: - str = "uu"; r1 = archive_read_support_filter_uu(_a); break; case ARCHIVE_FILTER_RPM: - str = "rpm"; r1 = archive_read_support_filter_rpm(_a); break; case ARCHIVE_FILTER_LZ4: - str = "lz4"; r1 = archive_read_support_filter_lz4(_a); break; case ARCHIVE_FILTER_ZSTD: - str = "zstd"; r1 = archive_read_support_filter_zstd(_a); break; case ARCHIVE_FILTER_LZIP: - str = "lzip"; r1 = archive_read_support_filter_lzip(_a); break; case ARCHIVE_FILTER_LZOP: - str = "lzop"; r1 = archive_read_support_filter_lzop(_a); break; case ARCHIVE_FILTER_LRZIP: - str = "lrzip"; r1 = archive_read_support_filter_lrzip(_a); break; case ARCHIVE_FILTER_GRZIP: - str = "grzip"; r1 = archive_read_support_filter_grzip(_a); break; default: @@ -114,22 +112,10 @@ archive_read_append_filter(struct archive *_a, int code) return (ARCHIVE_FATAL); } - if (code != ARCHIVE_FILTER_NONE) + if (r1 > ARCHIVE_FATAL && code != ARCHIVE_FILTER_NONE) { - number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); - - b = a->bidders; - for (i = 1; i < number_bidders; i++, b++) - { - if (!b->name || !strcmp(b->name, str)) - break; - } - if (!b->name || strcmp(b->name, str)) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Internal error: Unable to append filter"); - return (ARCHIVE_FATAL); - } + struct archive_read_filter_bidder *b; + struct archive_read_filter *f; f = calloc(1, sizeof(*f)); if (f == NULL) @@ -137,6 +123,7 @@ archive_read_append_filter(struct archive *_a, int code) archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } + b = get_last_bidder(a); f->bidder = b; f->archive = a; f->upstream = a->filter; @@ -162,37 +149,22 @@ int archive_read_append_filter_program_signature(struct archive *_a, const char *cmd, const void *signature, size_t signature_len) { - int r, number_bidders, i; + struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter_bidder *b; struct archive_read_filter *f; - struct archive_read *a = (struct archive_read *)_a; + int r; if (archive_read_support_filter_program_signature(_a, cmd, signature, signature_len) != (ARCHIVE_OK)) return (ARCHIVE_FATAL); - number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); - - b = a->bidders; - for (i = 0; i < number_bidders; i++, b++) - { - /* Program bidder name set to filter name after initialization */ - if (b->data && !b->name) - break; - } - if (!b->data) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Internal error: Unable to append program filter"); - return (ARCHIVE_FATAL); - } - f = calloc(1, sizeof(*f)); if (f == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } + b = get_last_bidder(a); f->bidder = b; f->archive = a; f->upstream = a->filter; @@ -202,7 +174,6 @@ archive_read_append_filter_program_signature(struct archive *_a, __archive_read_free_filters(a); return (ARCHIVE_FATAL); } - b->name = a->filter->name; a->bypass_filter_bidding = 1; return r; diff --git a/libarchive/archive_read_data.3 b/libarchive/archive_read_data.3 index 694f29264ec1..56e16f6fb9d5 100644 --- a/libarchive/archive_read_data.3 +++ b/libarchive/archive_read_data.3 @@ -70,6 +70,21 @@ will not overlap. Note that the blocks returned from this function can be much larger than the block size read from disk, due to compression and internal buffer optimizations. +All three of +.Fa buff , +.Fa len +and +.Fa offset +are written whenever the function returns +.Cm ARCHIVE_OK +or +.Cm ARCHIVE_EOF . +At the end of an entry +.Fa len +is zero and +.Fa offset +is the end of the entry's data, which is how a trailing sparse hole is +reported. .It Fn archive_read_data_skip A convenience function that repeatedly calls .Fn archive_read_data_block diff --git a/libarchive/archive_read_data_into_fd.c b/libarchive/archive_read_data_into_fd.c index d92ab9a51253..b63fbf9236a6 100644 --- a/libarchive/archive_read_data_into_fd.c +++ b/libarchive/archive_read_data_into_fd.c @@ -96,7 +96,7 @@ archive_read_data_into_fd(struct archive *a, int fd) size_t size, bytes_to_write; ssize_t bytes_written; int64_t fd_offset; - int64_t target_offset; + int64_t target_offset = 0; int64_t actual_offset = 0; int64_t declared_size; int can_lseek; diff --git a/libarchive/archive_read_disk.3 b/libarchive/archive_read_disk.3 index 990c1514c4d5..2f111802afee 100644 --- a/libarchive/archive_read_disk.3 +++ b/libarchive/archive_read_disk.3 @@ -56,7 +56,7 @@ Streaming Archive Library (libarchive, -larchive) .Ft int .Fn archive_read_disk_open "struct archive *" "const char *" .Ft int -.Fn archive_read_disk_open_w "struct archive *" "const wchar_t *" +.Fn archive_read_disk_open_w "struct archive *" "const wchar_t *" .Ft int .Fn archive_read_disk_set_behavior "struct archive *" "int" .Ft int @@ -109,7 +109,7 @@ Streaming Archive Library (libarchive, -larchive) .Fa "void (*excluded_func)(struct archive *, void *, struct archive entry *)" .Fa "void *" .Fc -.Ft int +.Ft int .Fo archive_read_disk_set_metadata_filter_callback .Fa "struct archive *" .Fa "int (*metadata_filter_func)(struct archive *, void*, struct archive_entry *)" @@ -191,15 +191,19 @@ By default, sparse file information is read from disk. This sets the mode used for handling symbolic links. The .Dq logical -mode follows all symbolic links. +mode follows all symbolic links, except that a symbolic link whose +target is an ancestor directory of the link is archived as a symbolic +link rather than being followed, to avoid loops. The .Dq physical mode does not follow any symbolic links. The .Dq hybrid -mode currently behaves identically to the -.Dq logical -mode. +mode follows symbolic links specified as the target of +.Fn archive_read_disk_open +or +.Fn archive_read_disk_open_w +but does not follow symbolic links anywhere else. .It Xo .Fn archive_read_disk_gname , .Fn archive_read_disk_uname @@ -267,13 +271,13 @@ This affects the file ownership fields and ACL values in the .Tn struct archive_entry object. .It Fn archive_read_disk_descend -If the current entry can be descended, this function will mark the directory as the next entry for +If the current entry can be descended, this function will mark the directory as the next entry for .Xr archive_read_header 3 to visit. .It Fn archive_read_disk_can_descend Returns 1 if the current entry is an unvisited directory and 0 otherwise. .It Fn archive_read_disk_current_filesystem -Returns the index of the most recent filesystem entry that has been visited through archive_read_disk +Returns the index of the most recent filesystem entry that has been visited through archive_read_disk. .It Fn archive_read_disk_current_filesystem_is_synthetic Returns 1 if the current filesystem is a virtual filesystem. Returns 0 if the current filesystem is not a virtual filesystem. Returns -1 if it is unknown. .It Fn archive_read_disk_current_filesystem_is_remote @@ -294,7 +298,7 @@ Allows the caller to set a callback function during calls to .Xr archive_read_header 3 to filter out metadata for each entry. The callback function receives the .Tn struct archive -object, void* custom filter data, and the +object, void* custom filter data, and the .Tn struct archive_entry . If the callback function returns an error, ARCHIVE_RETRY will be returned and the entry will not be further processed. .El @@ -316,12 +320,12 @@ file_to_archive(struct archive *a, const char *name) struct archive_entry *entry; int fd; + fd = open(name, O_RDONLY); + if (fd < 0) + return; ard = archive_read_disk_new(); archive_read_disk_set_standard_lookup(ard); entry = archive_entry_new(); - fd = open(name, O_RDONLY); - if (fd < 0) - return; archive_entry_copy_pathname(entry, name); archive_read_disk_entry_from_file(ard, entry, fd, NULL); archive_write_header(a, entry); diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index a3c9e5833a94..fe8d06a394cf 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -97,9 +97,6 @@ #include "archive_private.h" #include "archive_read_disk_private.h" -#ifndef HAVE_FCHDIR -#error fchdir function required. -#endif #ifndef O_BINARY #define O_BINARY 0 #endif @@ -114,6 +111,18 @@ #define HAVE_DIRFD #endif +#ifndef HAVE_FCHDIR +/* + * Stub function for platforms that do not support fchdir. This is OK on + * platforms with newer *at functions as fchdir isn't used there (e.g. WASI). + */ +static int fchdir(int fd) +{ + errno = ENOSYS; + return -1; +} +#endif + /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no diff --git a/libarchive/archive_read_disk_set_standard_lookup.c b/libarchive/archive_read_disk_set_standard_lookup.c index 01285d91bb56..ecd675db860f 100644 --- a/libarchive/archive_read_disk_set_standard_lookup.c +++ b/libarchive/archive_read_disk_set_standard_lookup.c @@ -47,11 +47,11 @@ #include "archive.h" #include "archive_integer.h" -#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) || defined(__wasi__) int archive_read_disk_set_standard_lookup(struct archive *a) { - archive_set_error(a, -1, "Standard lookups not available on Windows"); + archive_set_error(a, -1, "Standard lookups not available on this platform"); return (ARCHIVE_FATAL); } #else /* ! (_WIN32 && !__CYGWIN__) */ @@ -312,4 +312,4 @@ lookup_gname_helper(struct name_cache *cache, id_t id) } #endif -#endif /* ! (_WIN32 && !__CYGWIN__) */ +#endif /* ! (_WIN32 && !__CYGWIN__ || __wasi__) */ diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index eb4699a3f850..da5bb091281d 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -2056,11 +2056,11 @@ entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, uint32_t nsecs; mode_t mode; - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs); archive_entry_set_atime(entry, secs, nsecs); - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs); archive_entry_set_mtime(entry, secs, nsecs); - ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftCreationTime), &secs, &nsecs); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&bhfi->ftCreationTime), &secs, &nsecs); archive_entry_set_birthtime(entry, secs, nsecs); archive_entry_set_ctime(entry, secs, nsecs); archive_entry_set_dev(entry, bhfi_dev(bhfi)); diff --git a/libarchive/archive_read_free.3 b/libarchive/archive_read_free.3 index 7dc121fcaea3..ec477cde5a5d 100644 --- a/libarchive/archive_read_free.3 +++ b/libarchive/archive_read_free.3 @@ -59,7 +59,7 @@ than 2013. .It Fn archive_read_free Invokes .Fn archive_read_close -if it was not invoked manually, then release all resources. +if it was not invoked manually, then releases all resources. Note: In libarchive 1.x, this function was declared to return .Ft void , which made it impossible to detect certain errors when diff --git a/libarchive/archive_read_open.3 b/libarchive/archive_read_open.3 index 0e85d16f0d2f..9316ff3223a2 100644 --- a/libarchive/archive_read_open.3 +++ b/libarchive/archive_read_open.3 @@ -166,7 +166,7 @@ The callback functions must match the following prototypes: .El .Pp The open callback is invoked by -.Fn archive_open . +.Fn archive_read_open . It should return .Cm ARCHIVE_OK if the underlying file or data source is successfully @@ -210,7 +210,7 @@ performance gains when reading uncompressed archives from slow disk drives or other media that can skip quickly. .Pp -The close callback is invoked by archive_close when +The close callback is invoked by archive_read_close when the archive processing is complete. The callback should return .Cm ARCHIVE_OK diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c index 358e516faf18..97c944b82a12 100644 --- a/libarchive/archive_read_open_fd.c +++ b/libarchive/archive_read_open_fd.c @@ -109,8 +109,11 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size) archive_read_set_seek_callback(a, file_seek); archive_read_set_close_callback(a, file_close); r = archive_read_set_callback_data(a, mine); - if (r < 0) + if (r < 0) { + free(mine->buffer); + free(mine); return (r); + } return (archive_read_open1(a)); } diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index e82069e502bf..f6dcf82fe423 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -106,8 +106,11 @@ archive_read_open_FILE(struct archive *a, FILE *f) archive_read_set_seek_callback(a, FILE_seek); archive_read_set_close_callback(a, FILE_close); r = archive_read_set_callback_data(a, mine); - if (r < 0) + if (r < 0) { + free(mine->buffer); + free(mine); return (r); + } return (archive_read_open1(a)); } diff --git a/libarchive/archive_read_open_memory.c b/libarchive/archive_read_open_memory.c index 441662d464d7..54c0f94ed218 100644 --- a/libarchive/archive_read_open_memory.c +++ b/libarchive/archive_read_open_memory.c @@ -84,8 +84,10 @@ archive_read_open_memory2(struct archive *a, const void *buff, archive_read_set_skip_callback(a, memory_read_skip); archive_read_set_close_callback(a, memory_read_close); r = archive_read_set_callback_data(a, mine); - if (r < 0) + if (r < 0) { + free(mine); return (r); + } return (archive_read_open1(a)); } @@ -149,29 +151,27 @@ static int64_t memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence) { struct read_memory_data *mine = (struct read_memory_data *)client_data; + const unsigned char *p; (void)a; /* UNUSED */ switch (whence) { case SEEK_SET: - mine->p = mine->start + offset; + p = mine->start + offset; break; case SEEK_CUR: - mine->p += offset; + p = mine->p + offset; break; case SEEK_END: - mine->p = mine->end + offset; + p = mine->end + offset; break; default: - return ARCHIVE_FATAL; - } - if (mine->p < mine->start) { - mine->p = mine->start; return ARCHIVE_FAILED; } - if (mine->p > mine->end) { - mine->p = mine->end; + if (p < mine->start) return ARCHIVE_FAILED; - } + if (p > mine->end) + return ARCHIVE_FAILED; + mine->p = p; return (mine->p - mine->start); } diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 3bd8de2de663..43b7f853b671 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -68,8 +68,6 @@ struct archive_read_filter_bidder_vtable { struct archive_read_filter_bidder { /* Configuration data for the bidder. */ void *data; - /* Name of the filter */ - const char *name; const struct archive_read_filter_bidder_vtable *vtable; }; @@ -189,10 +187,6 @@ struct archive_read { */ int64_t entry_bytes_declared; - /* Nodes and offsets of compressed data block */ - unsigned int data_start_node; - unsigned int data_end_node; - /* * Format detection is mostly the same as compression * detection, with one significant difference: The bidders @@ -250,7 +244,6 @@ int __archive_read_register_format(struct archive_read *a, int __archive_read_register_bidder(struct archive_read *a, void *bidder_data, - const char *name, const struct archive_read_filter_bidder_vtable *vtable); const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *); diff --git a/libarchive/archive_read_set_options.3 b/libarchive/archive_read_set_options.3 index ef18dfaa271a..401796643c67 100644 --- a/libarchive/archive_read_set_options.3 +++ b/libarchive/archive_read_set_options.3 @@ -134,7 +134,7 @@ If either function returns .Cm ARCHIVE_FATAL will be returned immediately. -Otherwise, greater of the two values will be returned. +Otherwise, the greater of the two values will be returned. .\" .It Xo .Fn archive_read_set_options diff --git a/libarchive/archive_read_support_filter_bzip2.c b/libarchive/archive_read_support_filter_bzip2.c index 588953f2aa94..1cfc55dcad1a 100644 --- a/libarchive/archive_read_support_filter_bzip2.c +++ b/libarchive/archive_read_support_filter_bzip2.c @@ -91,7 +91,7 @@ archive_read_support_filter_bzip2(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "bzip2", + if (__archive_read_register_bidder(a, NULL, &bzip2_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c index 4e1f1dc52be8..4763995e8137 100644 --- a/libarchive/archive_read_support_filter_compress.c +++ b/libarchive/archive_read_support_filter_compress.c @@ -160,7 +160,7 @@ archive_read_support_filter_compress(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - return __archive_read_register_bidder(a, NULL, "compress (.Z)", + return __archive_read_register_bidder(a, NULL, &compress_bidder_vtable); } diff --git a/libarchive/archive_read_support_filter_grzip.c b/libarchive/archive_read_support_filter_grzip.c index 9d9ad46eaef4..8457ec7c76a7 100644 --- a/libarchive/archive_read_support_filter_grzip.c +++ b/libarchive/archive_read_support_filter_grzip.c @@ -62,7 +62,7 @@ archive_read_support_filter_grzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "grzip", + if (__archive_read_register_bidder(a, NULL, &grzip_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/archive_read_support_filter_gzip.c b/libarchive/archive_read_support_filter_gzip.c index 89ae0f371493..a4dff5490d9e 100644 --- a/libarchive/archive_read_support_filter_gzip.c +++ b/libarchive/archive_read_support_filter_gzip.c @@ -96,7 +96,7 @@ archive_read_support_filter_gzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "gzip", + if (__archive_read_register_bidder(a, NULL, &gzip_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -344,7 +344,7 @@ consume_header(struct archive_read_filter *f) __archive_read_filter_consume(f->upstream, len); /* Initialize CRC accumulator. */ - gzip->crc = crc32(0L, NULL, 0); + gzip->crc = __archive_crc32(0L, NULL, 0); /* Initialize compression library. */ gzip->stream.next_in = (unsigned char *)(uintptr_t) @@ -416,8 +416,12 @@ consume_trailer(struct archive_read_filter *f) /* GZip trailer is a fixed 8 byte structure. */ p = __archive_read_filter_ahead(f->upstream, 8, NULL); - if (p == NULL) + if (p == NULL) { + archive_set_error(&f->archive->archive, + ARCHIVE_ERRNO_MISC, + "Truncated gzip input"); return (ARCHIVE_FATAL); + } /* XXX TODO: Verify the length and CRC. */ diff --git a/libarchive/archive_read_support_filter_lrzip.c b/libarchive/archive_read_support_filter_lrzip.c index 33910fe10a09..65df9abe0370 100644 --- a/libarchive/archive_read_support_filter_lrzip.c +++ b/libarchive/archive_read_support_filter_lrzip.c @@ -61,7 +61,7 @@ archive_read_support_filter_lrzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "lrzip", + if (__archive_read_register_bidder(a, NULL, &lrzip_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -79,14 +79,12 @@ lrzip_bidder_bid(struct archive_read_filter_bidder *b, struct archive_read_filter *f) { const unsigned char *p; - ssize_t len; int i; (void)b; /* UNUSED */ /* Start by looking at the first six bytes of the header, which * is all fixed layout. */ - len = 6; - p = __archive_read_filter_ahead(f, len, NULL); + p = __archive_read_filter_ahead(f, 6, NULL); if (p == NULL) return (0); @@ -101,7 +99,7 @@ lrzip_bidder_bid(struct archive_read_filter_bidder *b, if ((i < 6) || (i > 10)) return 0; - return (int)len; + return (48); } static int diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c index 19086a1fefb2..4695e5bb0ba4 100644 --- a/libarchive/archive_read_support_filter_lz4.c +++ b/libarchive/archive_read_support_filter_lz4.c @@ -73,7 +73,6 @@ struct lz4 { int block_maximum_size; } flags; int64_t stream_size; - uint32_t dict_id; char *out_block; size_t out_block_size; @@ -82,7 +81,6 @@ struct lz4 { size_t decoded_size; void *xxh32_state; - char valid; /* True = decompressor is initialized */ char eof; /* True = found end of compressed data. */ }; @@ -118,7 +116,7 @@ archive_read_support_filter_lz4(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "lz4", + if (__archive_read_register_bidder(a, NULL, &lz4_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -180,6 +178,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *b, uint32_t frame_data_size; /* Skip over the magic number */ + bits_checked += 28; offset_in_buffer += 4; /* Ensure that we can read another 4 bytes. */ @@ -205,13 +204,11 @@ lz4_reader_bid(struct archive_read_filter_bidder *b, * if this is LZ4 data. */ if (archive_ckd_add_size(&min, - offset_in_buffer, min_lz4_frame_size)) + offset_in_buffer, min_lz4_frame_size) || + min > max_lookahead) return (0); /* TODO: should this be >= ? */ if (min > (size_t)avail) { - if (min > max_lookahead) - return (0); - buffer = __archive_read_filter_ahead(f, min, &avail); if (buffer == NULL) @@ -226,7 +223,6 @@ lz4_reader_bid(struct archive_read_filter_bidder *b, * follows, or this isn't LZ4 data. */ - bits_checked = offset_in_buffer; buffer = buffer + offset_in_buffer; if (magic_number == LZ4_MAGICNUMBER) { diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c index ebfaf01e8533..b7452be69546 100644 --- a/libarchive/archive_read_support_filter_lzop.c +++ b/libarchive/archive_read_support_filter_lzop.c @@ -109,7 +109,7 @@ archive_read_support_filter_lzop(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "lzop", + if (__archive_read_register_bidder(a, NULL, &lzop_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -274,7 +274,7 @@ consume_header(struct archive_read_filter *f) if (p == NULL) goto truncated; if (flags & CRC32_HEADER) - checksum = crc32(crc32(0, NULL, 0), p, len); + checksum = __archive_crc32(__archive_crc32(0, NULL, 0), p, len); else checksum = adler32(adler32(0, NULL, 0), p, len); #ifndef DONT_FAIL_ON_CRC_ERROR @@ -414,7 +414,7 @@ lzop_filter_read(struct archive_read_filter *f, const void **p) return (ARCHIVE_FATAL); } if (lzop->flags & CRC32_COMPRESSED) - cksum = crc32(crc32(0, NULL, 0), b, lzop->compressed_size); + cksum = __archive_crc32(__archive_crc32(0, NULL, 0), b, lzop->compressed_size); else if (lzop->flags & ADLER32_COMPRESSED) cksum = adler32(adler32(0, NULL, 0), b, lzop->compressed_size); else @@ -459,7 +459,7 @@ lzop_filter_read(struct archive_read_filter *f, const void **p) } if (lzop->flags & CRC32_UNCOMPRESSED) - cksum = crc32(crc32(0, NULL, 0), lzop->out_block, + cksum = __archive_crc32(__archive_crc32(0, NULL, 0), lzop->out_block, lzop->uncompressed_size); else if (lzop->flags & ADLER32_UNCOMPRESSED) cksum = adler32(adler32(0, NULL, 0), lzop->out_block, diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c index cf7c36137e1b..3a6eea517069 100644 --- a/libarchive/archive_read_support_filter_program.c +++ b/libarchive/archive_read_support_filter_program.c @@ -81,6 +81,8 @@ archive_read_support_filter_program(struct archive *a, const char *cmd) return (archive_read_support_filter_program_signature(a, cmd, NULL, 0)); } +#ifdef HAVE_WAITPID + /* * The bidder object stores the command and the signature to watch for. * The 'inhibit' entry here is used to ensure that unchecked filters never @@ -149,6 +151,11 @@ archive_read_support_filter_program_signature(struct archive *_a, archive_strcat(&state->description, cmd); if (signature != NULL && signature_len > 0) { + if (signature_len > (size_t)INT_MAX / 8) { + free_state(state); + archive_set_error(_a, EINVAL, "Signature too large"); + return (ARCHIVE_FATAL); + } state->signature_len = signature_len; state->signature = malloc(signature_len); if (state->signature == NULL) @@ -156,7 +163,7 @@ archive_read_support_filter_program_signature(struct archive *_a, memcpy(state->signature, signature, signature_len); } - if (__archive_read_register_bidder(a, state, NULL, + if (__archive_read_register_bidder(a, state, &program_bidder_vtable) != ARCHIVE_OK) { free_state(state); return (ARCHIVE_FATAL); @@ -257,7 +264,7 @@ child_stop(struct archive_read_filter *f, struct program *program) return (ARCHIVE_WARN); } -#if !defined(_WIN32) || defined(__CYGWIN__) +#ifdef WIFSIGNALED if (WIFSIGNALED(program->exit_status)) { #ifdef SIGPIPE /* If the child died because we stopped reading before @@ -274,7 +281,7 @@ child_stop(struct archive_read_filter *f, struct program *program) WTERMSIG(program->exit_status)); return (ARCHIVE_WARN); } -#endif /* !_WIN32 || __CYGWIN__ */ +#endif /* WIFSIGNALED */ if (WIFEXITED(program->exit_status)) { if (WEXITSTATUS(program->exit_status) == 0) @@ -495,3 +502,26 @@ program_filter_close(struct archive_read_filter *f) return (e); } + +#else + +int +archive_read_support_filter_program_signature(struct archive *a, + const char *cmd, const void *signature, size_t signature_len) +{ + (void)cmd; /* UNUSED */ + (void)signature; /* UNUSED */ + (void)signature_len; /* UNUSED */ + archive_set_error(a, ENOSYS, "Processes not supported"); + return (ARCHIVE_FATAL); +} + +int +__archive_read_program(struct archive_read_filter *f, const char *cmd) +{ + (void)f; /* UNUSED */ + (void)cmd; /* UNUSED */ + return (ARCHIVE_FATAL); +} + +#endif diff --git a/libarchive/archive_read_support_filter_rpm.c b/libarchive/archive_read_support_filter_rpm.c index 01bd1bf7af5b..1cfddf750d9b 100644 --- a/libarchive/archive_read_support_filter_rpm.c +++ b/libarchive/archive_read_support_filter_rpm.c @@ -71,7 +71,7 @@ archive_read_support_filter_rpm(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - return __archive_read_register_bidder(a, NULL, "rpm", + return __archive_read_register_bidder(a, NULL, &rpm_bidder_vtable); } diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c index c0f6f48ed413..364a3a041660 100644 --- a/libarchive/archive_read_support_filter_uu.c +++ b/libarchive/archive_read_support_filter_uu.c @@ -37,18 +37,14 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" /* Maximum lookahead during bid phase */ -#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */ +#define MAX_LINE_LENGTH 128*1024 /* in bytes */ struct uu { - int64_t total; - unsigned char *in_buff; -#define IN_BUFF_SIZE (1024) - ssize_t in_cnt; - size_t in_allocated; unsigned char *out_buff; #define OUT_BUFF_SIZE (64 * 1024) int state; @@ -92,29 +88,10 @@ archive_read_support_filter_uu(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - return __archive_read_register_bidder(a, NULL, "uu", + return __archive_read_register_bidder(a, NULL, &uudecode_bidder_vtable); } -static const unsigned char ascii[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ -}; - static const unsigned char uuchar[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ @@ -172,143 +149,157 @@ static const int base64num[128] = { 49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */ }; -static ssize_t -get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize) +/* + * Read the next line from upstream beginning at given offset. No more than + * MAX_LINE_LENGTH bytes will be read into stream. Bytes won't be consumed. + * The length of the line excluding the newline is stored in len. + * + * If parsed is non-zero, only already available bytes are considered. + * This option is supposed to be used with a 0 offset. + * + * Amount of unconsumed bytes, including newline character(s), or up to + * MAX_LINE_LENGTH if no newline was found, is stored in unconsumed. + * + * If a line is found, pointer to beginning of line is returned. + */ +static const unsigned char * +read_line_ahead(struct archive_read_filter *f, ssize_t parsed, + size_t offset, size_t *len, ssize_t *unconsumed) { - ssize_t len; + /* Read approximately two new lines each iteration. */ + size_t step = 160; + const unsigned char *line; + ssize_t avail; + size_t request, start; - len = 0; - while (len < avail) { - switch (ascii[*b]) { - case 0: /* Non-ascii character or control character. */ - if (nlsize != NULL) - *nlsize = 0; - return (-1); - case '\r': - if (avail-len > 1 && b[1] == '\n') { - if (nlsize != NULL) - *nlsize = 2; - return (len+2); + start = offset; + line = NULL; + *len = 0; + *unconsumed = 0; + request = offset == 0 ? 1 : offset + step; + do { + size_t remaining, nl = 1; + const unsigned char *b, *p; + + /* Read bytes from upstream filter. */ + b = __archive_read_filter_ahead(f, request, &avail); + if (avail > MAX_LINE_LENGTH) + avail = MAX_LINE_LENGTH; + *unconsumed = avail; + if (b == NULL) { + if (avail > 0 && (size_t)avail > offset) + b = __archive_read_filter_ahead(f, avail, + &avail); + if (b == NULL) + break; + } + remaining = avail - offset; + /* Locate end of line (\r, \n, \r\n). */ + p = memchr(b + offset, '\n', remaining); + if (p == NULL) { + p = memchr(b + offset, '\r', remaining); + if (p == b + avail - 1 && step > 1) { + /* Is it \r or \r\n, missing one byte? */ + p = NULL; + step = 1; } - /* FALL THROUGH */ - case '\n': - if (nlsize != NULL) - *nlsize = 1; - return (len+1); - case 1: - b++; - len++; - break; + } else if (p > b + start && p[-1] == '\r') { + /* "\r\n" takes two bytes. */ + nl++; + --p; } - } - if (nlsize != NULL) - *nlsize = 0; - return (avail); -} -static ssize_t -bid_get_line(struct archive_read_filter *f, - const unsigned char **b, ssize_t *avail, ssize_t *ravail, - ssize_t *nl, size_t* nbytes_read) -{ - ssize_t len; - int quit; - - quit = 0; - if (*avail == 0) { - *nl = 0; - len = 0; - } else - len = get_line(*b, *avail, nl); - - /* - * Read more bytes while it does not reach the end of line. - */ - while (*nl == 0 && len == *avail && !quit && - *nbytes_read < UUENCODE_BID_MAX_READ) { - ssize_t diff = *ravail - *avail; - size_t nbytes_req = (*ravail+1023) & ~1023U; - ssize_t tested; - - /* Increase reading bytes if it is not enough for at least - * two new lines. */ - if (nbytes_req < (size_t)*ravail + 160) - nbytes_req <<= 1; - - *b = __archive_read_filter_ahead(f, nbytes_req, avail); - if (*b == NULL) { - if (*ravail >= *avail) - return (0); - /* Reading bytes reaches the end of a stream. */ - *b = __archive_read_filter_ahead(f, *avail, avail); - quit = 1; + /* Return line or prepare to read more bytes. */ + if (p != NULL) { + line = b + start; + *len = p - line; + *unconsumed = *len + nl; + } else { + /* Keep offset for check of possibly missing \n in \r\n. */ + if (step != 1) + offset = avail; + if (archive_ckd_add_size(&request, avail, step)) + break; + if (request > MAX_LINE_LENGTH) + request = MAX_LINE_LENGTH; } - *nbytes_read = *avail; - *ravail = *avail; - *b += diff; - *avail -= diff; - tested = len;/* Skip some bytes we already determined. */ - len = get_line(*b + tested, *avail - tested, nl); - if (len >= 0) - len += tested; - } - return (len); + } while (line == NULL && avail < MAX_LINE_LENGTH && !parsed); + + return line; } #define UUDECODE(c) (((c) - 0x20) & 0x3f) +/* + * Parses the header line of a uuencoded file, "begin ", or of + * a base64 encoded file, "begin-base64 ". The mode is a + * sequence of at least three octal digits: encoders don't all write the same + * number of them, for example 644, 0644 or 4755 for the same kind of file. + * + * Returns the length of the "begin " or "begin-base64 " prefix, or 0 if the + * line is not a header. Stores the permission bits of the mode in *mode and + * the offset of the name in *name_offset, unless they are NULL. + */ +static size_t +parse_header(const unsigned char *p, size_t len, mode_t *mode, + size_t *name_offset) +{ + size_t i, l; + mode_t m; + + if (len >= 6 && memcmp(p, "begin ", 6) == 0) + l = 6; + else if (len >= 13 && memcmp(p, "begin-base64 ", 13) == 0) + l = 13; + else + return (0); + + m = 0; + for (i = l; i < len && p[i] >= '0' && p[i] <= '7'; i++) + m = ((m << 3) | (p[i] - '0')) & 0777; + /* + * Need at least three digits, as too lenient a check would have + * the bidder take other kinds of data for a header. Then a space and + * a one character name. + */ + if (i - l < 3 || i + 1 >= len || p[i] != ' ') + return (0); + + if (mode != NULL) + *mode = m; + if (name_offset != NULL) + *name_offset = i + 1; + return (l); +} + static int uudecode_bidder_bid(struct archive_read_filter_bidder *b, struct archive_read_filter *f) { const unsigned char *p; - ssize_t avail, ravail; - ssize_t len, nl; - int l; + size_t l; int firstline; - size_t nbytes_read; + size_t len, offset; + ssize_t unconsumed; (void)b; /* UNUSED */ - p = __archive_read_filter_ahead(f, 1, &avail); - if (p == NULL) - return (0); - + offset = 0; firstline = 20; - ravail = avail; - nbytes_read = avail; for (;;) { - len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read); - if (len < 0 || nl == 0) + p = read_line_ahead(f, 0, offset, &len, &unconsumed); + if (p == NULL) return (0); /* No match found. */ - if (len - nl >= 11 && memcmp(p, "begin ", 6) == 0) - l = 6; - else if (len -nl >= 18 && memcmp(p, "begin-base64 ", 13) == 0) - l = 13; - else - l = 0; + offset += unconsumed; - if (l > 0 && (p[l] < '0' || p[l] > '7' || - p[l+1] < '0' || p[l+1] > '7' || - p[l+2] < '0' || p[l+2] > '7' || p[l+3] != ' ')) - l = 0; - - p += len; - avail -= len; - if (l) + l = parse_header(p, len, NULL, NULL); + if (l != 0) break; firstline = 0; - - /* Do not read more than UUENCODE_BID_MAX_READ bytes */ - if (nbytes_read >= UUENCODE_BID_MAX_READ) - return (0); } - if (!avail) - return (0); - len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read); - if (len < 0 || nl == 0) - return (0);/* There are non-ascii characters. */ - avail -= len; + p = read_line_ahead(f, 0, offset, &len, &unconsumed); + if (p == NULL) + return (0); /* No match found. */ if (l == 6) { /* "begin " */ @@ -316,10 +307,13 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *b, return (0); /* Get a length of decoded bytes. */ l = UUDECODE(*p++); len--; + if (l == 0) + /* empty encoded file */ + return (firstline+30); if (l > 45) /* Normally, maximum length is 45(character 'M'). */ return (0); - if (l > len - nl) + if (l > len) return (0); /* Line too short. */ while (l) { if (!uuchar[*p++]) @@ -327,30 +321,27 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *b, --len; --l; } - if (len-nl == 1 && + if (len == 1 && (uuchar[*p] || /* Check sum. */ (*p >= 'a' && *p <= 'z'))) {/* Padding data(MINIX). */ ++p; --len; } - p += nl; - if (avail && uuchar[*p]) + offset += unconsumed; + p = read_line_ahead(f, 0, offset, &len, &unconsumed); + if (p != NULL && uuchar[*p]) return (firstline+30); } else if (l == 13) { /* "begin-base64 " */ - while (len-nl > 0) { + while (len > 0) { if (!base64[*p++]) return (0); - --len; + len--; } - p += nl; - - if (avail >= 5 && memcmp(p, "====\n", 5) == 0) - return (firstline+40); - if (avail >= 6 && memcmp(p, "====\r\n", 6) == 0) - return (firstline+40); - if (avail > 0 && base64[*p]) + if (len == 0) return (firstline+30); + if (len == 4 && memcmp(p, "====", 4) == 0) + return (firstline+40); } return (0); @@ -366,29 +357,23 @@ uudecode_reader_vtable = { static int uudecode_bidder_init(struct archive_read_filter *f) { - struct uu *uu; + struct uu *uu; void *out_buff; - void *in_buff; f->code = ARCHIVE_FILTER_UU; f->name = "uu"; uu = calloc(1, sizeof(*uu)); out_buff = malloc(OUT_BUFF_SIZE); - in_buff = malloc(IN_BUFF_SIZE); - if (uu == NULL || out_buff == NULL || in_buff == NULL) { + if (uu == NULL || out_buff == NULL) { archive_set_error(&f->archive->archive, ENOMEM, "Can't allocate data for uudecode"); free(uu); free(out_buff); - free(in_buff); return (ARCHIVE_FATAL); } f->data = uu; - uu->in_buff = in_buff; - uu->in_cnt = 0; - uu->in_allocated = IN_BUFF_SIZE; uu->out_buff = out_buff; uu->state = ST_FIND_HEAD; uu->mode_set = 0; @@ -398,45 +383,6 @@ uudecode_bidder_init(struct archive_read_filter *f) return (ARCHIVE_OK); } -static int -ensure_in_buff_size(struct archive_read_filter *f, - struct uu *uu, size_t size) -{ - - if (size > uu->in_allocated) { - unsigned char *ptr; - size_t newsize; - - /* - * Calculate a new buffer size for in_buff. - * Increase its value until it is enough for our needs. - */ - newsize = uu->in_allocated; - do { - if (newsize < IN_BUFF_SIZE*32) - newsize <<= 1; - else - newsize += IN_BUFF_SIZE; - } while (size > newsize); - /* Allocate the new buffer. */ - ptr = malloc(newsize); - if (ptr == NULL) { - archive_set_error(&f->archive->archive, - ENOMEM, - "Can't allocate data for uudecode"); - return (ARCHIVE_FATAL); - } - /* Move the remaining data in in_buff into the new buffer. */ - if (uu->in_cnt) - memmove(ptr, uu->in_buff, uu->in_cnt); - /* Replace in_buff with the new buffer. */ - free(uu->in_buff); - uu->in_buff = ptr; - uu->in_allocated = newsize; - } - return (ARCHIVE_OK); -} - static int uudecode_read_header(struct archive_read_filter *f, struct archive_entry *entry) { @@ -455,138 +401,59 @@ static ssize_t uudecode_filter_read(struct archive_read_filter *f, const void **buff) { struct uu *uu = f->data; - const unsigned char *b, *d; unsigned char *out; - ssize_t avail_in, ravail; - ssize_t used; ssize_t total; - ssize_t len, llen, nl, namelen; -read_more: - d = __archive_read_filter_ahead(f->upstream, 1, &avail_in); - if (d == NULL && avail_in < 0) - return (ARCHIVE_FATAL); - /* Quiet a code analyzer; make sure avail_in must be zero - * when d is NULL. */ - if (d == NULL) - avail_in = 0; - used = 0; - total = 0; out = uu->out_buff; - if (avail_in > 2 * UUENCODE_BID_MAX_READ) - avail_in = 2 * UUENCODE_BID_MAX_READ; - ravail = avail_in; - if (uu->state == ST_IGNORE) { - used = avail_in; - goto finish; - } - if (uu->in_cnt) { - if (uu->in_cnt > UUENCODE_BID_MAX_READ) { - archive_set_error(&f->archive->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid format data"); - return (ARCHIVE_FATAL); - } - /* - * If there is remaining data which is saved by - * a previous call, use it first. - */ - if (ensure_in_buff_size(f, uu, - avail_in + uu->in_cnt) != ARCHIVE_OK) - return (ARCHIVE_FATAL); - if (avail_in > 0) - memcpy(uu->in_buff + uu->in_cnt, - d, avail_in); - d = uu->in_buff; - avail_in += uu->in_cnt; - uu->in_cnt = 0; - } - for (;used < avail_in; d += llen, used += llen) { - ssize_t l, body; + total = 0; - b = d; - len = get_line(b, avail_in - used, &nl); - if (len < 0) { - /* Non-ascii character is found. */ - if (uu->state == ST_FIND_HEAD && - (uu->total > 0 || total > 0)) { - uu->state = ST_IGNORE; - used = avail_in; - goto finish; - } - archive_set_error(&f->archive->archive, - ARCHIVE_ERRNO_MISC, - "Insufficient compressed data"); - return (ARCHIVE_FATAL); - } - if (len > UUENCODE_BID_MAX_READ) { - archive_set_error(&f->archive->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Invalid format data"); - return (ARCHIVE_FATAL); - } - llen = len; - if ((nl == 0) && (uu->state != ST_UUEND)) { - if (total == 0 && ravail <= 0) { - /* There is nothing more to read, fail */ - archive_set_error(&f->archive->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Missing format data"); + while (uu->state != ST_IGNORE) { + const unsigned char *b; + ssize_t unconsumed; + ssize_t l, body; + ssize_t namelen; + size_t len, name_offset; + mode_t mode; + + b = read_line_ahead(f->upstream, total, 0, + &len, &unconsumed); + if (b == NULL) { + if (unconsumed < 0) return (ARCHIVE_FATAL); - } - /* - * Save remaining data which does not contain - * NL('\n','\r'). - */ - if (ensure_in_buff_size(f, uu, len) - != ARCHIVE_OK) - return (ARCHIVE_FATAL); - if (uu->in_buff != b) - memmove(uu->in_buff, b, len); - uu->in_cnt = len; - if (total == 0) { - /* Do not return 0; it means end-of-file. - * We should try to read more bytes. */ - __archive_read_filter_consume( - f->upstream, ravail); - goto read_more; - } - used += len; - break; - } - switch (uu->state) { - default: - case ST_FIND_HEAD: - /* Do not read more than UUENCODE_BID_MAX_READ bytes */ - if (total + len >= UUENCODE_BID_MAX_READ) { + if (unconsumed == MAX_LINE_LENGTH) { archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid format data"); return (ARCHIVE_FATAL); } - if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0) - l = 6; - else if (len - nl >= 18 && - memcmp(b, "begin-base64 ", 13) == 0) - l = 13; - else - l = 0; - if (l != 0 && b[l] >= '0' && b[l] <= '7' && - b[l+1] >= '0' && b[l+1] <= '7' && - b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') { + if (total != 0) + break; + if (uu->state != ST_FIND_HEAD) { + archive_set_error(&f->archive->archive, + ARCHIVE_ERRNO_MISC, + "Insufficient compressed data"); + return (ARCHIVE_FATAL); + } + /* Non-ascii character or end of stream is found. */ + uu->state = ST_IGNORE; + } + + switch (uu->state) { + case ST_IGNORE: + default: + break; + case ST_FIND_HEAD: + l = parse_header(b, len, &mode, &name_offset); + if (l != 0) { if (l == 6) uu->state = ST_READ_UU; else uu->state = ST_READ_BASE64; - uu->mode = (mode_t)( - ((int)(b[l] - '0') * 64) + - ((int)(b[l+1] - '0') * 8) + - (int)(b[l+2] - '0')); + uu->mode = mode; uu->mode_set = 1; - namelen = len - nl - 4 - l; + namelen = len - name_offset; if (namelen > 1) { - if (uu->name != NULL) - free(uu->name); + free(uu->name); uu->name = malloc(namelen + 1); if (uu->name == NULL) { archive_set_error( @@ -596,7 +463,7 @@ read_more: return (ARCHIVE_FATAL); } strncpy(uu->name, - (const char *)(b + l + 4), + (const char *)(b + name_offset), namelen); uu->name[namelen] = '\0'; } @@ -605,7 +472,7 @@ read_more: case ST_READ_UU: if (total + len * 2 > OUT_BUFF_SIZE) goto finish; - body = len - nl; + body = len; if (!uuchar[*b] || body <= 0) { archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, @@ -658,8 +525,8 @@ read_more: } break; case ST_UUEND: - if (len - nl == 3 && memcmp(b, "end ", 3) == 0) - uu->state = ST_FIND_HEAD; + if (len == 3 && memcmp(b, "end", 3) == 0) + uu->state = ST_IGNORE; else { archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC, @@ -670,10 +537,10 @@ read_more: case ST_READ_BASE64: if (total + len * 2 > OUT_BUFF_SIZE) goto finish; - l = len - nl; + l = len; if (l >= 3 && b[0] == '=' && b[1] == '=' && b[2] == '=') { - uu->state = ST_FIND_HEAD; + uu->state = ST_IGNORE; break; } while (l > 0) { @@ -713,14 +580,10 @@ read_more: } break; } + __archive_read_filter_consume(f->upstream, unconsumed); } finish: - if (ravail < avail_in) - used -= avail_in - ravail; - __archive_read_filter_consume(f->upstream, used); - *buff = uu->out_buff; - uu->total += total; return (total); } @@ -729,7 +592,6 @@ uudecode_filter_close(struct archive_read_filter *f) { struct uu *uu = f->data; - free(uu->in_buff); free(uu->out_buff); free(uu->name); free(uu); diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c index 7c8e0cb52e36..b00ba543208c 100644 --- a/libarchive/archive_read_support_filter_xz.c +++ b/libarchive/archive_read_support_filter_xz.c @@ -115,7 +115,7 @@ archive_read_support_filter_xz(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "xz", + if (__archive_read_register_bidder(a, NULL, &xz_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -147,7 +147,7 @@ archive_read_support_filter_lzma(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "lzma", + if (__archive_read_register_bidder(a, NULL, &lzma_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -180,7 +180,7 @@ archive_read_support_filter_lzip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "lzip", + if (__archive_read_register_bidder(a, NULL, &lzip_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/archive_read_support_filter_zstd.c b/libarchive/archive_read_support_filter_zstd.c index 12eac04ca3aa..c676d62b279c 100644 --- a/libarchive/archive_read_support_filter_zstd.c +++ b/libarchive/archive_read_support_filter_zstd.c @@ -25,10 +25,6 @@ #include "archive_platform.h" -#ifdef HAVE_ERRNO_H -#include -#endif - #ifdef HAVE_ERRNO_H #include #endif @@ -87,7 +83,7 @@ archive_read_support_filter_zstd(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - if (__archive_read_register_bidder(a, NULL, "zstd", + if (__archive_read_register_bidder(a, NULL, &zstd_bidder_vtable) != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -109,6 +105,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *b, { const unsigned char *buffer; ssize_t avail; + int bits_checked = 0; /* * Zstandard skippable frames contain a 4 byte magic number followed * by a 4 byte frame data size, then that number of bytes of data. @@ -147,6 +144,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *b, uint32_t frame_data_size; /* Skip over the magic number */ + bits_checked += 28; offset_in_buffer += 4; /* Ensure that we can read another 4 bytes. */ @@ -172,12 +170,10 @@ zstd_bidder_bid(struct archive_read_filter_bidder *b, * if this is zstd data. */ if (archive_ckd_add_size(&min, - offset_in_buffer, min_zstd_frame_size)) + offset_in_buffer, min_zstd_frame_size) || + min > max_lookahead) return (0); if (min > (size_t)avail) { - if (min > max_lookahead) - return (0); - buffer = __archive_read_filter_ahead(f, min, &avail); if (buffer == NULL) @@ -193,7 +189,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *b, */ if (magic_number == zstd_magic) - return (offset_in_buffer + 4); + return (bits_checked + 32); return (0); } diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 9c9f9a0eefdf..254e4a50a3d7 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -60,13 +60,11 @@ #include "archive_time_private.h" #include "archive_endian.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + #define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C" #define SFX_MIN_ADDR 0x27000 -#define SFX_MAX_ADDR 0x60000 +#define SFX_MAX_ADDR 0x80000 #define SFX_MAX_OFFSET (SFX_MAX_ADDR - SFX_MIN_ADDR) #define SFX_MAX_SEEK 0x800000 @@ -353,13 +351,21 @@ struct _7zip { /* Decoding BCJ and BCJ2 data. */ uint32_t bcj_state; - size_t odd_bcj_size; - unsigned char odd_bcj[4]; + size_t odd_bcj_size; /* Number of bytes currently stashed in odd_bcj. */ + /* Bytes that a filter didn't have enough lookahead to transform yet. + * Big enough to hold the largest lookahead (IA64). */ + unsigned char odd_bcj[16]; + /* Decoding BCJ data. */ size_t bcj_prevPosT; uint32_t bcj_prevMask; uint32_t bcj_ip; + /* Decoding Delta data. */ + unsigned delta_dist; + uint8_t delta_pos; + uint8_t delta_history[256]; + /* Decoding BCJ2 data. */ int64_t main_stream_bytes_remaining; unsigned char *sub_stream_buff[3]; @@ -459,10 +465,17 @@ static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); static void arm_Init(struct _7zip *); static size_t arm_Convert(struct _7zip *, uint8_t *, size_t); +static void armthumb_Init(struct _7zip *); +static size_t armthumb_Convert(struct _7zip *, uint8_t *, size_t); static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); static size_t sparc_Convert(struct _7zip *, uint8_t *, size_t); static size_t powerpc_Convert(struct _7zip *, uint8_t *, size_t); +static size_t riscv_Convert(struct _7zip *, uint8_t *, size_t); +static size_t ia64_Convert(struct _7zip *, uint8_t *, size_t); +static void delta_Init(struct _7zip *, unsigned); +static void delta_Decode(struct _7zip *, uint8_t *, size_t); +static size_t bcj_lookahead(int64_t); static int64_t seek_compat(struct archive_read *, int64_t, int, int); @@ -478,8 +491,7 @@ archive_read_support_format_7zip(struct archive *_a) zip = calloc(1, sizeof(*zip)); if (zip == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate 7zip data"); + archive_set_error(_a, ENOMEM, "Can't allocate 7zip data"); return (ARCHIVE_FATAL); } @@ -489,7 +501,6 @@ archive_read_support_format_7zip(struct archive *_a) */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; - r = __archive_read_register_format(a, zip, "7zip", @@ -505,7 +516,7 @@ archive_read_support_format_7zip(struct archive *_a) if (r != ARCHIVE_OK) free(zip); - return (ARCHIVE_OK); + return (r); } static int @@ -531,11 +542,11 @@ archive_read_format_7zip_has_encrypted_entries(struct archive_read *a) static int get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) { - const unsigned char *p; + const unsigned char *h; int64_t offset, sfx_offset; int r, window; - if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) { + if ((h = __archive_read_ahead(a, 6, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); @@ -543,7 +554,7 @@ get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) /* If first six bytes are the 7-Zip signature, * return the offset right now. */ - if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) { + if (memcmp(h, _7ZIP_SIGNATURE, 6) == 0) { *data_offset = 0; return (ARCHIVE_OK); } @@ -556,9 +567,9 @@ get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) * performing a seek, get_elf_sfx_offset requires one, * thus a performance difference between the two is expected. */ - if ((p[0] == 'M' && p[1] == 'Z')) + if ((h[0] == 'M' && h[1] == 'Z')) r = get_pe_sfx_offset(a, &sfx_offset); - else if (memcmp(p, "\x7F\x45LF", 4) == 0) + else if (memcmp(h, "\x7F\x45LF", 4) == 0) r = get_elf_sfx_offset(a, &sfx_offset, compat); else r = ARCHIVE_FATAL; @@ -567,27 +578,29 @@ get_data_offset(struct archive_read *a, int64_t *data_offset, int compat) offset = sfx_offset; window = 4096; - while (offset + window <= (sfx_offset + SFX_MAX_OFFSET)) { +#define SFX_MAX_READAHEAD (sfx_offset + SFX_MAX_OFFSET) + while (offset + window <= SFX_MAX_READAHEAD) { ssize_t bytes_avail; - const unsigned char *buff = __archive_read_ahead(a, - offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - goto fail; - continue; + + h = __archive_read_ahead(a, offset + window, &bytes_avail); + if (h == NULL) { + if (bytes_avail >= offset + 32) { + /* Remaining bytes are less than window. */ + window = bytes_avail - offset; + continue; + } + goto fail; } - p = buff + offset; - while (buff + bytes_avail - p >= 32) { - size_t step = check_7zip_header_in_sfx(p); + if (bytes_avail > SFX_MAX_READAHEAD) + bytes_avail = SFX_MAX_READAHEAD; + while (offset <= bytes_avail - 32) { + size_t step = check_7zip_header_in_sfx(h + offset); if (step == 0) { - *data_offset = p - buff; + *data_offset = offset; return (ARCHIVE_OK); } - p += step; + offset += step; } - offset = p - buff; } fail: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -600,9 +613,9 @@ archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { int64_t data_offset; - /* If someone has already bid more than 32, then avoid + /* If someone has already bid more than 48, then avoid trashing the look-ahead buffers with a seek. */ - if (best_bid > 32) + if (best_bid > 48) return (-1); if (get_data_offset(a, &data_offset, 0) < 0) @@ -623,7 +636,7 @@ check_7zip_header_in_sfx(const unsigned char *p) * Magic Code, so we should do this in order not to * make a mis-detection. */ - if (crc32(0, p + 12, 20) != archive_le32dec(p + 8)) + if (__archive_crc32(0, p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); @@ -965,7 +978,13 @@ archive_read_format_7zip_read_header(struct archive_read *a, } } - if (archive_entry_copy_pathname_l(entry, + if (zip_entry->utf16name == NULL) { + /* + * No kName property (eg from`7z a -si`): 7-Zip derives the + * name from the archive filename, which we don't know. + */ + archive_entry_set_pathname(entry, "data"); + } else if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->sconv) != 0) { if (errno == ENOMEM) { @@ -1135,7 +1154,7 @@ archive_read_format_7zip_read_data(struct archive_read *a, /* Update checksum */ if ((zip->entry->flg & CRC32_IS_SET) && bytes) - zip->entry_crc32 = crc32(zip->entry_crc32, *buff, + zip->entry_crc32 = __archive_crc32(zip->entry_crc32, *buff, (unsigned)bytes); /* If we hit the end, swallow any end-of-data marker. */ @@ -1302,6 +1321,15 @@ ppmd_read(void *p) * and we are on boundary; * last resort to read using __archive_read_ahead. */ + if (zip->pack_stream_inbytes_remaining <= 0 || + zip->ppstream.stream_in >= + (uint64_t)zip->pack_stream_inbytes_remaining) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated 7z file data"); + zip->ppstream.overconsumed = 1; + return (0); + } const uint8_t *data = __archive_read_ahead(a, zip->ppstream.stream_in + 1, NULL); if (data == NULL) { @@ -1339,9 +1367,13 @@ init_decompression(struct archive_read *a, struct _7zip *zip, if (coder2->codec != _7Z_X86 && coder2->codec != _7Z_X86_BCJ2 && coder2->codec != _7Z_ARM && + coder2->codec != _7Z_ARMTHUMB && coder2->codec != _7Z_ARM64 && + coder2->codec != _7Z_IA64 && coder2->codec != _7Z_POWERPC && - coder2->codec != _7Z_SPARC) { + coder2->codec != _7Z_RISCV && + coder2->codec != _7Z_SPARC && + coder2->codec != _7Z_DELTA) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %jx for %jx", @@ -1349,12 +1381,29 @@ init_decompression(struct archive_read *a, struct _7zip *zip, (uintmax_t)coder1->codec); return (ARCHIVE_FAILED); } + if (coder2->codec == _7Z_DELTA && + coder2->propertiesSize != 1) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Invalid Delta parameter"); + return (ARCHIVE_FAILED); + } zip->codec2 = coder2->codec; zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); else if (coder2->codec == _7Z_ARM) arm_Init(zip); + else if (coder2->codec == _7Z_ARMTHUMB) + armthumb_Init(zip); + else if (coder2->codec == _7Z_ARM64 || + coder2->codec == _7Z_IA64 || + coder2->codec == _7Z_POWERPC || + coder2->codec == _7Z_RISCV || + coder2->codec == _7Z_SPARC) + zip->bcj_ip = 0; + else if (coder2->codec == _7Z_DELTA) + delta_Init(zip, coder2->properties[0] + 1); } break; default: @@ -1407,73 +1456,86 @@ init_decompression(struct archive_read *a, struct _7zip *zip, zip->codec2 = coder2->codec; filters[fi].options = NULL; - switch (zip->codec2) { - case _7Z_X86: - if (zip->codec == _7Z_LZMA2) { + /* + * Only LZMA2 gets liblzma's branch filters; for LZMA1 + * we run our own instead, for the reason given above. + */ + if (zip->codec != _7Z_LZMA2 && + bcj_lookahead(zip->codec2) != 0) { + /* Use our filter. */ + if (zip->codec2 == _7Z_X86) + x86_Init(zip); + else if (zip->codec2 == _7Z_ARM) + arm_Init(zip); + else if (zip->codec2 == _7Z_ARMTHUMB) + armthumb_Init(zip); + else /* ARM64, IA64, POWERPC, RISCV, SPARC. */ + zip->bcj_ip = 0; + } else { + switch (zip->codec2) { + case _7Z_X86: filters[fi].id = LZMA_FILTER_X86; fi++; - } else + break; + case _7Z_X86_BCJ2: /* Use our filter. */ - x86_Init(zip); - break; - case _7Z_X86_BCJ2: - /* Use our filter. */ - 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 = - coder2->properties[0] + 1; - filters[fi].options = &delta_opt; - fi++; - break; - /* Following filters have not been tested yet. */ - case _7Z_POWERPC: - filters[fi].id = LZMA_FILTER_POWERPC; - fi++; - break; - case _7Z_IA64: - filters[fi].id = LZMA_FILTER_IA64; - fi++; - break; - case _7Z_ARM: - filters[fi].id = LZMA_FILTER_ARM; - fi++; - break; - case _7Z_ARMTHUMB: - filters[fi].id = LZMA_FILTER_ARMTHUMB; - fi++; - break; + 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 = + coder2->properties[0] + 1; + filters[fi].options = &delta_opt; + fi++; + break; + /* Following filters have not been tested yet. */ + case _7Z_POWERPC: + filters[fi].id = LZMA_FILTER_POWERPC; + fi++; + break; + case _7Z_IA64: + filters[fi].id = LZMA_FILTER_IA64; + fi++; + break; + case _7Z_ARM: + filters[fi].id = LZMA_FILTER_ARM; + fi++; + break; + case _7Z_ARMTHUMB: + filters[fi].id = LZMA_FILTER_ARMTHUMB; + fi++; + break; #ifdef LZMA_FILTER_ARM64 - case _7Z_ARM64: - filters[fi].id = LZMA_FILTER_ARM64; - fi++; - break; + case _7Z_ARM64: + filters[fi].id = LZMA_FILTER_ARM64; + fi++; + break; #endif #ifdef LZMA_FILTER_RISCV - case _7Z_RISCV: - filters[fi].id = LZMA_FILTER_RISCV; - fi++; - break; + case _7Z_RISCV: + filters[fi].id = LZMA_FILTER_RISCV; + fi++; + break; #endif - case _7Z_SPARC: - filters[fi].id = LZMA_FILTER_SPARC; - fi++; - break; - default: - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Unexpected codec ID: %jX", - (uintmax_t)zip->codec2); - return (ARCHIVE_FAILED); + case _7Z_SPARC: + filters[fi].id = LZMA_FILTER_SPARC; + fi++; + break; + default: + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Unexpected codec ID: %jX", + (uintmax_t)zip->codec2); + return (ARCHIVE_FAILED); + } } } @@ -1672,6 +1734,7 @@ decompress(struct archive_read *a, struct _7zip *zip, size_t t_avail_in, t_avail_out; uint8_t *bcj2_next_out; size_t bcj2_avail_out; + size_t odd_bcj_out = 0; int r, ret = ARCHIVE_OK; t_avail_in = o_avail_in = *used; @@ -1679,12 +1742,14 @@ decompress(struct archive_read *a, struct _7zip *zip, t_next_in = b; t_next_out = buff; - if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { + if (zip->codec != _7Z_LZMA2 && bcj_lookahead(zip->codec2) != 0) { int i; + size_t min_out = bcj_lookahead(zip->codec2); /* Do not copy out the BCJ remaining bytes when the output - * buffer size is less than five bytes. */ - if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) { + * buffer size is less than the filter's lookahead size. */ + if (o_avail_in != 0 && t_avail_out < min_out && + zip->odd_bcj_size) { *used = 0; *outbytes = 0; return (ret); @@ -1693,8 +1758,17 @@ decompress(struct archive_read *a, struct _7zip *zip, *t_next_out++ = zip->odd_bcj[i]; t_avail_out--; zip->odd_bcj_size--; + odd_bcj_out++; } - if (o_avail_in == 0 || t_avail_out == 0) { + /* + * PPMd is the only codec which can still produce output + * after its packed stream has been consumed: it is asked + * to flush the rest of the folder when called with no + * input. Returning here would skip that flush and leave + * the folder short, so let it through. + */ + if ((o_avail_in == 0 && zip->codec != _7Z_PPMD) || + t_avail_out == 0) { *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0) @@ -1898,10 +1972,20 @@ decompress(struct archive_read *a, struct _7zip *zip, zip->ppmd7_stat = 1; } - if (t_avail_in == 0) - /* XXX Flush out remaining decoded data XXX */ - flush_bytes = zip->folder_outbytes_remaining; - else + if (t_avail_in == 0) { + /* XXX Flush out remaining decoded data XXX + * + * The bytes a BCJ filter parked on the previous call + * have just been written to the output buffer, and + * count towards what the folder still owes. Asking + * PPMd to decode them a second time would run the + * range decoder off the end of the packed stream. */ + flush_bytes = zip->folder_outbytes_remaining - + (int64_t)odd_bcj_out; + /* Those bytes were all that was left of the folder. */ + if (flush_bytes <= 0) + break; + } else flush_bytes = 0; do { @@ -1939,34 +2023,64 @@ decompress(struct archive_read *a, struct _7zip *zip, if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF) return (ret); - *used = o_avail_in - t_avail_in; + if (zip->codec == _7Z_PPMD) + *used = zip->ppstream.stream_in; + else + *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; /* * Decode BCJ. */ - if (zip->codec != _7Z_LZMA2) { - if (zip->codec2 == _7Z_X86) { - size_t l = x86_Convert(zip, buff, *outbytes); + if (zip->codec != _7Z_LZMA2 && bcj_lookahead(zip->codec2) != 0) { + size_t l; - zip->odd_bcj_size = *outbytes - l; - if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && - o_avail_in && ret != ARCHIVE_EOF) { - memcpy(zip->odd_bcj, - ((unsigned char *)buff) + l, - zip->odd_bcj_size); - *outbytes = l; - } else - zip->odd_bcj_size = 0; - } else if (zip->codec2 == _7Z_ARM) { - *outbytes = arm_Convert(zip, buff, *outbytes); - } else if (zip->codec2 == _7Z_ARM64) { - *outbytes = arm64_Convert(zip, buff, *outbytes); - } else if (zip->codec2 == _7Z_SPARC) { - *outbytes = sparc_Convert(zip, buff, *outbytes); - } else if (zip->codec2 == _7Z_POWERPC) { - *outbytes = powerpc_Convert(zip, buff, *outbytes); + switch (zip->codec2) { + case _7Z_X86: + l = x86_Convert(zip, buff, *outbytes); + break; + case _7Z_ARM: + l = arm_Convert(zip, buff, *outbytes); + break; + case _7Z_ARMTHUMB: + l = armthumb_Convert(zip, buff, *outbytes); + break; + case _7Z_ARM64: + l = arm64_Convert(zip, buff, *outbytes); + break; + case _7Z_SPARC: + l = sparc_Convert(zip, buff, *outbytes); + break; + case _7Z_POWERPC: + l = powerpc_Convert(zip, buff, *outbytes); + break; + case _7Z_RISCV: + l = riscv_Convert(zip, buff, *outbytes); + break; + case _7Z_IA64: + l = ia64_Convert(zip, buff, *outbytes); + break; + default: + /* Unreachable: bcj_lookahead() returns non-zero only + * for the codecs handled above. Guards against the two + * lists drifting apart. */ + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "BCJ filter internal error"); + return (ARCHIVE_FAILED); } + + /* Keep the bytes which could not be converted yet, they are + * prepended to the output buffer on the next call. */ + zip->odd_bcj_size = *outbytes - l; + if (zip->odd_bcj_size > 0 && + zip->odd_bcj_size <= sizeof(zip->odd_bcj) && + o_avail_in && ret != ARCHIVE_EOF) { + memcpy(zip->odd_bcj, + ((unsigned char *)buff) + l, + zip->odd_bcj_size); + *outbytes = l; + } else + zip->odd_bcj_size = 0; } /* @@ -1995,6 +2109,13 @@ decompress(struct archive_read *a, struct _7zip *zip, *outbytes = o_avail_out - bcj2_avail_out; } + /* + * Decode Delta. LZMA1 and LZMA2 use liblzma's Delta filter instead. + */ + if (zip->codec2 == _7Z_DELTA && + zip->codec != _7Z_LZMA && zip->codec != _7Z_LZMA2) + delta_Decode(zip, buff, *outbytes); + return (ret); } @@ -3176,19 +3297,19 @@ read_Times(struct archive_read *a, int type) goto failed; switch (type) { case kCTime: - ntfs_to_unix(archive_le64dec(p), + __archive_ntfs_to_unix(archive_le64dec(p), &(entries[i].ctime), &(entries[i].ctime_ns)); entries[i].flg |= CTIME_IS_SET; break; case kATime: - ntfs_to_unix(archive_le64dec(p), + __archive_ntfs_to_unix(archive_le64dec(p), &(entries[i].atime), &(entries[i].atime_ns)); entries[i].flg |= ATIME_IS_SET; break; case kMTime: - ntfs_to_unix(archive_le64dec(p), + __archive_ntfs_to_unix(archive_le64dec(p), &(entries[i].mtime), &(entries[i].mtime_ns)); entries[i].flg |= MTIME_IS_SET; @@ -3267,7 +3388,7 @@ header_bytes(struct archive_read *a, size_t rbytes) } /* Update checksum */ - zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes); + zip->header_crc32 = __archive_crc32(zip->header_crc32, p, (unsigned)rbytes); return (p); } @@ -3306,7 +3427,7 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip, } /* CRC check. */ - if (crc32(0, p + 12, 20) + if (__archive_crc32(0, p + 12, 20) != archive_le32dec(p + 8)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, -1, "Header CRC error"); @@ -3650,8 +3771,9 @@ extract_pack_stream(struct archive_read *a, size_t minimum) if (zip->uncompressed_buffer_bytes_remaining == zip->uncompressed_buffer_size) break; - if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size && - zip->uncompressed_buffer_bytes_remaining + 5 > + if (zip->odd_bcj_size && + zip->uncompressed_buffer_bytes_remaining + + bcj_lookahead(zip->codec2) > zip->uncompressed_buffer_size) break; if (zip->pack_stream_inbytes_remaining == 0 && @@ -4280,6 +4402,54 @@ arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size) return i; } +static void +armthumb_Init(struct _7zip *zip) +{ + zip->bcj_ip = 4; +} + +static size_t +armthumb_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * SPDX-License-Identifier: 0BSD + */ + + size_t i; + uint32_t addr; + + for (i = 0; i + 4 <= size; i += 2) { + if ((buf[i + 1] & 0xF8) == 0xF0 + && (buf[i + 3] & 0xF8) == 0xF8) { + addr = (((uint32_t)buf[i + 1] & 0x07) << 19) + | ((uint32_t)buf[i] << 11) + | (((uint32_t)buf[i + 3] & 0x07) << 8) + | (uint32_t)buf[i + 2]; + addr <<= 1; + addr -= zip->bcj_ip + (uint32_t)i; + addr >>= 1; + buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07)); + buf[i] = (uint8_t)(addr >> 11); + buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07)); + buf[i + 2] = (uint8_t)addr; + i += 2; + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + static size_t arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) { @@ -4443,6 +4613,260 @@ powerpc_Convert(struct _7zip *zip, uint8_t *buf, size_t size) return i; } +static size_t +riscv_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_riscv(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * SPDX-License-Identifier: 0BSD + */ + + if (size < 8) + return 0; + size -= 8; + + size_t i; + for (i = 0; i <= size; i += 2) { + uint32_t inst = buf[i]; + + if (inst == 0xEF) { + // JAL. Only filter rd=x1(ra) and rd=x5(t0). + const uint32_t b1 = buf[i + 1]; + + if ((b1 & 0x0D) != 0) + continue; + + const uint32_t b2 = buf[i + 2]; + const uint32_t b3 = buf[i + 3]; + + uint32_t addr = ((b1 & 0xF0) << 13) + | (b2 << 9) | (b3 << 1); + addr -= zip->bcj_ip + (uint32_t)i; + + buf[i + 1] = (uint8_t)((b1 & 0x0F) + | ((addr >> 8) & 0xF0)); + buf[i + 2] = (uint8_t)(((addr >> 16) & 0x0F) + | ((addr >> 7) & 0x10) + | ((addr << 4) & 0xE0)); + buf[i + 3] = (uint8_t)(((addr >> 4) & 0x7F) + | ((addr >> 13) & 0x80)); + + i += 4 - 2; + } else if ((inst & 0x7F) == 0x17) { + // AUIPC, paired with a second instruction (inst2) + // that is within the next four bytes. + uint32_t inst2; + + inst |= (uint32_t)buf[i + 1] << 8; + inst |= (uint32_t)buf[i + 2] << 16; + inst |= (uint32_t)buf[i + 3] << 24; + + if (inst & 0xE80) { + // AUIPC's rd doesn't equal x0 or x2. + inst2 = archive_le32dec(buf + i + 4); + + if (((inst << 8) ^ (inst2 - 3)) & 0xF8003) { + i += 6 - 2; + continue; + } + + uint32_t addr = (inst & 0xFFFFF000) + + (inst2 >> 20); + + inst = 0x17 | (2 << 7) | (inst2 << 12); + inst2 = addr; + } else { + // AUIPC's rd equals x0 or x2. + const uint32_t inst2_rs1 = inst >> 27; + + if ((uint32_t)((inst - 0x3117) << 18) + >= (inst2_rs1 & 0x1D)) { + i += 4 - 2; + continue; + } + + uint32_t addr = archive_be32dec(buf + i + 4); + addr -= zip->bcj_ip + (uint32_t)i; + + inst2 = (inst >> 12) | (addr << 20); + inst = 0x17 | (inst2_rs1 << 7) + | ((addr + 0x800) & 0xFFFFF000); + } + + archive_le32enc(buf + i, inst); + archive_le32enc(buf + i + 4, inst2); + + i += 8 - 2; + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + +static size_t +ia64_Convert(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This function was adapted from + // static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size) + // in https://git.tukaani.org/xz-embedded.git + + /* + * Branch/Call/Jump (BCJ) filter decoders + * + * Authors: Lasse Collin + * Igor Pavlov + * + * SPDX-License-Identifier: 0BSD + */ + + static const uint8_t branch_table[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 + }; + + /* Loop counters */ + size_t i; + size_t j; + + /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */ + uint32_t slot; + + /* Bitwise offset of the instruction indicated by slot */ + uint32_t bit_pos; + + /* bit_pos split into byte and bit parts */ + uint32_t byte_pos; + uint32_t bit_res; + + /* Address part of an instruction */ + uint32_t addr; + + /* Mask used to detect which instructions to convert */ + uint32_t mask; + + /* 41-bit instruction stored somewhere in the lowest 48 bits */ + uint64_t instr; + + /* Instruction normalized with bit_res for easier manipulation */ + uint64_t norm; + + size &= ~(size_t)15; + + for (i = 0; i < size; i += 16) { + mask = branch_table[buf[i] & 0x1F]; + for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) { + if (((mask >> slot) & 1) == 0) + continue; + + byte_pos = bit_pos >> 3; + bit_res = bit_pos & 7; + instr = 0; + for (j = 0; j < 6; ++j) + instr |= (uint64_t)(buf[i + j + byte_pos]) + << (8 * j); + + norm = instr >> bit_res; + + if (((norm >> 37) & 0x0F) == 0x05 + && ((norm >> 9) & 0x07) == 0) { + addr = (norm >> 13) & 0x0FFFFF; + addr |= ((uint32_t)(norm >> 36) & 1) << 20; + addr <<= 4; + addr -= zip->bcj_ip + (uint32_t)i; + addr >>= 4; + + norm &= ~((uint64_t)0x8FFFFF << 13); + norm |= (uint64_t)(addr & 0x0FFFFF) << 13; + norm |= (uint64_t)(addr & 0x100000) + << (36 - 20); + + instr &= (1U << bit_res) - 1; + instr |= norm << bit_res; + + for (j = 0; j < 6; j++) + buf[i + j + byte_pos] + = (uint8_t)(instr >> (8 * j)); + } + } + } + + zip->bcj_ip += (uint32_t)i; + + return i; +} + +static void +delta_Init(struct _7zip *zip, unsigned dist) +{ + zip->delta_dist = dist; + zip->delta_pos = 0; + memset(zip->delta_history, 0, sizeof(zip->delta_history)); +} + +static void +delta_Decode(struct _7zip *zip, uint8_t *buf, size_t size) +{ + // This was adapted from src/liblzma/delta/delta_decoder.c's + // static void decode_buffer(lzma_delta_coder *coder, uint8_t *buffer, size_t size) + // function in https://github.com/tukaani-project/xz.git + + /* + * Delta filter decoder + * + * Author: Lasse Collin + * + * SPDX-License-Identifier: 0BSD + */ + + size_t i; + + for (i = 0; i < size; i++) { + buf[i] += zip->delta_history[ + (zip->delta_dist + zip->delta_pos) & 0xFF]; + zip->delta_history[zip->delta_pos--] = buf[i]; + } +} + +/* + * The widest instruction the BCJ filters rewrite, and the smallest output + * buffer that those filters can make progress with. Filters leave up to one + * byte less than this unconverted at the end of a buffer (when they don't + * have enough data to continue). Returns 0 for codecs we do not filter + * ourselves. + */ +static size_t +bcj_lookahead(int64_t codec2) +{ + switch (codec2) { + case _7Z_X86: + return (5); + case _7Z_RISCV: + return (8); + case _7Z_IA64: + return (16); + case _7Z_ARM: + case _7Z_ARMTHUMB: + case _7Z_ARM64: + case _7Z_POWERPC: + case _7Z_SPARC: + return (4); + default: + return (0); + } +} + /* * Brought from LZMA SDK. * diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 8585de28fd14..2445e7b6301e 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -106,8 +106,7 @@ archive_read_support_format_ar(struct archive *_a) ar = calloc(1, sizeof(*ar)); if (ar == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate ar data"); + archive_set_error(_a, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } ar->strtab = NULL; @@ -125,11 +124,9 @@ archive_read_support_format_ar(struct archive *_a) NULL, NULL); - if (r != ARCHIVE_OK) { + if (r != ARCHIVE_OK) free(ar); - return (r); - } - return (ARCHIVE_OK); + return (r); } static int @@ -251,56 +248,66 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, */ ar_parse_common_header(ar, entry, h); + /* + * '/' is the ranlib index. + * Ignore it since it contains linker information. + */ + if (strcmp(filename, "/") == 0) { + /* This is not a file entry. Ignore. */ + archive_entry_copy_pathname(entry, NULL); + + /* Skip special file entry. */ + return (archive_read_format_ar_skip(a)); + } + /* * '//' is the GNU filename table. * Later entries can refer to names in this table. */ if (strcmp(filename, "//") == 0) { char *st; - size_t entry_size; + int64_t entry_size; + size_t strtab_size; + + /* This is not a file entry. Ignore. */ + archive_entry_copy_pathname(entry, NULL); - archive_entry_copy_pathname(entry, filename); /* Get the size of the filename table. */ - number = ar_atol10(h + AR_size_offset, AR_size_size); - if (number > SIZE_MAX || number > 1024 * 1024 * 1024) { + entry_size = archive_entry_size(entry); + if (entry_size == 0 || entry_size > 1024 * 1024 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Filename table too large"); - return (ARCHIVE_FATAL); - } - entry_size = (size_t)number; - if (entry_size == 0) { - archive_set_error(&a->archive, EINVAL, - "Invalid string table"); + "Invalid filename table size"); return (ARCHIVE_FATAL); } + strtab_size = (size_t)entry_size; if (ar->strtab != NULL) { archive_set_error(&a->archive, EINVAL, - "More than one string table exists"); + "More than one filename table exists"); return (ARCHIVE_FATAL); } /* Read the filename table into memory. */ - st = malloc(entry_size); + st = malloc(strtab_size); if (st == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename table buffer"); return (ARCHIVE_FATAL); } ar->strtab = st; - ar->strtab_size = entry_size; + ar->strtab_size = strtab_size; if (*unconsumed) { __archive_read_consume(a, *unconsumed); *unconsumed = 0; } - if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL) + if ((b = __archive_read_ahead(a, strtab_size, NULL)) == NULL) + return (ARCHIVE_FATAL); + memcpy(st, b, strtab_size); + + /* Skip special file entry. */ + if (archive_read_format_ar_skip(a) != ARCHIVE_OK) return (ARCHIVE_FATAL); - memcpy(st, b, entry_size); - __archive_read_consume(a, entry_size); - /* All contents are consumed. */ - ar->entry_bytes_remaining = 0; - archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Parse the filename table. */ return (ar_parse_gnu_filename_table(a)); @@ -415,17 +422,20 @@ archive_read_format_ar_read_header(struct archive_read *a, a->archive.archive_format = ARCHIVE_FORMAT_AR; } - /* Read the header for the next file entry. */ - if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL) - /* Broken header. */ - return (ARCHIVE_EOF); + do { + /* Read the header for the next file entry. */ + if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL) + /* Broken header. */ + return (ARCHIVE_EOF); - unconsumed = 60; + unconsumed = 60; - ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed); + ret = _ar_read_header(a, entry, ar, (const char *)header_data, + &unconsumed); - if (unconsumed) - __archive_read_consume(a, unconsumed); + if (unconsumed) + __archive_read_consume(a, unconsumed); + } while (ret == ARCHIVE_OK && archive_entry_pathname(entry) == NULL); return ret; } @@ -522,18 +532,20 @@ ar_parse_gnu_filename_table(struct archive_read *a) size = ar->strtab_size; for (p = ar->strtab; p < ar->strtab + size - 1; ++p) { - if (*p == '/') { + /* Normalize backslash, even before separator check. */ + if (*p == '\\') + *p = '/'; + if (*p == '/' && *(p + 1) == '\n') { *p++ = '\0'; - if (*p != '\n') - goto bad_string_table; *p = '\0'; } } /* * GNU ar always pads the table to an even size. * The pad character is either '\n' or '`'. + * For Windows Archive files, the pad character can be '\0'. */ - if (p != ar->strtab + size && *p != '\n' && *p != '`') + if (p != ar->strtab + size && *p != '\n' && *p != '`' && *p != '\0') goto bad_string_table; /* Enforce zero termination. */ diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index f90250e1952e..4432b89ecb53 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -187,6 +187,8 @@ struct lzx_dec { uint8_t *w_buff; /* The insert position to the window. */ size_t w_pos; + /* The number of initialized bytes in the window. */ + size_t w_filled; /* The position where we can copy decoded code from the window. */ size_t copy_pos; /* The length how many bytes we can copy decoded code from @@ -284,6 +286,7 @@ struct lzx_stream { #define MAX_UNCOMPRESS_SIZE 0x8000 #define MAX_FILE_SIZE (UINT16_MAX * MAX_UNCOMPRESS_SIZE) #define MAX_E8_TRANSLATION (0x8000 * MAX_UNCOMPRESS_SIZE) +#define SFX_MAX_READAHEAD (1024 * 512) static const char * const compression_name[] = { "NONE", @@ -459,14 +462,13 @@ archive_read_support_format_cab(struct archive *_a) cab = calloc(1, sizeof(*cab)); if (cab == NULL) { - archive_set_error(&a->archive, ENOMEM, + archive_set_error(_a, ENOMEM, "Can't allocate CAB data"); return (ARCHIVE_FATAL); } archive_string_init(&cab->ws); if (archive_wstring_ensure(&cab->ws, 256) == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory"); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); free(cab); return (ARCHIVE_FATAL); } @@ -488,10 +490,10 @@ archive_read_support_format_cab(struct archive *_a) archive_wstring_free(&cab->ws); free(cab); } - return (ARCHIVE_OK); + return (r); } -static int +static size_t find_cab_magic(const char *p) { switch (p[4]) { @@ -517,46 +519,54 @@ find_cab_magic(const char *p) static int archive_read_format_cab_bid(struct archive_read *a, int best_bid) { - const char *p; - ssize_t bytes_avail, offset, window; + const char *h; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 64) return (-1); - if ((p = __archive_read_ahead(a, 8, NULL)) == NULL) + if ((h = __archive_read_ahead(a, 16, NULL)) == NULL) return (-1); - if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) - return (64); + if (memcmp(h, "MSCF", 4) == 0) { + if (memcmp(h + 4, "\0\0\0\0", 4) == 0) + return (64); + /* Some archives do not have all reserved bits set to zero. */ + if (memcmp(h + 12, "\0\0\0\0", 4) == 0) + return (32); + } /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward * up to 128k for an 'MSCF' marker. */ - if (p[0] == 'M' && p[1] == 'Z') { + if (h[0] == 'M' && h[1] == 'Z') { + ssize_t bytes_avail; + ssize_t offset, window; + offset = 0; window = 4096; - while (offset < (1024 * 128)) { - const char *h = __archive_read_ahead(a, offset + window, + while (offset + window <= SFX_MAX_READAHEAD) { + h = __archive_read_ahead(a, offset + window, &bytes_avail); if (h == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 128) - return (0); - continue; + if (bytes_avail >= offset + 8) { + /* Remaining bytes are less than window. */ + window = bytes_avail - offset; + continue; + } + return (0); } - p = h + offset; - while (p + 8 < h + bytes_avail) { - int next; - if ((next = find_cab_magic(p)) == 0) + if (bytes_avail > SFX_MAX_READAHEAD) + bytes_avail = SFX_MAX_READAHEAD; + while (offset <= bytes_avail - 8) { + size_t next = find_cab_magic(h + offset); + if (next == 0) return (64); - p += next; + offset += next; } - offset = p - h; } } return (0); @@ -619,8 +629,8 @@ cab_skip_sfx(struct archive_read *a) * like the cab header. */ while (p + 8 < q) { - int next; - if ((next = find_cab_magic(p)) == 0) { + size_t next = find_cab_magic(p); + if (next == 0) { skip = p - h; __archive_read_consume(a, skip); return (ARCHIVE_OK); @@ -1048,8 +1058,16 @@ archive_read_format_cab_read_header(struct archive_read *a, } /* If a cffolder of this file is changed, reset a cfdata to read * file contents from next cfdata. */ - if (prev_folder != cab->entry_cffolder) + if (prev_folder != cab->entry_cffolder) { cab->entry_cfdata = NULL; + /* + * Bytes of files we skipped so far belong to folders we + * are done with; reading this file will seek directly to + * the data of the new folder, so we must not charge the + * skipped bytes against the new folder's data. + */ + cab->bytes_skipped = 0; + } /* If a pathname is UTF-8, prepare a string conversion object * for UTF-8 and use it. */ @@ -2252,6 +2270,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits) } ds->w_pos = 0; + ds->w_filled = 0; ds->state = ST_RD_TRANSLATION; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; @@ -2707,6 +2726,11 @@ lzx_read_blocks(struct lzx_stream *strm, int last) strm->avail_out -= l; strm->total_out += l; ds->w_pos = (ds->w_pos + l) & ds->w_mask; + if (ds->w_filled < ds->w_size) { + ds->w_filled += l; + if (ds->w_filled > ds->w_size) + ds->w_filled = ds->w_size; + } ds->block_bytes_avail -= l; } /* FALL THROUGH */ @@ -2753,7 +2777,7 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Pre-tree for first 256 elements of main tree. */ - if (lzx_read_pre_tree(strm) < 0) { + if (lzx_read_pre_tree(strm) == ARCHIVE_EOF) { ds->state = ST_RD_PRE_MAIN_TREE_256; if (last) goto failed; @@ -2781,7 +2805,7 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Pre-tree for remaining elements of main tree. */ - if (lzx_read_pre_tree(strm) < 0) { + if (lzx_read_pre_tree(strm) == ARCHIVE_EOF) { ds->state = ST_RD_PRE_MAIN_TREE_REM; if (last) goto failed; @@ -2811,7 +2835,7 @@ lzx_read_blocks(struct lzx_stream *strm, int last) /* * Read Pre-tree for remaining elements of main tree. */ - if (lzx_read_pre_tree(strm) < 0) { + if (lzx_read_pre_tree(strm) == ARCHIVE_EOF) { ds->state = ST_RD_PRE_LENGTH_TREE; if (last) goto failed; @@ -2862,6 +2886,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) uint8_t mt_lookup_bits = mt->lookup_bits; size_t copy_len = ds->copy_len, copy_pos = ds->copy_pos; size_t w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; + size_t w_filled = ds->w_filled; uint8_t length_header = ds->length_header; uint8_t offset_bits = ds->offset_bits; uint16_t position_slot = ds->position_slot; @@ -2886,6 +2911,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->w_pos = w_pos; + ds->w_filled = w_filled; strm->avail_out = endp - noutp; return (ARCHIVE_EOF); } @@ -2922,6 +2948,8 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) * afterward. */ w_buff[w_pos] = c; w_pos = (w_pos + 1) & w_mask; + if (w_filled < w_size) + w_filled++; /* Store the decoded code to output buffer. */ *noutp++ = c; block_bytes_avail--; @@ -3049,6 +3077,8 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) /* * Compute a real position in window. */ + if (copy_pos == 0 || copy_pos > w_filled) + goto failed; copy_pos = (w_pos - copy_pos) & w_mask; /* FALL THROUGH */ case ST_COPY: @@ -3086,6 +3116,11 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) noutp += l; copy_pos = (copy_pos + l) & w_mask; w_pos = (w_pos + l) & w_mask; + if (w_filled < w_size) { + w_filled += l; + if (w_filled > w_size) + w_filled = w_size; + } block_bytes_avail -= l; if (copy_len <= l) /* A copy of current pattern ended. */ @@ -3114,6 +3149,7 @@ next_data: ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->state = state; ds->w_pos = w_pos; + ds->w_filled = w_filled; strm->avail_out = endp - noutp; return (ARCHIVE_OK); } @@ -3240,7 +3276,8 @@ lzx_huffman_init(struct huffman *hf, uint16_t symbol_count, uint8_t tbl_bits) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; } else - memset(hf->tbl, 0, tbl_size * sizeof(hf->bitlen[0])); + memset(hf->tbl, 0, + ((size_t)1 << hf->tbl_bits) * sizeof(hf->tbl[0])); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 7902cdf84918..9dba42218114 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -173,9 +173,7 @@ struct links_entry { char *name; }; -#define CPIO_MAGIC 0x13141516 struct cpio { - int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; @@ -234,10 +232,9 @@ archive_read_support_format_cpio(struct archive *_a) cpio = calloc(1, sizeof(*cpio)); if (cpio == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); + archive_set_error(_a, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } - cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, @@ -254,7 +251,7 @@ archive_read_support_format_cpio(struct archive *_a) if (r != ARCHIVE_OK) free(cpio); - return (ARCHIVE_OK); + return (r); } @@ -262,16 +259,16 @@ static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { struct cpio *cpio = a->format->data; - const unsigned char *p; + const void *h; int bid; (void)best_bid; /* UNUSED */ - if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) + if ((h = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); bid = 0; - if (memcmp(p, "070707", 6) == 0) { + if (memcmp(h, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; @@ -279,7 +276,7 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ - } else if (memcmp(p, "070727", 6) == 0) { + } else if (memcmp(h, "070727", 6) == 0) { /* afio large ASCII cpio archive */ cpio->read_header = header_odc; bid += 48; @@ -287,7 +284,7 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that almost hex * digits appear in appropriate header locations. XXX */ - } else if (memcmp(p, "070701", 6) == 0) { + } else if (memcmp(h, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; @@ -295,7 +292,7 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ - } else if (memcmp(p, "070702", 6) == 0) { + } else if (memcmp(h, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; @@ -304,12 +301,12 @@ archive_read_format_cpio_bid(struct archive_read *a, int best_bid) * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ - } else if (archive_be16dec(p) == 070707) { + } else if (archive_be16dec(h) == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ - } else if (archive_le16dec(p) == 070707) { + } else if (archive_le16dec(h) == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; diff --git a/libarchive/archive_read_support_format_empty.c b/libarchive/archive_read_support_format_empty.c index 0dccd9d9baba..396ae2d0aa31 100644 --- a/libarchive/archive_read_support_format_empty.c +++ b/libarchive/archive_read_support_format_empty.c @@ -87,9 +87,10 @@ archive_read_format_empty_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { (void)a; /* UNUSED */ - (void)buff; /* UNUSED */ - (void)size; /* UNUSED */ - (void)offset; /* UNUSED */ + + *buff = NULL; + *size = 0; + *offset = 0; return (ARCHIVE_EOF); } diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index c3da44c8266c..35f4c781bfae 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -320,9 +320,6 @@ struct heap_queue { }; struct iso9660 { - int magic; -#define ISO9660_MAGIC 0x96609660 - int opt_support_joliet; int opt_support_rockridge; @@ -423,7 +420,7 @@ static struct file_info * static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *start, const unsigned char *end); -static int register_CE(struct archive_read *a, int32_t location, +static int register_CE(struct archive_read *a, uint32_t location, struct file_info *file); static int read_CE(struct archive_read *a, struct iso9660 *iso9660); static void parse_rockridge_NM1(struct file_info *, @@ -464,11 +461,9 @@ archive_read_support_format_iso9660(struct archive *_a) iso9660 = calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate iso9660 data"); + archive_set_error(_a, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } - iso9660->magic = ISO9660_MAGIC; iso9660->cache_files.first = NULL; iso9660->cache_files.last = &(iso9660->cache_files.first); iso9660->re_files.first = NULL; @@ -491,11 +486,9 @@ archive_read_support_format_iso9660(struct archive *_a) NULL, NULL); - if (r != ARCHIVE_OK) { + if (r != ARCHIVE_OK) free(iso9660); - return (r); - } - return (ARCHIVE_OK); + return (r); } @@ -504,7 +497,7 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) { struct iso9660 *iso9660 = a->format->data; ssize_t bytes_read; - const unsigned char *p; + const unsigned char *h; int seenTerminator; /* If there's already a better bid than we can ever @@ -518,41 +511,41 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) * if the I/O layer gives us more, we'll take it. */ #define RESERVED_AREA (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE) - p = __archive_read_ahead(a, + h = __archive_read_ahead(a, RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE, &bytes_read); - if (p == NULL) + if (h == NULL) return (-1); /* Skip the reserved area. */ bytes_read -= RESERVED_AREA; - p += RESERVED_AREA; + h += RESERVED_AREA; /* Check each volume descriptor. */ seenTerminator = 0; for (; bytes_read > LOGICAL_BLOCK_SIZE; - bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) { + bytes_read -= LOGICAL_BLOCK_SIZE, h += LOGICAL_BLOCK_SIZE) { /* Do not handle undefined Volume Descriptor Type. */ - if (p[0] >= 4 && p[0] <= 254) + if (h[0] >= 4 && h[0] <= 254) return (0); /* Standard Identifier must be "CD001" */ - if (memcmp(p + 1, "CD001", 5) != 0) + if (memcmp(h + 1, "CD001", 5) != 0) return (0); - if (isPVD(iso9660, p)) + if (isPVD(iso9660, h)) continue; if (!iso9660->joliet.location) { - if (isJolietSVD(iso9660, p)) + if (isJolietSVD(iso9660, h)) continue; } - if (isBootRecord(p)) + if (isBootRecord(h)) continue; - if (isEVD(p)) + if (isEVD(h)) continue; - if (isSVD(p)) + if (isSVD(h)) continue; - if (isVolumePartition(iso9660, p)) + if (isVolumePartition(iso9660, h)) continue; - if (isVDSetTerminator(p)) { + if (isVDSetTerminator(h)) { seenTerminator = 1; break; } @@ -1517,8 +1510,7 @@ zisofs_read_data(struct archive_read *a, if (zisofs->block_pointers_alloc < xsize) { size_t alloc; - if (zisofs->block_pointers != NULL) - free(zisofs->block_pointers); + free(zisofs->block_pointers); alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); if (zisofs->block_pointers == NULL) { @@ -1533,8 +1525,7 @@ zisofs_read_data(struct archive_read *a, /* Allocate uncompressed data buffer. */ xsize = (size_t)1UL << zisofs->pz_log2_bs; if (zisofs->uncompressed_buffer_size < xsize) { - if (zisofs->uncompressed_buffer != NULL) - free(zisofs->uncompressed_buffer); + free(zisofs->uncompressed_buffer); zisofs->uncompressed_buffer = malloc(xsize); if (zisofs->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -2194,7 +2185,7 @@ parse_rockridge(struct archive_read *a, struct file_info *file, * 8 byte offset w/in above sector * 8 byte length of continuation */ - int32_t location = + uint32_t location = archive_le32dec(data); file->ce_offset = archive_le32dec(data+8); @@ -2341,22 +2332,24 @@ parse_rockridge(struct archive_read *a, struct file_info *file, } static int -register_CE(struct archive_read *a, int32_t location, +register_CE(struct archive_read *a, uint32_t location, struct file_info *file) { struct iso9660 *iso9660 = a->format->data; struct read_ce_queue *heap; - uint64_t offset, parent_offset; + uint64_t offset, parent_offset, ce_end; size_t hole, parent; - offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size; - if (((file->mode & AE_IFMT) == AE_IFREG && + if (archive_ckd_mul_u64(&offset, (uint64_t)location, + (uint64_t)iso9660->logical_block_size) || + archive_ckd_add_u64(&ce_end, offset, + ((uint64_t)file->ce_offset) + file->ce_size) || + ((file->mode & AE_IFMT) == AE_IFREG && offset >= file->offset) || offset < iso9660->current_position || (((uint64_t)file->ce_offset) + file->ce_size) > (uint64_t)iso9660->logical_block_size || - offset + file->ce_offset + file->ce_size - > iso9660->volume_size) { + ce_end > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid parameter in SUSP \"CE\" extension"); return (ARCHIVE_FATAL); @@ -3061,14 +3054,28 @@ re_get_entry(struct iso9660 *iso9660) static inline int rede_add_entry(struct file_info *file) { - struct file_info *re; + struct file_info *re, *slow; + int slow_turn = 0; /* * Find "RE" entry. + * + * A malformed image can chain "CL"/"RE" reparenting so that the + * parent pointers form a cycle in which no node carries the "re" + * flag. Advance a second pointer at half speed (Floyd's cycle + * detection) so such a loop is reported as a malformed archive + * instead of spinning forever. */ - re = file->parent; - while (re != NULL && !re->re) + re = slow = file->parent; + while (re != NULL && !re->re) { re = re->parent; + if (slow_turn) { + slow = slow->parent; + if (re == slow) + return (-1); /* Cyclic parent chain. */ + } + slow_turn = !slow_turn; + } if (re == NULL) return (-1); diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index 0a475461576d..fe98619f27c1 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -206,10 +206,12 @@ struct lha { * LHA header common member offset. */ #define H_METHOD_OFFSET 2 /* Compress type. */ -#define H_ATTR_OFFSET 19 /* DOS attribute. */ +#define H_ATTR_OFFSET 19 /* AmigaDOS or DOS attributes. */ #define H_LEVEL_OFFSET 20 /* Header Level. */ #define H_SIZE 22 /* Minimum header size. */ +#define SFX_MAX_READAHEAD (1024 * 512) + static int archive_read_format_lha_bid(struct archive_read *, int); static int archive_read_format_lha_options(struct archive_read *, const char *, const char *); @@ -228,7 +230,7 @@ static int lha_read_file_header_2(struct archive_read *, struct lha *); static int lha_read_file_header_3(struct archive_read *, struct lha *); static int lha_read_file_extended_header(struct archive_read *, struct lha *, uint16_t *, int, uint64_t, size_t *); -static size_t lha_check_header_format(const void *); +static size_t lha_check_header_format(const char *); static int lha_skip_sfx(struct archive_read *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); @@ -264,8 +266,7 @@ archive_read_support_format_lha(struct archive *_a) lha = calloc(1, sizeof(*lha)); if (lha == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate lha data"); + archive_set_error(_a, ENOMEM, "Can't allocate lha data"); return (ARCHIVE_FATAL); } archive_string_init(&lha->ws); @@ -285,16 +286,15 @@ archive_read_support_format_lha(struct archive *_a) if (r != ARCHIVE_OK) free(lha); - return (ARCHIVE_OK); + return (r); } static size_t -lha_check_header_format(const void *h) +lha_check_header_format(const char *h) { - const unsigned char *p = h; size_t next_skip_bytes; - switch (p[H_METHOD_OFFSET+3]) { + switch (h[H_METHOD_OFFSET + 3]) { /* * "-lh0-" ... "-lh7-" "-lhd-" * "-lzs-" "-lz5-" @@ -305,29 +305,27 @@ lha_check_header_format(const void *h) case 's': next_skip_bytes = 4; - /* b0 == 0 means the end of an LHa archive file. */ - if (p[0] == 0) + /* 0 means the end of an LHa archive file. */ + if (h[0] == 0) break; - if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l' - || p[H_METHOD_OFFSET+4] != '-') + if (h[H_METHOD_OFFSET] != '-' || h[H_METHOD_OFFSET + 1] != 'l' + || h[H_METHOD_OFFSET + 4] != '-') break; - if (p[H_METHOD_OFFSET+2] == 'h') { + if (h[H_METHOD_OFFSET + 2] == 'h') { /* "-lh?-" */ - if (p[H_METHOD_OFFSET+3] == 's') + if (h[H_METHOD_OFFSET + 3] == 's') break; - if (p[H_LEVEL_OFFSET] == 0) - return (0); - if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20) + if (h[H_LEVEL_OFFSET] <= 3) return (0); } - if (p[H_METHOD_OFFSET+2] == 'z') { + if (h[H_METHOD_OFFSET + 2] == 'z') { /* LArc extensions: -lzs-,-lz4- and -lz5- */ - if (p[H_LEVEL_OFFSET] != 0) + if (h[H_LEVEL_OFFSET] != 0) break; - if (p[H_METHOD_OFFSET+3] == 's' - || p[H_METHOD_OFFSET+3] == '4' - || p[H_METHOD_OFFSET+3] == '5') + if (h[H_METHOD_OFFSET + 3] == 's' + || h[H_METHOD_OFFSET + 3] == '4' + || h[H_METHOD_OFFSET + 3] == '5') return (0); } break; @@ -344,43 +342,46 @@ lha_check_header_format(const void *h) static int archive_read_format_lha_bid(struct archive_read *a, int best_bid) { - const char *p; - const void *buff; - ssize_t bytes_avail, offset, window; - size_t next; + const char *h; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 30) return (-1); - if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) + if ((h = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) return (-1); - if (lha_check_header_format(p) == 0) + if (lha_check_header_format(h) == 0) return (30); - if (p[0] == 'M' && p[1] == 'Z') { + if (h[0] == 'M' && h[1] == 'Z') { + ssize_t offset, window; + /* PE file */ offset = 0; window = 4096; - while (offset < (1024 * 20)) { - buff = __archive_read_ahead(a, offset + window, + while (offset + window <= SFX_MAX_READAHEAD) { + ssize_t bytes_avail; + + h = __archive_read_ahead(a, offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < (H_SIZE + 3)) - return (0); - continue; + if (h == NULL) { + if (bytes_avail >= offset + H_SIZE) { + /* Remaining bytes are less than window. */ + window = bytes_avail - offset; + continue; + } + return (0); } - p = (const char *)buff + offset; - while (p + H_SIZE < (const char *)buff + bytes_avail) { - if ((next = lha_check_header_format(p)) == 0) + if (bytes_avail > SFX_MAX_READAHEAD) + bytes_avail = SFX_MAX_READAHEAD; + while (offset <= bytes_avail - H_SIZE) { + size_t next = lha_check_header_format(h + offset); + if (next == 0) return (30); - p += next; + offset += next; } - offset = p - (const char *)buff; } } return (0); @@ -474,7 +475,7 @@ archive_read_format_lha_read_header(struct archive_read *a, struct lha *lha = a->format->data; struct archive_wstring linkname; struct archive_wstring pathname; - const unsigned char *p; + const char *p; const char *signature; int err; struct archive_mstring conv_buffer; @@ -500,7 +501,7 @@ archive_read_format_lha_read_header(struct archive_read *a, return (truncated_error(a)); } - signature = (const char *)p; + signature = p; if (lha->found_first_header == 0 && signature[0] == 'M' && signature[1] == 'Z') { /* This is an executable? Must be self-extracting... */ @@ -510,7 +511,7 @@ archive_read_format_lha_read_header(struct archive_read *a, if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) return (truncated_error(a)); - signature = (const char *)p; + signature = p; } /* signature[0] == 0 means the end of an LHa archive file. */ if (signature[0] == 0) @@ -588,6 +589,9 @@ archive_read_format_lha_read_header(struct archive_read *a, if (err < ARCHIVE_WARN) return (err); + if (!lha->directory && archive_strlen(&lha->filename) == 0) + if (memcmp(lha->method, "lh0", 3) == 0 && archive_strlen(&lha->dirname) > 0) + lha->directory = 1; /* empty directory entry */ if (!lha->directory && archive_strlen(&lha->filename) == 0) /* The filename has not been set */ @@ -812,7 +816,7 @@ lha_read_file_header_0(struct archive_read *a, struct lha *lha) headersum = p[H0_HEADER_SUM_OFFSET]; lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET); - lha->mtime = dos_to_unix(archive_le32dec(p + H0_DOS_TIME_OFFSET)); + lha->mtime = __archive_dos_to_unix(archive_le32dec(p + H0_DOS_TIME_OFFSET)); namelen = p[H0_NAME_LEN_OFFSET]; extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen; if ((namelen > 221 || extdsize < 0) && extdsize != -2) { @@ -912,7 +916,7 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha) /* Note: An extended header size is included in a compsize. */ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET); - lha->mtime = dos_to_unix(archive_le32dec(p + H1_DOS_TIME_OFFSET)); + lha->mtime = __archive_dos_to_unix(archive_le32dec(p + H1_DOS_TIME_OFFSET)); namelen = p[H1_NAME_LEN_OFFSET]; /* Calculate a padding size. The result will be normally 0 only(?) */ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen; @@ -1325,15 +1329,15 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, break; case EXT_TIMESTAMP: if (datasize == (sizeof(uint64_t) * 3)) { - ntfs_to_unix(archive_le64dec(extdheader), + __archive_ntfs_to_unix(archive_le64dec(extdheader), &lha->birthtime, &lha->birthtime_tv_nsec); extdheader += sizeof(uint64_t); - ntfs_to_unix(archive_le64dec(extdheader), + __archive_ntfs_to_unix(archive_le64dec(extdheader), &lha->mtime, &lha->mtime_tv_nsec); extdheader += sizeof(uint64_t); - ntfs_to_unix(archive_le64dec(extdheader), + __archive_ntfs_to_unix(archive_le64dec(extdheader), &lha->atime, &lha->atime_tv_nsec); lha->setflag |= BIRTHTIME_IS_SET | @@ -1342,11 +1346,19 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, break; case EXT_FILESIZE: if (datasize == sizeof(uint64_t) * 2) { - lha->compsize = archive_le64dec(extdheader); + uint64_t compsize, origsize; + + /* Range-check before the conversion to the + * signed type rather than relying on the + * implementation-defined result of it. */ + compsize = archive_le64dec(extdheader); extdheader += sizeof(uint64_t); - lha->origsize = archive_le64dec(extdheader); - if (lha->compsize < 0 || lha->origsize < 0) + origsize = archive_le64dec(extdheader); + if (compsize > INT64_MAX || + origsize > INT64_MAX) goto invalid; + lha->compsize = (int64_t)compsize; + lha->origsize = (int64_t)origsize; } break; case EXT_CODEPAGE: @@ -1551,7 +1563,7 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = a->format->data; - ssize_t bytes_avail; + ssize_t bytes_avail, max_in; int r; /* If we haven't yet read any data, initialize the decompressor. */ @@ -1598,6 +1610,17 @@ lha_read_data_lzh(struct archive_read *a, const void **buff, if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; + /* Setup buffer boundaries. strm.avail_in is an int, and the + * compressed size it is bounded by comes straight from the LHa + * header, so clamp the available byte count before the + * assignment. */ + if (INT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = INT_MAX; + if (bytes_avail > max_in) + bytes_avail = max_in; + lha->strm.avail_in = (int)bytes_avail; lha->strm.total_in = 0; lha->strm.avail_out = 0; diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index dab580a30376..73e9bf0506d1 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -91,6 +91,7 @@ struct mtree_option { struct mtree_entry { struct archive_rb_node rbnode; struct mtree_entry *next_dup; + struct mtree_entry *last_dup; struct mtree_entry *next; struct mtree_option *options; char *name; @@ -108,7 +109,6 @@ struct mtree { const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; - struct archive_rb_tree entry_rbtree; struct archive_string current_dir; struct archive_string contents_name; @@ -224,8 +224,7 @@ archive_read_support_format_mtree(struct archive *_a) mtree = calloc(1, sizeof(*mtree)); if (mtree == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate mtree data"); + archive_set_error(_a, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->checkfs = 0; @@ -233,12 +232,22 @@ archive_read_support_format_mtree(struct archive *_a) __archive_rb_tree_init(&mtree->rbtree, &rb_ops); - r = __archive_read_register_format(a, mtree, "mtree", - mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); + r = __archive_read_register_format(a, + mtree, + "mtree", + mtree_bid, + archive_read_format_mtree_options, + read_header, + read_data, + skip, + NULL, + cleanup, + NULL, + NULL); if (r != ARCHIVE_OK) free(mtree); - return (ARCHIVE_OK); + return (r); } static int @@ -620,16 +629,16 @@ static int mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; - const char *p; + const void *h; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ - p = __archive_read_ahead(a, strlen(signature), NULL); - if (p == NULL) + h = __archive_read_ahead(a, strlen(signature), NULL); + if (h == NULL) return (-1); - if (memcmp(p, signature, strlen(signature)) == 0) + if (memcmp(h, signature, strlen(signature)) == 0) return (8 * (int)strlen(signature)); /* @@ -940,15 +949,15 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, parse_escapes(entry->name, entry); entry->next_dup = NULL; + entry->last_dup = entry; if (entry->full) { if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) { struct mtree_entry *alt; alt = (struct mtree_entry *)__archive_rb_tree_find_node( &mtree->rbtree, entry->name); if (alt != NULL) { - while (alt->next_dup) - alt = alt->next_dup; - alt->next_dup = entry; + alt->last_dup->next_dup = entry; + alt->last_dup = entry; } } } @@ -1067,7 +1076,6 @@ static int read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree = a->format->data; - char *p; int r, use_next; if (mtree->fd >= 0) { @@ -1096,14 +1104,9 @@ read_header(struct archive_read *a, struct archive_entry *entry) mtree->this_entry->used = 1; if (archive_strlen(&mtree->current_dir) > 0) { /* Roll back current path. */ - p = mtree->current_dir.s - + mtree->current_dir.length - 1; - while (p >= mtree->current_dir.s && *p != '/') - --p; - if (p >= mtree->current_dir.s) - --p; - mtree->current_dir.length - = p - mtree->current_dir.s + 1; + archive_string_dirname(&mtree->current_dir); + if (strcmp(mtree->current_dir.s, ".") == 0) + archive_string_empty(&mtree->current_dir); } } if (!mtree->this_entry->used) { @@ -1429,7 +1432,7 @@ parse_device(dev_t *pdev, struct archive *a, char *val) * Decode and pack it accordingly. */ *dev++ = '\0'; - if ((pack = pack_find(val)) == NULL) { + if ((pack = __archive_pack_find(val)) == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown format `%s'", val); return ARCHIVE_WARN; @@ -1709,6 +1712,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, ARCHIVE_ENTRY_DIGEST_SHA512); } if (strcmp(key, "size") == 0) { + *parsed_kws |= MTREE_HAS_SIZE; archive_entry_set_size(entry, mtree_atol(&val, 10)); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c index ff764f503042..61baeef0d9f1 100644 --- a/libarchive/archive_read_support_format_rar.c +++ b/libarchive/archive_read_support_format_rar.c @@ -36,9 +36,7 @@ #endif #include "archive.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" @@ -136,6 +134,8 @@ #define MAX_SYMBOL_LENGTH 0xF #define MAX_SYMBOLS 20 +#define SFX_MAX_READAHEAD (1024 * 512) + /* Virtual Machine Properties */ #define VM_MEMORY_SIZE 0x40000 #define VM_MEMORY_MASK (VM_MEMORY_SIZE - 1) @@ -328,7 +328,6 @@ struct rar unsigned int unp_buffer_size; unsigned char *unp_buffer; unsigned int dictionary_size; - char start_new_block; char entry_eof; unsigned long crc_calculated; int found_first_header; @@ -736,7 +735,7 @@ archive_read_support_format_rar(struct archive *_a) rar = calloc(1, sizeof(*rar)); if (rar == NULL) { - archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data"); + archive_set_error(_a, ENOMEM, "Can't allocate rar data"); return (ARCHIVE_FATAL); } @@ -747,17 +746,17 @@ archive_read_support_format_rar(struct archive *_a) rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, - rar, - "rar", - archive_read_format_rar_bid, - archive_read_format_rar_options, - archive_read_format_rar_read_header, - archive_read_format_rar_read_data, - archive_read_format_rar_read_data_skip, - archive_read_format_rar_seek_data, - archive_read_format_rar_cleanup, - archive_read_support_format_rar_capabilities, - archive_read_format_rar_has_encrypted_entries); + rar, + "rar", + archive_read_format_rar_bid, + archive_read_format_rar_options, + archive_read_format_rar_read_header, + archive_read_format_rar_read_data, + archive_read_format_rar_read_data_skip, + archive_read_format_rar_seek_data, + archive_read_format_rar_cleanup, + archive_read_support_format_rar_capabilities, + archive_read_format_rar_has_encrypted_entries); if (r != ARCHIVE_OK) free(rar); @@ -788,39 +787,41 @@ archive_read_format_rar_has_encrypted_entries(struct archive_read *_a) static int archive_read_format_rar_bid(struct archive_read *a, int best_bid) { - const char *p; + const char *h; /* If there's already a bid > 30, we'll never win. */ if (best_bid > 30) return (-1); - if ((p = __archive_read_ahead(a, 7, NULL)) == NULL) + if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return (-1); - if (memcmp(p, RAR_SIGNATURE, 7) == 0) + if (memcmp(h, RAR_SIGNATURE, 7) == 0) return (30); - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { + if ((h[0] == 'M' && h[1] == 'Z') || memcmp(h, "\x7F\x45LF", 4) == 0) { /* This is a PE file */ ssize_t offset = 0x10000; ssize_t window = 4096; ssize_t bytes_avail; - while (offset + window <= (1024 * 128)) { - const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - return (0); - continue; + + while (offset + window <= SFX_MAX_READAHEAD) { + h = __archive_read_ahead(a, offset + window, &bytes_avail); + if (h == NULL) { + if (bytes_avail >= offset + 0x10) { + /* Remaining bytes are less than window. */ + window = bytes_avail - offset; + continue; + } + return (0); } - p = buff + offset; - while (p + 7 < buff + bytes_avail) { - if (memcmp(p, RAR_SIGNATURE, 7) == 0) + if (bytes_avail > SFX_MAX_READAHEAD) + bytes_avail = SFX_MAX_READAHEAD; + while (offset <= bytes_avail - 7) { + if (memcmp(h + offset, RAR_SIGNATURE, 7) == 0) return (30); - p += 0x10; + offset += 0x10; } - offset = p - buff; } } return (0); @@ -836,7 +837,7 @@ skip_sfx(struct archive_read *a) total = 0; window = 4096; - while (total + window <= (1024 * 128)) { + while (total + window <= SFX_MAX_READAHEAD) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ @@ -1005,7 +1006,7 @@ archive_read_format_rar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } - crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); + crc32_val = __archive_crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); if ((crc32_val & 0xffff) != archive_le16dec(p)) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -1064,7 +1065,7 @@ archive_read_format_rar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } p = h; - crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read); + crc32_val = __archive_crc32(crc32_val, (const unsigned char *)p, to_read); __archive_read_consume(a, to_read); skip -= to_read; } @@ -1403,7 +1404,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, "Invalid header size"); return (ARCHIVE_FATAL); } - crc32_computed = crc32(0, (const unsigned char *)p + 2, 7 - 2); + crc32_computed = __archive_crc32(0, (const unsigned char *)p + 2, 7 - 2); __archive_read_consume(a, 7); if (!(rar->file_flags & FHD_SOLID)) @@ -1441,7 +1442,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, } /* File Header CRC check. */ - crc32_computed = crc32(crc32_computed, h, (unsigned)(header_size - 7)); + crc32_computed = __archive_crc32(crc32_computed, h, (unsigned)(header_size - 7)); crc32_read = archive_le16dec(rar_header.crc); if ((crc32_computed & 0xffff) != crc32_read) { #ifndef DONT_FAIL_ON_CRC_ERROR @@ -1510,12 +1511,14 @@ read_header(struct archive_read *a, struct archive_entry *entry, * consumed at the end. */ if (head_type == NEWSUB_HEAD) { - if (rar->packed_size > INT64_MAX - header_size) { + int64_t skip; + if (archive_ckd_add_i64(&skip, rar->packed_size, header_size) || + skip < 7) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid RAR file: Overlarge extended header"); return (ARCHIVE_FATAL); } - if (__archive_read_consume(a, header_size + rar->packed_size - 7) < 0) { + if (__archive_read_consume(a, skip - 7) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid RAR file: Cannot read extended header data"); return (ARCHIVE_FATAL); @@ -1866,7 +1869,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, static time_t get_time(int ttime) { - struct tm tm; + struct tm tm = { 0 }; tm.tm_sec = 2 * (ttime & 0x1f); tm.tm_min = (ttime >> 5) & 0x3f; tm.tm_hour = (ttime >> 11) & 0x1f; @@ -2037,7 +2040,7 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, rar->bytes_remaining -= bytes_avail; rar->bytes_unconsumed = bytes_avail; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, + rar->crc_calculated = __archive_crc32(rar->crc_calculated, *buff, (unsigned)bytes_avail); return (ARCHIVE_OK); } @@ -2096,7 +2099,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, + rar->crc_calculated = __archive_crc32(rar->crc_calculated, *buff, (unsigned)*size); rar->unp_offset = 0; return (ARCHIVE_OK); @@ -2132,7 +2135,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, + rar->crc_calculated = __archive_crc32(rar->crc_calculated, *buff, (unsigned)*size); return (ret); } @@ -2291,7 +2294,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, rar->offset_outgoing += *size; ending_block: /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); + rar->crc_calculated = __archive_crc32(rar->crc_calculated, *buff, (unsigned)*size); return ret; } @@ -2993,7 +2996,6 @@ expand(struct archive_read *a, int64_t *end) if(newfile) { - rar->start_new_block = 1; if (!rar_br_read_ahead(a, br, 1)) goto truncated_data; rar->start_new_table = rar_br_bits(br, 1); @@ -3477,8 +3479,11 @@ run_filters(struct archive_read *a) if (tend < 0) return 0; end = (size_t)tend; - if (end != start + filter->blocklength) + if (end != start + filter->blocklength) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Bad RAR file data: filter block was not fully decompressed"); return 0; + } if (!filters->vm) { @@ -3554,7 +3559,7 @@ compile_program(const uint8_t *bytes, size_t length) prog = calloc(1, sizeof(*prog)); if (!prog) return NULL; - prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32); + prog->fingerprint = __archive_crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32); if (membr_bits(&br, 1)) { @@ -3712,7 +3717,9 @@ execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm) uint8_t *src, *dst; uint32_t i, idx; - if (length > PROGRAM_WORK_SIZE / 2) + if (length > PROGRAM_WORK_SIZE / 2 || + numchannels == 0 || + numchannels > 128) return 0; src = &vm->memory[0]; @@ -3827,7 +3834,9 @@ execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm) uint8_t *src, *dst; uint32_t i, j; - if (length > PROGRAM_WORK_SIZE / 2) + if (length > PROGRAM_WORK_SIZE / 2 || + numchannels == 0 || + numchannels > 128) return 0; src = &vm->memory[0]; diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index faddad9ff497..1cf5112c382e 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -38,9 +38,7 @@ #endif #include "archive.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + #include "archive_entry.h" #include "archive_entry_locale.h" @@ -87,11 +85,24 @@ static const unsigned char rar5_signature_xor[] = { }; static const size_t g_unpack_window_size = 0x20000; +/* Bid returned when the RAR5 signature is found at offset 0. Bidders + * conventionally return the number of bits they verified, so an exact + * match of the 8-byte signature bids 64 (like the ar and warc readers + * do for their 8-byte magics). This must stay above the seekable Zip + * bidder's 32 so that a RAR archive which merely stores an uncompressed + * Zip is still recognized as RAR (see issue #2249). The weaker SFX + * heuristic, which only scans for the signature somewhere inside an + * executable, deliberately keeps its lower bid of 30. */ +#define RAR5_SIGNATURE_BID ((int)(8 * sizeof(rar5_signature_xor))) + /* 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) +/* maximum header size for RAR5 is 2MB */ +#define MAX_HEADER_SIZE (2 * 1024 * 1024) + struct file_header { ssize_t bytes_remaining; ssize_t unpacked_size; @@ -152,6 +163,8 @@ enum REDIR_TYPE { #define OWNER_GROUP_GID 0x08 #define OWNER_MAXNAMELEN 256 +#define SFX_MAX_READAHEAD (1024 * 512) + enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ @@ -404,7 +417,7 @@ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { return CDE_PARAM; cdeque_clear(d); - d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); + d->arr = malloc(sizeof(size_t*) * max_capacity_power_of_2); return d->arr ? CDE_OK : CDE_ALLOC; } @@ -889,16 +902,6 @@ static void reset_file_context(struct rar5 *rar5) { free_filters(rar5); } -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) { @@ -914,13 +917,10 @@ static int read_ahead(struct archive_read* a, size_t how_many, } static int consume(struct archive_read* a, int64_t how_many) { - int ret; + if(__archive_read_consume(a, how_many) < 0) + return (ARCHIVE_FATAL); - ret = how_many == __archive_read_consume(a, how_many) - ? ARCHIVE_OK - : ARCHIVE_FATAL; - - return ret; + return (ARCHIVE_OK); } /** @@ -935,9 +935,11 @@ static int consume(struct archive_read* a, int64_t how_many) { * 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. + * In case of error (!ARCHIVE_OK), *pvalue contains an invalid value. + * + * Returns ARCHIVE_OK if *pvalue was successfully read. + * Returns ARCHIVE_FATAL if there was an error. + * Returns ARCHIVE_EOF if end of file was reached. */ static int read_var(struct archive_read* a, uint64_t* pvalue, @@ -945,16 +947,36 @@ static int read_var(struct archive_read* a, uint64_t* pvalue, { uint64_t multiplier; uint64_t result = 0; - size_t i; + size_t i, max_bytes; + ssize_t avail; const uint8_t* p; - /* We will read maximum of 10 bytes. We don't have to handle the - * situation to read the RAR5 variable-sized value stored at the end of - * the file, because such situation will never happen. */ - if(!read_ahead(a, 10, &p)) - return 0; + /* We will read a maximum of 10 bytes, since that's the longest a + * RAR5 varint can legally be. Ask for a full 10-byte lookahead + * first, since that's the common case. However, a varint is + * self-terminating (a byte with the MSB clear ends the value, so + * most varints take far fewer than 10 bytes), and it's legal for + * one to end fewer than 10 bytes before the end of the archive -- + * e.g. a file header's extra field butting up against ENDARC with + * no padding. If a full 10-byte lookahead isn't available, fall + * back to however many bytes actually remain and decode within + * that instead of failing outright; only reaching the real end of + * the archive with zero bytes left, or exhausting the available + * bytes without hitting a terminator, is a genuine error. */ + max_bytes = 10; + p = __archive_read_ahead(a, max_bytes, &avail); + if(p == NULL) { + if(avail == 0) + return ARCHIVE_EOF; + if(avail < 1) + return ARCHIVE_FATAL; + max_bytes = (size_t)avail; + p = __archive_read_ahead(a, max_bytes, NULL); + if(p == NULL) + return ARCHIVE_FATAL; + } - for(multiplier = 1, i = 0; i < 10; i++, multiplier *= 128) { + for(multiplier = 1, i = 0; i < max_bytes; i++, multiplier *= 128) { uint64_t val; uint8_t b; @@ -965,7 +987,9 @@ static int read_var(struct archive_read* a, uint64_t* pvalue, if(archive_ckd_mul_u64(&val, b & 0x7F, multiplier) || archive_ckd_add_u64(&result, result, val)) { /* Integer overflow occurred. */ - return 0; + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Number too large"); + return ARCHIVE_FATAL; } /* MSB set to 1 means we need to continue decoding process. @@ -991,17 +1015,19 @@ static int read_var(struct archive_read* a, uint64_t* pvalue, * needs to consume. This is why we handle * such situation here automatically. */ if(ARCHIVE_OK != consume(a, 1 + i)) { - return 0; + return ARCHIVE_FATAL; } } /* End of decoding process, return success. */ - return 1; + return ARCHIVE_OK; } } - /* All continuation bits were set. This is an error. */ - return 0; + /* Either all 10 bytes had their continuation bit set (a malformed + * varint), or fewer than 10 bytes were available and none of them + * terminated the value (a truncated archive). Both are errors. */ + return ARCHIVE_FATAL; } static int read_var_sized(struct archive_read* a, size_t* pvalue, @@ -1010,10 +1036,17 @@ static int read_var_sized(struct archive_read* a, size_t* pvalue, uint64_t v; uint64_t v_size = 0; - const int ret = pvalue_len ? read_var(a, &v, &v_size) - : read_var(a, &v, NULL); + int ret = pvalue_len ? read_var(a, &v, &v_size) + : read_var(a, &v, NULL); +#if UINT64_MAX > SIZE_MAX + if(ret == ARCHIVE_OK && v > SIZE_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Number too large"); + ret = ARCHIVE_FATAL; + } +#endif - if(ret == 1 && pvalue) { + if(ret == ARCHIVE_OK && pvalue) { *pvalue = (size_t) v; } @@ -1111,52 +1144,55 @@ static char read_u64(struct archive_read* a, uint64_t* pvalue) { } static int bid_standard(struct archive_read* a) { - const uint8_t* p; + const uint8_t* h; char signature[sizeof(rar5_signature_xor)]; rar5_signature(signature); - if(!read_ahead(a, sizeof(rar5_signature_xor), &p)) + if(!read_ahead(a, sizeof(rar5_signature_xor), &h)) return -1; - if(!memcmp(signature, p, sizeof(rar5_signature_xor))) - return 30; + if(!memcmp(h, signature, sizeof(rar5_signature_xor))) + return RAR5_SIGNATURE_BID; return -1; } static int bid_sfx(struct archive_read *a) { - const char *p; + const char *h; - if ((p = __archive_read_ahead(a, 7, NULL)) == NULL) + if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) return -1; - if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { + if ((h[0] == 'M' && h[1] == 'Z') || memcmp(h, "\x7F\x45LF", 4) == 0) { /* This is a PE file */ char signature[sizeof(rar5_signature_xor)]; ssize_t offset = 0x10000; ssize_t window = 4096; - ssize_t bytes_avail; rar5_signature(signature); - while (offset + window <= (1024 * 512)) { - const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); - if (buff == NULL) { - /* Remaining bytes are less than window. */ - window >>= 1; - if (window < 0x40) - return 0; - continue; + while (offset + window <= SFX_MAX_READAHEAD) { + ssize_t bytes_avail; + + h = __archive_read_ahead(a, offset + window, &bytes_avail); + if (h == NULL) { + if (bytes_avail >= offset + 0x10) { + /* Remaining bytes are less than window. */ + window = bytes_avail - offset; + continue; + } + return 0; } - p = buff + offset; - while (p + 8 < buff + bytes_avail) { - if (memcmp(p, signature, sizeof(signature)) == 0) + if (bytes_avail > SFX_MAX_READAHEAD) + bytes_avail = SFX_MAX_READAHEAD; + while (offset <= bytes_avail - 8) { + if (memcmp(h + offset, signature, + sizeof(signature)) == 0) return 30; - p += 0x10; + offset += 0x10; } - offset = p - buff; } } @@ -1166,7 +1202,7 @@ static int bid_sfx(struct archive_read *a) static int rar5_bid(struct archive_read* a, int best_bid) { int my_bid; - if(best_bid > 30) + if(best_bid >= RAR5_SIGNATURE_BID) return -1; my_bid = bid_standard(a); @@ -1220,26 +1256,30 @@ static int process_main_locator_extra_block(struct archive_read* a, struct rar5 *rar5) { uint64_t locator_flags; + int r; enum LOCATOR_FLAGS { QLIST = 0x01, RECOVERY = 0x02, }; - if(!read_var(a, &locator_flags, NULL)) { - return ARCHIVE_EOF; + r = read_var(a, &locator_flags, NULL); + if(r != ARCHIVE_OK) { + return r; } if(locator_flags & QLIST) { - if(!read_var(a, &rar5->qlist_offset, NULL)) { - return ARCHIVE_EOF; + r = read_var(a, &rar5->qlist_offset, NULL); + if(r != ARCHIVE_OK) { + return r; } /* qlist is not used */ } if(locator_flags & RECOVERY) { - if(!read_var(a, &rar5->rr_offset, NULL)) { - return ARCHIVE_EOF; + r = read_var(a, &rar5->rr_offset, NULL); + if(r != ARCHIVE_OK) { + return r; } /* rr is not used */ @@ -1253,13 +1293,15 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5 *rar5, { size_t hash_type = 0; size_t value_len; + int r; enum HASH_TYPE { BLAKE2sp = 0x00 }; - if(!read_var_sized(a, &hash_type, &value_len)) - return ARCHIVE_EOF; + r = read_var_sized(a, &hash_type, &value_len); + if(r != ARCHIVE_OK) + return r; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { @@ -1307,7 +1349,7 @@ static int parse_htime_item(struct archive_read* a, char unix_time, if(!read_u64(a, &windows_time)) return ARCHIVE_EOF; - ntfs_to_unix(windows_time, sec, nsec); + __archive_ntfs_to_unix(windows_time, sec, nsec); *extra_data_size -= 8; } @@ -1323,17 +1365,20 @@ static int parse_file_extra_version(struct archive_read* a, struct archive_string version_string; struct archive_string name_utf8_string; const char* cur_filename; + int r; /* Flags are ignored. */ - if(!read_var_sized(a, &flags, &value_len)) - return ARCHIVE_EOF; + r = read_var_sized(a, &flags, &value_len); + if(r != ARCHIVE_OK) + return r; *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; + r = read_var_sized(a, &version, &value_len); + if(r != ARCHIVE_OK) + return r; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) @@ -1374,6 +1419,7 @@ static int parse_file_extra_htime(struct archive_read* a, char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime; size_t flags = 0; size_t value_len; + int ret; enum HTIME_FLAGS { IS_UNIX = 0x01, @@ -1383,8 +1429,9 @@ static int parse_file_extra_htime(struct archive_read* a, HAS_UNIX_NS = 0x10, }; - if(!read_var_sized(a, &flags, &value_len)) - return ARCHIVE_EOF; + ret = read_var_sized(a, &flags, &value_len); + if(ret != ARCHIVE_OK) + return ret; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { @@ -1399,18 +1446,24 @@ static int parse_file_extra_htime(struct archive_read* a, rar5->file.e_atime_ns = rar5->file.e_ctime_ns = rar5->file.e_mtime_ns = 0; if(has_mtime) { - parse_htime_item(a, unix_time, &rar5->file.e_mtime, + ret = parse_htime_item(a, unix_time, &rar5->file.e_mtime, &rar5->file.e_mtime_ns, extra_data_size); + if(ret != ARCHIVE_OK) + return ret; } if(has_ctime) { - parse_htime_item(a, unix_time, &rar5->file.e_ctime, + ret = parse_htime_item(a, unix_time, &rar5->file.e_ctime, &rar5->file.e_ctime_ns, extra_data_size); + if(ret != ARCHIVE_OK) + return ret; } if(has_atime) { - parse_htime_item(a, unix_time, &rar5->file.e_atime, + ret = parse_htime_item(a, unix_time, &rar5->file.e_atime, &rar5->file.e_atime_ns, extra_data_size); + if(ret != ARCHIVE_OK) + return ret; } if(has_mtime && has_unix_ns) { @@ -1455,25 +1508,31 @@ static int parse_file_extra_redir(struct archive_read* a, struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size) { uint64_t value_size = 0; + size_t varint_len = 0; size_t target_size = 0; char target_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; + int r; - if(!read_var(a, &rar5->file.redir_type, &value_size)) - return ARCHIVE_EOF; + r = read_var(a, &rar5->file.redir_type, &value_size); + if(r != ARCHIVE_OK) + return r; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; - if(!read_var(a, &rar5->file.redir_flags, &value_size)) - return ARCHIVE_EOF; + r = read_var(a, &rar5->file.redir_flags, &value_size); + if(r != ARCHIVE_OK) + return r; 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)) + r = read_var_sized(a, &target_size, &varint_len); + if(r != ARCHIVE_OK) + return r; + if(ARCHIVE_OK != consume(a, (int64_t)varint_len)) return ARCHIVE_EOF; - *extra_data_size -= target_size + 1; if(target_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -1487,6 +1546,14 @@ static int parse_file_extra_redir(struct archive_read* a, return ARCHIVE_FATAL; } + if((int64_t)(target_size + varint_len) > *extra_data_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Extra data size is too small for link target"); + return ARCHIVE_FATAL; + } + + *extra_data_size -= (int64_t)(target_size + varint_len); + if(!read_ahead(a, target_size, &p)) return ARCHIVE_EOF; @@ -1530,19 +1597,35 @@ static int parse_file_extra_owner(struct archive_read* a, uint64_t id = 0; size_t name_len = 0; size_t name_size = 0; + size_t varint_len = 0; char namebuf[OWNER_MAXNAMELEN]; const uint8_t* p; + int r; - if(!read_var(a, &flags, &value_size)) - return ARCHIVE_EOF; + r = read_var(a, &flags, &value_size); + if(r != ARCHIVE_OK) + return r; 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)) + r = read_var_sized(a, &name_size, &varint_len); + if(r != ARCHIVE_OK) + return r; + + /* The name cannot be larger than the remaining extra data of + * this field. Rejecting an oversized length here also avoids + * requesting a huge allocation from read_ahead() below. */ + if(*extra_data_size < 0 || + name_size > (uint64_t)*extra_data_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Owner name is too long"); + return ARCHIVE_FATAL; + } + if(ARCHIVE_OK != consume(a, (int64_t)varint_len)) return ARCHIVE_EOF; - *extra_data_size -= name_size + 1; + *extra_data_size -= (int64_t)(name_size + varint_len); if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; @@ -1561,9 +1644,19 @@ static int parse_file_extra_owner(struct archive_read* a, archive_entry_set_uname(e, namebuf); } if ((flags & OWNER_GROUP_NAME) != 0) { - if(!read_var_sized(a, &name_size, NULL)) + r = read_var_sized(a, &name_size, &varint_len); + if(r != ARCHIVE_OK) + return r; + + if(*extra_data_size < 0 || + name_size > (uint64_t)*extra_data_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Group name is too long"); + return ARCHIVE_FATAL; + } + if(ARCHIVE_OK != consume(a, (int64_t)varint_len)) return ARCHIVE_EOF; - *extra_data_size -= name_size + 1; + *extra_data_size -= (int64_t)(name_size + varint_len); if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; @@ -1582,8 +1675,9 @@ static int parse_file_extra_owner(struct archive_read* a, archive_entry_set_gname(e, namebuf); } if ((flags & OWNER_USER_UID) != 0) { - if(!read_var(a, &id, &value_size)) - return ARCHIVE_EOF; + r = read_var(a, &id, &value_size); + if(r != ARCHIVE_OK) + return r; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; @@ -1591,8 +1685,9 @@ static int parse_file_extra_owner(struct archive_read* a, 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; + r = read_var(a, &id, &value_size); + if(r != ARCHIVE_OK) + return r; if(ARCHIVE_OK != consume(a, (int64_t)value_size)) return ARCHIVE_EOF; *extra_data_size -= value_size; @@ -1610,20 +1705,24 @@ static int process_head_file_extra(struct archive_read* a, uint64_t var_size; while(extra_data_size > 0) { + int r; + /* Make sure we won't fail if the file declares only unsupported attributes. */ int ret = ARCHIVE_OK; - if(!read_var(a, &extra_field_size, &var_size)) - return ARCHIVE_EOF; + r = read_var(a, &extra_field_size, &var_size); + if(r != ARCHIVE_OK) + return r; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } - if(!read_var(a, &extra_field_id, &var_size)) - return ARCHIVE_EOF; + r = read_var(a, &extra_field_id, &var_size); + if(r != ARCHIVE_OK) + return r; extra_field_size -= var_size; extra_data_size -= var_size; @@ -1662,6 +1761,12 @@ static int process_head_file_extra(struct archive_read* a, /* fallthrough */ default: /* Skip unsupported entry. */ + if (extra_field_size > (uint64_t)extra_data_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "RAR5 extra-field size exceeds remaining extra-field data"); + return ARCHIVE_FATAL; + } extra_data_size -= extra_field_size; if (ARCHIVE_OK != consume(a, extra_field_size)) { return ARCHIVE_EOF; @@ -1734,6 +1839,7 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, char name_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; int sanity_ret; + int r; enum FILE_FLAGS { DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, @@ -1763,16 +1869,32 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, if(block_flags & HFL_EXTRA_DATA) { uint64_t edata_size = 0; - if(!read_var(a, &edata_size, NULL)) - return ARCHIVE_EOF; + r = read_var(a, &edata_size, NULL); + if(r != ARCHIVE_OK) + return r; + + /* The extra area is part of the file header, which + * process_base_block() has already bounded to MAX_HEADER_SIZE. + * A declared extra-area size larger than that cannot be valid, + * and if left unchecked it is later used as the ceiling for + * the owner/group name length in parse_file_extra_owner(), + * letting a crafted archive drive read_ahead() into an + * attacker-controlled allocation. Reject it here. */ + if(edata_size > MAX_HEADER_SIZE) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "File extra data size is too large"); + return ARCHIVE_FATAL; + } /* Intentional type cast from unsigned to signed. */ extra_data_size = (int64_t) edata_size; } if(block_flags & HFL_DATA) { - if(!read_var_sized(a, &data_size, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &data_size, NULL); + if(r != ARCHIVE_OK) + return r; if(data_size > SSIZE_MAX) { archive_set_error(&a->archive, @@ -1786,11 +1908,13 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, rar5->file.bytes_remaining = 0; } - if(!read_var_sized(a, &file_flags, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &file_flags, NULL); + if(r != ARCHIVE_OK) + return r; - if(!read_var(a, &unpacked_size, NULL)) - return ARCHIVE_EOF; + r = read_var(a, &unpacked_size, NULL); + if(r != ARCHIVE_OK) + return r; if(file_flags & UNKNOWN_UNPACKED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, @@ -1807,8 +1931,9 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, return sanity_ret; } - if(!read_var_sized(a, &file_attr, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &file_attr, NULL); + if(r != ARCHIVE_OK) + return r; if(file_flags & UTIME) { if(!read_u32(a, &mtime)) @@ -1820,8 +1945,9 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, return ARCHIVE_EOF; } - if(!read_var_sized(a, &compression_info, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &compression_info, NULL); + if(r != ARCHIVE_OK) + return r; c_method = (int) (compression_info >> 7) & 0x7; c_version = (int) (compression_info & 0x3f); @@ -1912,8 +2038,9 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, rar5->file.service = 0; - if(!read_var_sized(a, &host_os, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &host_os, NULL); + if(r != ARCHIVE_OK) + return r; if(host_os == HOST_WINDOWS) { /* Host OS is Windows */ @@ -1964,8 +2091,9 @@ static int process_head_file(struct archive_read* a, struct rar5 *rar5, return ARCHIVE_FATAL; } - if(!read_var_sized(a, &name_size, NULL)) - return ARCHIVE_EOF; + r = read_var_sized(a, &name_size, NULL); + if(r != ARCHIVE_OK) + return r; if(name_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2093,14 +2221,17 @@ static int process_head_main(struct archive_read* a, struct rar5 *rar5, (void) entry; if(block_flags & HFL_EXTRA_DATA) { - if(!read_var(a, &extra_data_size, NULL)) - return ARCHIVE_EOF; + int r; + r = read_var(a, &extra_data_size, NULL); + if(r != ARCHIVE_OK) + return r; } else { extra_data_size = 0; } - if(!read_var_sized(a, &archive_flags, NULL)) { - return ARCHIVE_EOF; + ret = read_var_sized(a, &archive_flags, NULL); + if(ret != ARCHIVE_OK) { + return ret; } rar5->main.volume = (archive_flags & VOLUME) > 0; @@ -2108,8 +2239,9 @@ static int process_head_main(struct archive_read* a, struct rar5 *rar5, if(archive_flags & VOLUME_NUMBER) { size_t v = 0; - if(!read_var_sized(a, &v, NULL)) { - return ARCHIVE_EOF; + ret = read_var_sized(a, &v, NULL); + if(ret != ARCHIVE_OK) { + return ret; } if (v > UINT_MAX) { @@ -2140,12 +2272,14 @@ static int process_head_main(struct archive_read* a, struct rar5 *rar5, return ARCHIVE_OK; } - if(!read_var_sized(a, &extra_field_size, NULL)) { - return ARCHIVE_EOF; + ret = read_var_sized(a, &extra_field_size, NULL); + if(ret != ARCHIVE_OK) { + return ret; } - if(!read_var_sized(a, &extra_field_id, NULL)) { - return ARCHIVE_EOF; + ret = read_var_sized(a, &extra_field_id, NULL); + if(ret != ARCHIVE_OK) { + return ret; } if(extra_field_size == 0) { @@ -2314,20 +2448,23 @@ static int process_base_block(struct archive_read* a, } /* Read header size. */ - if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { - return ARCHIVE_EOF; + ret = read_var_sized(a, &raw_hdr_size, &hdr_size_len); + if(ret != ARCHIVE_OK) { + return ret; } - hdr_size = raw_hdr_size + hdr_size_len; - - /* Sanity check, maximum header size for RAR5 is 2MB. */ - if(hdr_size > (2 * 1024 * 1024)) { + /* Sanity check. Bounding raw_hdr_size (instead of the sum) also + * ensures that adding hdr_size_len below cannot wrap hdr_size + * around SIZE_MAX. */ + if(raw_hdr_size > MAX_HEADER_SIZE - hdr_size_len) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Base block header is too large"); return ARCHIVE_FATAL; } + hdr_size = raw_hdr_size + hdr_size_len; + /* Additional sanity checks to weed out invalid files. */ if(raw_hdr_size == 0 || hdr_size_len == 0 || hdr_size < SMALLEST_RAR5_BLOCK_SIZE) @@ -2346,7 +2483,7 @@ static int process_base_block(struct archive_read* a, } /* Verify the CRC32 of the header data. */ - computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); + computed_crc = (uint32_t) __archive_crc32(0, p, (int) hdr_size); if(computed_crc != hdr_crc) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2365,11 +2502,13 @@ static int process_base_block(struct archive_read* a, return ARCHIVE_EOF; } - if(!read_var_sized(a, &header_id, NULL)) - return ARCHIVE_EOF; + ret = read_var_sized(a, &header_id, NULL); + if(ret != ARCHIVE_OK) + return ret; - if(!read_var_sized(a, &header_flags, NULL)) - return ARCHIVE_EOF; + ret = read_var_sized(a, &header_flags, NULL); + if(ret != ARCHIVE_OK) + return ret; rar5->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; rar5->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; @@ -2507,7 +2646,7 @@ static int try_skip_sfx(struct archive_read *a) rar5_signature(signature); - while (total + window <= (1024 * 512)) { + while (total + window <= SFX_MAX_READAHEAD) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ @@ -2633,7 +2772,7 @@ static void update_crc(struct rar5 *rar5, const uint8_t* p, size_t to_read) { * `stored_crc32` info filled in. */ if(rar5->file.stored_crc32 > 0) { rar5->file.calculated_crc32 = - crc32(rar5->file.calculated_crc32, p, + __archive_crc32(rar5->file.calculated_crc32, p, (unsigned int)to_read); } @@ -3454,6 +3593,18 @@ static int scan_for_signature(struct archive_read* a) { return ARCHIVE_FATAL; } +/* Report that the archive ended while the current file was still expected to + * continue in a following volume (its header had the 'split after' flag set), + * which typically happens when a multivolume set is opened without supplying + * its later volumes. Returning a clean EOF here would silently hand back a + * short, corrupted read, so this is a fatal error. */ +static int truncated_multivolume_error(struct archive_read* a) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated RAR5 archive: file continues in a subsequent volume " + "that is not available"); + 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) { @@ -3550,8 +3701,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size, rar5->cstate.switch_multivolume = 1; /* Reallocate the memory which will hold the whole block. */ - if(rar5->vol.push_buf) - free((void*) rar5->vol.push_buf); + free(rar5->vol.push_buf); /* Increasing the allocation block by 8 is due to bit reading functions, * which are using additional 2 or 4 bytes. Allocating the block size @@ -3591,8 +3741,11 @@ static int merge_block(struct archive_read* a, ssize_t block_size, } if(!read_ahead(a, cur_block_size, &lp)) { + /* We are in the middle of merging a block that spans + * volumes, so more data must follow; running out of + * input here means the following volume is missing. */ rar5->cstate.switch_multivolume = 0; - return ARCHIVE_EOF; + return truncated_multivolume_error(a); } /* Sanity check; there should never be a situation where this @@ -3636,7 +3789,13 @@ static int merge_block(struct archive_read* a, ssize_t block_size, rar5->merge_mode++; ret = advance_multivolume(a); rar5->merge_mode--; - if(ret != ARCHIVE_OK) { + if(ret == ARCHIVE_EOF) { + /* The block is only partially merged but the + * input ended, so the volume that holds the rest + * of it is missing. */ + rar5->cstate.switch_multivolume = 0; + return truncated_multivolume_error(a); + } else if(ret != ARCHIVE_OK) { rar5->cstate.switch_multivolume = 0; return ret; } @@ -3659,7 +3818,15 @@ static int process_block(struct archive_read* a) { /* If we don't have any data to be processed, this most probably means * we need to switch to the next volume. */ if(rar5->main.volume && rar5->file.bytes_remaining == 0) { + /* Capture 'split after' before advancing: advance_multivolume() + * parses following headers and overwrites this flag. */ + int split_after = rar5->generic.split_after > 0; ret = advance_multivolume(a); + if(ret == ARCHIVE_EOF && split_after) { + /* The file is promised to continue in a following + * volume, but the input ended first. */ + return truncated_multivolume_error(a); + } if(ret != ARCHIVE_OK) return ret; } @@ -4063,6 +4230,13 @@ static int do_unstore_file(struct archive_read* a, size_t to_read; const uint8_t* p; + /* This function returns without any data on more than one path, + * and the end of the stored data is the position all of them + * should report. */ + if(buf) *buf = NULL; + if(size) *size = 0; + if(offset) *offset = rar5->cstate.last_unstore_ptr; + if(rar5->file.bytes_remaining == 0 && rar5->main.volume > 0 && rar5->generic.split_after > 0) { @@ -4072,7 +4246,16 @@ static int do_unstore_file(struct archive_read* a, ret = advance_multivolume(a); rar5->cstate.switch_multivolume = 0; - if(ret != ARCHIVE_OK) { + if(ret == ARCHIVE_EOF) { + /* We only reach this advance because 'split after' was + * set, i.e. the file is promised to continue in a + * following volume, but the input ended before we could + * reach it (for example, only the first volume of the + * set was supplied). The data decoded so far is + * incomplete, so report truncation rather than silently + * returning a short read as a clean end of file. */ + return truncated_multivolume_error(a); + } else if(ret != ARCHIVE_OK) { /* Failed to advance to next multivolume archive * file. */ return ret; @@ -4272,6 +4455,8 @@ static int rar5_read_data(struct archive_read *a, const void **buff, struct rar5 *rar5 = a->format->data; int ret; + if (buff) + *buff = NULL; if (size) *size = 0; @@ -4307,6 +4492,11 @@ static int rar5_read_data(struct archive_read *a, const void **buff, } if(rar5->file.eof == 1) { + /* The entry is over; report the end of the unpacked data, + * so that a sparse-file aware caller does not have to + * guess where it is. */ + if (offset) + *offset = rar5->cstate.last_write_ptr; return ARCHIVE_EOF; } @@ -4458,12 +4648,12 @@ static void rar5_deinit(struct rar5 *rar5) { } int archive_read_support_format_rar5(struct archive *_a) { - struct archive_read* ar; - int ret; + struct archive_read* ar = (struct archive_read*)_a; struct rar5 *rar5; + int r; - if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) - return ret; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_support_format_rar5"); rar5 = malloc(sizeof(*rar5)); if(rar5 == NULL) { @@ -4479,7 +4669,7 @@ int archive_read_support_format_rar5(struct archive *_a) { return ARCHIVE_FATAL; } - ret = __archive_read_register_format(ar, + r = __archive_read_register_format(ar, rar5, "rar5", rar5_bid, @@ -4492,10 +4682,9 @@ int archive_read_support_format_rar5(struct archive *_a) { rar5_capabilities, rar5_has_encrypted_entries); - if(ret != ARCHIVE_OK) { + if(r != ARCHIVE_OK) { rar5_deinit(rar5); free(rar5); } - - return ARCHIVE_OK; + return r; } diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c index cda3c1e49c59..ba1dde2bb27b 100644 --- a/libarchive/archive_read_support_format_raw.c +++ b/libarchive/archive_read_support_format_raw.c @@ -64,8 +64,7 @@ archive_read_support_format_raw(struct archive *_a) raw = calloc(1, sizeof(*raw)); if (raw == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate raw data"); + archive_set_error(_a, ENOMEM, "Can't allocate raw data"); return (ARCHIVE_FATAL); } @@ -81,6 +80,7 @@ archive_read_support_format_raw(struct archive *_a) archive_read_format_raw_cleanup, NULL, NULL); + if (r != ARCHIVE_OK) free(raw); return (r); @@ -137,8 +137,15 @@ archive_read_format_raw_read_data(struct archive_read *a, raw->unconsumed = 0; } - if (raw->end_of_file) + if (raw->end_of_file) { + /* Keep reporting the end of the entry, so that a caller + * that asks once more after end-of-file still gets a + * position it can use. */ + *buff = NULL; + *size = 0; + *offset = raw->offset; return (ARCHIVE_EOF); + } /* Get whatever bytes are immediately available. */ *buff = __archive_read_ahead(a, 1, &avail); diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index b73c0d9372e4..2c7712b9ee61 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -187,6 +187,8 @@ static int header_old_tar(struct archive_read *, struct tar *, struct archive_entry *, const void *); static int header_pax_extension(struct archive_read *, struct tar *, struct archive_entry *, const void *, int64_t *); +static int header_pax_records(struct archive_read *, struct tar *, + struct archive_entry *, const void *h, int64_t *, int); static int header_pax_global(struct archive_read *, struct tar *, struct archive_entry *, const void *h, int64_t *); static int header_gnu_longlink(struct archive_read *, struct tar *, @@ -272,8 +274,7 @@ archive_read_support_format_tar(struct archive *_a) tar = calloc(1, sizeof(*tar)); if (tar == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate tar data"); + archive_set_error(_a, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } #ifdef HAVE_COPYFILE_H @@ -281,7 +282,9 @@ archive_read_support_format_tar(struct archive *_a) tar->process_mac_extensions = 1; #endif - r = __archive_read_register_format(a, tar, "tar", + r = __archive_read_register_format(a, + tar, + "tar", archive_read_format_tar_bid, archive_read_format_tar_options, archive_read_format_tar_read_header, @@ -294,7 +297,7 @@ archive_read_support_format_tar(struct archive *_a) if (r != ARCHIVE_OK) free(tar); - return (ARCHIVE_OK); + return (r); } static int @@ -568,11 +571,21 @@ archive_read_format_tar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } else { struct sparse_block *sb; + size_t added = 0; + int count; for (sb = tar->sparse_list; sb != NULL; sb = sb->next) { - if (!sb->hole) + if (!sb->hole) { archive_entry_sparse_add_entry(entry, sb->offset, sb->remaining); + added++; + } + } + count = archive_entry_sparse_count(entry); + if (count < 0 || (size_t)count != added) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Malformed sparse map data"); + return (ARCHIVE_FATAL); } } @@ -1737,34 +1750,102 @@ read_mac_metadata_blob(struct archive_read *a, } /* - * Parse a file header for a pax extended archive entry. + * Attributes that we act on when we see them in an `x` header. + * + * A `g` header supplies defaults for every following member, which we + * do not implement. Silently ignoring one of these would therefore + * change how those members are interpreted, so we reject the archive + * instead. Anything not listed here we already ignore wherever it + * appears, so ignoring it in a `g` header is no less correct; that + * keeps well-formed archives readable, in particular the `g` headers + * that star always writes for its `exustar` format. + */ +#define PAX_GLOBAL_EXACT(k) { k, sizeof(k) - 1, 0 } +#define PAX_GLOBAL_PREFIX(k) { k, sizeof(k) - 1, 1 } +static const struct pax_global_unsupported_attr { + const char *key; + size_t length; + int is_prefix; +} pax_global_unsupported_attrs[] = { + /* POSIX-standard attributes that describe the member. */ + PAX_GLOBAL_EXACT("atime"), + PAX_GLOBAL_EXACT("ctime"), + PAX_GLOBAL_EXACT("gid"), + PAX_GLOBAL_EXACT("gname"), + PAX_GLOBAL_EXACT("hdrcharset"), + PAX_GLOBAL_EXACT("linkpath"), + PAX_GLOBAL_EXACT("mtime"), + PAX_GLOBAL_EXACT("path"), + PAX_GLOBAL_EXACT("size"), + PAX_GLOBAL_EXACT("uid"), + PAX_GLOBAL_EXACT("uname"), + /* Vendor attributes that we honor. */ + PAX_GLOBAL_EXACT("GNU.sparse"), + PAX_GLOBAL_PREFIX("GNU.sparse."), + PAX_GLOBAL_EXACT("LIBARCHIVE.creationtime"), + PAX_GLOBAL_EXACT("LIBARCHIVE.symlinktype"), + PAX_GLOBAL_PREFIX("LIBARCHIVE.xattr."), + PAX_GLOBAL_EXACT("RHT.security.selinux"), + PAX_GLOBAL_EXACT("SCHILY.acl.access"), + PAX_GLOBAL_EXACT("SCHILY.acl.ace"), + PAX_GLOBAL_EXACT("SCHILY.acl.default"), + PAX_GLOBAL_EXACT("SCHILY.dev"), + PAX_GLOBAL_EXACT("SCHILY.devmajor"), + PAX_GLOBAL_EXACT("SCHILY.devminor"), + PAX_GLOBAL_EXACT("SCHILY.fflags"), + PAX_GLOBAL_EXACT("SCHILY.ino"), + PAX_GLOBAL_EXACT("SCHILY.nlink"), + PAX_GLOBAL_EXACT("SCHILY.realsize"), + PAX_GLOBAL_PREFIX("SCHILY.xattr."), + PAX_GLOBAL_EXACT("SUN.holesdata"), +}; + +/* + * TODO: This is a linear scan over the table above. It runs once per + * attribute in a `g` header, and `g` headers are rare, so the cost is + * not measurable today. If that ever changes -- or if the table grows + * much larger -- a sorted table with a binary search, or a hash of the + * exact keys with a separate short prefix list, would be worth doing. + */ +static int +pax_global_attribute_unsupported(const char *key, size_t key_length) +{ + const struct pax_global_unsupported_attr *attr; + size_t i; + + for (i = 0; i < sizeof(pax_global_unsupported_attrs) + / sizeof(pax_global_unsupported_attrs[0]); i++) { + attr = &pax_global_unsupported_attrs[i]; + if (attr->is_prefix) { + if (key_length > attr->length + && memcmp(key, attr->key, attr->length) == 0) + return (1); + } else if (key_length == attr->length + && memcmp(key, attr->key, attr->length) == 0) { + return (1); + } + } + return (0); +} + +/* + * Parse a file header for a pax global archive entry. */ static int header_pax_global(struct archive_read *a, struct tar *tar, struct archive_entry *entry, const void *h, int64_t *unconsumed) { - const struct archive_entry_header_ustar *header; - int64_t size, to_consume; + return (header_pax_records(a, tar, entry, h, unconsumed, 1)); +} - (void)tar; /* UNUSED */ - (void)entry; /* UNUSED */ - - header = (const struct archive_entry_header_ustar *)h; - size = tar_atol(header->size, sizeof(header->size)); - if (size < 0 || size > entry_limit) { - archive_set_error(&a->archive, EINVAL, - "Special header has invalid size: %lld", - (long long)size); - return (ARCHIVE_FATAL); - } - if (size == 0) { - archive_set_error(&a->archive, EINVAL, - "Invalid empty pax global extended header"); - return (ARCHIVE_FATAL); - } - to_consume = ((size + 511) & ~511); - *unconsumed += to_consume; - return (ARCHIVE_OK); +/* + * Parse a file header for a pax extended archive entry. + */ +static int +header_pax_extension(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h, int64_t *unconsumed) +{ + return (header_pax_records(a, tar, entry, h, unconsumed, 0)); } /* @@ -1863,9 +1944,18 @@ header_ustar(struct archive_read *a, struct tar *tar, return (err); } +/* + * Parse the attribute records in the body of an `x` or `g` header. + * + * With global == 0 each attribute is applied to the entry. With + * global == 1 the attributes are only inspected: we do not implement + * global defaults, so we reject any attribute that we would otherwise + * have honored and ignore the rest. + */ static int -header_pax_extension(struct archive_read *a, struct tar *tar, - struct archive_entry *entry, const void *h, int64_t *unconsumed) +header_pax_records(struct archive_read *a, struct tar *tar, + struct archive_entry *entry, const void *h, int64_t *unconsumed, + int global) { /* Sanity checks: The largest `x` body I've ever heard of was * a little over 4MB. So I doubt there has ever been a @@ -1910,7 +2000,8 @@ header_pax_extension(struct archive_read *a, struct tar *tar, } if (ext_size == 0) { archive_set_error(&a->archive, EINVAL, - "Invalid empty pax extended header"); + global ? "Invalid empty pax global extended header" + : "Invalid empty pax extended header"); return (ARCHIVE_FATAL); } @@ -2034,8 +2125,25 @@ header_pax_extension(struct archive_read *a, struct tar *tar, return (ARCHIVE_WARN); } - /* pax_attribute will consume value_length - 1 */ - r = pax_attribute(a, tar, entry, attr_name.s, archive_strlen(&attr_name), value_length - 1, unconsumed); + if (global) { + if (pax_global_attribute_unsupported(attr_name.s, + archive_strlen(&attr_name))) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Unsupported pax global extended header attribute '%s'", + attr_name.s); + archive_string_free(&attr_name); + *unconsumed += ext_size + ext_padding; + return (ARCHIVE_FATAL); + } + /* Not an attribute we would have acted on, so + * ignoring it here changes nothing. */ + __archive_read_consume(a, value_length - 1); + r = ARCHIVE_OK; + } else { + /* pax_attribute will consume value_length - 1 */ + r = pax_attribute(a, tar, entry, attr_name.s, archive_strlen(&attr_name), value_length - 1, unconsumed); + } ext_size -= value_length - 1; // Release the allocated attr_name (either here or before every return in this function) @@ -3136,6 +3244,8 @@ gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, { struct sparse_block *p; + if (remaining == 0) + return (ARCHIVE_OK); p = calloc(1, sizeof(*p)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c index d70c718320e1..1e0053f02729 100644 --- a/libarchive/archive_read_support_format_warc.c +++ b/libarchive/archive_read_support_format_warc.c @@ -72,6 +72,7 @@ #include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" +#include "archive_string.h" typedef enum { WT_NONE, @@ -95,16 +96,6 @@ typedef enum { LAST_WT } warc_type_t; -typedef struct { - size_t len; - const char *str; -} warc_string_t; - -typedef struct { - size_t len; - char *str; -} warc_strbuf_t; - struct warc { /* Content length of the current record */ int64_t cntlen; @@ -113,8 +104,8 @@ struct warc { /* Bytes to consume before the next read */ int64_t unconsumed; - /* String pool */ - warc_strbuf_t pool; + /* Reusable buffer while parsing file names. */ + struct archive_string fname; /* Previous version */ unsigned int pver; /* Stringified format name */ @@ -132,7 +123,8 @@ static int archive_read_format_warc_read_header(struct archive_read *, /* Private routines */ static unsigned int warc_read_version(const char *, size_t); static unsigned int warc_read_type(const char *, size_t); -static warc_string_t warc_read_uri(const char *, size_t); +static void warc_read_uri(const char *, size_t, + struct archive_string *); static int64_t warc_read_length(const char *, size_t); static time_t warc_read_date(const char *, size_t); static time_t warc_read_last_modified(const char *, size_t); @@ -150,8 +142,7 @@ archive_read_support_format_warc(struct archive *_a) ARCHIVE_STATE_NEW, "archive_read_support_format_warc"); if ((warc = calloc(1, sizeof(*warc))) == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate warc data"); + archive_set_error(_a, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } @@ -168,11 +159,9 @@ archive_read_support_format_warc(struct archive *_a) NULL, NULL); - if (r != ARCHIVE_OK) { + if (r != ARCHIVE_OK) free(warc); - return (r); - } - return (ARCHIVE_OK); + return (r); } static int @@ -180,9 +169,7 @@ archive_read_format_warc_cleanup(struct archive_read *a) { struct warc *warc = a->format->data; - if (warc->pool.len > 0U) { - free(warc->pool.str); - } + archive_string_free(&warc->fname); archive_string_free(&warc->sver); free(warc); a->format->data = NULL; @@ -192,20 +179,20 @@ archive_read_format_warc_cleanup(struct archive_read *a) static int archive_read_format_warc_bid(struct archive_read *a, int best_bid) { - const char *hdr; + const void *h; ssize_t nrd; unsigned int ver; (void)best_bid; /* UNUSED */ /* Check the first line, which should already be a record header. */ - if ((hdr = __archive_read_ahead(a, 12, &nrd)) == NULL) { + if ((h = __archive_read_ahead(a, 12, &nrd)) == NULL) { /* Not enough data to identify this format. */ return -1; } /* Parse the record version number. */ - ver = warc_read_version(hdr, nrd); + ver = warc_read_version(h, nrd); if (ver < 1200U || ver > 10000U) { /* Only WARC 0.12 through WARC 1.0 are supported. */ return -1; @@ -225,9 +212,6 @@ archive_read_format_warc_read_header(struct archive_read *a, const char *buf; ssize_t nrd; const char *eoh; - char *tmp; - /* Reuse the header buffer while parsing the file name. */ - warc_string_t fnam; /* WARC record type */ warc_type_t ftyp; /* Content length, or a negative error indicator */ @@ -314,31 +298,14 @@ start_over: case WT_RSP: /* Read the filename only for record types that are expected to * have a target URI. */ - fnam = warc_read_uri(buf, eoh - buf); + warc_read_uri(buf, eoh - buf, &warc->fname); /* Avoid creating directory endpoints as files. */ - if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') { + if (archive_strlen(&warc->fname) == 0 || + warc->fname.s[warc->fname.length - 1] == '/') { /* Skip this record. */ - fnam.len = 0U; - fnam.str = NULL; + archive_string_empty(&warc->fname); break; } - /* Copy the name into the reusable string pool to avoid a malloc/free - * roundtrip for each entry. */ - if (fnam.len + 1U > warc->pool.len) { - warc->pool.len = ((fnam.len + 64U) / 64U) * 64U; - tmp = realloc(warc->pool.str, warc->pool.len); - if (tmp == NULL) { - archive_set_error( - &a->archive, ENOMEM, - "Out of memory"); - return (ARCHIVE_FATAL); - } - warc->pool.str = tmp; - } - memcpy(warc->pool.str, fnam.str, fnam.len); - warc->pool.str[fnam.len] = '\0'; - /* Hide the pool implementation behind the parsed string. */ - fnam.str = warc->pool.str; /* Use a Last-Modified record header when present; otherwise fall back * to WARC-Date. */ @@ -355,8 +322,7 @@ start_over: case WT_CONT: case LAST_WT: default: - fnam.len = 0U; - fnam.str = NULL; + archive_string_empty(&warc->fname); break; } @@ -366,10 +332,10 @@ start_over: switch (ftyp) { case WT_RSRC: case WT_RSP: - if (fnam.len > 0U) { + if (archive_strlen(&warc->fname) > 0) { /* Populate the entry object. */ archive_entry_set_filetype(entry, AE_IFREG); - archive_entry_copy_pathname(entry, fnam.str); + archive_entry_copy_pathname(entry, warc->fname.s); archive_entry_set_size(entry, cntlen); archive_entry_set_perm(entry, 0644); /* WARC-Date becomes ctime; mtime comes from Last-Modified or WARC-Date. */ @@ -457,15 +423,8 @@ archive_read_format_warc_skip(struct archive_read *a) return (ARCHIVE_OK); } - /* Private routines */ -static void* -deconst(const void *c) -{ - return (void *)(uintptr_t)c; -} - -static char* +static const char* xmemmem(const char *hay, const size_t haysize, const char *needle, const size_t needlesize) { @@ -482,7 +441,7 @@ xmemmem(const char *hay, const size_t haysize, * found anywhere in the haystack; otherwise find the first candidate * that begins with *NEEDLE. */ if (needlesize == 0UL) { - return deconst(hay); + return hay; } else if ((hay = memchr(hay, *needle, haysize)) == NULL) { /* No candidate match remains. */ return NULL; @@ -501,7 +460,7 @@ xmemmem(const char *hay, const size_t haysize, return NULL; } else if (eqp) { /* Found a match. */ - return deconst(hay); + return hay; } /* Loop through the rest of the haystack and update the rolling XOR @@ -514,7 +473,7 @@ xmemmem(const char *hay, const size_t haysize, * NEEDLESIZE - 1 characters for equality. CAND is always before * HP by design, so no range check is needed. */ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) { - return deconst(cand); + return cand; } } return NULL; @@ -552,24 +511,34 @@ time_from_tm(struct tm *t) /* Use platform timegm() if available. */ return (timegm(t)); #else + int64_t days, result; + /* Otherwise, calculate directly using POSIX assumptions. */ /* First, fix up tm_yday based on the year, month, and day. */ if (mktime(t) == (time_t)-1) return ((time_t)-1); /* Then compute timegm() from first principles. */ - return (t->tm_sec - + t->tm_min * 60 - + t->tm_hour * 3600 - + t->tm_yday * 86400 - + (t->tm_year - 70) * 31536000 - + ((t->tm_year - 69) / 4) * 86400 - - ((t->tm_year - 1) / 100) * 86400 - + ((t->tm_year + 299) / 400) * 86400); + days = (int64_t)t->tm_yday + + ((int64_t)t->tm_year - 70) * 365 + + ((int64_t)t->tm_year - 69) / 4 + - ((int64_t)t->tm_year - 1) / 100 + + ((int64_t)t->tm_year + 299) / 400; + if (archive_ckd_mul_i64(&result, days, 86400) || + archive_ckd_add_i64(&result, result, + (int64_t)t->tm_hour * 3600 + t->tm_min * 60 + + t->tm_sec)) + return ((time_t)-1); + if (result < 0) { + if (TIME_MIN == 0 || result < (int64_t)TIME_MIN) + return ((time_t)-1); + } else if ((uint64_t)result > (uint64_t)TIME_MAX) + return ((time_t)-1); + return ((time_t)result); #endif } static time_t -xstrpisotime(const char *s, char **endptr) +xstrpisotime(const char *s, const char **endptr) { /* Like strptime(), but only for ISO 8601 Zulu strings. */ struct tm tm; @@ -617,7 +586,7 @@ xstrpisotime(const char *s, char **endptr) out: if (endptr != NULL) { - *endptr = deconst(s); + *endptr = s; } return res; } @@ -701,22 +670,23 @@ warc_read_type(const char *buf, size_t bsz) return WT_NONE; } -static warc_string_t -warc_read_uri(const char *buf, size_t bsz) +static void +warc_read_uri(const char *buf, size_t bsz, struct archive_string *str) { static const char _key[] = "\r\nWARC-Target-URI:"; const char *val, *uri, *eol, *p; - warc_string_t res = {0U, NULL}; + + archive_string_empty(str); if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { /* Header field is absent. */ - return res; + return; } /* Skip leading whitespace. */ val += sizeof(_key) - 1U; if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) { /* Header field has no end of line. */ - return res; + return; } while (val < eol && (*val == ' ' || *val == '\t')) @@ -725,18 +695,18 @@ warc_read_uri(const char *buf, size_t bsz) /* Locate the :// separator. */ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) { /* Ignore values without a :// separator. */ - return res; + return; } /* Spaces inside a URI are not allowed; CRLF should follow. */ for (p = val; p < eol; p++) { if (isspace((unsigned char)*p)) - return res; + return; } /* Require enough room for the shortest supported scheme. */ if (uri < (val + 3U)) - return res; + return; /* Move uri past the :// separator. */ uri += 3U; @@ -751,11 +721,14 @@ warc_read_uri(const char *buf, size_t bsz) while (uri < eol && *uri++ != '/'); } else { /* Unsupported URI scheme. */ - return res; + return; } - res.str = uri; - res.len = eol - uri; - return res; + + /* Require valid C string representation. */ + if (memchr(uri, '\0', eol - uri) != NULL) + return; + + archive_strncpy(str, uri, eol - uri); } static int64_t @@ -803,8 +776,7 @@ static time_t warc_read_date(const char *buf, size_t bsz) { static const char _key[] = "\r\nWARC-Date:"; - const char *val, *eol; - char *on = NULL; + const char *val, *eol, *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { @@ -830,8 +802,7 @@ static time_t warc_read_last_modified(const char *buf, size_t bsz) { static const char _key[] = "\r\nLast-Modified:"; - const char *val, *eol; - char *on = NULL; + const char *val, *eol, *on = NULL; time_t res; if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) { diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c index dbe2c25d93a1..c5d377ee6770 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -30,16 +30,8 @@ #ifdef HAVE_STDLIB_H #include #endif -#if HAVE_LIBXML_XMLREADER_H -#include -#elif HAVE_BSDXML_H -#include -#elif HAVE_EXPAT_H -#include -#elif HAVE_XMLLITE_H -#include -#include -#include +#ifdef HAVE_LIMITS_H +#include #endif #ifdef HAVE_BZLIB_H #include @@ -59,12 +51,11 @@ #include "archive_integer.h" #include "archive_private.h" #include "archive_read_private.h" +#include "archive_xml_private.h" -#if (!defined(HAVE_LIBXML_XMLREADER_H) && \ - !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H) && \ - !defined(HAVE_XMLLITE_H)) ||\ - !defined(HAVE_ZLIB_H) || \ - !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) +#if !defined(ARCHIVE_XML_READ) ||\ + !defined(HAVE_ZLIB_H) || \ + !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) /* * xar needs several external libraries. * o libxml2, expat or (Windows only) xmllite --- XML parser @@ -318,7 +309,6 @@ struct unknown_tag { struct xar { uint64_t offset; /* Current position in the file. */ - int64_t total; uint64_t h_base; int end_of_file; #define OUTBUFF_SIZE (1024 * 64) @@ -434,7 +424,7 @@ static void xml_end(void *, const char *); static int xml_data(void *, const char *, size_t); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); -#if defined(HAVE_LIBXML_XMLREADER_H) +#if defined(ARCHIVE_XML_READER_XML2) static int xml2_xmlattr_setup(struct archive_read *, struct xmlattr_list *, xmlTextReaderPtr); static int xml2_read_cb(void *, char *, int); @@ -442,7 +432,7 @@ static int xml2_close_cb(void *); static void xml2_error_hdr(void *, const char *, xmlParserSeverities, xmlTextReaderLocatorPtr); static int xml2_read_toc(struct archive_read *); -#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) +#elif defined(ARCHIVE_XML_READER_BSDXML) || defined(ARCHIVE_XML_READER_EXPAT) struct expat_userData { int state; struct archive_read *archive; @@ -453,15 +443,15 @@ static void expat_start_cb(void *, const XML_Char *, const XML_Char **); static void expat_end_cb(void *, const XML_Char *); static void expat_data_cb(void *, const XML_Char *, int); static int expat_read_toc(struct archive_read *); -#elif defined(HAVE_XMLLITE_H) +#elif defined(ARCHIVE_XML_READER_XMLLITE) static int xmllite_read_toc(struct archive_read *); #endif int archive_read_support_format_xar(struct archive *_a) { - struct xar *xar; struct archive_read *a = (struct archive_read *)_a; + struct xar *xar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, @@ -469,8 +459,7 @@ archive_read_support_format_xar(struct archive *_a) xar = calloc(1, sizeof(*xar)); if (xar == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate xar data"); + archive_set_error(_a, ENOMEM, "Can't allocate xar data"); return (ARCHIVE_FATAL); } @@ -491,6 +480,7 @@ archive_read_support_format_xar(struct archive *_a) xar_cleanup, NULL, NULL); + if (r != ARCHIVE_OK) free(xar); return (r); @@ -499,38 +489,38 @@ archive_read_support_format_xar(struct archive *_a) static int xar_bid(struct archive_read *a, int best_bid) { - const unsigned char *b; + const char *h; int bid; (void)best_bid; /* UNUSED */ - b = __archive_read_ahead(a, HEADER_SIZE, NULL); - if (b == NULL) + h = __archive_read_ahead(a, HEADER_SIZE, NULL); + if (h == NULL) return (-1); bid = 0; /* * Verify magic code */ - if (archive_be32dec(b) != HEADER_MAGIC) + if (archive_be32dec(h) != HEADER_MAGIC) return (0); bid += 32; /* * Verify header size */ - if (archive_be16dec(b+4) != HEADER_SIZE) + if (archive_be16dec(h+4) != HEADER_SIZE) return (0); bid += 16; /* * Verify header version */ - if (archive_be16dec(b+6) != HEADER_VERSION) + if (archive_be16dec(h+6) != HEADER_VERSION) return (0); bid += 16; /* * Verify type of checksum */ - switch (archive_be32dec(b+24)) { + switch (archive_be32dec(h+24)) { case CKSUM_NONE: case CKSUM_SHA1: case CKSUM_MD5: @@ -602,11 +592,11 @@ read_toc(struct archive_read *a) if (r != ARCHIVE_OK) return (r); -#ifdef HAVE_LIBXML_XMLREADER_H +#ifdef ARCHIVE_XML_READER_XML2 r = xml2_read_toc(a); -#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) +#elif defined(ARCHIVE_XML_READER_BSDXML) || defined(ARCHIVE_XML_READER_EXPAT) r = expat_read_toc(a); -#elif defined(HAVE_XMLLITE_H) +#elif defined(ARCHIVE_XML_READER_XMLLITE) r = xmllite_read_toc(a); #endif if (r != ARCHIVE_OK) @@ -918,7 +908,6 @@ xar_read_data(struct archive_read *a, *offset = xar->entry_total; xar->entry_total += *size; - xar->total += *size; xar->offset += used; xar->entry_remaining -= used; xar->entry_unconsumed = used; @@ -1063,6 +1052,11 @@ rd_contents(struct archive_read *a, const void **buff, size_t *size, *used = bytes; if (decompress(a, buff, size, b, used) != ARCHIVE_OK) return (ARCHIVE_FATAL); + if (remaining > 0 && *used == 0 && *size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "XAR decompressor made no progress"); + return (ARCHIVE_FATAL); + } /* * Update checksum of a compressed data and a extracted data. @@ -1643,6 +1637,11 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, avail_out = *outbytes; switch (xar->rd_encoding) { case GZIP: + /* avail_in and avail_out are 32 bits wide in zlib. */ + if (avail_in > UINT_MAX) + avail_in = UINT_MAX; + if (avail_out > UINT_MAX) + avail_out = UINT_MAX; xar->stream.next_in = (Bytef *)(uintptr_t)b; xar->stream.avail_in = (uInt)avail_in; xar->stream.next_out = (unsigned char *)outbuff; @@ -1662,6 +1661,11 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes, break; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: + /* avail_in and avail_out are 32 bits wide in bzlib. */ + if (avail_in > UINT_MAX) + avail_in = UINT_MAX; + if (avail_out > UINT_MAX) + avail_out = UINT_MAX; xar->bzstream.next_in = (char *)(uintptr_t)b; xar->bzstream.avail_in = (unsigned int)avail_in; xar->bzstream.next_out = (char *)outbuff; @@ -3164,7 +3168,7 @@ xml_parse_file_ext2(struct xar *xar, const char *name) return (1); } -#ifdef HAVE_LIBXML_XMLREADER_H +#ifdef ARCHIVE_XML_READER_XML2 static int xml2_xmlattr_setup(struct archive_read *a, @@ -3321,7 +3325,7 @@ xml2_read_toc(struct archive_read *a) return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL); } -#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) +#elif defined(ARCHIVE_XML_READER_BSDXML) || defined(ARCHIVE_XML_READER_EXPAT) static int expat_xmlattr_setup(struct archive_read *a, @@ -3445,7 +3449,7 @@ expat_read_toc(struct archive_read *a) return (ud.state); } -#elif defined(HAVE_XMLLITE_H) +#elif defined(ARCHIVE_XML_READER_XMLLITE) struct ArchiveStreamAdapter { const ISequentialStreamVtbl *lpVtbl; /* see asaStaticVtable */ @@ -3765,6 +3769,6 @@ out: return r; } -#endif /* defined(XMLLITE) */ +#endif /* defined(ARCHIVE_XML_READER_XMLLITE) */ #endif /* Support xar format */ diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 4148c4c90412..546c90bdeb12 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -45,6 +45,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif #ifdef HAVE_STDLIB_H #include #endif @@ -74,9 +77,7 @@ #include "archive_time_private.h" #include "archive_ppmd8_private.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + /* length of local file header, not including filename and extra */ #define ZIP_LOCHDR_LEN 30U @@ -96,6 +97,12 @@ struct zip_entry { time_t mtime; time_t atime; time_t ctime; + /* File creation time, from an NTFS extra field only */ + time_t btime; + /* Nanosecond precision, from an NTFS extra field only */ + uint32_t mtime_ns; + uint32_t atime_ns; + uint32_t btime_ns; uint32_t crc32; uint16_t mode; uint16_t zip_flags; /* From GP Flags Field */ @@ -135,6 +142,12 @@ struct trad_enc_ctx { /* Bits used in flags. */ #define LA_USED_ZIP64 (1 << 0) #define LA_FROM_CENTRAL_DIRECTORY (1 << 1) +#define LA_MTIME_FROM_EXTRA (1 << 2) +#define LA_ATIME_FROM_EXTRA (1 << 3) +#define LA_CTIME_FROM_EXTRA (1 << 4) +#define LA_NTFS_MTIME (1 << 5) +#define LA_NTFS_ATIME (1 << 6) +#define LA_NTFS_BTIME (1 << 7) /* * See "WinZip - AES Encryption Information" @@ -230,7 +243,6 @@ struct zip { unsigned char *decrypted_ptr; size_t decrypted_buffer_size; size_t decrypted_bytes_remaining; - size_t decrypted_unconsumed_bytes; /* Traditional PKWARE decryption. */ struct trad_enc_ctx tctx; @@ -337,7 +349,7 @@ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; -#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) +#define CRC32(c, b) (__archive_crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; @@ -479,6 +491,7 @@ zipx_read_header_and_decrypt(struct archive_read *a, const void **buf, size_t in return (ARCHIVE_OK); } +#if (HAVE_LZMA_H && HAVE_LIBLZMA) || defined(HAVE_BZLIB_H) || (HAVE_ZSTD_H && HAVE_LIBZSTD) || defined(HAVE_ZLIB_H) /* Decrypt bulk compressed data for zipx decompression. * Manages the decryption buffer, handles partial fills, and returns decrypted * data pointer + length. `sp` is set to the raw pointer for HMAC accounting. */ @@ -575,6 +588,7 @@ zip_read_decrypt_update(struct zip *zip, ssize_t to_consume, const void *sp) if (zip->hctx_valid) archive_hmac_sha1_update(&zip->hctx, sp, to_consume); } +#endif /* * Common code for streaming or seeking modes. @@ -586,7 +600,7 @@ zip_read_decrypt_update(struct zip *zip, ssize_t to_consume, const void *sp) static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { - return crc32(crc, buff, (unsigned int)len); + return __archive_crc32(crc, buff, (unsigned int)len); } /* Used by "ignorecrc32" option to speed up tests. */ @@ -647,6 +661,23 @@ compression_name(const int compression) return "??"; } +/* + * Interpret a raw 32-bit Unix timestamp, preferring the signed + * (pre-1970-capable) reading unless it would land before 1960, in + * which case the unsigned (post-2038-capable) reading is used instead. + * On a platform where time_t is no wider than 32 bits, that unsigned + * reading could never be represented anyway, so it's always skipped. + * See https://github.com/libarchive/libarchive/issues/3550. + */ +static time_t +to_time_t(uint32_t raw) +{ + const int32_t year_1960_offset = (10 * 365 + 3) * 24 * 60 * 60; + const int32_t t = (int32_t)raw; + return (sizeof(time_t) <= sizeof(raw) || t >= -year_1960_offset) + ? (time_t)t : (time_t)raw; +} + /* * The extra data is stored as a list of * id1+size1+data1 + id2+size2+data2 ... @@ -756,12 +787,96 @@ process_extra(struct archive_read *a, struct archive_entry *entry, * on which file starts, but we don't handle * multi-volume Zip files. */ break; + case 0x000a: + { + /* NTFS Extra Field: 4 reserved bytes, then one or + * more Tag(2)/Size(2)/Data(Size) attribute blocks. + * Only Tag 1 (size 24: Mtime, Atime, Ctime, each an + * 8-byte Windows FILETIME) is defined; skip any + * other tag, per spec. Unlike every other ZIP + * timestamp source, FILETIME has no negative/pre- + * epoch ambiguity (it's unsigned ticks since + * 1601), and it's 100ns-precision rather than + * 1-second, so it also supplies mtime_ns/atime_ns. + * "Ctime" here is Windows creation time, not POSIX + * ctime, so it maps to btime. */ + unsigned sub_offset = offset; + unsigned sub_remaining = datasize; + + if (sub_remaining < 4) + break; + sub_offset += 4; + sub_remaining -= 4; + + while (sub_remaining >= 4) { + unsigned short tag = + archive_le16dec(p + sub_offset); + unsigned short size = + archive_le16dec(p + sub_offset + 2); + sub_offset += 4; + sub_remaining -= 4; + if (size > sub_remaining) + break; + if (tag == 1 && size == 24) { + /* A raw FILETIME of 0 marks a slot + * the writer didn't have a value + * for (see the matching comment in + * archive_write_set_format_zip.c); + * treat it as absent rather than + * decoding it as 1601-01-01. */ + uint64_t raw; + int64_t secs; + uint32_t nsecs; + + raw = archive_le64dec(p + sub_offset); + if (raw != 0) { + __archive_ntfs_to_unix(raw, + &secs, &nsecs); + zip_entry->mtime = (time_t)secs; + zip_entry->mtime_ns = nsecs; + zip_entry->flags |= LA_NTFS_MTIME; + zip_entry->flags |= LA_MTIME_FROM_EXTRA; + } + + raw = archive_le64dec(p + sub_offset + 8); + if (raw != 0) { + __archive_ntfs_to_unix(raw, + &secs, &nsecs); + zip_entry->atime = (time_t)secs; + zip_entry->atime_ns = nsecs; + zip_entry->flags |= LA_NTFS_ATIME; + zip_entry->flags |= LA_ATIME_FROM_EXTRA; + } + + raw = archive_le64dec(p + sub_offset + 16); + if (raw != 0) { + __archive_ntfs_to_unix(raw, + &secs, &nsecs); + zip_entry->btime = (time_t)secs; + zip_entry->btime_ns = nsecs; + zip_entry->flags |= LA_NTFS_BTIME; + } + } + sub_offset += size; + sub_remaining -= size; + } + break; + } case 0x000d: /* PKWARE Unix Extra Field fixed metadata. */ if (datasize >= 12) { - zip_entry->atime = archive_le32dec(p + offset); - zip_entry->mtime = - archive_le32dec(p + offset + 4); + if (!(zip_entry->flags & LA_NTFS_ATIME)) { + zip_entry->atime = + to_time_t(archive_le32dec(p + offset)); + zip_entry->atime_ns = 0; + zip_entry->flags |= LA_ATIME_FROM_EXTRA; + } + if (!(zip_entry->flags & LA_NTFS_MTIME)) { + zip_entry->mtime = + to_time_t(archive_le32dec(p + offset + 4)); + zip_entry->mtime_ns = 0; + zip_entry->flags |= LA_MTIME_FROM_EXTRA; + } zip_entry->uid = archive_le16dec(p + offset + 8); zip_entry->gid = @@ -812,7 +927,12 @@ process_extra(struct archive_read *a, struct archive_entry *entry, #endif if (datasize < 4) break; - zip_entry->mtime = archive_le32dec(p + offset); + if (!(zip_entry->flags & LA_NTFS_MTIME)) { + zip_entry->mtime = + to_time_t(archive_le32dec(p + offset)); + zip_entry->mtime_ns = 0; + zip_entry->flags |= LA_MTIME_FROM_EXTRA; + } offset += 4; datasize -= 4; } @@ -820,7 +940,12 @@ process_extra(struct archive_read *a, struct archive_entry *entry, { if (datasize < 4) break; - zip_entry->atime = archive_le32dec(p + offset); + if (!(zip_entry->flags & LA_NTFS_ATIME)) { + zip_entry->atime = + to_time_t(archive_le32dec(p + offset)); + zip_entry->atime_ns = 0; + zip_entry->flags |= LA_ATIME_FROM_EXTRA; + } offset += 4; datasize -= 4; } @@ -828,7 +953,9 @@ process_extra(struct archive_read *a, struct archive_entry *entry, { if (datasize < 4) break; - zip_entry->ctime = archive_le32dec(p + offset); + zip_entry->ctime = + to_time_t(archive_le32dec(p + offset)); + zip_entry->flags |= LA_CTIME_FROM_EXTRA; offset += 4; datasize -= 4; } @@ -838,9 +965,18 @@ process_extra(struct archive_read *a, struct archive_entry *entry, { /* Info-ZIP Unix Extra Field (old version) "UX". */ if (datasize >= 8) { - zip_entry->atime = archive_le32dec(p + offset); - zip_entry->mtime = - archive_le32dec(p + offset + 4); + if (!(zip_entry->flags & LA_NTFS_ATIME)) { + zip_entry->atime = + to_time_t(archive_le32dec(p + offset)); + zip_entry->atime_ns = 0; + zip_entry->flags |= LA_ATIME_FROM_EXTRA; + } + if (!(zip_entry->flags & LA_NTFS_MTIME)) { + zip_entry->mtime = + to_time_t(archive_le32dec(p + offset + 4)); + zip_entry->mtime_ns = 0; + zip_entry->flags |= LA_MTIME_FROM_EXTRA; + } } if (datasize >= 12) { zip_entry->uid = @@ -1155,7 +1291,16 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, } zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); - zip_entry->mtime = dos_to_unix(archive_le32dec(p + 10)); + /* This DOS timestamp carries no information a Central Directory + * prescan wouldn't already have, and no sub-second precision: + * only use it (and reset mtime_ns to match, since it has none) as + * a fallback default, not an unconditional override of a more + * precise value the prescan may have already established from an + * extra field the Local Header doesn't itself repeat. */ + if (!(zip_entry->flags & LA_MTIME_FROM_EXTRA)) { + zip_entry->mtime = __archive_dos_to_unix(archive_le32dec(p + 10)); + zip_entry->mtime_ns = 0; + } zip_entry->crc32 = archive_le32dec(p + 14); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[11]; @@ -1350,9 +1495,17 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, archive_entry_set_mode(entry, zip_entry->mode); archive_entry_set_uid(entry, zip_entry->uid); archive_entry_set_gid(entry, zip_entry->gid); - archive_entry_set_mtime(entry, zip_entry->mtime, 0); - archive_entry_set_ctime(entry, zip_entry->ctime, 0); - archive_entry_set_atime(entry, zip_entry->atime, 0); + archive_entry_set_mtime(entry, zip_entry->mtime, zip_entry->mtime_ns); + /* Without an extra field to supply them, leave atime and ctime + * unset, rather than reporting them as set to the Unix epoch. */ + if (zip_entry->flags & LA_CTIME_FROM_EXTRA) + archive_entry_set_ctime(entry, zip_entry->ctime, 0); + if (zip_entry->flags & LA_ATIME_FROM_EXTRA) + archive_entry_set_atime(entry, zip_entry->atime, + zip_entry->atime_ns); + if (zip_entry->flags & LA_NTFS_BTIME) + archive_entry_set_birthtime(entry, zip_entry->btime, + zip_entry->btime_ns); if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { size_t linkname_length; @@ -2347,17 +2500,18 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, } } - if(zip->end_of_entry && zip->entry_bytes_remaining > 0) { - ssize_t remaining = (ssize_t)zip->entry_bytes_remaining; - const void *p = __archive_read_ahead(a, remaining, NULL); - if (p != NULL) { - if (zip->hctx_valid) - archive_hmac_sha1_update(&zip->hctx, - p, remaining); - __archive_read_consume(a, remaining); - zip->entry_compressed_bytes_read += remaining; - zip->entry_bytes_remaining = 0; - } + while(zip->end_of_entry && zip->entry_bytes_remaining > 0) { + const void *p = __archive_read_ahead(a, 1, &bytes_avail); + if (p == NULL) + break; + if (bytes_avail > zip->entry_bytes_remaining) + bytes_avail = (ssize_t)zip->entry_bytes_remaining; + if (zip->hctx_valid) + archive_hmac_sha1_update(&zip->hctx, + p, bytes_avail); + __archive_read_consume(a, bytes_avail); + zip->entry_compressed_bytes_read += bytes_avail; + zip->entry_bytes_remaining -= bytes_avail; } /* Free lzma decoder handle because we'll no longer need it. */ @@ -2615,7 +2769,7 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip = a->format->data; - ssize_t bytes_avail = 0, to_consume; + ssize_t bytes_avail = 0, max_in, to_consume; const void *compressed_buff; const void *sp; int r; @@ -2649,7 +2803,14 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, zip_read_decrypt(zip, compressed_buff, bytes_avail, &compressed_buff, &bytes_avail, &sp); - /* Setup buffer boundaries. */ + /* Setup buffer boundaries. bzstream.avail_in is 32 bits wide, + * so clamp the available byte count before the assignment. */ + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (bytes_avail > max_in) + bytes_avail = max_in; zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; zip->bzstream.avail_in = (uint32_t)bytes_avail; zip->bzstream.total_in_hi32 = 0; @@ -2888,7 +3049,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip = a->format->data; - ssize_t bytes_avail, to_consume = 0; + ssize_t bytes_avail, max_in, to_consume = 0; const void *compressed_buff; const void *sp; int r; @@ -2931,6 +3092,16 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, zip_read_decrypt(zip, compressed_buff, bytes_avail, &compressed_buff, &bytes_avail, &sp); + /* stream.avail_in is a uInt, which is 32 bits wide even where + * ssize_t is 64 bits. Clamp the available byte count so that a + * read-ahead window larger than 4 GiB is not truncated. */ + if (UINT_MAX >= SSIZE_MAX) + max_in = SSIZE_MAX; + else + max_in = UINT_MAX; + if (bytes_avail > max_in) + bytes_avail = max_in; + /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the @@ -3653,11 +3824,11 @@ archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) static int archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) { - const char *p; + const char *h; (void)best_bid; /* UNUSED */ - if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) + if ((h = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* @@ -3668,13 +3839,13 @@ archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) * * So we've effectively verified ~29 total bits of check data. */ - if (p[0] == 'P' && p[1] == 'K') { - if ((p[2] == '\001' && p[3] == '\002') - || (p[2] == '\003' && p[3] == '\004') - || (p[2] == '\005' && p[3] == '\006') - || (p[2] == '\006' && p[3] == '\006') - || (p[2] == '\007' && p[3] == '\010') - || (p[2] == '0' && p[3] == '0')) + if (h[0] == 'P' && h[1] == 'K') { + if ((h[2] == '\001' && h[3] == '\002') + || (h[2] == '\003' && h[3] == '\004') + || (h[2] == '\005' && h[3] == '\006') + || (h[2] == '\006' && h[3] == '\006') + || (h[2] == '\007' && h[3] == '\010') + || (h[2] == '0' && h[3] == '0')) return (29); } @@ -3881,8 +4052,7 @@ archive_read_support_format_zip_streamable(struct archive *_a) zip = calloc(1, sizeof(*zip)); if (zip == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate zip data"); + archive_set_error(_a, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } @@ -3911,7 +4081,7 @@ archive_read_support_format_zip_streamable(struct archive *_a) if (r != ARCHIVE_OK) free(zip); - return (ARCHIVE_OK); + return (r); } /* ------------------------------------------------------------------------ */ @@ -4027,7 +4197,7 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { struct zip *zip = a->format->data; int64_t file_size, current_offset; - const char *p; + const char *h; int i, tail; /* If someone has already bid more than 32, then avoid @@ -4045,22 +4215,26 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) current_offset = __archive_read_seek(a, -tail, SEEK_END); if (current_offset < 0) return 0; - if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) + if ((h = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; /* Boyer-Moore search backwards from the end, since we want * to match the last EOCD in the file (there can be more than * one if there is an uncompressed Zip archive as a member - * within this Zip archive). */ - for (i = tail - 22; i > 0;) { - switch (p[i]) { + * within this Zip archive). + * + * The lower bound is inclusive (>= 0, not > 0): a minimal, + * valid, zero-entry archive is exactly 22 bytes long, putting + * the EOCD signature at index 0 of this search window. */ + for (i = tail - 22; i >= 0;) { + switch (h[i]) { case 'P': - if (memcmp(p + i, "PK\005\006", 4) == 0) { - int ret = read_eocd(zip, p + i, + if (memcmp(h + i, "PK\005\006", 4) == 0) { + int ret = read_eocd(zip, h + i, current_offset + i); /* Zip64 EOCD locator precedes * regular EOCD if present. */ - if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) { - int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20); + if (i >= 20 && memcmp(h + i - 20, "PK\006\007", 4) == 0) { + int ret_zip64 = read_zip64_eocd(a, zip, h + i - 20); if (ret_zip64 > ret) ret = ret_zip64; } @@ -4269,7 +4443,7 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, zip->has_encrypted_entries = 1; } zip_entry->compression = (char)archive_le16dec(p + 10); - zip_entry->mtime = dos_to_unix(archive_le32dec(p + 12)); + zip_entry->mtime = __archive_dos_to_unix(archive_le32dec(p + 12)); zip_entry->crc32 = archive_le32dec(p + 16); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[13]; @@ -4704,7 +4878,7 @@ archive_read_support_format_zip_seekable(struct archive *_a) if (r != ARCHIVE_OK) free(zip); - return (ARCHIVE_OK); + return (r); } /*# vim:set noet:*/ diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 87e21c8d7f2e..474dff81db88 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -62,6 +62,7 @@ #endif #include "archive_endian.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_string.h" #include "archive_string_composition.h" @@ -318,9 +319,8 @@ archive_string_ensure(struct archive_string *as, size_t s) new_length = as->buffer_length + as->buffer_length; else { /* Buffers 8k and over grow by at least 25% each time. */ - new_length = as->buffer_length + as->buffer_length / 4; - /* Be safe: If size wraps, fail. */ - if (new_length < as->buffer_length) { + if (archive_ckd_add_size(&new_length, + as->buffer_length, as->buffer_length / 4)) { /* On failure, wipe the string and return NULL. */ archive_string_free(as); errno = ENOMEM;/* Make sure errno has ENOMEM. */ @@ -445,7 +445,7 @@ static const char * default_iconv_charset(const char *charset) { if (charset != NULL && charset[0] != '\0') return charset; -#if HAVE_LOCALE_CHARSET && !defined(__APPLE__) +#if HAVE_LOCALE_CHARSET && HAVE_LOCALCHARSET_H && !defined(__APPLE__) /* locale_charset() is broken on Mac OS */ return locale_charset(); #elif HAVE_NL_LANGINFO @@ -785,6 +785,10 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as, r = WideCharToMultiByte(to_cp, 0, ws, (int)wslen, as->s + as->length, (int)(as->buffer_length - as->length - 1), NULL, dp); + if (r == 0 && wslen == 0) { + count = 0; + break; + } if (r == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the MBS buffer and retry. */ @@ -1316,17 +1320,7 @@ create_sconv_object(const char *fc, const char *tc, else if (strcmp(fc, "CP932") == 0) sc->cd = iconv_open(tc, "SJIS"); } -#if defined(__FreeBSD__) && !defined(HAVE_LIBICONV) - /* - * FreeBSD's native iconv() by default returns the number of - * invalid characters in the input string, as specified by - * POSIX, but iconv_strncat_in_locale() assumes GNU iconv - * semantics. - */ - int v = 1; - - (void)iconvctl(sc->cd, ICONV_SET_ILSEQ_INVALID, &v); -#elif defined(_WIN32) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) /* * archive_mstring on Windows directly convert multi-bytes * into archive_wstring in order not to depend on locale @@ -1593,9 +1587,15 @@ static struct { unsigned ocp; const char *locale; } acp_ocp_map[] = { - { 950, 950, "Chinese_Taiwan" }, + { 950, 950, "Chinese_Hong Kong S.A.R." }, + { 950, 950, "Chinese_Macau S.A.R." }, { 936, 936, "Chinese_People's Republic of China" }, + { 936, 936, "Chinese (Simplified)_China" }, + { 936, 936, "Chinese (Simplified)_People's Republic of China" }, { 950, 950, "Chinese_Taiwan" }, + { 950, 950, "Chinese (Traditional)_Hong Kong SAR" }, + { 950, 950, "Chinese (Traditional)_Macau SAR" }, + { 950, 950, "Chinese (Traditional)_Taiwan" }, { 1250, 852, "Czech_Czech Republic" }, { 1252, 850, "Danish_Denmark" }, { 1252, 850, "Dutch_Netherlands" }, @@ -1621,14 +1621,20 @@ static struct { { 1252, 850, "Italian_Switzerland" }, { 932, 932, "Japanese_Japan" }, { 949, 949, "Korean_Korea" }, + { 949, 949, "Korean_South Korea" }, + { 949, 949, "Korean (Unified)_Korea" }, + { 1252, 850, "Norwegian (Bokmål)_Norway" }, { 1252, 850, "Norwegian (BokmOl)_Norway" }, - { 1252, 850, "Norwegian (BokmOl)_Norway" }, + { 1252, 850, "Norwegian (Nynorsk)_Norway" }, { 1252, 850, "Norwegian-Nynorsk_Norway" }, { 1250, 852, "Polish_Poland" }, - { 1252, 850, "Portuguese_Portugal" }, { 1252, 850, "Portuguese_Brazil" }, + { 1252, 850, "Portuguese_Portugal" }, + { 1252, 850, "Portuguese (Brazil)_Brazil" }, + { 1252, 850, "Portuguese (Portugal)_Portugal" }, { 1251, 866, "Russian_Russia" }, { 1250, 852, "Slovak_Slovakia" }, + { 1250, 852, "Slovak_Slovak Republic" }, { 1252, 850, "Spanish_Spain" }, { 1252, 850, "Spanish_Mexico" }, { 1252, 850, "Spanish_Spain" }, @@ -2126,8 +2132,16 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, while (remaining >= from_size) { size_t result = iconv(cd, &itp, &remaining, &outp, &avail); - if (result != (size_t)-1) + if (result == 0) break; /* Conversion completed. */ + if (result != (size_t)-1) { + /* + * Conversion did not fail, but it performed + * some non-identical character substitutions. + */ + return_value = -1; + break; + } if (errno == EILSEQ || errno == EINVAL) { /* @@ -2163,6 +2177,17 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, avail -= rbytes; } else { /* Skip the illegal input bytes. */ + if (avail < 1) { + as->length = outp - as->s; + bs = as->buffer_length + + (remaining * to_size) + 1; + if (NULL == + archive_string_ensure(as, bs)) + return (-1); + outp = as->s + as->length; + avail = as->buffer_length + - as->length - to_size; + } *outp++ = '?'; avail--; } diff --git a/libarchive/archive_string_sprintf.c b/libarchive/archive_string_sprintf.c index 69b0cdcd83d5..c49de3e11221 100644 --- a/libarchive/archive_string_sprintf.c +++ b/libarchive/archive_string_sprintf.c @@ -50,11 +50,15 @@ * them to an archive_string. */ static void -append_uint(struct archive_string *as, uintmax_t d, unsigned base) +append_uint(struct archive_string *as, uintmax_t d, unsigned base, + int upper) { - static const char digits[] = "0123456789abcdef"; + static const char digits_lower[] = "0123456789abcdef"; + static const char digits_upper[] = "0123456789ABCDEF"; + const char *digits = upper ? digits_upper : digits_lower; + if (d >= base) - append_uint(as, d/base, base); + append_uint(as, d/base, base, upper); archive_strappend_char(as, digits[d % base]); } @@ -68,7 +72,7 @@ append_int(struct archive_string *as, intmax_t d, unsigned base) ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d); } else ud = d; - append_uint(as, ud, base); + append_uint(as, ud, base, 0); } @@ -186,9 +190,10 @@ archive_string_vsprintf(struct archive_string *as, const char *fmt, } /* Format it in the correct base. */ switch (*p) { - case 'o': append_uint(as, u, 8); break; - case 'u': append_uint(as, u, 10); break; - default: append_uint(as, u, 16); break; + case 'o': append_uint(as, u, 8, 0); break; + case 'u': append_uint(as, u, 10, 0); break; + case 'x': append_uint(as, u, 16, 0); break; + case 'X': append_uint(as, u, 16, 1); break; } break; default: diff --git a/libarchive/archive_time.c b/libarchive/archive_time.c index bc7708288976..6bf8fd71b584 100644 --- a/libarchive/archive_time.c +++ b/libarchive/archive_time.c @@ -49,7 +49,7 @@ #include /* Windows FILETIME to NTFS time. */ uint64_t -FILETIME_to_ntfs(const FILETIME* filetime) +__archive_FILETIME_to_ntfs(const FILETIME* filetime) { ULARGE_INTEGER utc; utc.HighPart = filetime->dwHighDateTime; @@ -60,7 +60,7 @@ FILETIME_to_ntfs(const FILETIME* filetime) /* Convert an MSDOS-style date/time into Unix-style time. */ int64_t -dos_to_unix(uint32_t dos_time) +__archive_dos_to_unix(uint32_t dos_time) { uint16_t msTime, msDate; struct tm ts; @@ -83,7 +83,7 @@ dos_to_unix(uint32_t dos_time) /* Convert into MSDOS-style date/time. */ uint32_t -unix_to_dos(int64_t unix_time) +__archive_unix_to_dos(int64_t unix_time) { struct tm *t; uint32_t dt; @@ -132,7 +132,7 @@ unix_to_dos(int64_t unix_time) /* Convert NTFS time to Unix sec/nsec */ void -ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs) +__archive_ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs) { if (ntfs > INT64_MAX) { ntfs -= NTFS_EPOC_TICKS; @@ -143,7 +143,17 @@ ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs) lldiv_t tdiv; int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS; + /* lldiv() truncates toward zero, not toward -infinity, so + * for a negative |value| that isn't an exact multiple of + * NTFS_TICKS, tdiv.rem would itself come out negative here. + * Adjust to floor division so *nsecs always ends up + * non-negative, with the sign folded entirely into *secs, + * as callers (e.g. struct timespec) expect. */ tdiv = lldiv(value, NTFS_TICKS); + if (tdiv.rem < 0) { + tdiv.quot -= 1; + tdiv.rem += NTFS_TICKS; + } *secs = tdiv.quot; *nsecs = (uint32_t)(tdiv.rem * 100); } @@ -151,7 +161,7 @@ ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs) /* Convert Unix sec/nsec to NTFS time */ uint64_t -unix_to_ntfs(int64_t secs, uint32_t nsecs) +__archive_unix_to_ntfs(int64_t secs, uint32_t nsecs) { uint64_t ntfs; diff --git a/libarchive/archive_time_private.h b/libarchive/archive_time_private.h index f802607c3053..4c8fbb87c1f2 100644 --- a/libarchive/archive_time_private.h +++ b/libarchive/archive_time_private.h @@ -31,17 +31,17 @@ #include /* NTFS time to Unix sec/nsec. */ -void ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs); +void __archive_ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs); /* DOS time to Unix sec. */ -int64_t dos_to_unix(uint32_t dos); +int64_t __archive_dos_to_unix(uint32_t dos); /* Unix sec/nsec to NTFS time. */ -uint64_t unix_to_ntfs(int64_t secs, uint32_t nsecs); +uint64_t __archive_unix_to_ntfs(int64_t secs, uint32_t nsecs); /* Unix sec to DOS time. */ -uint32_t unix_to_dos(int64_t secs); +uint32_t __archive_unix_to_dos(int64_t secs); #if defined(_WIN32) && !defined(__CYGWIN__) #include #include /* Windows FILETIME to NTFS time. */ -uint64_t FILETIME_to_ntfs(const FILETIME* filetime); +uint64_t __archive_FILETIME_to_ntfs(const FILETIME* filetime); #endif #endif /* ARCHIVE_TIME_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_umask.c b/libarchive/archive_umask.c new file mode 100644 index 000000000000..23b8aeda965a --- /dev/null +++ b/libarchive/archive_umask.c @@ -0,0 +1,101 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dave Borman at Cray Research, Inc. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include "archive_platform.h" +#include "archive_umask_private.h" + +#ifdef HAVE_SYS_SINGLE_THREADED_H +#include +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +/* Return the current umask. Attempt to read it without changing. */ +mode_t +__archive_get_umask(void) +{ + mode_t mask; + +#ifdef __linux__ +#ifdef HAVE_SYS_SINGLE_THREADED_H + if (!__libc_single_threaded) +#endif + { + /* + * Starting with Linux 4.7, the current process umask can be accessed + * via /proc/self/status. + */ + + // Lines in status files are almost always less than 100 bytes, so + // 1 KiB should be plenty. Plus, the umask field should show up in + // the first few lines & always be very short. So even if we split + // other lines, that should never happen with umask. We assume any + // lines we split do not contain "Umask:" in the middle of them. + char line[1024]; + // The standard doesn't guarantee mode_t size, so scan a specific + // size ourselves. We know the umask will always be 9 bits, so 32 + // bits should be plenty. + int int_umask = -1; + + FILE *fp = fopen("/proc/self/status", "re"); + if (fp != NULL) { + while (fgets(line, sizeof(line), fp)) { + if (sscanf(line, "Umask: %o", &int_umask) == 1) + break; + } + fclose(fp); + if (int_umask != -1) + return (int_umask); + } + } +#elif defined(KERN_PROC_UMASK) + /* + * FreeBSD has a sysctl interface for requesting the umask without + * temporarily modifying it. Note that this does not work if the sysctl + * security.bsd.unprivileged_proc_debug is set to 0. + */ + u_short smask; + size_t len = sizeof(smask); + if (sysctl((int[4]){ CTL_KERN, KERN_PROC, KERN_PROC_UMASK, 0 }, + 4, &smask, &len, NULL, 0) == 0) + return (smask); +#endif + + umask(mask = umask(0)); + return (mask); +} diff --git a/libarchive/archive_umask_private.h b/libarchive/archive_umask_private.h new file mode 100644 index 000000000000..98e41a49c2a9 --- /dev/null +++ b/libarchive/archive_umask_private.h @@ -0,0 +1,49 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dave Borman at Cray Research, Inc. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef ARCHIVE_UMASK_PRIVATE_H_INCLUDED +#define ARCHIVE_UMASK_PRIVATE_H_INCLUDED + +#ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST +#error This header is only to be used internally to libarchive. +#endif +#endif + +#include + +/* Return the current umask. Attempt to read it without changing. */ +extern mode_t __archive_get_umask(void); + +#endif diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 5e0b916de432..b0a5f2dde755 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -41,14 +41,6 @@ #ifdef HAVE_STRING_H #include #endif -#if defined(_WIN32) && !defined(__CYGWIN__) -#include - -/* Common in other bcrypt implementations, but missing from VS2008. */ -#ifndef BCRYPT_SUCCESS -#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS) -#endif -#endif #ifdef HAVE_ZLIB_H #include #endif @@ -243,7 +235,6 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) DWORD attr; wchar_t *xp, *ep; int fd; - BCRYPT_ALG_HANDLE hAlg = NULL; fd = -1; ws = NULL; archive_string_init(&temp_name); @@ -331,12 +322,6 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) abort(); } - if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, - NULL, 0))) { - la_dosmaperr(GetLastError()); - goto exit_tmpfile; - } - for (;;) { wchar_t *p; HANDLE h; @@ -344,13 +329,10 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) CREATEFILE2_EXTENDED_PARAMETERS createExParams; #endif - /* Generate a random file name through CryptGenRandom(). */ + /* Generate a random file name. */ p = xp; - if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p, - (DWORD)(ep - p)*sizeof(wchar_t), 0))) { - la_dosmaperr(GetLastError()); + if (archive_random(p, (ep - p)*sizeof(wchar_t)) != ARCHIVE_OK) goto exit_tmpfile; - } for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; @@ -404,8 +386,6 @@ __archive_mktempx(const char *tmpdir, wchar_t *template) break;/* success! */ } exit_tmpfile: - if (hAlg != NULL) - BCryptCloseAlgorithmProvider(hAlg, 0); free(ws); if (template == temp_name.s) archive_wstring_free(&temp_name); diff --git a/libarchive/archive_version_details.c b/libarchive/archive_version_details.c index be11aad67e2e..a0c2ae5bd181 100644 --- a/libarchive/archive_version_details.c +++ b/libarchive/archive_version_details.c @@ -84,6 +84,7 @@ #include "archive_string.h" #include "archive_cryptor_private.h" #include "archive_digest_private.h" +#include "archive_xml_private.h" static void archive_regex_version(struct archive_string* str) @@ -102,13 +103,16 @@ archive_regex_version(struct archive_string* str) static void archive_xml_version(struct archive_string* str) { -#if HAVE_LIBXML_XMLVERSION_H && HAVE_LIBXML2 +#if defined(ARCHIVE_XML_USE_XML2) archive_strcat(str, " libxml2/"); archive_strcat(str, archive_libxml2_version()); -#elif HAVE_BSDXML_H && HAVE_LIBBSDXML +#elif defined(ARCHIVE_XML_USE_XMLLITE) + archive_strcat(str, " xmllite/"); + archive_strcat(str, archive_xmllite_version()); +#elif defined(ARCHIVE_XML_USE_BSDXML) archive_strcat(str, " bsdxml/"); archive_strcat(str, archive_libbsdxml_version()); -#elif HAVE_EXPAT_H && HAVE_LIBEXPAT +#elif defined(ARCHIVE_XML_USE_EXPAT) archive_strcat(str, " expat/"); archive_strcat(str, archive_libexpat_version()); #else @@ -320,7 +324,7 @@ archive_liblzo2_version(void) const char * archive_libbsdxml_version(void) { -#if HAVE_BSDXML_H && HAVE_LIBBSDXML +#if defined(ARCHIVE_XML_USE_BSDXML) return XML_ExpatVersion(); #else return NULL; @@ -330,7 +334,7 @@ archive_libbsdxml_version(void) const char * archive_libxml2_version(void) { -#if HAVE_LIBXML_XMLVERSION_H && HAVE_LIBXML2 +#if defined(ARCHIVE_XML_USE_XML2) return LIBXML_DOTTED_VERSION; #else return NULL; @@ -340,13 +344,23 @@ archive_libxml2_version(void) const char * archive_libexpat_version(void) { -#if HAVE_EXPAT_H && HAVE_LIBEXPAT +#if defined(ARCHIVE_XML_USE_EXPAT) return XML_ExpatVersion(); #else return NULL; #endif } +const char * +archive_xmllite_version(void) +{ +#if defined(ARCHIVE_XML_USE_XMLLITE) + return "system"; +#else + return NULL; +#endif +} + const char * archive_mbedtls_version(void) { diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index b787a0d37688..f02231c2cc36 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -598,9 +598,9 @@ __hstat(HANDLE handle, struct ustat *st) mode |= S_IFREG; st->st_mode = mode; - ntfs_to_unix(FILETIME_to_ntfs(&info.ftLastAccessTime), &st->st_atime, &st->st_atime_nsec); - ntfs_to_unix(FILETIME_to_ntfs(&info.ftLastWriteTime), &st->st_mtime, &st->st_mtime_nsec); - ntfs_to_unix(FILETIME_to_ntfs(&info.ftCreationTime), &st->st_ctime, &st->st_ctime_nsec); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&info.ftLastAccessTime), &st->st_atime, &st->st_atime_nsec); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&info.ftLastWriteTime), &st->st_mtime, &st->st_mtime_nsec); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&info.ftCreationTime), &st->st_ctime, &st->st_ctime_nsec); st->st_size = ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1)) + (int64_t)(info.nFileSizeLow); diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index fef7cf90df4c..42c265d04159 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -125,6 +125,7 @@ #endif #define waitpid __la_waitpid #define write __la_write +#define HAVE_WAITPID 1 #if !defined(__WATCOMC__) diff --git a/libarchive/archive_write.3 b/libarchive/archive_write.3 index 227e4e028449..3cfe57813e03 100644 --- a/libarchive/archive_write.3 +++ b/libarchive/archive_write.3 @@ -174,7 +174,7 @@ myclose(struct archive *a, void *client_data) void write_archive(const char *outname, const char **filename) { - struct mydata *mydata = malloc(sizeof(struct mydata)); + struct mydata *mydata = malloc(sizeof(*mydata)); struct archive *a; struct archive_entry *entry; struct stat st; @@ -185,7 +185,7 @@ write_archive(const char *outname, const char **filename) a = archive_write_new(); mydata->name = outname; /* Set archive format and filter according to output file extension. - * If it fails, set default format. Platform depended function. + * If it fails, set default format. Platform dependent function. * See supported formats in archive_write_set_format_filter_by_ext.c */ if (archive_write_set_format_filter_by_ext(a, outname) != ARCHIVE_OK) { archive_write_add_filter_gzip(a); diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index e1a3ffebb1ea..fca2c18872d5 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -58,6 +58,7 @@ #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" +#include "archive_write_set_format_private.h" static int _archive_filter_code(struct archive *, int); static const char *_archive_filter_name(struct archive *, int); @@ -89,6 +90,25 @@ archive_write_vtable = { .archive_write_data = _archive_write_data, }; +int +__archive_write_option_header_charset(struct archive_write *a, + const char *key, const char *val, + struct archive_string_conv **opt_sconv) +{ + if (strcmp(key, "hdrcharset") != 0) + return (ARCHIVE_WARN); + if (val == NULL || val[0] == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s: hdrcharset option needs a character-set name", + a->format_name); + return (ARCHIVE_FAILED); + } + + *opt_sconv = archive_string_conversion_to_charset( + &a->archive, val, 0); + return (*opt_sconv != NULL ? ARCHIVE_OK : ARCHIVE_FATAL); +} + /* * Allocate, initialize and return an archive object. */ @@ -621,8 +641,7 @@ _archive_write_close(struct archive *_a) int r = ARCHIVE_OK, r1 = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, - "archive_write_close"); + ARCHIVE_STATE_ANY, "archive_write_close"); if (a->archive.state == ARCHIVE_STATE_NEW || a->archive.state == ARCHIVE_STATE_CLOSED) return (ARCHIVE_OK); /* Okay to close() when not open. */ diff --git a/libarchive/archive_write_add_filter_compress.c b/libarchive/archive_write_add_filter_compress.c index f8824204933c..b12a2e03c929 100644 --- a/libarchive/archive_write_add_filter_compress.c +++ b/libarchive/archive_write_add_filter_compress.c @@ -238,9 +238,17 @@ output_byte(struct archive_write_filter *f, unsigned char c) if (compress->compressed_buffer_size == compress->compressed_offset) { int ret = __archive_write_filter(f->next_filter, compress->compressed, compress->compressed_buffer_size); + /* + * Empty the buffer whether or not the downstream write + * succeeded. Otherwise a failed flush would leave + * compressed_offset at compressed_buffer_size, and the next + * output_byte() (callers such as output_code() keep going + * without checking the return value) would write one byte + * past the end of the buffer. + */ + compress->compressed_offset = 0; if (ret != ARCHIVE_OK) return ARCHIVE_FATAL; - compress->compressed_offset = 0; } return ARCHIVE_OK; @@ -260,13 +268,17 @@ output_code(struct archive_write_filter *f, int ocode) */ bit_offset = compress->bit_offset % 8; compress->bit_buf |= (ocode << bit_offset) & 0xff; - output_byte(f, compress->bit_buf); + ret = output_byte(f, compress->bit_buf); + if (ret != ARCHIVE_OK) + return ret; bits = compress->code_len - (8 - bit_offset); ocode >>= 8 - bit_offset; /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { - output_byte(f, ocode & 0xff); + ret = output_byte(f, ocode & 0xff); + if (ret != ARCHIVE_OK) + return ret; ocode >>= 8; bits -= 8; } diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c index a7679a728f4a..b3be114b4daa 100644 --- a/libarchive/archive_write_add_filter_gzip.c +++ b/libarchive/archive_write_add_filter_gzip.c @@ -222,7 +222,7 @@ archive_compressor_gzip_open(struct archive_write_filter *f) } } - gzip->crc = crc32(0L, NULL, 0); + gzip->crc = __archive_crc32(0L, NULL, 0); gzip->stream.next_out = gzip->compressed; gzip->stream.avail_out = (uInt)gzip->compressed_buffer_size; @@ -263,8 +263,8 @@ archive_compressor_gzip_open(struct archive_write_filter *f) } if (ofn_length < ofn_max_length) { gzip->compressed[3] |= 0x8; - strcpy((char*)gzip->compressed + 10, - gzip->original_filename); + memcpy(gzip->compressed + 10, + gzip->original_filename, ofn_length + 1); gzip->stream.next_out += ofn_length + 1; gzip->stream.avail_out -= ofn_length + 1; } else { @@ -322,7 +322,7 @@ archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, int ret; /* Update statistics */ - gzip->crc = crc32(gzip->crc, (const Bytef *)buff, (uInt)length); + gzip->crc = __archive_crc32(gzip->crc, (const Bytef *)buff, (uInt)length); gzip->total_in += length; /* Compress input data to output buffer */ diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c index 8b3b575fae4d..0c7b9e49fb5b 100644 --- a/libarchive/archive_write_add_filter_lz4.c +++ b/libarchive/archive_write_add_filter_lz4.c @@ -52,7 +52,6 @@ struct lz4 { int compression_level; - unsigned header_written:1; unsigned version_number:1; unsigned block_independence:1; unsigned block_checksum:1; @@ -292,7 +291,10 @@ archive_filter_lz4_open(struct archive_write_filter *f) return (ARCHIVE_FATAL); } - return (ARCHIVE_OK); + /* Write the stream descriptor now so that the stream checksum + * state is initialized and the stream is well-formed even if + * no data is ever written through this filter. */ + return (lz4_write_stream_descriptor(f)); } /* @@ -310,14 +312,6 @@ archive_filter_lz4_write(struct archive_write_filter *f, size_t remaining; ssize_t size; - /* If we haven't written a stream descriptor, we have to do it first. */ - if (!lz4->header_written) { - ret = lz4_write_stream_descriptor(f); - if (ret != ARCHIVE_OK) - return (ret); - lz4->header_written = 1; - } - p = (const char *)buff; remaining = length; while (remaining) { diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c index ee0869717e95..6b2901804e73 100644 --- a/libarchive/archive_write_add_filter_program.c +++ b/libarchive/archive_write_add_filter_program.c @@ -57,6 +57,8 @@ archive_write_set_compression_program(struct archive *a, const char *cmd) } #endif +#ifdef HAVE_WAITPID + struct archive_write_program_data { #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE child; @@ -396,3 +398,59 @@ free_data(struct program *program) free(program); } } + +#else + +int +archive_write_add_filter_program(struct archive *a, const char *cmd) +{ + (void)cmd; /* UNUSED */ + archive_set_error(a, ENOSYS, "Processes not supported"); + return (ARCHIVE_FATAL); +} + +struct archive_write_program_data * +__archive_write_program_allocate(const char *program) +{ + (void)program; /* UNUSED */ + return (NULL); +} + +int +__archive_write_program_free(struct archive_write_program_data *data) +{ + (void)data; /* UNUSED */ + return (ARCHIVE_OK); +} + +int +__archive_write_program_open(struct archive_write_filter *f, + struct archive_write_program_data *data, const char *cmd) +{ + (void)f; /* UNUSED */ + (void)data; /* UNUSED */ + (void)cmd; /* UNUSED */ + return (ARCHIVE_FATAL); +} + +int +__archive_write_program_write(struct archive_write_filter *f, + struct archive_write_program_data *data, const void *buff, size_t length) +{ + (void)f; /* UNUSED */ + (void)data; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)length; /* UNUSED */ + return (ARCHIVE_FATAL); +} + +int +__archive_write_program_close(struct archive_write_filter *f, + struct archive_write_program_data *data) +{ + (void)f; /* UNUSED */ + (void)data; /* UNUSED */ + return (ARCHIVE_FATAL); +} + +#endif diff --git a/libarchive/archive_write_add_filter_zstd.c b/libarchive/archive_write_add_filter_zstd.c index 826227fd4764..42a6cca41976 100644 --- a/libarchive/archive_write_add_filter_zstd.c +++ b/libarchive/archive_write_add_filter_zstd.c @@ -70,7 +70,6 @@ struct zstd { size_t max_frame_in; size_t min_frame_out; size_t max_frame_out; - size_t cur_frame; size_t cur_frame_in; size_t cur_frame_out; ZSTD_CStream *cstream; @@ -494,7 +493,6 @@ drive_compressor(struct archive_write_filter *f, break; case resetting: ZSTD_CCtx_reset(zstd->cstream, ZSTD_reset_session_only); - zstd->cur_frame++; zstd->cur_frame_in = 0; zstd->cur_frame_out = 0; zstd->state = running; diff --git a/libarchive/archive_write_data.3 b/libarchive/archive_write_data.3 index 9f239f7c9ece..fb4b6b7d34b0 100644 --- a/libarchive/archive_write_data.3 +++ b/libarchive/archive_write_data.3 @@ -54,7 +54,7 @@ Returns number of bytes written or -1 on error. .Tn archive_write handles, only for .Tn archive_write_disk -handles. +handles.) .El .\" .Sh EXAMPLE .\" diff --git a/libarchive/archive_write_disk.3 b/libarchive/archive_write_disk.3 index b046168c338c..60c6bfa6210c 100644 --- a/libarchive/archive_write_disk.3 +++ b/libarchive/archive_write_disk.3 @@ -327,13 +327,13 @@ is created as well as the final object In theory, this can be exploited to create an entire directory hierarchy with a single request. Of course, this does not work if the -.Cm ARCHIVE_EXTRACT_NODOTDOT +.Cm ARCHIVE_EXTRACT_SECURE_NODOTDOT option is specified. .Pp Implicit directories are always created obeying the current umask. Explicit objects are created obeying the current umask unless .Cm ARCHIVE_EXTRACT_PERM -is specified, in which case they current umask is ignored. +is specified, in which case the current umask is ignored. .Pp SGID and SUID bits are restored only if the correct user and group could be set. diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index a27e73188b37..2923086e7c56 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -29,6 +29,8 @@ #if !defined(_WIN32) || defined(__CYGWIN__) +#include "archive_umask_private.h" + #ifdef HAVE_SYS_TYPES_H #include #endif @@ -646,7 +648,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) * user edits their umask during the extraction for some * reason. */ - umask(a->user_umask = umask(0)); + a->user_umask = __archive_get_umask(); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; @@ -995,8 +997,11 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) } /* If this write would run beyond the file size, truncate it. */ - if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) - start_size = size = (size_t)(a->filesize - a->offset); + if (a->filesize >= 0 && a->filesize - a->offset < (int64_t)size) { + int64_t diff = a->filesize - a->offset; + + start_size = size = diff < 0 ? 0 : (size_t)diff; + } /* Write the data. */ while (size > 0) { @@ -1612,8 +1617,11 @@ hfs_write_data_block(struct archive_write_disk *a, const char *buff, } /* If this write would run beyond the file size, truncate it. */ - if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) - start_size = size = (size_t)(a->filesize - a->offset); + if (a->filesize >= 0 && a->filesize - a->offset < (int64_t)size) { + int64_t diff = a->filesize - a->offset; + + start_size = size = diff < 0 ? 0 : (size_t)diff; + } /* Write the data. */ while (size > 0) { @@ -2014,8 +2022,8 @@ archive_write_disk_new(void) a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = &archive_write_disk_vtable; a->start_time = time(NULL); - /* Query and restore the umask. */ - umask(a->user_umask = umask(0)); + /* Query the umask. */ + a->user_umask = __archive_get_umask(); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ diff --git a/libarchive/archive_write_disk_set_standard_lookup.c b/libarchive/archive_write_disk_set_standard_lookup.c index 5705a978f979..d6bedf16deb4 100644 --- a/libarchive/archive_write_disk_set_standard_lookup.c +++ b/libarchive/archive_write_disk_set_standard_lookup.c @@ -158,6 +158,8 @@ lookup_gid(void *private_data, const char *gname, int64_t gid) # endif /* HAVE_GETGRNAM_R */ #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a gname->gid lookup for Windows. */ +#elif defined(__wasi__) + /* WASI has no way to perform GID lookups, so just pass through */ #else #error No way to perform gid lookups on this platform #endif @@ -228,6 +230,8 @@ lookup_uid(void *private_data, const char *uname, int64_t uid) #endif /* HAVE_GETPWNAM_R */ #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a uname->uid lookup for Windows. */ +#elif defined(__wasi__) + /* WASI has no way to perform GID lookups, so just pass through */ #else #error No way to look up uids on this platform #endif diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index a78c0d2d3476..7224be0588e0 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -29,6 +29,8 @@ #if defined(_WIN32) && !defined(__CYGWIN__) +#include "archive_umask_private.h" + #ifdef HAVE_SYS_TYPES_H #include #endif @@ -168,8 +170,6 @@ struct archive_write_disk { int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ int64_t filesize; - /* Dir we were in before this restore; only for deep paths. */ - int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ mode_t mode; /* UID/GID to use in restoring this entry. */ @@ -829,7 +829,6 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; a->offset = 0; - a->restore_pwd = -1; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); if (archive_entry_size_is_set(a->entry)) @@ -863,7 +862,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) * user edits their umask during the extraction for some * reason. */ - umask(a->user_umask = umask(0)); + a->user_umask = __archive_get_umask(); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; @@ -1068,8 +1067,11 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) } /* If this write would run beyond the file size, truncate it. */ - if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) - start_size = size = (size_t)(a->filesize - a->offset); + if (a->filesize >= 0 && a->filesize - a->offset < (int64_t)size) { + int64_t diff = a->filesize - a->offset; + + start_size = size = diff < 0 ? 0 : (size_t)diff; + } /* Write the data. */ while (size > 0) { @@ -1368,8 +1370,8 @@ archive_write_disk_new(void) a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = &archive_write_disk_vtable; a->start_time = time(NULL); - /* Query and restore the umask. */ - umask(a->user_umask = umask(0)); + /* Query the umask. */ + a->user_umask = __archive_get_umask(); if (archive_wstring_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); @@ -2360,15 +2362,15 @@ cleanup_pathname(struct archive_write_disk *a, wchar_t *name) (p[7] == L'S' || p[7] == L's') && (p[8] == L'I' || p[8] == L'i') && (p[9] == L'C' || p[9] == L'c') && - (p[9] == L'A' || p[9] == L'a') && - (p[9] == L'L' || p[9] == L'l') && - (p[9] == L'D' || p[9] == L'd') && - (p[9] == L'R' || p[9] == L'r') && - (p[9] == L'I' || p[9] == L'i') && - (p[9] == L'V' || p[9] == L'v') && - (p[9] == L'E' || p[9] == L'e') && - (p[10] >= L'0' && p[10] <= L'9') && - p[11] == L'\0') { + (p[10] == L'A' || p[10] == L'a') && + (p[11] == L'L' || p[11] == L'l') && + (p[12] == L'D' || p[12] == L'd') && + (p[13] == L'R' || p[13] == L'r') && + (p[14] == L'I' || p[14] == L'i') && + (p[15] == L'V' || p[15] == L'v') && + (p[16] == L'E' || p[16] == L'e') && + (p[17] >= L'0' && p[17] <= L'9') && + p[18] == L'\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path is a physical drive name"); return (ARCHIVE_FAILED); @@ -2694,17 +2696,17 @@ set_times(struct archive_write_disk *a, h = hw; } - wintm.QuadPart = unix_to_ntfs(atime, atime_nanos); + wintm.QuadPart = __archive_unix_to_ntfs(atime, atime_nanos); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; - wintm.QuadPart = unix_to_ntfs(mtime, mtime_nanos); + wintm.QuadPart = __archive_unix_to_ntfs(mtime, mtime_nanos); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; /* * SetFileTime() supports birthtime. */ if (birthtime > 0 || birthtime_nanos > 0) { - wintm.QuadPart = unix_to_ntfs(birthtime, birthtime_nanos); + wintm.QuadPart = __archive_unix_to_ntfs(birthtime, birthtime_nanos); fbtime.dwLowDateTime = wintm.LowPart; fbtime.dwHighDateTime = wintm.HighPart; pfbtime = &fbtime; @@ -2935,7 +2937,7 @@ older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry) int64_t sec; uint32_t nsec; - ntfs_to_unix(FILETIME_to_ntfs(&st->ftLastWriteTime), &sec, &nsec); + __archive_ntfs_to_unix(__archive_FILETIME_to_ntfs(&st->ftLastWriteTime), &sec, &nsec); /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (sec < archive_entry_mtime(entry)) diff --git a/libarchive/archive_write_open.3 b/libarchive/archive_write_open.3 index b12d097028c0..db9eaa17633b 100644 --- a/libarchive/archive_write_open.3 +++ b/libarchive/archive_write_open.3 @@ -206,7 +206,7 @@ to register an error code and message and return -1. .Fn archive_close_callback "struct archive *" "void *client_data" .El .Pp -The close callback is invoked by archive_close when +The close callback is invoked by archive_write_close when the archive processing is complete. If the open callback fails, the close callback is not invoked. The callback should return diff --git a/libarchive/archive_write_open_fd.c b/libarchive/archive_write_open_fd.c index a795552020df..45ec492d52df 100644 --- a/libarchive/archive_write_open_fd.c +++ b/libarchive/archive_write_open_fd.c @@ -48,6 +48,7 @@ #endif #include "archive.h" +#include "archive_private.h" struct write_fd_data { int fd; @@ -62,6 +63,9 @@ archive_write_open_fd(struct archive *a, int fd) { struct write_fd_data *mine; + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_open_fd"); + mine = malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); diff --git a/libarchive/archive_write_open_file.c b/libarchive/archive_write_open_file.c index 6271b368d07f..c4e73fd89d9c 100644 --- a/libarchive/archive_write_open_file.c +++ b/libarchive/archive_write_open_file.c @@ -45,6 +45,7 @@ #endif #include "archive.h" +#include "archive_private.h" struct write_FILE_data { FILE *f; @@ -59,6 +60,9 @@ archive_write_open_FILE(struct archive *a, FILE *f) { struct write_FILE_data *mine; + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_open_FILE"); + mine = malloc(sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); diff --git a/libarchive/archive_write_open_filename.c b/libarchive/archive_write_open_filename.c index 430de21fde14..df513260b648 100644 --- a/libarchive/archive_write_open_filename.c +++ b/libarchive/archive_write_open_filename.c @@ -98,6 +98,9 @@ open_filename(struct archive *a, int mbs_fn, const void *filename) struct write_file_data *mine; int r; + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "open_filename"); + mine = calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); diff --git a/libarchive/archive_write_open_memory.c b/libarchive/archive_write_open_memory.c index 3b929ac7f60d..e88d88c6ce8d 100644 --- a/libarchive/archive_write_open_memory.c +++ b/libarchive/archive_write_open_memory.c @@ -30,6 +30,7 @@ #include #include "archive.h" +#include "archive_private.h" struct write_memory_data { size_t used; @@ -52,6 +53,9 @@ archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t { struct write_memory_data *mine; + archive_check_magic(a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_open_memory"); + mine = calloc(1, sizeof(*mine)); if (mine == NULL) { archive_set_error(a, ENOMEM, "No memory"); diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c index b5cc8e5001be..acaa1c0f5396 100644 --- a/libarchive/archive_write_set_format_7zip.c +++ b/libarchive/archive_write_set_format_7zip.c @@ -51,9 +51,7 @@ #endif #include "archive.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" @@ -648,7 +646,7 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN); if (bytes < 0) return ((int)bytes); - zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes); + zip->entry_crc32 = __archive_crc32(zip->entry_crc32, p, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; } @@ -704,7 +702,7 @@ compress_out(struct archive_write *a, const void *buff, size_t s, return (0); if ((zip->crc32flg & PRECODE_CRC32) && s) - zip->precode_crc32 = crc32(zip->precode_crc32, buff, + zip->precode_crc32 = __archive_crc32(zip->precode_crc32, buff, (unsigned)s); zip->stream.next_in = (const unsigned char *)buff; zip->stream.avail_in = s; @@ -720,7 +718,7 @@ compress_out(struct archive_write *a, const void *buff, size_t s, zip->stream.next_out = zip->wbuff; zip->stream.avail_out = sizeof(zip->wbuff); if (zip->crc32flg & ENCODED_CRC32) - zip->encoded_crc32 = crc32(zip->encoded_crc32, + zip->encoded_crc32 = __archive_crc32(zip->encoded_crc32, zip->wbuff, sizeof(zip->wbuff)); if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF) continue; @@ -733,7 +731,7 @@ compress_out(struct archive_write *a, const void *buff, size_t s, if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK) return (ARCHIVE_FATAL); if ((zip->crc32flg & ENCODED_CRC32) && bytes) - zip->encoded_crc32 = crc32(zip->encoded_crc32, + zip->encoded_crc32 = __archive_crc32(zip->encoded_crc32, zip->wbuff, (unsigned)bytes); } @@ -753,7 +751,7 @@ _7z_write_data(struct archive_write *a, const void *buff, size_t s) bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN); if (bytes < 0) return (bytes); - zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes); + zip->entry_crc32 = __archive_crc32(zip->entry_crc32, buff, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; return (bytes); } @@ -975,7 +973,7 @@ _7z_close(struct archive_write *a) archive_le64enc(&wb[12], header_offset);/* Next Header Offset */ archive_le64enc(&wb[20], header_size);/* Next Header Size */ archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */ - archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */ + archive_le32enc(&wb[8], __archive_crc32(0, &wb[12], 20));/* Start Header CRC */ zip->wbuff_remaining -= 32; /* @@ -1359,7 +1357,7 @@ make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) for (;file != NULL; file = file->next) { if ((file->flg & flg) == 0) continue; - archive_le64enc(filetime, unix_to_ntfs(file->times[ti].time, + archive_le64enc(filetime, __archive_unix_to_ntfs(file->times[ti].time, file->times[ti].time_ns)); r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN); if (r < 0) diff --git a/libarchive/archive_write_set_format_cpio_binary.c b/libarchive/archive_write_set_format_cpio_binary.c index 6648b18d90ac..1866598a250b 100644 --- a/libarchive/archive_write_set_format_cpio_binary.c +++ b/libarchive/archive_write_set_format_cpio_binary.c @@ -236,28 +236,8 @@ archive_write_binary_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = a->format_data; - int ret = ARCHIVE_FAILED; - - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - cpio->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (cpio->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &cpio->opt_sconv)); } /* diff --git a/libarchive/archive_write_set_format_cpio_newc.c b/libarchive/archive_write_set_format_cpio_newc.c index b9de7d362e22..9f6c88edd23b 100644 --- a/libarchive/archive_write_set_format_cpio_newc.c +++ b/libarchive/archive_write_set_format_cpio_newc.c @@ -41,6 +41,7 @@ #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" +#include "archive_integer.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_write_set_format_private.h" @@ -62,6 +63,17 @@ struct cpio { uint64_t entry_bytes_remaining; int padding; + int64_t ino_next; + + struct { + int64_t old_devmajor; + int64_t old_devminor; + int64_t old_ino; + int64_t new_ino; + } *ino_list; + size_t ino_list_size; + size_t ino_list_next; + struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; @@ -138,28 +150,88 @@ archive_write_newc_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = a->format_data; - int ret = ARCHIVE_FAILED; + return (__archive_write_option_header_charset(a, key, val, + &cpio->opt_sconv)); +} - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - cpio->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (cpio->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; +/* + * The newc format stores 32-bit inode numbers and relies on them to identify + * hardlinked files. Generate unique in-range values so distinct 64-bit inode + * numbers cannot collide when written to the archive. + */ +static int64_t +synthesize_ino_value(struct archive_write *a, struct archive_entry *entry) +{ + struct cpio *cpio = a->format_data; + int64_t devmajor = archive_entry_devmajor(entry); + int64_t devminor = archive_entry_devminor(entry); + int64_t ino = archive_entry_ino64(entry); + int64_t ino_new; + size_t i; + + if (ino == 0) + return (0); + + /* Directory link counts do not represent separate archive entries. */ + if (archive_entry_nlink(entry) < 2 || + archive_entry_filetype(entry) == AE_IFDIR) { + if (cpio->ino_next == UINT32_MAX) { + archive_set_error(&a->archive, ERANGE, + "No available inode values for cpio format"); + return (ARCHIVE_FATAL); } - return (ret); + return (++cpio->ino_next); } - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + /* TODO: Revisit the flat list if large hardlink sets become slow. */ + for (i = 0; i < cpio->ino_list_next; ++i) { + if (cpio->ino_list[i].old_devmajor == devmajor && + cpio->ino_list[i].old_devminor == devminor && + cpio->ino_list[i].old_ino == ino) + return (cpio->ino_list[i].new_ino); + } + + if (cpio->ino_next == UINT32_MAX) { + archive_set_error(&a->archive, ERANGE, + "No available inode values for cpio format"); + return (ARCHIVE_FATAL); + } + ino_new = ++cpio->ino_next; + + if (cpio->ino_list_size <= cpio->ino_list_next) { + size_t newsize, size; + + if (cpio->ino_list_size < 512) + newsize = 512; + else if (archive_ckd_mul_size(&newsize, + cpio->ino_list_size, 2)) { + archive_set_error(&a->archive, ENOMEM, + "No memory for inode translation table"); + return (ARCHIVE_FATAL); + } + if (archive_ckd_mul_size(&size, + newsize, sizeof(cpio->ino_list[0]))) { + archive_set_error(&a->archive, ENOMEM, + "No memory for inode translation table"); + return (ARCHIVE_FATAL); + } + void *newlist = realloc(cpio->ino_list, size); + if (newlist == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for inode translation table"); + return (ARCHIVE_FATAL); + } + + cpio->ino_list_size = newsize; + cpio->ino_list = newlist; + } + + cpio->ino_list[cpio->ino_list_next].old_devmajor = devmajor; + cpio->ino_list[cpio->ino_list_next].old_devminor = devminor; + cpio->ino_list[cpio->ino_list_next].old_ino = ino; + cpio->ino_list[cpio->ino_list_next].new_ino = ino_new; + ++cpio->ino_list_next; + return (ino_new); } static struct archive_string_conv * @@ -267,15 +339,14 @@ write_header(struct archive_write *a, struct archive_entry *entry) format_hex(archive_entry_devminor(entry), h + c_devminor_offset, c_devminor_size); - ino = archive_entry_ino64(entry); - if (ino > 0xffffffff) { - archive_set_error(&a->archive, ERANGE, - "large inode number truncated"); - ret_final = ARCHIVE_WARN; + ino = synthesize_ino_value(a, entry); + if (ino < 0) { + ret_final = (int)ino; + goto exit_write_header; } /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ - format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size); + format_hex(ino, h + c_ino_offset, c_ino_size); format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); @@ -442,6 +513,7 @@ archive_write_newc_free(struct archive_write *a) { struct cpio *cpio = a->format_data; + free(cpio->ino_list); free(cpio); a->format_data = NULL; return (ARCHIVE_OK); diff --git a/libarchive/archive_write_set_format_cpio_odc.c b/libarchive/archive_write_set_format_cpio_odc.c index c384d4827288..d0eb2034ad12 100644 --- a/libarchive/archive_write_set_format_cpio_odc.c +++ b/libarchive/archive_write_set_format_cpio_odc.c @@ -133,28 +133,8 @@ archive_write_odc_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = a->format_data; - int ret = ARCHIVE_FAILED; - - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - cpio->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (cpio->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &cpio->opt_sconv)); } /* diff --git a/libarchive/archive_write_set_format_filter_by_ext.c b/libarchive/archive_write_set_format_filter_by_ext.c index 1bb33b04bf91..516e0f3a5a4e 100644 --- a/libarchive/archive_write_set_format_filter_by_ext.c +++ b/libarchive/archive_write_set_format_filter_by_ext.c @@ -52,19 +52,20 @@ struct { const char *name; int (*format)(struct archive *); int (*filter)(struct #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) { ".a", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, -#else +#else { ".a", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, -#endif +#endif { ".tar", archive_write_set_format_pax_restricted, archive_write_add_filter_none}, { ".tgz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.gz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.bz2", archive_write_set_format_pax_restricted, archive_write_add_filter_bzip2}, { ".tar.xz", archive_write_set_format_pax_restricted, archive_write_add_filter_xz}, + { ".tar.zst", archive_write_set_format_pax_restricted, archive_write_add_filter_zstd}, { NULL, NULL, NULL } }; -static +static int cmpsuff(const char *str, const char *suffix) { size_t length_str, length_suffix; @@ -86,28 +87,28 @@ static int get_array_index(const char *name) { int i; - for (i = 0; names[i].name != NULL; i++) + for (i = 0; names[i].name != NULL; i++) { if (cmpsuff(name, names[i].name) == 0) return i; - } + } return -1; - + } int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename) { int names_index = get_array_index(filename); - + if (names_index >= 0) - { + { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; - } + } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; @@ -118,24 +119,20 @@ int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext) { int names_index = get_array_index(filename); - + if (names_index < 0) names_index = get_array_index(def_ext); - + if (names_index >= 0) - { + { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; - } + } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } - - - - diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c index 5a767e1bd72b..49268a68e0ac 100644 --- a/libarchive/archive_write_set_format_gnutar.c +++ b/libarchive/archive_write_set_format_gnutar.c @@ -203,28 +203,8 @@ archive_write_gnutar_options(struct archive_write *a, const char *key, const char *val) { struct gnutar *gnutar = a->format_data; - int ret = ARCHIVE_FAILED; - - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - gnutar->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (gnutar->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &gnutar->opt_sconv)); } static int diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c index ee50f7c0842b..d3f7398ee4f8 100644 --- a/libarchive/archive_write_set_format_iso9660.c +++ b/libarchive/archive_write_set_format_iso9660.c @@ -58,7 +58,7 @@ #include "archive_rb.h" #include "archive_write_private.h" -#if defined(_WIN32) && !defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) || defined(__wasi__) #define getuid() 0 #define getgid() 0 #endif @@ -2525,7 +2525,9 @@ get_tmfromtime(struct tm *tm, time_t *t) #if HAVE_LOCALTIME_S localtime_s(tm, t); #elif HAVE_LOCALTIME_R +# ifdef HAVE_TZSET tzset(); +# endif localtime_r(t, tm); #else memcpy(tm, localtime(t), sizeof(*tm)); @@ -2886,52 +2888,39 @@ set_directory_record_rr(unsigned char *bp, int dr_len, * +----+----+----+----+----+----+----+----+ * <----------------- len -----------------> */ - size_t nmlen = file->basename.length; + size_t reclen, nmlen = file->basename.length; const char *nm = file->basename.s; size_t nmmax; - if (extra_space(&ctl) < 6) - bp = extra_next_record(&ctl, 6); - if (bp != NULL) { - bp[1] = 'N'; - bp[2] = 'M'; - bp[4] = 1; /* version */ - } - nmmax = extra_space(&ctl); - if (nmmax > 0xff) - nmmax = 0xff; - while (nmlen + 5 > nmmax) { - length = (int)nmmax; - if (bp != NULL) { - bp[3] = length; - bp[5] = 0x01;/* Alternate Name continues - * in next "NM" field */ - memcpy(bp+6, nm, length - 5); - bp += length; - } - nmlen -= length - 5; - nm += length - 5; - extra_tell_used_size(&ctl, length); - if (extra_space(&ctl) < 6) { + while (nmlen) { + /* We need at least six bytes (five header, one content) */ + if (extra_space(&ctl) < 6) bp = extra_next_record(&ctl, 6); + reclen = extra_space(&ctl); + if (reclen > 0xff) + reclen = 0xff; /* We can only fit 255 bytes in each NM record */ + nmmax = reclen - 5; /* Record length is guaranteed to be >= 6 */ + if (nmmax > nmlen) { + /* ... but the name is shorter. */ + nmmax = nmlen; + reclen = nmmax + 5; } - nmmax = extra_space(&ctl); - if (nmmax > 0xff) - nmmax = 0xff; + if (bp != NULL) { + /* Fill out the new record */ bp[1] = 'N'; bp[2] = 'M'; - bp[4] = 1; /* version */ + bp[3] = reclen; /* record length */ + bp[4] = 1; /* version */ + bp[5] = (nmlen > nmmax) ? 0x01 : 0x00; /* continues in next NM record */ + memcpy(bp + 6, nm, nmmax); + bp += reclen; } + + nmlen -= nmmax; + nm += nmmax; + extra_tell_used_size(&ctl, reclen); } - length = 5 + (int)nmlen; - if (bp != NULL) { - bp[3] = length; - bp[5] = 0; - memcpy(bp+6, nm, nmlen); - bp += length; - } - extra_tell_used_size(&ctl, length); } /* Write "PX" System Use Entry. */ @@ -5464,8 +5453,8 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = (size_t)archive_entry_size(isoent->file->entry); - block = ((int)size + LOGICAL_BLOCK_SIZE -1) - >> LOGICAL_BLOCK_BITS; + block = (int)((size >> LOGICAL_BLOCK_BITS) + + ((size & (LOGICAL_BLOCK_SIZE - 1)) != 0)); location += block; iso9660->total_file_block += block; isoent->file->content.blocks = block; @@ -6787,17 +6776,24 @@ isoent_rr_move(struct archive_write *a) /* If "rr_moved" directory is already existing, * we have to use it. */ rr_moved = isoent_find_child(rootent, "rr_moved"); - if (rr_moved != NULL && - rr_moved != rootent->children.first) { - /* - * It's necessary that rr_move is the first entry - * of the root. - */ - /* Remove "rr_moved" entry from children chain. */ - isoent_remove_child(rootent, rr_moved); + if (rr_moved != NULL) { + if (!rr_moved->dir) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unable to relocate directories into rr_moved:" + " the existing entry is not a directory"); + return (ARCHIVE_FATAL); + } + if (rr_moved != rootent->children.first) { + /* + * It's necessary that rr_move is the first entry + * of the root. + */ + /* Remove "rr_moved" entry from children chain. */ + isoent_remove_child(rootent, rr_moved); - /* Add "rr_moved" entry into the head of children chain. */ - isoent_add_child_head(rootent, rr_moved); + /* Add "rr_moved" entry to the head of children chain. */ + isoent_add_child_head(rootent, rr_moved); + } } /* diff --git a/libarchive/archive_write_set_format_mtree.c b/libarchive/archive_write_set_format_mtree.c index b3433507e91a..209fb027680b 100644 --- a/libarchive/archive_write_set_format_mtree.c +++ b/libarchive/archive_write_set_format_mtree.c @@ -997,7 +997,7 @@ write_mtree_entry(struct archive_write *a, struct mtree_entry *me) keys = get_global_set_keys(mtree, me); if ((keys & F_NLINK) != 0 && - me->nlink != 1 && me->filetype != AE_IFDIR) + me->nlink != 0 && me->nlink != 1 && me->filetype != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", me->nlink); if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) { @@ -1613,7 +1613,11 @@ sum_update(struct mtree *mtree, const void *buff, size_t n) static void sum_final(struct mtree *mtree, struct reg_info *reg) { +#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) \ + || defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) \ + || defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512) struct ae_digest digest; +#endif if (mtree->compute_sum & F_CKSUM) { uint64_t len; @@ -1932,15 +1936,8 @@ mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, len = archive_strlen(&file->parentdir); } - /* - * Find out the position which points to the last position of - * path separator('/'). - */ - slash = NULL; - for (; *p != '\0'; p++) { - if (*p == '/') - slash = p; - } + /* Find the last path separator. */ + slash = strrchr(p, '/'); if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; diff --git a/libarchive/archive_write_set_format_private.h b/libarchive/archive_write_set_format_private.h index ef9dee9d808c..68f7f51c68cb 100644 --- a/libarchive/archive_write_set_format_private.h +++ b/libarchive/archive_write_set_format_private.h @@ -34,6 +34,11 @@ #include "archive.h" #include "archive_entry.h" +#include "archive_string.h" +#include "archive_write_private.h" + +int __archive_write_option_header_charset(struct archive_write *, + const char *, const char *, struct archive_string_conv **); void __archive_write_entry_filetype_unsupported(struct archive *a, struct archive_entry *entry, const char *format); diff --git a/libarchive/archive_write_set_format_shar.c b/libarchive/archive_write_set_format_shar.c index ef4b3d907c08..f8b89ea099ad 100644 --- a/libarchive/archive_write_set_format_shar.c +++ b/libarchive/archive_write_set_format_shar.c @@ -165,6 +165,13 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) const char *name; char *p, *pp; + name = archive_entry_pathname(entry); + if (name == NULL || *name == '\0') { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't record entry in shar file without pathname"); + return (ARCHIVE_FAILED); + } + if (!shar->wrote_header) { archive_strcat(&shar->work, "#!/bin/sh\n"); archive_strcat(&shar->work, "# This is a shell archive\n"); @@ -178,7 +185,6 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } - name = archive_entry_pathname(entry); /* Handle some preparatory issues. */ switch(archive_entry_filetype(entry)) { diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c index a55cbb75b0f7..8f348e926259 100644 --- a/libarchive/archive_write_set_format_ustar.c +++ b/libarchive/archive_write_set_format_ustar.c @@ -206,28 +206,8 @@ archive_write_ustar_options(struct archive_write *a, const char *key, const char *val) { struct ustar *ustar = a->format_data; - int ret = ARCHIVE_FAILED; - - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - ustar->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (ustar->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &ustar->opt_sconv)); } static int diff --git a/libarchive/archive_write_set_format_v7tar.c b/libarchive/archive_write_set_format_v7tar.c index 74875547b089..1a0b8405f68f 100644 --- a/libarchive/archive_write_set_format_v7tar.c +++ b/libarchive/archive_write_set_format_v7tar.c @@ -183,28 +183,8 @@ archive_write_v7tar_options(struct archive_write *a, const char *key, const char *val) { struct v7tar *v7tar = a->format_data; - int ret = ARCHIVE_FAILED; - - if (strcmp(key, "hdrcharset") == 0) { - if (val == NULL || val[0] == 0) - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - else { - v7tar->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (v7tar->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); - } - - /* Note: The "warn" return is just to inform the options - * supervisor that we didn't handle it. It will generate - * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &v7tar->opt_sconv)); } static int diff --git a/libarchive/archive_write_set_format_xar.c b/libarchive/archive_write_set_format_xar.c index 0058693792c1..db3939254e3a 100644 --- a/libarchive/archive_write_set_format_xar.c +++ b/libarchive/archive_write_set_format_xar.c @@ -32,17 +32,6 @@ #include #endif #include -#if HAVE_LIBXML_XMLWRITER_H -#include -#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20703 -#define XAR_WRITER_HAS_XML -#endif /* LIBXML_VERSION */ -#elif HAVE_XMLLITE_H -#include -#include -#include -#define XAR_WRITER_HAS_XML -#endif #ifdef HAVE_BZLIB_H #include #endif @@ -62,6 +51,7 @@ #include "archive_rb.h" #include "archive_string.h" #include "archive_write_private.h" +#include "archive_xml_private.h" /* * Differences to xar utility. @@ -78,9 +68,9 @@ * */ -#if !defined(XAR_WRITER_HAS_XML) ||\ - !defined(HAVE_ZLIB_H) || \ - !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) +#if !defined(ARCHIVE_XML_WRITE) ||\ + !defined(HAVE_ZLIB_H) || \ + !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) /* * xar needs several external libraries. * o libxml2 or xmllite (on Windows) @@ -2277,15 +2267,8 @@ file_gen_utility_names(struct archive_write *a, struct file *file) archive_strncpy(&(file->symlink), pp, len2); cleanup_backslash(file->symlink.s, file->symlink.length); } - /* - * - Count up directory elements. - * - Find out the position which points the last position of - * path separator('/'). - */ - slash = NULL; - for (; *p != '\0'; p++) - if (*p == '/') - slash = p; + /* Find the last path separator. */ + slash = strrchr(p, '/'); if (slash == NULL) { /* The pathname doesn't have a parent directory. */ file->parentdir.length = len; @@ -3396,7 +3379,7 @@ getalgname(enum sumalg sumalg) } } -#if HAVE_LIBXML_XMLWRITER_H +#if defined(ARCHIVE_XML_WRITER_XML2) #define BAD_CAST_CONST (const xmlChar *) @@ -3524,7 +3507,7 @@ xml_writer_get_final_content_and_length(struct xml_writer *ctx, return (0); } -#elif HAVE_XMLLITE_H +#elif defined(ARCHIVE_XML_WRITER_XMLLITE) struct xml_writer { IXmlWriter *writer; @@ -3782,7 +3765,9 @@ xml_writer_get_final_content_and_length(struct xml_writer *ctx, *out = (const char *)GlobalLock(gbl); if (*out == NULL) { - hr = HRESULT_FROM_WIN32(GetLastError()); + DWORD e = GetLastError(); + + hr = (e == ERROR_SUCCESS) ? E_FAIL : HRESULT_FROM_WIN32(e); return (hr); } @@ -3794,6 +3779,6 @@ xml_writer_get_final_content_and_length(struct xml_writer *ctx, return (hr); } -#endif /* HAVE_LIBXML_XMLWRITER_H */ +#endif /* ARCHIVE_XML_WRITER_XML2 */ #endif /* Support xar format */ diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index 9c7073fb76bd..b588e915fbf3 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -75,9 +75,7 @@ #include "archive_write_private.h" #include "archive_write_set_format_private.h" -#ifndef HAVE_ZLIB_H -#include "archive_crc32.h" -#endif + #define ZIP_ENTRY_FLAG_ENCRYPTED (1 << 0) #define ZIP_ENTRY_FLAG_LZMA_EOPM (1 << 1) @@ -322,7 +320,7 @@ cd_alloc(struct zip *zip, size_t length) static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { - return crc32(crc, buff, (unsigned int)len); + return __archive_crc32(crc, buff, (unsigned int)len); } static unsigned long @@ -517,23 +515,6 @@ archive_write_zip_options(struct archive_write *a, const char *key, zip->crc32func = fake_crc32; } return (ARCHIVE_OK); - } else if (strcmp(key, "hdrcharset") == 0) { - /* - * Set the character set used in translating filenames. - */ - if (val == NULL || val[0] == 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "%s: hdrcharset option needs a character-set name", - a->format_name); - } else { - zip->opt_sconv = archive_string_conversion_to_charset( - &a->archive, val, 0); - if (zip->opt_sconv != NULL) - ret = ARCHIVE_OK; - else - ret = ARCHIVE_FATAL; - } - return (ret); } else if (strcmp(key, "zip64") == 0) { /* * Bias decisions about Zip64: force them to be @@ -554,7 +535,8 @@ archive_write_zip_options(struct archive_write *a, const char *key, /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ - return (ARCHIVE_WARN); + return (__archive_write_option_header_charset(a, key, val, + &zip->opt_sconv)); } int @@ -1171,7 +1153,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) else archive_le16enc(local_header + 8, zip->entry_compression); archive_le32enc(local_header + 10, - unix_to_dos(archive_entry_mtime(zip->entry))); + __archive_unix_to_dos(archive_entry_mtime(zip->entry))); if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) == 0) { archive_le32enc(local_header + 14, zip->entry_crc32); archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size); @@ -1202,7 +1184,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) else archive_le16enc(zip->file_header + 10, zip->entry_compression); archive_le32enc(zip->file_header + 12, - unix_to_dos(archive_entry_mtime(zip->entry))); + __archive_unix_to_dos(archive_entry_mtime(zip->entry))); archive_le16enc(zip->file_header + 28, (uint16_t)filename_length); /* Following Info-Zip, store mode in the "external attributes" field. */ archive_le32enc(zip->file_header + 38, @@ -2354,7 +2336,7 @@ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; -#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) +#define CRC32(c, b) (__archive_crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; diff --git a/libarchive/archive_write_set_options.3 b/libarchive/archive_write_set_options.3 index e03b5175771d..df721b123f40 100644 --- a/libarchive/archive_write_set_options.3 +++ b/libarchive/archive_write_set_options.3 @@ -197,7 +197,7 @@ Use as compression method. Supported values are .Dq bzip2 , -.Dq gzipi , +.Dq gzip , .Dq lzo .Pq ultra fast , and @@ -419,7 +419,7 @@ If enabled, the Primary Volume Descriptor may contain lowercase ASCII characters If disabled, characters will be converted to uppercase ASCII. Default: disabled. .It Cm allow-sharp-tilde -If enabled, sharp and tilde characters will be permitted in filenames, in violation if the ISO9660 specification. +If enabled, sharp and tilde characters will be permitted in filenames, in violation of the ISO9660 specification. If disabled, such characters will be converted to underscore characters. Default: disabled. .It Cm allow-vernum @@ -639,7 +639,7 @@ Supported values are .Dq lzma and .Dq xz . -.It Cm compression_level +.It Cm compression-level The value is a decimal integer from 1 to 9 specifying the compression level. .It Cm toc-checksum Ns = Ns Ar type Use @@ -680,7 +680,7 @@ other values will enable .Dq lzma , or .Dq zstd -compression (in order of priority, depending on what libraries +compression (in order of priority, depending on what libraries are linked) with the given level. .It Cm threads The value is interpreted as a decimal integer specifying the @@ -751,7 +751,7 @@ compressor should use the maximum compression level. a = archive_write_new(); archive_write_add_filter_gzip(a); archive_write_set_format_iso9660(a); -archive_write_set_options(a, "boot=kernel.img,compression=9"); +archive_write_set_options(a, "boot=kernel.img,gzip:compression-level=9"); archive_write_open_filename(a, filename, blocksize); .Ed .\" diff --git a/libarchive/archive_xml_private.h b/libarchive/archive_xml_private.h new file mode 100644 index 000000000000..bb20ec876600 --- /dev/null +++ b/libarchive/archive_xml_private.h @@ -0,0 +1,83 @@ +/*- +* Copyright (c) 2026 Tobias Stoeckmann +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARCHIVE_XML_PRIVATE_H_INCLUDED +#define ARCHIVE_XML_PRIVATE_H_INCLUDED + +#ifndef __LIBARCHIVE_BUILD +#error This header is only to be used internally to libarchive. +#endif +#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED +#error "Should have include config.h first!" +#endif + +/* + * Choose XML support from third party libraries in this order of preference: + * + * 1. libxml2 + * 2. XmlLite + * 3. expat + * 4. bsdxml + */ + +#if HAVE_LIBXML_XMLREADER_H && HAVE_LIBXML2 +#include +#define ARCHIVE_XML_READ 1 +#define ARCHIVE_XML_READER_XML2 1 +#define ARCHIVE_XML_USE_XML2 1 +#elif HAVE_XMLLITE_H && HAVE_LIBXMLLITE +#include +#include +#include +#define ARCHIVE_XML_READ 1 +#define ARCHIVE_XML_READER_XMLLITE 1 +#define ARCHIVE_XML_USE_XMLLITE 1 +#elif HAVE_EXPAT_H && HAVE_LIBEXPAT +#include +#define ARCHIVE_XML_READ 1 +#define ARCHIVE_XML_READER_EXPAT 1 +#define ARCHIVE_XML_USE_EXPAT 1 +#elif HAVE_BSDXML_H +#include +#define ARCHIVE_XML_READ 1 +#define ARCHIVE_XML_READER_BSDXML 1 +#define ARCHIVE_XML_USE_BSDXML 1 +#endif + +#if HAVE_LIBXML_XMLWRITER_H && HAVE_LIBXML2 +#include +#define ARCHIVE_XML_WRITE 1 +#define ARCHIVE_XML_WRITER_XML2 1 +#define ARCHIVE_XML_USE_XML2 1 +#elif HAVE_XMLLITE_H && HAVE_LIBXMLLITE +#include +#include +#include +#define ARCHIVE_XML_WRITE 1 +#define ARCHIVE_XML_WRITER_XMLLITE 1 +#define ARCHIVE_XML_USE_XMLLITE 1 +#endif + +#endif diff --git a/libarchive/cpio.5 b/libarchive/cpio.5 index 21c30d78d3c5..cf84dfba8e68 100644 --- a/libarchive/cpio.5 +++ b/libarchive/cpio.5 @@ -105,7 +105,7 @@ it also holds a couple of bits that are irrelevant to the cpio format, because the field is actually a raw copy of the mode field in the inode representing the file. These are the IALLOC flag, which shows that the inode entry is in use, and the ILARG flag, which shows that the -file it represents is large enough to have indirect blocks pointers in +file it represents is large enough to have indirect block pointers in the inode. The mode is decoded as follows: .Pp diff --git a/libarchive/filter_fork_posix.c b/libarchive/filter_fork_posix.c index 7c48519336ff..93981e6032e4 100644 --- a/libarchive/filter_fork_posix.c +++ b/libarchive/filter_fork_posix.c @@ -70,6 +70,15 @@ #include "filter_fork.h" +#ifdef __APPLE__ +/* + * On Apple platforms, shared libraries cannot access the environ global + * variable directly (it isn't exported the same way as on other Unix systems), + * so it must be retrieved via _NSGetEnviron() instead. + */ +#include +#endif + int __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, pid_t *out_child) @@ -154,8 +163,23 @@ __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, if (r != 0) goto actions_inited; } + + /* + * Pass the current environment to the filter program. Without this, + * posix_spawnp() would be called with a NULL envp, which spawns the filter + * program with an empty environment instead of inheriting ours, on any + * platform where posix_spawnp() is actually used (see the HAVE_POSIX_SPAWNP + * #undef above: most systems fall back to fork()+exec(), which inherits the + * environment regardless of what we do here). + */ +#ifdef __APPLE__ + char** env = *_NSGetEnviron(); +#else + char** env = environ; +#endif + r = posix_spawnp(&child, cmdline->path, &actions, &attr, - cmdline->argv, NULL); + cmdline->argv, env); if (r != 0) goto actions_inited; posix_spawn_file_actions_destroy(&actions); @@ -266,4 +290,20 @@ __archive_check_child(int in, int out) #endif } +#elif !(defined(_WIN32) && !defined(__CYGWIN__)) + +#include "archive.h" + +int +__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, + pid_t *out_child) +{ + return (ARCHIVE_FAILED); +} + +void +__archive_check_child(int in, int out) +{ +} + #endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */ diff --git a/libarchive/filter_fork_windows.c b/libarchive/filter_fork_windows.c index a0fd63894044..9fbf1c2bf37d 100644 --- a/libarchive/filter_fork_windows.c +++ b/libarchive/filter_fork_windows.c @@ -77,7 +77,7 @@ load_WaitForInputIdle(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) { (void)Parameter; /* UNUSED */ *Context = (user32 != NULL) ? - (PVOID)GetProcAddress(user32, "WaitForInputIdle") : failing_wait; + (PVOID)GetProcAddress(user32, "WaitForInputIdle") : (PVOID)failing_wait; return TRUE; } diff --git a/libarchive/libarchive-formats.5 b/libarchive/libarchive-formats.5 index eb720eb740f5..83bcff6cc332 100644 --- a/libarchive/libarchive-formats.5 +++ b/libarchive/libarchive-formats.5 @@ -366,7 +366,7 @@ Central Directory, which is ordinarily located at the end of a Zip archive and is thus inaccessible to the streaming reader. If the program using libarchive has enabled seek support, then -libarchive will use this to processes the central directory first. +libarchive will use this to process the central directory first. .Pp In particular, the seeking reader must be used to correctly handle self-extracting archives. diff --git a/libarchive/libarchive.3 b/libarchive/libarchive.3 index c67172bf654f..d7b1191e066e 100644 --- a/libarchive/libarchive.3 +++ b/libarchive/libarchive.3 @@ -179,7 +179,7 @@ operations. .Sh READING ENTRIES FROM DISK The .Xr archive_read_disk 3 -supports for populating +supports populating .Xr archive_entry 3 objects from information in the filesystem. This includes the information accessible from the diff --git a/libarchive/libarchive_internals.3 b/libarchive/libarchive_internals.3 index 2978b48c3e97..a3ab8ed4e56c 100644 --- a/libarchive/libarchive_internals.3 +++ b/libarchive/libarchive_internals.3 @@ -329,7 +329,7 @@ from most ISO9660 readers. The libarchive support utilizes a work-queue design that keeps a list of known entries sorted by their location in the input. Whenever libarchive's ISO9660 implementation is asked for the next -header, checks this list to find the next item on the disk. +header, it checks this list to find the next item on the disk. Directories are parsed when they are encountered and new items are added to the list. This design relies heavily on the ISO9660 image being optimized so that diff --git a/libarchive/mtree.5 b/libarchive/mtree.5 index 5ea53613166f..f7977381201e 100644 --- a/libarchive/mtree.5 +++ b/libarchive/mtree.5 @@ -139,7 +139,7 @@ A device with and optional .Ar subunit fields. -Their meaning is specified by the operating's system +Their meaning is specified by the operating system's .Ar format . See below for valid formats. .It Ar number diff --git a/libarchive/tar.5 b/libarchive/tar.5 index 725a7d68374a..3ba7dd3f9271 100644 --- a/libarchive/tar.5 +++ b/libarchive/tar.5 @@ -837,7 +837,7 @@ To recreate the original file on disk, each separate file can be extracted and the Mac OS X .Fn copyfile function can be used to unpack the separate -metadata file and apply it to th regular file. +metadata file and apply it to the regular file. Conversely, the same function provides a .Dq pack option to encode the extended metadata from diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 44f839f87f5d..8a50531b721d 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -9,6 +9,7 @@ IF(ENABLE_TEST) ../../test_utils/test_main.c read_open_memory.c test.h + test_archive_crc32.c test_7zip_filename_encoding.c test_acl_from_text_overread.c test_acl_nfs4.c @@ -28,15 +29,18 @@ IF(ENABLE_TEST) test_archive_match_owner.c test_archive_match_path.c test_archive_match_time.c + test_archive_ntfs_time.c test_archive_parse_date.c test_archive_pathmatch.c test_archive_pathmatch_oob.c test_archive_read.c test_archive_read_add_passphrase.c + test_archive_read_append_filter.c test_archive_read_close_twice.c test_archive_read_multiple_data_objects.c test_archive_read_next_header_empty.c test_archive_read_next_header_raw.c + test_archive_read_open_filenames.c test_archive_read_open2.c test_archive_read_set_options.c test_archive_read_support.c @@ -74,6 +78,7 @@ IF(ENABLE_TEST) test_compat_xz.c test_compat_zip.c test_compat_zstd.c + test_create_child_environment.c test_empty_write.c test_entry.c test_entry_strmode.c @@ -88,6 +93,8 @@ IF(ENABLE_TEST) test_open_filename.c test_pax_filename_encoding.c test_pax_xattr_header.c + test_read_data_block_offset_at_eof.c + test_read_data_into_fd_no_offset_reported.c test_read_data_into_fd_size_exceeds_declared.c test_read_data_large.c test_read_disk.c @@ -106,6 +113,7 @@ IF(ENABLE_TEST) test_read_filter_program.c test_read_filter_program_signature.c test_read_filter_uudecode.c + test_read_filter_uudecode_newlines.c test_read_filter_uudecode_raw.c test_read_filter_zstd_raw.c test_read_format_7zip.c @@ -122,9 +130,11 @@ IF(ENABLE_TEST) test_read_format_cab_filename.c test_read_format_cab_lzx_16bit.c test_read_format_cab_lzx_e8.c + test_read_format_cab_lzx_invalid_history.c test_read_format_cab_lzx_oob.c test_read_format_cab_mszip_oob.c test_read_format_cab_multi.c + test_read_format_cab_skip_across_folders.c test_read_format_cab_skip_malformed.c test_read_format_cpio_afio.c test_read_format_cpio_bin.c @@ -149,6 +159,7 @@ IF(ENABLE_TEST) test_read_format_gtar_lzma.c test_read_format_gtar_redundant_L.c test_read_format_gtar_sparse.c + test_read_format_gtar_sparse_exceeds_length.c test_read_format_gtar_sparse_length.c test_read_format_gtar_sparse_skip_entry.c test_read_format_huge_rpm.c @@ -166,9 +177,12 @@ IF(ENABLE_TEST) test_read_format_isorr_new_compress.c test_read_format_isorr_rr_moved.c test_read_format_isozisofs_compress.c + test_read_format_iso_rockridge_ce_overflow.c + test_read_format_iso_rockridge_cl_re_cycle.c test_read_format_iso_rockridge_zf_overflow.c test_read_format_iso_zisofs_overflow.c test_read_format_lha.c + test_read_format_lha_amiga_script.c test_read_format_lha_bugfix_0.c test_read_format_lha_filename.c test_read_format_lha_filename_utf16.c @@ -176,6 +190,7 @@ IF(ENABLE_TEST) test_read_format_lha_symlink_missing_target.c test_read_format_mtree.c test_read_format_mtree_crash747.c + test_read_format_mtree_size.c test_read_format_pax_bz2.c test_read_format_rar.c test_read_format_rar_encryption.c @@ -184,12 +199,18 @@ IF(ENABLE_TEST) test_read_format_rar_encryption_partially.c test_read_format_rar_invalid1.c test_read_format_rar_filter.c + test_read_format_rar_filter_incomplete_block.c test_read_format_rar_overflow.c test_read_format_rar5.c test_read_format_rar5_bad_filter.c test_read_format_rar5_bad_tables.c test_read_format_rar5_block_hdr_fail_loop.c + test_read_format_rar5_extra_field_underflow.c + test_read_format_rar5_header_size_wraparound.c test_read_format_rar5_loop_bug.c + test_read_format_rar5_redir_overflow.c + test_read_format_rar5_truncated_htime.c + test_read_format_rar5_varint_near_eof.c test_read_format_raw.c test_read_format_tar.c test_read_format_tar_acl_oob_read.c @@ -202,6 +223,7 @@ IF(ENABLE_TEST) test_read_format_tar_invalid_pax_size.c test_read_format_tar_mac_metadata.c test_read_format_tar_pax_g_large.c + test_read_format_tar_pax_global_header.c test_read_format_tar_pax_large_attr.c test_read_format_tar_pax_negative_time.c test_read_format_tar_pax_sun_holesdata.c @@ -218,7 +240,10 @@ IF(ENABLE_TEST) test_read_format_xar_doublelink.c test_read_format_zip.c test_read_format_zip_7075_utf8_paths.c + test_read_format_zip_ambiguous_timestamps.c test_read_format_zip_comment_stored.c + test_read_format_zip_dos_mtime_precedence.c + test_read_format_zip_empty.c test_read_format_zip_encryption_data.c test_read_format_zip_encryption_header.c test_read_format_zip_encryption_partially.c @@ -226,17 +251,22 @@ IF(ENABLE_TEST) test_read_format_zip_filename.c test_read_format_zip_high_compression.c test_read_format_zip_jar.c + test_read_format_zip_lzma_alone_large_size.c test_read_format_zip_mac_metadata.c test_read_format_zip_malformed.c test_read_format_zip_msdos.c + test_read_format_zip_negative_timestamps.c test_read_format_zip_nested.c test_read_format_zip_nofiletype.c + test_read_format_zip_ntfs_precedence.c + test_read_format_zip_ntfs_timestamp.c test_read_format_zip_padded.c test_read_format_zip_sfx.c test_read_format_zip_size_exceeds_declared.c test_read_format_zip_symlink_unsupported_compression.c test_read_format_zip_traditional_encryption_data.c test_read_format_zip_uncompressed_size_off_by_4gib.c + test_read_format_zip_unset_times.c test_read_format_zip_winzip_aes.c test_read_format_zip_winzip_aes_large.c test_read_format_zip_zip64.c @@ -273,6 +303,7 @@ IF(ENABLE_TEST) test_write_disk_perms.c test_write_disk_secure.c test_write_disk_secure_noabsolutepaths.c + test_write_disk_secure_physicaldrive.c test_write_disk_secure744.c test_write_disk_secure745.c test_write_disk_secure746.c @@ -311,6 +342,7 @@ IF(ENABLE_TEST) test_write_format_iso9660_joliet_id.c test_write_format_iso9660_rockridge.c test_write_format_iso9660_rockridge_overflow.c + test_write_format_iso9660_rr_moved_notdir.c test_write_format_iso9660_zisofs.c test_write_format_iso9660_zisofs_overflow.c test_write_format_iso9660_bugs.c diff --git a/libarchive/test/read_open_memory.c b/libarchive/test/read_open_memory.c index 0c714a31e809..fee5eccb5d77 100644 --- a/libarchive/test/read_open_memory.c +++ b/libarchive/test/read_open_memory.c @@ -188,27 +188,27 @@ static int64_t memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence) { struct read_memory_data *mine = (struct read_memory_data *)client_data; + const unsigned char *p; (void)a; /* UNUSED */ switch (whence) { case SEEK_SET: - mine->p = mine->start + offset; + p = mine->start + offset; break; case SEEK_END: - mine->p = mine->end + offset; + p = mine->end + offset; break; case SEEK_CUR: - mine->p += offset; + p = mine->p + offset; break; - } - if (mine->p < mine->start) { - mine->p = mine->start; + default: return ARCHIVE_FAILED; } - if (mine->p > mine->end) { - mine->p = mine->end; + if (p < mine->start) return ARCHIVE_FAILED; - } + if (p > mine->end) + return ARCHIVE_FAILED; + mine->p = p; return (mine->p - mine->start); } diff --git a/libarchive/test/test_archive_crc32.c b/libarchive/test/test_archive_crc32.c new file mode 100644 index 000000000000..f6525cccce72 --- /dev/null +++ b/libarchive/test/test_archive_crc32.c @@ -0,0 +1,49 @@ +#include "test.h" +#define __LIBARCHIVE_TEST 1 +#include "archive_private.h" + +DEFINE_TEST(test_archive_crc32) +{ + unsigned char buf[8192]; + size_t i; + unsigned long crc_lib, crc_ref; + + /* Initialize buffer with some data */ + for (i = 0; i < sizeof(buf); i++) + buf[i] = (unsigned char)(i ^ (i >> 8)); + + /* 1. Empty */ + crc_lib = __archive_crc32(0, NULL, 0); + crc_ref = bitcrc32(0, NULL, 0); + assertEqualInt(crc_lib, crc_ref); + + crc_lib = __archive_crc32(0, buf, 0); + crc_ref = bitcrc32(0, buf, 0); + assertEqualInt(crc_lib, crc_ref); + + /* 2. Basic */ + crc_lib = __archive_crc32(0, buf, 128); + crc_ref = bitcrc32(0, buf, 128); + assertEqualInt(crc_lib, crc_ref); + + /* 3. Incremental */ + crc_lib = __archive_crc32(0, buf, 33); + crc_lib = __archive_crc32(crc_lib, buf + 33, 95); + crc_ref = bitcrc32(0, buf, 33); + crc_ref = bitcrc32(crc_ref, buf + 33, 95); + assertEqualInt(crc_lib, crc_ref); + + /* 4. Unaligned */ + crc_lib = __archive_crc32(0, buf + 1, 127); + crc_ref = bitcrc32(0, buf + 1, 127); + assertEqualInt(crc_lib, crc_ref); + + crc_lib = __archive_crc32(0, buf + 3, 128); + crc_ref = bitcrc32(0, buf + 3, 128); + assertEqualInt(crc_lib, crc_ref); + + /* 5. Large */ + crc_lib = __archive_crc32(0, buf, sizeof(buf)); + crc_ref = bitcrc32(0, buf, sizeof(buf)); + assertEqualInt(crc_lib, crc_ref); +} diff --git a/libarchive/test/test_archive_ntfs_time.c b/libarchive/test/test_archive_ntfs_time.c new file mode 100644 index 000000000000..ed65fce8e32a --- /dev/null +++ b/libarchive/test/test_archive_ntfs_time.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +#define __LIBARCHIVE_BUILD 1 +#include "archive_time_private.h" + +/* NTFS epoch offset: seconds between 1601-01-01 and 1970-01-01. */ +#define NTFS_EPOCH_SECS 11644473600LL +#define NTFS_TICKS_PER_SEC 10000000LL + +DEFINE_TEST(test_archive_ntfs_to_unix) +{ + int64_t secs; + uint32_t nsecs; + + /* Exactly the Unix epoch. */ + __archive_ntfs_to_unix( + (uint64_t)(NTFS_EPOCH_SECS * NTFS_TICKS_PER_SEC), &secs, &nsecs); + assertEqualInt(0, secs); + assertEqualInt(0, nsecs); + + /* A normal post-1970, second-aligned date. */ + __archive_ntfs_to_unix( + (uint64_t)((NTFS_EPOCH_SECS + 1000000000LL) * NTFS_TICKS_PER_SEC), + &secs, &nsecs); + assertEqualInt(1000000000, secs); + assertEqualInt(0, nsecs); + + /* A normal post-1970 date with sub-second precision. */ + __archive_ntfs_to_unix( + (uint64_t)((NTFS_EPOCH_SECS + 1000000000LL) * NTFS_TICKS_PER_SEC + + 1234567), &secs, &nsecs); + assertEqualInt(1000000000, secs); + assertEqualInt(123456700, nsecs); + + /* A pre-1970, second-aligned date: FILETIME has no sign ambiguity + * (it's unsigned ticks since 1601), so this is perfectly legitimate, + * not malformed. */ + __archive_ntfs_to_unix( + (uint64_t)(NTFS_EPOCH_SECS * NTFS_TICKS_PER_SEC + - 2 * NTFS_TICKS_PER_SEC), &secs, &nsecs); + assertEqualInt(-2, secs); + assertEqualInt(0, nsecs); + + /* A pre-1970, non-second-aligned date. lldiv() truncates toward + * zero, not toward -infinity, so this specifically exercises the + * floor-division adjustment: nsecs must land in + * [0, 999999999], with the sign folded entirely into secs, not a + * huge value from a negative remainder miscast to uint32_t. */ + __archive_ntfs_to_unix( + (uint64_t)(NTFS_EPOCH_SECS * NTFS_TICKS_PER_SEC - 15), + &secs, &nsecs); + assertEqualInt(-1, secs); + assertEqualInt(999998500, nsecs); + + /* Same, one full second further back, to double check the whole- + * second component also keeps adjusting correctly alongside the + * fractional one. */ + __archive_ntfs_to_unix( + (uint64_t)(NTFS_EPOCH_SECS * NTFS_TICKS_PER_SEC + - NTFS_TICKS_PER_SEC - 15), &secs, &nsecs); + assertEqualInt(-2, secs); + assertEqualInt(999998500, nsecs); +} diff --git a/libarchive/test/test_archive_read.c b/libarchive/test/test_archive_read.c index 583e5166a0be..a030e67d0c01 100644 --- a/libarchive/test/test_archive_read.c +++ b/libarchive/test/test_archive_read.c @@ -61,3 +61,368 @@ DEFINE_TEST(test_archive_read_ahead_eof) assert(0 == archive_read_free(a)); } + +DEFINE_TEST(test_archive_read_ahead_zero) +{ + struct archive *a; + struct archive_read *ar; + ssize_t avail; + + /* prepare a reader of raw in-memory data */ + assert((a = archive_read_new()) != NULL); + ar = (struct archive_read *)a; + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buf, sizeof(buf))); + + /* perform a zero read which can be fulfilled */ + assert(NULL != __archive_read_ahead(ar, 0, &avail)); + assertEqualIntA(ar, sizeof(buf), avail); + + /* perform a read which can be fulfilled */ + assert(NULL != __archive_read_ahead(ar, 1, &avail)); + assertEqualIntA(ar, sizeof(buf), avail); + + /* perform the zero read again */ + assert(NULL != __archive_read_ahead(ar, 0, &avail)); + assertEqualIntA(ar, sizeof(buf), avail); + + /* consume all available bytes */ + assertEqualIntA(ar, sizeof(buf), + __archive_read_consume(ar, sizeof(buf))); + + /* perform a read which cannot be fulfilled */ + assert(NULL == __archive_read_ahead(ar, 1, &avail)); + assertEqualIntA(ar, 0, avail); + + /* perform another zero read which can be fulfilled */ + assert(NULL != __archive_read_ahead(ar, 0, &avail)); + assertEqualIntA(ar, 0, avail); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* prepare a reader of raw in-memory data */ + assert((a = archive_read_new()) != NULL); + ar = (struct archive_read *)a; + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_empty(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buf, 0)); + + /* perform a zero read which can be fulfilled */ + assert(NULL != __archive_read_ahead(ar, 0, &avail)); + assertEqualIntA(ar, 0, avail); + + /* perform a read which cannot be fulfilled */ + assert(NULL == __archive_read_ahead(ar, 1, &avail)); + assertEqualIntA(ar, 0, avail); + + /* perform another zero read which can be fulfilled */ + assert(NULL != __archive_read_ahead(ar, 0, &avail)); + assertEqualIntA(ar, 0, avail); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +static ssize_t +eof_read_callback(struct archive *a, void *data, const void **buffer) +{ + (void)a; + (void)data; + (void)buffer; + + return (0); +} + +static int +evil_open_callback(struct archive *a, void *data) +{ + (void)data; + + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_set_read_callback(a, NULL)); + return (ARCHIVE_OK); +} + +DEFINE_TEST(test_archive_read_state_open) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* Prepare callbacks which modify callbacks when used. */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_set_open_callback(a, evil_open_callback)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_set_read_callback(a, eof_read_callback)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_set_callback_data(a, NULL)); + + /* Try to open archive. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open1(a)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +struct read_memory_data { + const unsigned char *start; + const unsigned char *p; + const unsigned char *end; + ssize_t read_size; +}; + +/* + * Set read position to beginning of memory. + */ +static int +memory_read_open(struct archive *a, void *client_data) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + + (void)a; /* UNUSED */ + mine->p = mine->start; + return (ARCHIVE_OK); +} + +/* + * This is scary simple: Just advance a pointer. Limiting + * to read_size is not technically necessary, but it exercises + * more of the internal logic when used with a small block size + * in a test harness. Production use should not specify a block + * size; then this is much faster. + */ +static ssize_t +memory_read(struct archive *a, void *client_data, const void **buff) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + ssize_t size; + + (void)a; /* UNUSED */ + *buff = mine->p; + size = mine->end - mine->p; + if (size > mine->read_size) + size = mine->read_size; + mine->p += size; + return (size); +} + +/* + * Advancing is just as simple. Again, this is doing more than + * necessary in order to better exercise internal code when used + * as a test harness. + */ +static int64_t +memory_read_skip(struct archive *a, void *client_data, int64_t skip) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + + (void)a; /* UNUSED */ + if ((int64_t)skip > (int64_t)(mine->end - mine->p)) + skip = mine->end - mine->p; + /* Round down to block size. */ + skip /= mine->read_size; + skip *= mine->read_size; + mine->p += skip; + return (skip); +} + +/* + * Seeking. + */ +static int64_t +memory_read_seek(struct archive *a, void *client_data, + int64_t offset, int whence) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + const unsigned char *p; + + (void)a; /* UNUSED */ + switch (whence) { + case SEEK_SET: + p = mine->start + offset; + break; + case SEEK_CUR: + p = mine->p + offset; + break; + case SEEK_END: + p = mine->end + offset; + break; + default: + return ARCHIVE_FAILED; + } + if (p < mine->start) + return ARCHIVE_FAILED; + if (p > mine->end) + return ARCHIVE_FAILED; + mine->p = p; + return (mine->p - mine->start); +} + +/* + * Seeking always fails. + */ +static int64_t +memory_read_failing_seek(struct archive *a, void *client_data, + int64_t offset, int whence) +{ + (void)a; /* UNUSED */ + (void)client_data; + (void)offset; + (void)whence; + + errno = ESPIPE; + return (ARCHIVE_FAILED); +} + +/* + * Close is just cleaning up our one small bit of data. + */ +static int +memory_read_close(struct archive *a, void *client_data) +{ + struct read_memory_data *mine = (struct read_memory_data *)client_data; + (void)a; /* UNUSED */ + free(mine); + return (ARCHIVE_OK); +} + +/* + * Switching keeps memory allocated and just resets read pointer. + */ +static int +memory_read_switch(struct archive *a, void *client_data1, void *client_data2) +{ + (void)client_data1; /* UNUSED */ + return memory_read_open(a, client_data2); +} + +DEFINE_TEST(test_archive_read_consume_beyond_eof) +{ + struct archive *a; + struct archive_entry *ae; + struct read_memory_data *mine; + char *buff1, *buff2; + size_t buffsize = 128 * 1024; + size_t contentsize = 65 * 1024; + size_t used; + + buff1 = calloc(1, buffsize); + assert(buff1 != NULL); + buff2 = calloc(1, buffsize); + assert(buff2 != NULL); + + /* Create a new archive in memory. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff1, buffsize, &used)); + + /* Add a large (>64 KB) file. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_pathname(ae, "file1"); + archive_entry_set_mode(ae, S_IFREG | 0664); + archive_entry_set_size(ae, 67 * 1024); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, contentsize, archive_write_data(a, buff2, contentsize)); + + /* Add a small file at the end. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_mtime(ae, 1, 10); + archive_entry_set_pathname(ae, "file2"); + archive_entry_set_mode(ae, S_IFREG | 0664); + archive_entry_set_size(ae, 6); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, 6, archive_write_data(a, "canary", 6)); + + /* Close out the archive. */ + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* Split archive into two memory areas. */ + memcpy(buff2, buff1 + 64 * 1024, 64 * 1024); + memset(buff1 + 64 * 1024, 0, 64 * 1024); + + /* Prepare a tar reader. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a)); + + /* Add first volume. */ + mine = calloc(1, sizeof(*mine)); + assert(mine != NULL); + mine->start = (const unsigned char *)buff1; + mine->end = mine->start + 64 * 1024; + mine->read_size = 1024; + assertEqualIntA(a, ARCHIVE_OK, + archive_read_append_callback_data(a, mine)); + + /* Add second volume. */ + mine = calloc(1, sizeof(*mine)); + assert(mine != NULL); + mine->start = (const unsigned char *)buff2; + mine->end = mine->start + 64 * 1024; + mine->read_size = 1024; + assertEqualIntA(a, ARCHIVE_OK, + archive_read_append_callback_data(a, mine)); + + /* Register callbacks: seek but no skip */ + archive_read_set_open_callback(a, memory_read_open); + archive_read_set_read_callback(a, memory_read); + archive_read_set_seek_callback(a, memory_read_seek); + archive_read_set_close_callback(a, memory_read_close); + archive_read_set_switch_callback(a, memory_read_switch); + + /* Open archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_open1(a)); + + /* Read both files. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* Verify end of archive is reached. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_archive_read_seek_fails) +{ + struct archive *a; + struct archive_entry *ae; + struct read_memory_data *mine; + char *buff; + size_t size; + + size = 1024; + buff = calloc(1, size); + assert(buff != NULL); + + assert((a = archive_read_new()) != NULL); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + /* Create memory reader which fails seeking. */ + mine = calloc(1, sizeof(*mine)); + assert(mine != NULL); + mine->start = mine->p = (const unsigned char *)buff; + mine->end = mine->start + size; + mine->read_size = 512; + archive_read_set_open_callback(a, memory_read_open); + archive_read_set_read_callback(a, memory_read); + archive_read_set_seek_callback(a, memory_read_failing_seek); + archive_read_set_skip_callback(a, memory_read_skip); + archive_read_set_close_callback(a, memory_read_close); + assertEqualIntA(a, ARCHIVE_OK, archive_read_set_callback_data(a, mine)); + + /* Read empty tar archive. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_open1(a)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_FORMAT_TAR, archive_format(a)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + free(buff); +} diff --git a/libarchive/test/test_archive_read_append_filter.c b/libarchive/test/test_archive_read_append_filter.c new file mode 100644 index 000000000000..5c301d5207ae --- /dev/null +++ b/libarchive/test/test_archive_read_append_filter.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(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.h" +#include "test.h" + +DEFINE_TEST(test_archive_read_append_filter) +{ + struct archive *a; + int r; + + a = archive_read_new(); + assert(a != NULL); + + /* Append a program bidder (without filter). */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_program(a, "prog1")); + + /* Append a filter program which comes with its own bidder. */ + r = archive_read_append_filter_program(a, "prog2"); + + /* Verify that filter uses its own bidder. */ + if (r == ARCHIVE_FATAL) { + assertEqualStringA(a, "Can't initialize filter; unable to run program \"prog2\"", + archive_error_string(a)); + } else { + assertEqualIntA(a, ARCHIVE_OK, r); + assertEqualStringA(a, "Program: prog2", archive_filter_name(a, 0)); + } + + archive_read_free(a); +} diff --git a/libarchive/test/test_archive_read_open_filenames.c b/libarchive/test/test_archive_read_open_filenames.c new file mode 100644 index 000000000000..4c23b152f504 --- /dev/null +++ b/libarchive/test/test_archive_read_open_filenames.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(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 "test.h" + +DEFINE_TEST(test_archive_read_open_filenames_null) +{ + const char *filenames[] = { + "zero", + "nonexisting", + NULL + }; + char *buf; + struct archive *a; + struct archive_entry *ae; + + /* Create a file sufficiently large for bidders to not reach EOF. */ + buf = calloc(512, 1024); + assert(buf != NULL); + dumpfile("zero", buf, 512 * 1024); + free(buf); + + /* Open file and a non-existing one as well. */ + a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + /* Verify that archive can be opened. */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filenames(a, filenames, 4096)); + + /* Verify that no entry is found. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_archive_read_open_filenames_split_uaf) +{ + const char *reffiles[] = { + "test_archive_read_open_filenames_split_uaf_1.tar", + "test_archive_read_open_filenames_split_uaf_2.tar", + NULL + }; + struct archive *a; + struct archive_entry *ae; + + /* Prepare formats and filters for sufficiently large bid requests. */ + a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a)); + + extract_reference_files(reffiles); + + /* Verify that archive can be opened. */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filenames(a, reffiles, 4096)); + + /* Verify that an entry is found. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* Verify that no further entry can be found. */ + assertEqualIntA(a, ARCHIVE_RETRY, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_archive_read_open_filenames_split_uaf_1.tar.uu b/libarchive/test/test_archive_read_open_filenames_split_uaf_1.tar.uu new file mode 100644 index 000000000000..5a5bf6bdab2a --- /dev/null +++ b/libarchive/test/test_archive_read_open_filenames_split_uaf_1.tar.uu @@ -0,0 +1,9 @@ +begin 644 test_archive_read_open_filenames_split_uaf_1.tar +M:6YP=70N='AT```````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#8T-"``,#`Q-S4P(``P,#$W-3`@`#`P,#`P,#`P,#$T +M(#$U,C0U,#8T,S8P(#`Q,S4V,@`@,``````````````````````````````` +M```````````````````````````````````````````````````````````` +@```````````````````````````````````````````` +` +end diff --git a/libarchive/test/test_archive_read_open_filenames_split_uaf_2.tar.uu b/libarchive/test/test_archive_read_open_filenames_split_uaf_2.tar.uu new file mode 100644 index 000000000000..c0a7abd7794a --- /dev/null +++ b/libarchive/test/test_archive_read_open_filenames_split_uaf_2.tar.uu @@ -0,0 +1,26 @@ +begin 644 test_archive_read_open_filenames_split_uaf_2.tar +M=7-T87(`,#!T;V)I87,``````````````````````````````````'1O8FEA +M" to change encoding. */ setlocale(LC_ALL, ".1252") == NULL #else - setlocale(LC_ALL, "en_US.ISO8859-1") == NULL + !setCheckedLocale("en_US.ISO8859-1", "\xA0", L'\x00A0') #endif ) { skipping("No Latin-1 locale found on this system."); @@ -1010,16 +1012,20 @@ DEFINE_TEST(test_archive_string_conversion_fail_latin1) assert((a = archive_write_new()) != NULL); #if defined(_WIN32) && !defined(__CYGWIN__) - assertA(NULL != (sconv = - archive_string_conversion_to_charset(a, "CP1252", 0))); - assertEqualString("CP1252", - archive_string_conversion_charset_name(sconv)); + charset = "CP1252"; #else - assertA(NULL != (sconv = - archive_string_conversion_to_charset(a, "ISO8859-1", 0))); - assertEqualString("ISO8859-1", - archive_string_conversion_charset_name(sconv)); + charset = "ISO8859-1"; #endif + sconv = archive_string_conversion_to_charset(a, charset, 0); +#if !defined(_WIN32) || defined(__CYGWIN__) + if (sconv == NULL) { + charset = "ISO-8859-1"; + sconv = archive_string_conversion_to_charset(a, charset, 0); + } +#endif + assertA(NULL != sconv); + assertEqualString(charset, + archive_string_conversion_charset_name(sconv)); test_archive_string_conversion_fail_utf16_mbs(a, sconv); test_archive_string_conversion_fail_utf8_mbs(a, sconv); @@ -1111,7 +1117,7 @@ DEFINE_TEST(test_archive_string_update_utf8_utf8) memset(&mstr, 0, sizeof(mstr)); - if (setlocale(LC_ALL, "en_US.UTF-8") == NULL) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("UTF-8 not supported on this system."); return; } @@ -1139,7 +1145,7 @@ DEFINE_TEST(test_archive_string_update_utf8_koi8) memset(&mstr, 0, sizeof(mstr)); - if (setlocale(LC_ALL, "ru_RU.KOI8-R") == NULL) { + if (!setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("KOI8-R locale not available on this system."); return; } diff --git a/libarchive/test/test_archive_write_set_format_filter_by_ext.c b/libarchive/test/test_archive_write_set_format_filter_by_ext.c index 849a9c5a078d..2c70c9c37417 100644 --- a/libarchive/test/test_archive_write_set_format_filter_by_ext.c +++ b/libarchive/test/test_archive_write_set_format_filter_by_ext.c @@ -27,7 +27,7 @@ #include "test.h" static void -test_format_filter_by_ext(const char *output_file, +test_format_filter_by_ext(const char *output_file, int format_id, int filter_id, int dot_stored, const char * def_ext) { struct archive_entry *ae; @@ -70,7 +70,7 @@ test_format_filter_by_ext(const char *output_file, return; } } - + assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); @@ -198,6 +198,11 @@ DEFINE_TEST(test_archive_write_set_format_filter_by_ext_tar_xz) test_format_filter_by_ext("./data/test.tar.xz", ARCHIVE_FORMAT_TAR, ARCHIVE_FILTER_XZ, 0, NULL); } +DEFINE_TEST(test_archive_write_set_format_filter_by_ext_tar_zst) +{ + test_format_filter_by_ext("./data/test.tar.zst", ARCHIVE_FORMAT_TAR, ARCHIVE_FILTER_ZSTD, 0, NULL); +} + DEFINE_TEST(test_archive_write_set_format_filter_by_no_ext_def_zip) { test_format_filter_by_ext("./data/test", ARCHIVE_FORMAT_ZIP, ARCHIVE_FILTER_NONE, 0, ".zip"); diff --git a/libarchive/test/test_compat_lz4_skippable_frames_B4.tar.lz4.uu b/libarchive/test/test_compat_lz4_skippable_frames_B4.tar.lz4.uu index f45b1ac8f8dd..b77a13f6c882 100644 --- a/libarchive/test/test_compat_lz4_skippable_frames_B4.tar.lz4.uu +++ b/libarchive/test/test_compat_lz4_skippable_frames_B4.tar.lz4.uu @@ -1,3 +1,13 @@ +This is test_compat_lz4_B4.tar.lz4 prefixed with two skippable +frames, each with a different magic number in the lz4/zstd magic +number range. The first one contains no user data, the second +one contains 10 bytes of user data (the values 0 through 9). + +This is useful to test lz4/zstd detection, because they share +the same format for skippable frames. We should scan a reasonable +amount of data looking for a non-skippable frame to decide if +it is lz4 or zstd data. + begin 664 test_compat_lz4_skippable_frames_B4.tar.lz4.uu M4"I-&`````!1*DT8"@`````!`@,$!08'"`D$(DT89$"GE1$``&]X9FEL90`! M`$OA,#`P-C0T(``P,#`W-C4(`"(P,A``(C(P`0#_!R`Q,C,U,S4U,S4Q,B`P diff --git a/libarchive/test/test_compat_uudecode.c b/libarchive/test/test_compat_uudecode.c index d6936d9dcd5d..3c35e449e0f3 100644 --- a/libarchive/test/test_compat_uudecode.c +++ b/libarchive/test/test_compat_uudecode.c @@ -25,7 +25,7 @@ */ #include "test.h" -static char archive_data[] = { +static char archive_data_1[] = { "begin 644 test_read_uu.Z\n" "M'YV0+@`('$BPH,&#\"!,J7,BP(4(8$&_4J`$\"`,08$F%4O)AQ(\\2/(#7&@#%C\n" "M!@T8-##.L`$\"QL@:-F(``%'#H/1A2\n" @@ -38,11 +38,21 @@ static char archive_data[] = { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }; -/* - * Compatibility: uudecode command ignores junk data placed after the "end" - * marker. - */ -DEFINE_TEST(test_compat_uudecode) +static char archive_data_2[] = { +"begin 644 test_read_uu.Z\n" +"M'YV0+@`('$BPH,&#\"!,J7,BP(4(8$&_4J`$\"`,08$F%4O)AQ(\\2/(#7&@#%C\n" +"M!@T8-##.L`$\"QL@:-F(``%'#H/1A2\n" +"IHU\"0%9=*G4JUJM6K6+-JWO8,.*'4NVK-FS:-.J7Hfile.eof test, and archive_read_format_raw_read_data() returns + * end-of-file from raw->end_of_file on every call after the first. The + * checks seed both outputs and require the handler to replace them. + */ + +/* + * Read to the end of the current entry, checking every block on the way, + * and return the position the handler reports at ARCHIVE_EOF. + */ +static int64_t +read_entry_to_eof(struct archive *a) +{ + const void *buff; + size_t size; + int64_t offset; + int r; + + for (;;) { + size = (size_t)-1; + offset = -1; + r = archive_read_data_block(a, &buff, &size, &offset); + failure("read_data must report a size and an offset for " + "every block, including the last one"); + assert(size != (size_t)-1); + assert(offset != -1); + if (r != ARCHIVE_OK) { + assertEqualIntA(a, ARCHIVE_EOF, r); + assertEqualInt(0, size); + return (offset); + } + } +} + +DEFINE_TEST(test_read_data_block_offset_at_eof) +{ + const char *rarname = "test_read_format_rar5_hardlink.rar"; + const char *rawname = "test_read_format_raw.data"; + struct archive *a; + struct archive_entry *ae; + const void *buff; + size_t size; + int64_t offset; + + /* + * rar5: "hardlink.txt" carries no data, so the first call to + * read_data ends the entry. + */ + extract_reference_file(rarname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, rarname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(5, read_entry_to_eof(a)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("hardlink.txt", archive_entry_pathname(ae)); + size = (size_t)-1; + offset = -1; + assertEqualIntA(a, ARCHIVE_EOF, + archive_read_data_block(a, &buff, &size, &offset)); + failure("rar5 must report a position for an entry it ends on the " + "first call"); + assertEqualInt(0, offset); + assertEqualInt(0, size); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* + * raw: the entry has data, so it is the second and every later + * end-of-file that goes through the short path. + */ + extract_reference_file(rawname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, rawname, 512)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("data", archive_entry_pathname(ae)); + assertEqualInt(4, read_entry_to_eof(a)); + + size = (size_t)-1; + offset = -1; + assertEqualIntA(a, ARCHIVE_EOF, + archive_read_data_block(a, &buff, &size, &offset)); + failure("raw must keep reporting a position once the entry has " + "ended"); + assertEqualInt(4, offset); + assertEqualInt(0, size); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_data_into_fd_no_offset_reported.c b/libarchive/test/test_read_data_into_fd_no_offset_reported.c new file mode 100644 index 000000000000..8711ccf085b9 --- /dev/null +++ b/libarchive/test/test_read_data_into_fd_no_offset_reported.c @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 2026 Kaixuan Li + * 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 "test.h" + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define open _open +#define close _close +#endif + +/* + * archive_read_data_into_fd() must not depend on archive_read_data_block() + * having written *offset. A format's read_data callback may end an entry + * on its very first call, and for an entry whose size is not set the + * declared-size clamp does not apply either, so an offset the callback + * never wrote reaches pad_to() and becomes a trailing hole of arbitrary + * size. + * + * "hardlink.txt" in the reference archive is such an entry: rar5 ends it + * on the first call, and archive_entry_size_is_set() is false for it. + * Extracting it must produce an empty file, not a hole. + * + * Before the fix this failed below with "Seek error" (EINVAL), where + * pad_to() lseek()s to the value the stack happened to hold. That value + * varies by compiler and version, so the visible symptom is not portable; + * MemorySanitizer reports the read unconditionally. + */ +DEFINE_TEST(test_read_data_into_fd_no_offset_reported) +{ + const char *refname = "test_read_format_rar5_hardlink.rar"; + char tmpfilename[] = "no_offset_reported"; + struct archive *a; + struct archive_entry *ae; + int fd; + struct stat st; + + extract_reference_file(refname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + fd = open(tmpfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + assert(fd >= 0); + assertEqualIntA(a, ARCHIVE_OK, archive_read_data_into_fd(a, fd)); + close(fd); + assertEqualInt(0, stat(tmpfilename, &st)); + assertEqualInt(5, (int)st.st_size); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("hardlink.txt", archive_entry_pathname(ae)); + assertEqualInt(0, archive_entry_size_is_set(ae)); + + fd = open(tmpfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + assert(fd >= 0); + failure("archive_read_data_into_fd() must succeed for an entry whose " + "reader reported no offset"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_data_into_fd(a, fd)); + close(fd); + + assertEqualInt(0, stat(tmpfilename, &st)); + failure("an entry whose reader reported no offset must not produce a " + "trailing hole; got %d bytes", (int)st.st_size); + assertEqualInt(0, (int)st.st_size); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_filter_uudecode.c b/libarchive/test/test_read_filter_uudecode.c index 1bc9385c3f6a..f6bbaf6ac532 100644 --- a/libarchive/test/test_read_filter_uudecode.c +++ b/libarchive/test/test_read_filter_uudecode.c @@ -34,7 +34,14 @@ static const char archive[] = { "end\n" }; +static const char empty_archive[] = { +"begin 644 -\n" +"`\n" +"end\n" +}; + static const char archive64[] = { +"UTF-8 grinning face with smiling eyes \xF0\x9F\x98\x81\n" "begin-base64 644 test_read_uu.Z\n" "H52QLgAIHEiwoMGDCBMqXMiwIUIYEG/UqAECAMQYEmFUvJhxI8SPIDXGgDFjBg0YNDDOsAECxsga\n" "NmIAAFHDoc2bOHPqBFBnDp0wcizCoJOmzc6ERI0ePRhSo1CQFZdKnUq1qtWrWLNq3cq1q9evYMOK\n" @@ -42,6 +49,11 @@ static const char archive64[] = { "====\n" }; +static const char empty_archive64[] = { +"begin-base64 644 -\n" +"====\n" +}; + static const char extradata[] = { "From uudecode@libarchive Mon Jun 2 03:03:31 2008\n" "Return-Path: \n" @@ -158,6 +170,21 @@ test_read_uu_sub(const char *uudata, size_t uusize, int no_nl) free(buff); } +static void +test_empty(const char *uudata) +{ + struct archive_entry *ae; + struct archive *a; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, uudata, strlen(uudata), 2)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_filter_uudecode) { /* Read the traditional uuencoded data. */ @@ -165,6 +192,8 @@ DEFINE_TEST(test_read_filter_uudecode) /* Read the traditional uuencoded data with very long line extra * data in front of it. */ test_read_uu_sub(archive, sizeof(archive)-1, 1); + /* Read an empty uuencoded file. */ + test_empty(empty_archive); } DEFINE_TEST(test_read_filter_uudecode_base64) @@ -174,4 +203,6 @@ DEFINE_TEST(test_read_filter_uudecode_base64) /* Read the Base64 uuencoded data with very long line extra data * in front of it. */ test_read_uu_sub(archive64, sizeof(archive64)-1, 1); + /* Read an empty base64 encoded file. */ + test_empty(empty_archive64); } diff --git a/libarchive/test/test_read_filter_uudecode_newlines.c b/libarchive/test/test_read_filter_uudecode_newlines.c new file mode 100644 index 000000000000..dd017548ce8f --- /dev/null +++ b/libarchive/test/test_read_filter_uudecode_newlines.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(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 "test.h" + +#define __LIBARCHIVE_TEST +#include "archive_string.h" + +static void +create_data(struct archive_string *s, const char *newline) +{ + int i; + + archive_string_sprintf(s, "begin 644 zeros%s", newline); + for (i = 0; i < 18; i++) + archive_string_sprintf(s, "M````````````````````````````````" + "````````````````````````````%s", newline); + archive_string_sprintf(s, "`%s", newline); + archive_string_sprintf(s, "end%s", newline); +} + +static void +test_data(const char *newline) +{ + char buf[1024]; + struct archive *a; + struct archive_entry *ae; + struct archive_string s; + + archive_string_init(&s); + create_data(&s, newline); + + /* Read 810 uu-encoded 0 bytes. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_uu(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory2(a, s.s, archive_strlen(&s), 4)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualIntA(a, 810, archive_read_data(a, &buf, sizeof(buf))); + assertMemoryFilledWith(buf, 810, 0x00); + assertEqualIntA(a, 0, archive_read_data(a, &buf, sizeof(buf))); + + /* Clean up resources. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + archive_string_free(&s); +} + +DEFINE_TEST(test_read_filter_uudecode_newlines) +{ + test_data("\r"); + test_data("\n"); + test_data("\r\n"); +} diff --git a/libarchive/test/test_read_filter_uudecode_raw.c b/libarchive/test/test_read_filter_uudecode_raw.c index 99f5b43f60cb..0f02bda22ff7 100644 --- a/libarchive/test/test_read_filter_uudecode_raw.c +++ b/libarchive/test/test_read_filter_uudecode_raw.c @@ -26,19 +26,21 @@ DEFINE_TEST(test_read_filter_uudecode_raw) { - struct archive_entry *ae; - struct archive *a; + struct archive_entry *ae; + struct archive *a; + char buf[1024]; const char *name = "test_read_filter_uudecode_raw.uu"; - assert((a = archive_read_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); - copy_reference_file(name); - assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 670)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + copy_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 670)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("LICENSE.txt", archive_entry_pathname(ae)); assertEqualInt((AE_IFREG | 0755), archive_entry_mode(ae)); + assertEqualIntA(a, 465, archive_read_data(a, buf, sizeof(buf))); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); @@ -47,16 +49,16 @@ DEFINE_TEST(test_read_filter_uudecode_raw) DEFINE_TEST(test_read_filter_uudecode_base64_raw) { - struct archive_entry *ae; - struct archive *a; + struct archive_entry *ae; + struct archive *a; const char *name = "test_read_filter_uudecode_base64_raw.uu"; - assert((a = archive_read_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); - copy_reference_file(name); - assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 670)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + copy_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 670)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("LICENSE2.txt", archive_entry_pathname(ae)); assertEqualInt((AE_IFREG | 0600), archive_entry_mode(ae)); @@ -65,3 +67,80 @@ DEFINE_TEST(test_read_filter_uudecode_base64_raw) assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } + +/* + * Reads the given data, which should be uuencoded or base64 encoded + * "hi\n", with the uudecode filter and the raw format. If the filter is + * expected to recognize it, the entry has the given name and mode. + */ +static void +check_header(const char *data, const char *want_name, int want_mode) +{ + struct archive_entry *ae; + struct archive *a; + char buf[16]; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, data, strlen(data))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + if (want_name != NULL) { + assertEqualInt(ARCHIVE_FILTER_UU, archive_filter_code(a, 0)); + assertEqualString(want_name, archive_entry_pathname(ae)); + assertEqualInt((AE_IFREG | want_mode), archive_entry_mode(ae)); + assertEqualIntA(a, 3, archive_read_data(a, buf, sizeof(buf))); + assertEqualMem("hi\n", buf, 3); + } else { + /* Not a header: the data is read as it is. */ + assertEqualInt(ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); + } + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* + * The mode in the "begin" line is a sequence of octal digits. Encoders + * don't all write three of them, for example 0644 instead of 644. + */ +DEFINE_TEST(test_read_filter_uudecode_mode_digits) +{ + /* The usual three digits. */ + check_header("begin 644 test.txt\n#:&D*\n`\nend\n", "test.txt", 0644); + + /* Four digits, with a leading zero. */ + check_header("begin 0744 test.txt\n#:&D*\n`\nend\n", "test.txt", 0744); + + /* Four digits: only the permission bits are kept. */ + check_header("begin 4755 test.txt\n#:&D*\n`\nend\n", "test.txt", 0755); + + /* Same for the base64 encoding. */ + check_header("begin-base64 600 test.txt\naGkK\n====\n", + "test.txt", 0600); + check_header("begin-base64 0600 test.txt\naGkK\n====\n", + "test.txt", 0600); +} + +/* Lines that are not a valid header are not recognized. */ +DEFINE_TEST(test_read_filter_uudecode_invalid_mode) +{ + /* A character that is not an octal digit. */ + check_header("begin 64x test.txt\n#:&D*\n`\nend\n", NULL, 0); + check_header("begin 648 test.txt\n#:&D*\n`\nend\n", NULL, 0); + + /* Fewer than three digits. */ + check_header("begin 44 test.txt\n#:&D*\n`\nend\n", NULL, 0); + check_header("begin-base64 6 test.txt\naGkK\n====\n", NULL, 0); + + /* No digit. */ + check_header("begin test.txt\n#:&D*\n`\nend\n", NULL, 0); + + /* No space after the mode. */ + check_header("begin 644test.txt\n#:&D*\n`\nend\n", NULL, 0); + + /* No name. */ + check_header("begin 644 \n#:&D*\n`\nend\n", NULL, 0); +} diff --git a/libarchive/test/test_read_filter_uudecode_raw.uu b/libarchive/test/test_read_filter_uudecode_raw.uu index 8a5f8b5a607d..3f6444188e7b 100644 --- a/libarchive/test/test_read_filter_uudecode_raw.uu +++ b/libarchive/test/test_read_filter_uudecode_raw.uu @@ -12,3 +12,17 @@ M(%1(12!33T945T%212!/4B!42$4@55-%($]2($]42$52($1%04Q)3D=3($E. /(%1(12!33T945T%212X* ` end +begin 755 LICENSE2.txt +M5$A%(%-/1E1705)%($E3(%!23U9)1$5$(.*`G$%3($E3XH"=+"!7251(3U54 +M(%=!4E)!3E19($]&($%.62!+24Y$+"!%6%!215-3($]2($E-4$Q)140L($E. +M0TQ51$E.1R!"550@3D]4($Q)34E4140@5$\@5$A%(%=!4E)!3E1)15,@3T8@ +M34520TA!3E1!0DE,2519+"!&251.15-3($9/4B!!(%!!4E1)0U5,05(@4%52 +M4$]312!!3D0@3D].24Y&4DE.1T5-14Y4+B!)3B!.3R!%5D5.5"!32$%,3"!4 +M2$4@05542$]24R!/4B!#3U!94DE'2%0@2$],1$524R!"12!,24%"3$4@1D]2 +M($%.62!#3$%)32P@1$%-04=%4R!/4B!/5$A%4B!,24%"24Q)5%DL(%=(151( +M15(@24X@04X@04-424].($]&($-/3E1204-4+"!43U)4($]2($]42$525TE3 +M12P@05))4TE.1R!&4D]-+"!/550@3T8@3U(@24X@0T].3D5#5$E/3B!7251( +M(%1(12!33T945T%212!/4B!42$4@55-%($]2($]42$52($1%04Q)3D=3($E. +/(%1(12!33T945T%212X* +` +end diff --git a/libarchive/test/test_read_format_7zip.c b/libarchive/test/test_read_format_7zip.c index 66b5930e4319..eb9c94d64d4c 100644 --- a/libarchive/test/test_read_format_7zip.c +++ b/libarchive/test/test_read_format_7zip.c @@ -828,6 +828,49 @@ test_ppmd(void) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +static void +test_ppmd_small_block(void) +{ +#ifdef HAVE_LZMA_H + const char *refname = "test_read_format_7zip_ppmd_small_block.7z"; + const void *block; + struct archive_entry *ae; + struct archive *a; + int64_t offset; + size_t archive_size, size, total; + void *archive_data; + int i, r; + + extract_reference_file(refname); + archive_data = slurpfile(&archive_size, "%s", refname); + assert(archive_data != NULL); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + read_open_memory_seek(a, archive_data, archive_size, 1000)); + + for (i = 0; i < 4; i++) { + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(1024, archive_entry_size(ae)); + total = 0; + while ((r = archive_read_data_block(a, &block, &size, + &offset)) == ARCHIVE_OK) + total += size; + assertEqualIntA(a, ARCHIVE_EOF, r); + assertEqualInt(1024, total); + } + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + free(archive_data); +#else + skipping("lzma tests require liblzma"); +#endif +} + static void test_symname(void) { @@ -1143,11 +1186,280 @@ DEFINE_TEST(test_read_format_7zip_lzma2_arm) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +DEFINE_TEST(test_read_format_7zip_lzma1_arm) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_arm_filter("test_read_format_7zip_lzma1_arm.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +static void +test_armthumb_filter(const char *refname) +{ + struct archive *a; + struct archive_entry *ae; + char buff[98304]; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0xa4287015; + + assert((a = archive_read_new()) != NULL); + + extract_reference_file(refname); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("thumb_payload.bin", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff), archive_entry_size(ae)); + assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); + computed_crc = bitcrc32(computed_crc, buff, sizeof(buff)); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_deflate_armthumb) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + } else { + test_armthumb_filter( + "test_read_format_7zip_deflate_armthumb.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_lzma1_armthumb) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_armthumb_filter("test_read_format_7zip_lzma1_armthumb.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +static void +test_ia64_filter(const char *refname) +{ + struct archive *a; + struct archive_entry *ae; + char buff[98304]; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0xa0b3ced4; + + assert((a = archive_read_new()) != NULL); + + extract_reference_file(refname); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); + assertEqualString("ia64_payload.bin", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff), archive_entry_size(ae)); + assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); + computed_crc = bitcrc32(computed_crc, buff, sizeof(buff)); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_deflate_ia64) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + } else { + test_ia64_filter("test_read_format_7zip_deflate_ia64.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_lzma1_ia64) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_ia64_filter("test_read_format_7zip_lzma1_ia64.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_deflate_delta) +{ + const char *refname = "test_read_format_7zip_deflate_delta.7z"; + struct archive *a; + struct archive_entry *ae; + char buff[1024]; + ssize_t bytes; + int64_t total = 0; + uint32_t computed_crc = 0; + uint32_t expected_crc = 0x0a49c61b; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + extract_reference_file(refname); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("delta_payload.bin", archive_entry_pathname(ae)); + assertEqualInt(262144, archive_entry_size(ae)); + while ((bytes = archive_read_data(a, buff, sizeof(buff))) > 0) { + computed_crc = bitcrc32(computed_crc, buff, bytes); + total += bytes; + } + assertEqualInt(0, bytes); + assertEqualInt(262144, total); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_ppmd) { test_ppmd(); } +DEFINE_TEST(test_read_format_7zip_ppmd_small_block) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + /* + * Despite the contents of the small block archive being + * in PPMd, the directory itself is LZMA-compressed. + */ + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_ppmd_small_block(); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +static void +test_ppmd_filter(const char *refname, const char *pathname, size_t size, + uint32_t expected_crc) +{ + struct archive *a; + struct archive_entry *ae; + char buff[16384]; + uint32_t computed_crc; + + assert(size <= sizeof(buff)); + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(pathname, archive_entry_pathname(ae)); + assertEqualInt(size, archive_entry_size(ae)); + assertEqualInt(size, archive_read_data(a, buff, size)); + computed_crc = bitcrc32(0, buff, size); + assertEqualInt(computed_crc, expected_crc); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_ppmd_bcj) +{ + test_ppmd_filter("test_read_format_7zip_ppmd_bcj.7z", + "ppmd-bcj.bin", 2000, 0x2247d78f); +} + +DEFINE_TEST(test_read_format_7zip_ppmd_arm64) +{ + test_ppmd_filter("test_read_format_7zip_ppmd_arm64.7z", + "ppmd-arm64.bin", 13066, 0x7b8abc67); +} + static void test_arm64_filter(const char *refname) { @@ -1205,6 +1517,22 @@ DEFINE_TEST(test_read_format_7zip_lzma2_arm64) #endif } +DEFINE_TEST(test_read_format_7zip_lzma1_arm64) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_arm64_filter("test_read_format_7zip_lzma1_arm64.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_deflate_arm64) { struct archive *a; @@ -1221,6 +1549,111 @@ DEFINE_TEST(test_read_format_7zip_deflate_arm64) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +DEFINE_TEST(test_read_format_7zip_deflate_arm64_unaligned_chunk) +{ + struct archive *a; + struct archive_entry *ae; + char buff[127536]; + uint32_t computed_crc; + + /* + * This archive is large enough that the compressed stream does not + * fit in the reader's input window, which makes the decompressor + * return a chunk whose size is not a multiple of the filter's block + * size. The bytes at the end of such a chunk cannot be converted + * until the following chunk has been decompressed, so they must be + * carried over instead of dropped. + */ + const char *refname = "test_read_format_7zip_deflate_arm64_large.7z"; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("bcj-arm64.bin", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff), archive_entry_size(ae)); + assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); + computed_crc = bitcrc32(0, buff, sizeof(buff)); + assertEqualInt(computed_crc, 0xfef87379); + + assertEqualInt(1, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_deflate_arm_arm64) +{ + struct archive *a; + struct archive_entry *ae; + char buff_arm[7804]; + char buff_arm64[70368]; + uint32_t computed_crc; + const char *refname = "test_read_format_7zip_deflate_arm_arm64.7z"; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* First folder: ARM+Deflate. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("hw-gnueabihf", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff_arm), archive_entry_size(ae)); + assertEqualInt(sizeof(buff_arm), + archive_read_data(a, buff_arm, sizeof(buff_arm))); + computed_crc = bitcrc32(0, buff_arm, sizeof(buff_arm)); + assertEqualInt(computed_crc, 0x355ec4e1); + + /* Second folder: ARM64+Deflate. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("hw-arm64", archive_entry_pathname(ae)); + assertEqualInt(sizeof(buff_arm64), archive_entry_size(ae)); + assertEqualInt(sizeof(buff_arm64), + archive_read_data(a, buff_arm64, sizeof(buff_arm64))); + computed_crc = bitcrc32(0, buff_arm64, sizeof(buff_arm64)); + assertEqualInt(computed_crc, 0xde97d594); + + assertEqualInt(2, archive_file_count(a)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_win_attrib) { struct archive *a; @@ -1518,7 +1951,6 @@ DEFINE_TEST(test_read_format_7zip_extract_second) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } -#ifdef LZMA_FILTER_RISCV static void test_riscv_filter(const char *refname) { @@ -1556,7 +1988,6 @@ test_riscv_filter(const char *refname) assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } -#endif DEFINE_TEST(test_read_format_7zip_lzma2_riscv) { @@ -1578,6 +2009,36 @@ DEFINE_TEST(test_read_format_7zip_lzma2_riscv) #endif } +DEFINE_TEST(test_read_format_7zip_lzma1_riscv) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("7zip:lzma decoding is not supported on this platform"); + } else { + test_riscv_filter("test_read_format_7zip_lzma1_riscv.7z"); + } + archive_read_free(a); +} + +DEFINE_TEST(test_read_format_7zip_deflate_riscv) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_gzip(a)) { + skipping( + "7zip:deflate decoding is not supported on this platform"); + } else { + test_riscv_filter("test_read_format_7zip_deflate_riscv.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + static void test_sparc_filter(const char *refname) { @@ -1635,6 +2096,22 @@ DEFINE_TEST(test_read_format_7zip_lzma2_sparc) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } +DEFINE_TEST(test_read_format_7zip_lzma1_sparc) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_sparc_filter("test_read_format_7zip_lzma1_sparc.7z"); + } + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + DEFINE_TEST(test_read_format_7zip_zstd_sparc) { struct archive *a; @@ -1720,6 +2197,47 @@ DEFINE_TEST(test_read_format_7zip_lzma2_powerpc) } else { test_powerpc_filter("test_read_format_7zip_lzma2_powerpc.7z"); } - + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* + * A degenerate 7-Zip header can leave __archive_read_filter_ahead() with an + * empty client buffer and an empty copy buffer, that is with + * f->client_next == NULL and f->avail == 0. The unconditional + * "f->client_next -= f->avail" was then a zero offset applied to a null + * pointer; a UBSan build aborts on it. The observable behaviour is + * unchanged, so only a sanitizer build catches a regression here. + */ +DEFINE_TEST(test_read_format_7zip_null_client_next) +{ + const char *refname = "test_read_format_7zip_null_client_next.7z"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_7zip_lzma1_powerpc) +{ + struct archive *a; + + assert((a = archive_read_new()) != NULL); + + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping( + "7zip:lzma decoding is not supported on this platform"); + } else { + test_powerpc_filter("test_read_format_7zip_lzma1_powerpc.7z"); + } + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } diff --git a/libarchive/test/test_read_format_7zip_deflate_arm64_large.7z.uu b/libarchive/test/test_read_format_7zip_deflate_arm64_large.7z.uu new file mode 100644 index 000000000000..408159308cf2 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate_arm64_large.7z.uu @@ -0,0 +1,1580 @@ +This archive holds a synthetic 127536-byte payload, generated by the script +below: 62000 bytes of incompressible noise followed by 64 KiB of branch-heavy +filler. The noise makes the deflate stream (68837 bytes) larger than the +reader's 64 KiB input window, so the decompressor hands back a chunk whose +size is not a multiple of the ARM64 filter's 4-byte block; the branch +instructions after it make any mishandled carry-over show up as a CRC error. + + python3 gen.py bcj-arm64.bin # prints: 127536 0xfef87379 + + import struct, sys, zlib + noise_words = 15500 # 62000 bytes of incompressible data + words = 16384 # 64 KiB of branch-heavy filler + state = 0x2545f491 + out = bytearray() + for i in range(noise_words): + state = (state * 1103515245 + 12345) & 0xffffffff + out += struct.pack('> 2) & 0x03ffffff) # arm64 bl + elif i % 16 == 4: + w = 0xEB000000 | (((0x40000 - pc - 8) >> 2) & 0x00ffffff) # arm bl + elif i % 16 == 8: + w = 0x48000001 | ((0x40000 - pc) & 0x03fffffc) # ppc bl + elif i % 16 == 12: + w = 0x40000000 | (((0x40000 - pc) >> 2) & 0x3fffffff) # sparc call + else: + w = 0xd503201f # nop + out += struct.pack('-`L6Q3)I%GKSI?6`>^6\^'\18[8A<3.JMIBR'T+ +MLPQKZ,5WN`$DO`*VTMF6YW11JY18]]0]_\4@,G-`+H-X-A\`:CM;.5*8[GZ0 +M_A#?&=Z++";MVO78HBJ*-N_'^W0T/!AY'--Q#Y505C&27-=ER_/$#NPAK3&: +MC>(,F)YSB'8S,%/]PJD[5;PN%2XVSSBG*UQRG`AEZ9PD.E8*C^N2B'A(6*P[ +MX;;W>@:<-VC'&-*R5$:R\6V$B4%MYM`*R0_?@"-"M:F0J1G-E,\ +M.8N0*?)RB<(E;X["3Q.O2A$.O$YX=$7^GN":>8E#R^*QRZB:V>'!.;:\9I5U +MBZ?@>:=4N8&9_;"KC?*AM<)#X-GQP!;KTOE?A\<^ZW>GGSVPA^S>RH"U`M7\ +M2GV#FKL498O8_?RI,14@HQ8D!!"70<:3A!\P96W3+Z.B:Q9!,V"C[/"O4>-I +M.0.Q[I\3:H_,*44A@V4Z@])[@'15_]USP3$,X5I7_/3.J:(,WFXQ3 +MRCA(3+41`,R"=F4'-W=75![DKYEY38Q?B`)C_#T3]"Q@4.8S/TF@)2M.K5%7 +M;[[HZ'P7^8T%^![^6E"?NXO2[&QH+WZK@0^;"R +MD%?G`P@+2H"#==BY+1HM_@4JA%\A+#1&6&^7=>PFX@J$'+Y[=!M,F$)0%_': +M!GO6U@8`^-WBET3P4A,M-1V88HIQ.//W38JPS+`=*??$@J[JX4-/($9MW`/. +M\.7<#"6Z`^:O:W+N%6W1#/[L5M0P<"(M5R4=H:L^FDEO,-)KX["#HT:4('C9XVKPW +MTVFII.#SLPW.(%#"XZ6;L0 +M2;[@U.T?Q1$Q;)":HS666.#*2GHE.Y076EA'UBNQ8`EZEDE:BQOJBW"L"$DZ( +M-I92(=C"HT9$5J('EJS[-#.#0G[<#R9O`\L["[Y'OY<0I_.B^3(4K+@?:/>C'7246SGD/4;+#C*N(0R#EQ;:`)T:,#D)*P5^>SCKWR@AWBR)#.KU +M_RF]BM%<@_MS&5$8#$^X<:;G`59\OM#75('KQ-$TXZTX_];B![X0FE1=K]RT]%9YW3C-'U*\HBDC +M6Q?)(*C('-F%:8V>2XS[?X;ZE]G8P +M>'EWQC3(Y/+Z"4T3,W8"WBY>$U,EF5!9YJ=)ERH03MBP9V\-B")\NM^Z!5_\ +M<@,G6>V`MBHH +MN>3*Q/[P*7Q?,+V'K+K$^'43K.D*'["M>W,N*9C5&#SQ<3?SUB&YQ%?,IJQ$ +MLY%W+3Q`.&*%?7WSUN&XL+^8>BEND:^NE3*M3^^X&MPFPM_EPZE7NEX6M6LQ +M/VG(U+AT8;DW7H:L.QI'>9W/='7J-)V*54,2"Q;.XV*NF.!T\T*9,X7T7L;% +M<\!7ZM%PJ +M$7]>&TO1X[ADX;:1(O@H]A55F?=]+ZAD%*0XS59K18*;M'*3`M0FT!+`<^P(%OQ&W-HSP%$S50WU>ZO>^\=L*U49XB68244A; +MWK:HC/RW>0\6),4VMHT8.^-"FK*+4_H7]Y`/K3*)L,WGCAA4L:_H'2R\E/M+ +M1S+8.K`J$!+]<'GK0AFJ^[NISZ\ZE2_9RG]/G",\A>J\T,>Y(G` +M?"NB^$S>Z)A%$LS'KG2\)5W;FDG.F#N]?K4CVK=R1PA6",ET42>^H*D +M@JL@4(<(,U2VH5J6R<;4)YZ''.Y0M'=]6=W_')Y2=SPJ(_J%@R!;5"S9O&XN +MGK:&JW\5-'!,"?2#E4V%!JJ>HSJ;BGZS.&[^B1$N.KYV^_2J=S4QXN0U2-Y- +MFH(W`EDM4A.R^2=0S$2G28YKA$X#G+QO7,,S?%TR/07&U3!:!CE:BU#3C6C5 +M!5R!O?%K)BYQ\&=JI#,4X!:(O;![3;(&LEP#1H,5@.F+$;F;MS+^V[7S7S_J +MT:P=AN)U.BWO"KJ/B7MRGF6H9N5XQH]D_V3IP#.+YO%47 +MC7IJ!>W+6\J-)?@2:<[1TU2*-@Y.HS>Q5`2D9IBM#=P!8,+7Y/#3*3EM$$E' +MV`DL_6`.KBS'^)+BESSFOR;%?T-&&IT)4TL`$F4H*L/)05/A(>9PE/DG5GXV +MU,`T.WWB+D!RM::&PRUP:$`6AXUY*1<@\J`$S([ +MDL3Y00'=$/M9**#(E\SP[,-*/IPCZS"Z6+-UVY+ +MS!\/T@L.G#*CVZ5$D/YZL/54*X"]@(C<_IDAAN511EH>[`?TD6`T.>D:76/S +M]M+4U<2CR4_BH+1Z`EG8!\4>;!@V_UR+MLQZYML5H52@*BQ;^!O*PEBX=V90 +MD3EKQ/;@5Z3W[#DT9%?GX,W=>#J"%IE2DV&6.M"%=([)B4)WSAC&I^^#.TC\ +M+F2=A?G@W-KSKED+<&9RZ#Y#7P&I/)RFW$5XK+K48]=N+];9IS)?P","RW0M"CE)0N%O*WSZ5IZES;FLQEGNU1 +M.F>-;^O/A"5(4:CKX;L>Q0:]6KS'?]Z0]#F5U!TUZL&2HU5>8U$YUV`!QB\9 +M!OH;WF:F)[^@Y8*,TWF%U>J;'>H2KM+;B=W(>)RV0%%?/:ZV<](LM^B@Z20( +M]?2-GZ=D0A4*(E-9T<.0@HQ2B:?[@(Y#*/6O-X[TO#??]44S;HJ:"NFPRY^0 +MH:@31OW!OL,,9C:]G:=M!\E4PKP1_86,L?)25DA#/3.SP*]-3OF$(+$^K/)D +MGVK"X>P'/C*U=UM72DYIF[L1[9C8MOF),=H8&!8%]@>7#E'>A&B_^FWH;V^B +M7,7\,_V-B?"(#)UIGJ^)[J!`4(\YH<(<1,G1);@:;_K=&42KWP$*"(8T/J&Q +M8R_&WQ?1A\M\IK3ZCYK=QNA(4C*BXR.9T!0@#HR_V?.O1YXA#=5_I`G53.QS +M'I7T(EBJN:4MFPF>L3B!&9`118M)=L9]>W>D25SD>($F32%./`+4]\D3$:K\ +M4#]/;4F%Z/=.+A,&;ZN:#'P`\40%+:ND6N%W6XN/4)YHJ(M$@90V92:Y0E9G +MF3;H%..F3KWWM@NR09%5`V6)LH` +M$*[K=Y]/C7+FW&SN7^61UXNZ%%LU:Z_TNYFY +MG1BON1*!>T_CH-7>X-J,"IFAZ9%>G`PE/VBW;`S%E,M5OJEU:B`"G%M)Q`[X +M)4_OT>H46#;9&8\W(/2#I*E,<@UC+-_"4B)UTXC@F!"\?.<)(\D5#ME&\2_? +M(,`\B5E?Q>9701IX&QQ+/V;4]@#4E4$J55?F^VA1)X57X-3#_Q)]*8E.>`#2+ZAH2X?\@UQ;+FOA34+V4O*&]*,.Y$IPE@`E:2Q)9P5 +MEBK>WA<&EIL$RH6![6N<+"):RDVS[.H><(*70^G9X$-N]E<<#Z&=99Q5RJNE +M*^2=>@L19BL_'8&(+_6H(=T0\$9E%,,'HZY@-+QQ/ETJ_0'2CZ%%I7-9`(K@I%)#I/`-`S0^-2)R8!=U,Y#@XWOTN#^_-'8V85@-#;:SI-U +M"Z^1;^@1WQ$!@-]4IO[O?>#DN2[Y^ +M/"XGWU62C"RR\GGU=*?8BJ)M[_MP\!D8Q>YJ<6M'T%9=BSO7(4O7Q!J74JU- +M%JSB^/<1_+[`K +MC+9*M=61MK#J+>F]VPA"U7BO,F"0>MN5MCXDP+=73I$D2Y\+C2:0O4*0+4!3 +MN&:*D/47W8F>9>V.;HAQKX::>+S:+@E%F@`9FN6@JLO>DENHYFR9P965CV;! +MEXJGG&ZZ5,7]I/W,)(7RC!<#B&VCY.W;S/IIHE="##/TW\ +M:15AU.Y+$CJ/"`%U'&>F#R6?[#CZ.-O0JYX/1`C90."A".TAQNH3.(=Z)\VT +M?8YLW8TPD%+MTP4C./=L(,%O!MDJ+4F>C!\H?S-[DDS/7_N5F[S9JM3S5IN( +M&?8XE&"3$5R8"':1$IMW$WXFY+NF$DVHE?0"3XYU$W`V!U"R!2I)?*':3ED6 +M]&_Z#9U\HQL"!91\/UJ\`KZ+SBE$:'L]S8%K-_DF1"#F9\CD*Q3F(HR]/FY4 +MLGP\#0.$:[2`3U)QN0E%4?ZQ<2%?71@*K.-,#W6(([0*\#+&>W"/&IB.>CWQ +M-C%FU@(8,5>9FK]$_-5?+5&186)V:0?S<\6[L)A8?2G37B6NEFR(3URYY-R/ +M)E'E>&AMNF]O$!AS69?(S7I"8;ZLJ8;-E,!'AIY1=/Y2[YW?E7$2/XS\S +M#N"-V[69V/7N7@<"!C_WS[T,J)MH567"_&H[8\Y;R%;Z^#AA&=$!D74VI/&U +M-X^OSZ3L[*8-ZM+MPLTKV=/G8S80+UYY"1K1U0X$[2LO+ON;/"Q?F<5-:+\: +M4WER&(,6E2C0$`Y!`85#YH9)O_BX3)74QK8M?7!?PW(KEP'#:\9(0'PC37F7 +M=P&^C*CP'P&:ZVPUR$M,B52V.L\N)TW!U+@WI4#`);J%O7P]PRVZ<>%VCI1(T[GIF>)HK_MBP +MB((7ZB$T^%)&FXMS$"^X17OA_\J8O=V&\BYG;B=]&*19X5>]G:!S_?*HD1DW3&%S>L'AX(, +MQE63'Z^+T&OAD\EWM-C.;LSO[R$B9?QTN6.%QX-NVJG$2@ON&'KHY*9\`5<^ +MX*:)TP3G7V8>E&=Y]CUBN)\RFIZG@Q,OI@!I3W0YY8O]]D17DL%<)J +M]9L>(XH]"QS[;T4`&%C1G'$"BLA6J.<#UQ!Q1\3=CWNM5&N[XO--ZW/C2_,P +M$H_4J8Z562YLV_7/0_?B7"$+^V5L#W$Z';91ZTU.G$CWUE?A:8]O!M-8-\?= +MP5WT/P@,'<.W#9(9'QICC_1Q8&=C$AET$G+>/)%5O[X6(8R9APO5.,V'ZDAP +M8]N'`EQXPMJ@48TU!;8)@F:WQA?])(XU*HVM]%V&$!V64Q?8.I!H3P*)E0N= +MCIETUJ_50JB\?>JU10&/XIK`I(G+'?'>J+F_^<%L(P%F3'YEI\OQKE3(*A/] +M$SEW\L@2VD-[97#`%98?^?('P#Z"`%6?B-K2[,VF9K7%2[A*A'YMNP]I1MC< +M**,Q","2%ILHQ9?LCMJ$[KKN;?9;F*)2BRDSN\LN\&XZO&F,3K#N]F]"C]?\ +M\!R*[PPEAKIK^I/HV*M=>24(+'G,H5\R$<;U&X.'*>ZTM`!Y_]U4].-2J-%` +M(]?Y>R!T_S#98>:BGO>]5'_"B)A,LK=*E4)2^ZKOC6:;!_%P.*?3PQ%S86MV +M7+.Y=X+.,.3^M])-+UG0`LKP!!//GC=0)6@-27.6G$Z$I39O21W5?$:RI`7[ +M27%:E]DQBPU?;VA.&R802/27)L\)4&?WKNX4Z8IP4(NAE[*WLS,#HRD+@(*W +M1[G`Y>'^G*$W7VP9V*Q&4K)UKYA3"HOVAGMO$JF8(?,.\;##@0-;0F0\^0%YR +M@RP_AH13#(L.Q%4,UW]J5A`36T=%V/A+GWS1&,E2-F_5QS?^AM>D+WE[#7'U +M^\)(`";PD+%3_\&6P!P1%^3O%010;:_M>03G(E!<-+.J +M!`AP:'%/T,V[EVY,$YD//Y49G)M<\J7Y?U]ZP2NB*[WP_XC598TABYOJ1GLD +M10K:&#_PG*TF=S) +M;D?TSIFA?N]P_VK\%P9KA2[/]=J$08D++?R!Z+>:SP$N6:ZF%,,]YXZ^VI1J +M5V`]J2FJ,M5[AX,R6P$`G(JQ.9R,>WX2CK_?]E4QH$.GB\Z>L,O:A(2C`;X<#A!0;>Z9C'C-V;],(C1QV*6.R2 +MU#7+8RZ<9V`:-+<9JWBGWJ?8P+]-&5.,?#"XU=_?$NIC0W/;!A]->-6N,E&D +M2VRVU.O/MS7]'"31MX"---6U0H;8TU-V)<60VS+RB8SM_X[$[-.O)(=SO"`2 +M+$5H&5>:F_3]RURK&ZB,/D[!0VW19M=PWO@`D994RT.,_5K)]_(#(W=#FDCD +MP$B\I/FIU88^;5DRGY=0Z^PP?6.U[+U#2A_[OKL.L='8;X(:,9^M8!;F4Z&7 +MV]?+A+&:O6W]"ZVB3:`$,YHT_?!ATPQI`WB-[J%9&8^FE,8"'_=,J&V,VW&`#!BA]C03;2#3X/=&S2T4F.;1"-VV#$@)SMOV9C; +MQ)YBZ`I_43+73)5[/)7IXRRJ"G0,FX8D$CBZ*[W,4$6R\O9X`\+#IH"UR'>Y=\((_H==C5][ +MMERLJ<.===8)L0HF!O1[;O&[F+270_%D+D;6F.A'5W<>"$2"GR$M7S/O8FRM +MB?,QJ2NP?C1Y*<$31J[L^:E/^AK"W-7:A>5&?N^Z)7QL:^PV;,AS+=5A;%'R +MAN.V;T?DU4ET!(HQG6W7"A*RC>7C?8(UX/-\BYE&PO5>W9!(/Q75'0QN[0U5 +ML^=N:G$)&EO&CYCX7@E)T2^]7S8ZQ70W;7J+I'+Q'PWXDWG"PZ(`TZ7^AA`5 +M)>4)")4W#EK=DB_,@RX\043*[YB6AAPK+2M\9%Z(*.#"(>+Z)D:&/E`'L*S7-$637EU_ +M`M[2P,QHHT5:=J"`N219M.Q#'AB2(/^8P:;,!D81%3VKOBJ8P^X;QB"],OZ4;2$Y/?`6US(0)=Z# +M46,9`,]Q8#E3A]=^_0_7WX)_?BS;I(WUZ0`ZBG,J(OMM`^(8?AH2<3!#9E8^ +MQ"O7[A!ZQ&-%`2)VU?.$7\%P&Z892QSL5__1%*]<=477;DH\826/-'[A@S;`3 +M&>(:>=X2K/F_W+>QC%]%Z]6&[L'J?F*=VX67F'CHKD51NQT[MI]AK+>D_N`D +M%"8_C;LQ-4(!8*E3U4X9D$["W(F#"X:.[_`9KW-GRKS#I9M%SY_FFG:0M\N; +MP0&H7^G&P1IS<&9B;Z6G*4QA5,Y(0/VAU7;R/O\V0[D'4;/P5!@>:7K=V^Y,SVZ/=<:J]M@G@C2;4.A +M#?&EQ@M0A8>'SX>T!A(HW>+O<%(>,<$C%9-^(-HB\=G/#!^>S9[Z?^!W/DQX +MJP"5D''>JB6F^)L%M,DXS3UL$:$GW';R&+1W8,/%Y(2>CDT]5+("P!F/$XT# +MRE`+,0A)831^3MIG>6_G#_Q\C"-:!`Z(4QN2[;-?YRI=)?BN^'K`RA`77]=CP* +MP6&]>VTL0YA':`OQ^\:8UN/BNE=FBD%$1691+68^J&)G@1GS$&4AL'$DTBDX +M2#*NEY)"3\DU@=SX5BGE+0-PNH!T36NKKT/(QL@78<.]TX;N6;='DYOY=(<' +MGITT,LP2;=;+XQQT#>"FSQ69?8)_7D@J"C^DP0P,43AV55KT.6J,3I-;138K +M^'&?KM%&;0PV!<%L-]R)VZ2U5<0-?Z[6PCX0)-,$%AH0B`H?"?]0N0Z%)Q\O +M&S+%/!7X+\6"@:L:Y%JE2SM/'RA)SU]!AGRPYBV'7!:6>L[I!Z##L__)U6>+C6DWV1K*Y-]M\9E?<7 +M$MIDI@N=S8``@()]`U.3/$9]T,0.ZLE<(3'.[^_A[PZ.%?Q=X[&%_%FQVCH? +M`@NKUDGH70;N`=S"TJ8JQH7G[,)XE'#7L%`_@)8R2YI;@W!'W@`"!;$Y"KYQ +M?N@=MM^1!GM6B43KU]V*',0F`J*M +M:5)BXN07HG.`M;0PZUS*J?-8D"YMT^W/L-W-7(I=@F4A)&$Z+JW7ZXJN$TCP +M9JSA;KKD!O0O%,?J:'?TR![L'1@.2Y)*QT-C;']38(#95QD9^5;>?0OPOVOR +M+(Q"QM35+?D$ZIG-D=L$;"YX^]H)4=*KX;9JXZNW$QPY9E^`E8U""DQ"?+/& +M4S14)Y#!_?&)>F6?CAJ!6*_"XYR\9J4T138B`9I1>&;+VC.!J#+`D\'Q-$YF +M[7EJIU@C_U31.5_]Z%UD\GFGR4/8HJ;`K@S8^1?M1`,[/9M$K4Z +MED!*5=BBNPQ5`=B5NYO42$-Q[U;0O@!J)#+K0S6)I!\$<) +M(&GQ?@ON]]#RCT28/ASS4F$E.PS:^J38M*N:WA4()2HNH61J*\86?)R'-NI2 +MM(G`'=VI)XI2V9)F([0I4B"-MN;9!GHAGCCATW]O6;Y,6T?'E3?[?ZI`)-N; +MA)^'..`T1!&X),IVO=WO=\]G,.3'<^I-Q(N8`CO@Z1/L_PM0?I=_25C=?4X% +MF]EO-O/*?"_^S`4PFO!:*";5B\HF<6C'O/&!QY(4)G`.BF>$M9$4\DKGO5HC +MH+)HX1\#`(QL@!OOI+GE+]G^77FW7YG$2:QOZ@UU).!E"EP)DWMLPRXVXV26 +M\9(;;=8NZ;)751+)1`@92RUMQ4)B8B%S\^_\*K!DP*=^PC&$BCKAN.[ +M0:C@6D^*68#09%@\*Y2>?38PG?$/N;FPGV2`8.L/U[+VI\SSRXK4^0\H&5 +M&K_H$$MZ;X4H'+QH05VV6-[A8_ +M_[:$HLS,?>H5B[Y=*LZ_WQO$]].XZ2!&D<.)7_:B9+KWAL2&9.G67,T']NF" +M^*^IDYLP%-`WRP[)4V@RSAII%N]=/YK\`'11A6.9Q=H5@)P+ZLWIZ#!^&0&S +M$0FFM=GTYQMO.I1S>?<]?F58,H;;KX./!T``-433.<$PNG[3MPS?H"G^+*$W +M\O4WT^B*J7GY^VLQ=1BD$S1Q7NPJ5M30!]?,(`7$Z:HRK7"7-^+?G>)S7SLB +M,-Y.L*EJ9R(N&#Z0SW]BMURM^X!E"",+.HF7C>M),5)(0T3:X<5`#0;_Y(W' +MF=CT]$O^M1W?(JN2!4)K8PN[*6`SKK,94!.QWNCVD[_ZR+2,);.DU=3_2^JT +MA`#;@YS^>`XSKU'I]<^V-7%^MX)5%22:QK.-R5YJ0O?2VU.3-=^0-.5AB7'[ +MNXY%G3^O$?S:O`D1)T6=H!::+*RZRQD"BJ@%P^3!R++:9GB0W:>'%F!4U!89 +M_2]B,/*T&]]#]QG5P.$VYOG.IA@^+JR?G\1:].Q9B"2U8?R1IODXE;L+L878 +M*)=K,63>3!;''6R7J%JLA/K!O6T2!"RB/J?HF$"7E_`ZID)I:%R,[J)>58\3 +M!*$<%DP\)2+*$/K_%8JK6;CT"'@2(Z&[GVW&(;17A^4@GR866Y3=<-MO4I3` +MY"-3G)P@0/9_V3TC/)ZCKT9__M9&3#Y/P)7>@(&J6^YCFP/G.SCS5PD1S]T; +M=HBNZG<^*)OD"C7:34L_8@*V6@,M;HL)S!%HFDIK@9X_/B;[)SQGL^^P%/5B1[VA1BVRHXCA`Q^Z=H!. +M!`*YG,!B_DC9ZU^H-9*LTI_R=4M%G0KW_"1[:[9NF&V-%/$I+#/6>?O?5T2V +MCD3+MSXM=,@N8EV-1O/.<#BP5P@J*29E\J[M9_-/9S]0W#Z3G!TC<;]G<);:!+CR\;C6NOUX`QY69GKML]> +M'@$^/\)N&`P7$F)5J`$D:L*\G%M#EX?XET\%T70!!3:;W/TWNOP/I#OBS`V- +M5^_"-$_ZT\+8Q1!NV8H)[7R.#MM?62^Y8CT\6\]@Q5!^,AJ:PGE+N>OL*._4 +M#4$T>.7F/6`_^*BZE-35'2]]T])<VS?>2H^PKXC_S@\X,$7$P2[96D/2(\$(>S +M)HB8<#0US^FC06YN^!GA#WO4U)PGD5BEE8BWQP2NYYS6((5>P(>>`&$:G +MU-D'O::P-,X`_UW48X+2\2/,HR)H.:"9]EA968YU'EFK(/]%4*G,K[MN%3)" +M&2KIK_T;0VV=N/S9$Y':(`WV;;MV]_62*V0LCDC-CF?O@G,H_)/Z]@C0JC,R +MR4KK"LY%;C?OK(Q^_*-P+H7*U.C:\"Q@"RDA1^@#(NT!BAQ"ID#YU>=*-S.4 +M=@<]/<7&C#+!Z(J#KJ,N`&@O]SEXWR!^OMV*WZ_H`RP$(S7U7C:QBD23J_MJ +M4OD8-]*]\=\CKDS0+K"!`L2)9--N$<@T8&"J9OQTAD9_3.R60=IK-K +MDL"(;V.JTBI@YBY7&8=I]]Y3;E6_B3LYC`@,B]5[`@?JSX>9VP(I^7@AMV51 +M`/QUM@`+U+?QJF4DW?C)C5`O`$)ROIA3\O(PD*=X7(EHS4N.<$5_KV"P=+RL +MZ**0#AN7F@!4J+Y<>["FF2V+N@'S'[NY$6$GD, +MEY<3QH2]4>1M&:0VHCGLFC,6<*7P+>^U:=]USNY->$:/X@M='#FQQB4)A.#Z +M6I]*JQCN*@C+)E*A$I'4#@H#?XWPQTPAPQN5A0)3JG8$0YN"BM8X!J?K$>920793BU1WK03VY$WB +MC4W2;G\",=$Z$ZJ,,5!D:%))1N/*3ENEAV_4C8]\=??2!?X0DUK>?XB+2,WG +M:&T$I8%UJ*(FADTX9^)%_Q3X9CZ]Z.7DLM[[@0,^Q(6`@<5XN5.-0OXSQ1]? +MMT)1K#7!WW5RIE(*DCPC>VH%\Y@`XK7QP/A:UL09\E3LP0X&3FB/2_C#12C"&7]!"Q!*YLB(H2?. +M5SS4V%S+?1J1:'*-B/7#)6E^%KD8E'GA&9F^#DXO'UNND6QN^G\U',Z>REA8 +ML#N*Y_98!5:1L491D98W"T$7C[HP!!_2&>V<`@4B-SMZLP5/7'`GU#SI&OEZ +M;J-Y.P]*JK><2JPLI7R_U'J((#HK>,LQB'2!#B$^T$=&LAYU!VR'X#11_3!= +MF]$,TJR#&:/!)/B@3+@!69"1QA[$RR/_U+>@S))E[179P5PJ!.S)&\(^Q;@/ +MOQ:1\7/&]C@``BS]TQYZ]V1]N&1O,9#-%54`@NYL^I-9F4O0'4B$R4&J*LYP +M__3O^W6R_$;9>(4Q#(O:RR7]"VC04>C6\9@!8>/MILLDV>=Y&U.4>8&Q/0RD +MHS+\P9R#S1N:`)O&3#DORA5^J8_@W[Y#>"QG>ACUA96OBM_X(?MISSD8RKR" +MD)>%EE9J#1C7JJ!HQ&_`.:U^E6[BU0V)HJTS3P7;BOV:.X8VGY5`K5-J4'?V]WB/R*FA20S6!5EX;H@82` +MS9C0*\"6HC3]>C/U)%OP(.2I:5;+0>[X'7:/L:]B'%R","7P.;GZM72FJ]=_ +MJ`@>9^NA:3[HQC=(>H=#XGJT$E3$W?ZV8%(*@"LCD156(*9Y7=FK*7J>>?"X +M?QRF,4P$HPF5+(!DJI%F*)L!BD=-UW'/TPY)![-4U9&F`" +MK/O0$PD=]5#7T@U)/4#X3H9\-6\C165\&!;&!67&?EJY'M2+ARKC:$#JSH%, +MS;$F$7\N9Q&X;!3[5OR]+P$WLAD[L0-=JHF`M#(YN0J6Z/X>/0-?QNMVK)A. +M!769`_8*+<@]>VFPJYB38JKQ5X%4U@]$F?@M4I1$4;G3+8)"NV)3R1KSC.P> +ML#V<]"D4N0VV.3P6Y==R$?ZGER>ZCNNQ-VFNG*@/($N[_81_'T(8:7G9' +M3\J;=),U49U0=6;&9/&4XYC2:>!RTAN96=NP7O0'2#_@B_8,W9L'5?;^O6KX +M6J];06AM^+VO`-&BA;PV,2C[-YC?]*3!'A4-F_#%PBJ^S].`"[D05%B!"=N8 +M$`XQR%4O5P,$/*%6%\4>:T4:4%H#2S?X?RB5BNQ!XF-UYE.]Z2?]$'_4VX?Z +M?6'+ZG+(^A/#1`;E0.%P'GF8,=*^^2A"'VI^#FS1(J,U0RIWRO-V#3N)*=)8 +MF"%"L=W(D):"^$`7?IUW!.)#1^VCW-DB,C+SL^3Q/W`:'T/ID>SP;DYEK`\9 +M'-R<;3-XI6/S.WKCFZ4K-POMB,?7[B&5VTM&O73L!QN$:334Y=M=8KO[TF>O +M]Z-@O86@_R7&6/C_8[MXQH%[EA6`/9@J'W3T&T%L.[@BT'Z1"(/[ +M]@.-=/?3@QUDLL!CS9R^C()I?523N!?,T)`(-U,`3AF?*967*[GJ!N*E'SGA#U3_0Q&0V>5@^QO<@#. +M"00YYO`(?I3-O=_-.DLLRCW,]:SP4XIZJ@S[:*@F&%W3LG$C[*)6M;UKUYF* +MP\0R+1"MA4ZPXM#W3G/\]!$PMRR^J<_Z=RX9Q@;/[)AE7!9>D66]!SFS^OTU"0Q'31)SI(V>LYCZ)5M8$PT-1GUR0C> +M*0$QOZ?T]XS.`5O5R?N8Z@5R"ML`5BUX1T/&42X\J;:68LBWSZDB)&,AGHU> +M1%)":/G)4[`!8I"-H[&)5B6%CL8YR*_^['J\\MOV1=(#F9J]#W?+UI0\J'[3 +MT,%-E/AF&1PKIQ28=53=-KTB^?.3-C[OZCR?\>!, +M[()?A;76%A)*P2*NNPCM!-CA-XTQ*:NL%JA3^)=UV<^$0S4+;2=8O*(OVC@S +MU+6H\!.%3FG-7%96L$^4CX#OH1Q_OZDEU^L*^A"63:N6;5T(<=,>H<"G`,9" +MI$&'\FSJM)6RW=W%WDM2Q1%4(S`[%C]9:>H57$4JT1!0$7-OQ?NC<'>V&D!!^)#2)W$,*E)_+3WVZE$,>9Z`G2@.H.7,[4+Z:]/#Q$ +MO!S%A5N;&BL8A$MVB(LH:">&0;ESU^;>_<['VH@5-R`SD=PV-_ +M;$`4=:YY3X7=ON2/(!]YZMUL-+=Z-6J""-6"L9@K%T_.V6S?%Q +M%VV<2`2EHFK!.__):(^X-0U\D1].'/;.!Q;W0J9*9/4[ +M\\TCI`2"Y%FZDQ=R>M`#=73)+]S_SL;%!>^9',G\C.X5A?]N#-J!^X(+YD(O +MZ'P5.0$/I5VFX9\%Y]<]FM(X,G+8)(,+H*<``?E,.9U3:GY_E]+? +MW,UL+"UM@/731PZ*%:@;^V?=KQCP%7YQN@XI5@!ZW->(D"3$]84(K8R#2^++ +MK?9SV^H.,*K.UJE&^3XNQ&!3S[N-95PYK#UEI/9T.O4XONM%U+U(CW$_X2&R +MM@8K,67'5:_=]%>TKAW[35"2\22)8X=!CV#_N,\9+-2SWI0%I+T%%%=A:V82#'MSY3;R2F%[R-Y8CN0N-(GE,/4R&0 +M`#MLB4VK#H[QA3&O377'O)7W+D4YB*H.$,[,B?O`&TM-I1"I\ +M[DX-48]/RPHJQP[&F_^<;#@_ +MG"$1*QH(=K1I#'?Z00?D%G(@36?E*P*BM`@3Q]&-4+VK&$DKKI!.W,ZN;\&' +MIWQ>EWP%,R6[6F]`OXL%^0EHYCFS@?I*D"8G!LEG;_!D%`'[C+V]JVJRCQU\ +M`YL*,(`:$5>Y>%M'_O30F%_D$;:L7JVY=>>QQ@ICPX=[9QIZF+GG3/&%QGS6 +MI;QF5P!>TD37:I`MD*S-8DDU%O-*V%2P(^"Q*0)O4:Z9HI-/HZ)QW,H;3N67 +MS'NZHH*L:R405\BX"!EAS;,#AC`HUD>MB?MTF508G=Y^1A+/1K;CUBGIX-C; +MW)G'[Z)>RCZH/_X8)PRCU>951.R':BXI2UL_J5SXX[\@T=#YLC;'H^0W=C(* +MI$<+'0VI>=K"(%TDTSZNQ'9!H&(L)+ +MM718*#OP>T&0/^#F:4KL^&C7E=3AH=%][[,2085*[[/ +M@GH?B/+O;)>W-C61UL7**9A@.X?!&5B^/(&Q"^P8EACW@Q=E_A]ANI."1#[?3J9RSA36E,4\->IEV%BNUGF&(;0CV +M(4,FDD;31"\'>=%&--IZ7%WP`L?2W6HFHYZ"S:!E!6)9-0.;'@5UUO^!EHK, +M.^LG%M&/`DZ?F1C6Q)PD)>9-I3;H`ZD'-^<8E$;NNN3?\EN0DRMRB&5(>%XJN)4WIM^L``X4IM8$4)_W'9_=A)W +M:8Z"Y%G/PTWN!/<"'4.*$R;V0E`P&LA)(K_;3@=*VF\0$_1\`?J?!9K.ZUI* +MP[Z+1&H5:+FCS8'1HS\FLEO-9YZVSQ0$K[^]!#LLLLK`=P.ZA+*`38(4N2\8 +MX/[?[*I?\XZOK,%^J'4.`]0*_C(7>V;9;YA,[%KQ'(.+UO`*[5?OC=Y$FJ4U +M+9<;X6)$G9KS*9B%L!:$8REY1]JN1`_63W(!&-SMBR'E?C6_NOV.`&OD#$9V +M&MB6821$\H8['ZY'7!.W=!Q&/YVE/<,2BB/>XW4?Q>"+XE29_E,Z7C6L,#^- +M258,AA1D$/K +M%M.=R1,0K2!_"<`$``ZR?D0O1`8)/(K+:\53,!`:X7=M2_2\UB@.Y?U!9\?_ +MYO2B!B>*Y$G4Y)#9?39BF')Y&;##H06>0'J!GWF]X"J^N@$`'Y>6-&SZ(PS1=)Y)=MV"#TICZUJ,]KV>@&'>966SV3!YPQ3__$&ZDS!Y%_8%S3'C=H+:=FF3U;H@T.E17\D=_C'. +M'+S([SSPM#T79?('8G+)9)GWH1C +MQ2R#8&7&2QF:G'[>:OR+;W +MOSWX*_J4)"S(3XWSA3U"V4LN4\V)G9#F;?&).VLKCD?"_:_K6:.\VW*K10=# +MKII.'\/+DV.#J/=O(L'2$?MFNA-7IZ$5)U3FH*G]V:>X\A:1FD.Q\#7`$U!J +M^1B=L#ZP%9J?'N-$1KL"EA!;#91*DKB9NP5EG]B:9(\Q[A-0%HGUU9="5(:$ +MC/2U;3P(+J(@.84S<9"`\.QO0&DR>;ONI"QFC^U6&1SH_E$EC.DU^B'"YJO3 +M7B@(:B`VH<5+]N9C`)"'_[1_M!Y6;]T:/AA2]HXB(PU8FR!RD`39AP99GB4" +M$']8E*Y,D%HYE2<&!M-=:!% +M`IB=SQ.%]GU0HS1^21D,9DXR43IO7SI(?*3(B`4!=!-:)9*OBX,W!FB,.9B! +MJ+A9)CV]BV?-F!D4!T]YO4M&2+(%,,(#V=KI@("?V[GF$/_^RI0L7P*HSZPD +MO(]U-5"/"IGN@GME]%F8WQQ\\;/[*]8[9!N9B/]&X\P@T +M6K`)U.$I\%OPKN\'@T]!_,/<$&C@Y66:)[I8Y]MKHV7XR%[32F%[D%F&1B(V +M1PNY#W2?(].=;'B($D7,JN,4\?/@/I46KB4+%SI!6`3N0CCMOQQT(AX&T;-@%0H,N4K%FC)>\>VZ$Z_Y_A7LP!"Z,5'`3V*HN,#1L]NN.X;LS2D607 +M9?8OP,3WC[7B9+XUZ^&*##\=3CF +MA33E=MH2=MD+HZ!_Z/64+@&4R5&F@K+WYV0T#92(MT<];SJK,B/T.X-H6-0` +MFLYQ.<+C@WY`/3[?"2\)+%:#?_5(-2&*YF@"^V2D51BIY6)Q?][U5N'VO-=5 +M2LS$/AB=T+$*EN*\EQAS>/0L,(.\_*FK7+,NQ7A(SR@4UURB'M=E6:O<.@90 +M\^N"U(5(B"&HX29]?09,*,/'8O:Q].#JQ!U01%F2(NTE8V1L-6`83XT9T5I^ +MWM6VBK_CMHR,6OU'U66^;.IQUB?;_/_1MJ-KE5&*C.RVPJ$5MXOW321O@B6- +M>C[00E3_25,L;SJ060D":*6?TGDWTU63?RWAP(3Q(@3Y-(N4!;[ +M:DD0E9].7>1^;Z[]DWQ'`V<%:!6B6@"M08O"8,QH7_M"@7^)3B;(*K1G_)8R +M%`K;Z;V2S2ZR0&L+`_@,QH"S:-RYG444_K7(S5\170:LAV6?=5R9:`HT]GI[ +M9&LHF')YX/%*,,[6ALNH5\U!@40@W_TMI6U,8CK1(//GJ\*P_,]<*6>L`ZZ: +MC$I/$)-EW#.PNN5,^R2ZLXON:V(:7LBQ^F1ATIBIAE$Q'D>Z>O5T(NT#G3,O +M!A(`0"RMB/ZM^D@Q,UFE5.;(X:G\87EN\//WX'*(= +MT16J8C8HM\4WPP#-I!!8>-!.:97"D<7YTUN4B1"35V`)KGB"#@A?-B_BON,\ +MT(H`Y2'UT!J7!\Q+2LL`;Z0HTT?M;(5;,!`I34&NU;*^H,K.X37_UAWK]FQT"+,EOE. +MX! +M>JIX$"ORK9N(9AN!(4@8)T;TUC4'AA3P,>4B[G%/;;_0#)>E0:#A_C<`,UMX3EY-OY^*S<1 +MWQ@RQ2RY8A7U;WR3BH&6@OMC2;`8/-AZ<1;QPE8LXX'71,"EQ`$A_:VH+_?B +MMWTG.UA^%] +MXVL&5SV]QQ%&&/1C*O8=%SG]DMW'MY``IN_S6/BU(.'"Y%?R5:*0KZU[`*JU%V]`@0A;BA<[J)QGDDPX==QJBT)%_J +MW:CIWE)LWM(C2R$G(-@CSMGUW/F>^]+T?W:S0$Q6OA^5%AZ-JC/OC)O[$@,X +MBZ`,$8<6,';@Y!YWMAMUY")OI4V#2RT"CK9E$T.+15");;Y)!UKX3H@#6&_] +M7,=\ZJEJ!<^R!UK;$R6+`>97:#+I_8%6%HXF4Z3V9RNQ"A0-4T&]V=!/LGMR +M`P,7&S>`YMU&N52VC_Z@B#Y?(*Y#K.IZ!W6#WL\*STFO>V,^RY@%`KCQX2#B +MUM$]/E>\Q?=$X]V`+:Q0A&(UG8+SQO^NL.]W!"G>.(2N19W<3]_%[-Q69.#E +M,U@GN@Y\Z&LA*V?(!$X582E=4H9<3!9':5A8=*6B`9WZ8:P2NX'RXU(H"N"D +M_EJ9H^BY7G8\:S\Z@W\,+UE25>"6JVJ:51Y;.WM7^"_PSM$LLEPV\RI\-S(H +MQJ13]&L-Q5N]P@PK2].Z0S\0!O6D":6,I`XS(=@O,84N/',,7<6(T<4:6B?0M5,'(J9!W#_L#.0!.>8WGBJ[>^ +M>\9U'\0JDFPC\=(U+?\:RI5JJCN#02U8"H.UL6<"CI9$A%87&#M%!'1*].W- +M)'(B%+A"LQ[/LG#,_&[I6XW6;E!GJP_SDIB`O&SL)[KB4TKN1DDG@]L4A%_=M]K5D1'A3S<9C>()+G'B3 +M\AEBT$*GGVLA#S`LAJU+HFW"P`4+C[*:8 +MI6CGPF@YE(ZGPCW]0,DRF0+8@Z9T\@``N=(Y,,46?A:]B]\GT7\L'*Z;]9:_ +M2Z8N$)?[8DI7&,_V'7&MOSE6=]O3US-2-<3$%6^MK]#AXK(OB',VG$$P:0S8 +MJ9EU4"X;FMC/QGGA7.B&;F4G93TZO#`3ZP#V4T@NR!3AU`7K!F)>G\?`L?/T +MYE6,'=ZI@9*8;AYCHG^Z8'YD!QD_B^+>J]D(OP'`8HP@`V75LX>NZJ=0U-OZ +M?$)XN0<$4;B<_+98B6ZW:4:\)/6ZV(V((_Q"2LJ84^K-X9`_1#&)(,U^CL@V +M<*_80J2\Q-5413Q>)IK?VB[+4&ZNJ'"8Z<%7*[)F6W?QIRZ/Q%3O5\W]KE2H +M\L<-#D,.]D7`K.[,^3W"5CYQ+$>?2V$L[-1Q9K7`W^=*8_KGNP(9I=A3'8(Q +MLQ@'=GP#E9GF:-O7\%K49,.9).E;U_GJI.9=Z;>I:0.)Y/F!&>:[-VJR'! +M=R78$.1E?#A-"ENF`@EU%A.B'[)0_(NH2?Y:X$ZSKG5O3%C2?(V\PP4V3+1: +MML8)BT#'F&@%`_F!+5^()MXI`V=:YY$4$+>OO2!0&[*V15H#-@4M@!G_2KD+ +M8^'^B]0N7R^;=ZQ-_/=UJA\U"FKISWMB;3*8F+8R\7C-U]882!Y1JQGKIIN'EKX)<# +MR%?-BV&`W<.&9W/.1QA2*'0H1/R=P1#K$G:.S>/QC='@5[4QF=H\0E[AV:D_ +MR2/A#!)(:56'=\IJM=[@6[H5U/A":BG10W;&-KZJ#3>A:ZNDEGQOT%[*E,*' +M7!S3&<^&$'D^#`F8@?'']T_TSDL]SL`/\7DZOA6DW^-JCG/,+8#J(Z&Z@W0$9BNZF(S$\Y_$L4I21@6X] +M1/ZS,M0[8(/%S`0`,[!U.>>//7X!SUW?-@PI+']E0O6]_KF*M]7O^V&G.AAB +M07QQ1$K*5L+?8M*MK>IS%;JU,%RVW*D0W`8NQGS$SY46 +MHUP+7;]E#CSY.A<3,NN_$-1(@5V`X2OD:@9MBQG';SDT]&EMMQVEEE:24^'5 +M8T%3;6`Q\9\9=OTFWA8]A+^0KOB,`RB%U5IF+>K"_]G;>47J>,P7Q5'//LBV +M(X]/M]B7420X.1:-#U!U0L7A*U-)1\R0LN-[B1>[RX[SRKRO)W.FO&=Y%46C +MM!F:N@;(RX_4I*A#4[3!+@$B9N8%`:==FE14\FSD_?4P-?(">@]#+05]P-_0 +MT_GTFZ(^7*%RGUK##NPWQ$2UYQT]2OY23;L!#:W8YK)V,4J2;Q:URN:7_G^L +MA)C;L6U8$!VB##5L,^V[(_"XN^%I#N=([E#[/H\IOFD<=#MJ)2A90OJ-S]NK +MS_T="+:9(Z$A&=#&CW@F^,Y'EK0J&"E02H674N)=IB.)6DX@/F?FV6.CNY[1 +MT_]_E$*U3!S2CY5DW6^J:2>VF_EU=#BQ*O$1M7SZ=G9J`G>4L`SDJ'4_39'F +M8@*$_TH3`9!D4&]664GUE\=.WN6';YOOI'PP.Z(%G>$76I'%GXM_!']HV$AD +M@01DK29INXEGB3FX%!,'9;UG2P&R\>2_F&C+EX!,S!BYPDMY_G:L3E\^))*L +ML.F@==%<"`H%U8Q[8?A-F"N7@/$/-A_69T9(5YHAWT1IG[0MNHHH8BN9\_.$ +M.].PU/AAUQENAG*F +M]D?'9U5TJ]$CG8@[,A(Q9PWCD,^KX`H8&)D1S=Y>3`/./UA@IPSUHMY5+E0% +M:M"S55LY#&/X51!=T5KV#S:)-BHW$,MLI-GPL@W3M(O"`EH=TW@V4!#L,\8) +MDVC:#HE)W2_/Y2X\N5,JQ59^"!HHF3U++XJ"*"U!3D&:6+]>5!?Z[Y1L#@?XNY2;.F5VS5[>R'*RQNA.X$I +M^5@PKJJQE?5ZEMH2YA?VI-H$^@&([=LE)^.QNIG+? +M#Y&:N9R%G!^EFP(9>KLFN"LO^2^(7[J_(4VFUD85U1@'DUFY-.Q9M%V:>132 +M/[W%HUARB:"7PTU9?SJ:%IN+E?_;QT[,C5I6%;B*TRKW9`L;.9'` +M:XKV6[.%]TNG*63*:BW-U'K-@D&QC9.P^@+0*#R+R?#L_<[SM9/OPA4T_`&# +M0H702L':?D$D"Y\EV^A!_)(!\.Q2IJ[(8><@#0*4E$?Y/8LW<3(/04^#Y``$ +M`&93^CF>EN)^[&PWWT7CL"SBB#GUY#E.BE+G//M@8$H8];?%<=N0Y%8-\-[7 +M$^N&"?EL&>,3;QQ[=R?3L<*<=;/_KD@X@2F/@`JM@Y"E@&:VK +MM]Z!++V_'SGKC.:XF]4!08#JW?KIV_AI''C?4W=1YIRKMNZ@D[='!;LD>Z,K +MC9;XQ4)`Q493J)RPD"4O,8D.Y>N.'NM!KW8_9+P*B3]%"@?8YJE^ELO.EG2H +M%CH3P063$&9QH$ZGC,'W5/5MIOT\B7#R/;(C0TSPW,`27TCYJ[$(/D>B$9]I +MP&(. +MQIHT3H=JLDZTK=:)W?V,)E*=3]68/$&4(/$*Z-F:X,">/*9K?R-T?$S_?1.5 +M"S=QJH0UQ)MXL9XXQ#%'$3@FI/F_J(B]\TR4V!01SHN:`$)>+OIWZ:*NZ;X';)&TF]%@Z9[BG +M;1060Y&]KL)QLBQ0Y`-T;6>`?T7@N7EPQ_YA$$Y?34F#K!-#,G7XE;FFM`R6 +M>V#?#9B^H]'QIEHHUK+<'%>)^2]$+&+%+<'A=&(FR6+S8R/KL,AWYRE#1O6N +M1AA;;(_4^+82X2P89]Y3Y'=IG/="Y+J)U/ +MXO$2[`MBXR_MB."])CZ92)G_7K>XAS_G.,(,V&GB5=4LS&KKU"Q;N%[T^&CB +MF=%Q,JDV5,Z!-W]&^J0<468-6AL2PGTC_M/7>8L07]4""8HPS@ZTKZ`O'H#$ +M/%P9^\6]3C8:`[?H2V[ZV"@`/@E!<6(:YC:R4R>D?\O4]H9F?>"C@G+;VNO# +M6SC'0*S&>GD'DTB^/'#N`IW=8PP4DL2P)AI8E +M;%\7Y8.\!+V_>NWB4N0B!;THL[M7BG"E_4'IP`?&;E%*"P]@B%R"1UQ;)]B:.'_>ZJ5=D +M#49 +M;#OH%(0``<<+BJ8Y;(GG3PDYE)?YDCW2['`R2A)5@P,1X`"9HI`Y5=EU?M>6 +MR-]45@(KM1"[[7W5V&(A:*G%RD_A66`SXUP"PB,0-?!"MQ)LZ +MXJ,-=7/3B2$P0@X$J?Y<!GDE03>[*=COZY?*HS) +MM=C5J!<7ZOA!M-MWZLAX\KM*4?VV%K:YONJWMH[H)+[Y2(T='5Y"NW294P?. +M?I"8)H&)!4M/CDF7KZ_%I\V\K00#17%5T9IP0DK+#;4-J.E,-L'%M3[/VL!K4UCMU* +M-.@HN_\(9M@,8G4Q>+FA%DM]Q9?R$'E=C;69\]:(">U(SJWG$\)YI%M`1 +M!I?NIJH"C\>9JQRZX9(E]GA>^D,>VJM-]20(7%[WH<]G2<:E_%F'&3G(M#"! +MJ]W$$,)26`T=(\<#D2"D6LW9T5D>GJ<$L7^R08Q,XI4)E;*,$JJ?C[B;]TC_ +M.-=DRA'CTI-V#"`B=W*UY.0N+&E-GW%F`GIX_Q._!%M05>_.2>/%4TXT^$EO +M.?)@?'9\KP5K`,1:1Z>?B_V2^VA^6$N!LJ$W)G\"Q6?G,:(4&6MDO?6UW+)G +MAW<#D^N+@+)JT;DPT3O^3`#=7UP*.ZQV"-QU'\NX"CN0FWM?(F*84=Q5\3T[ +M8];]?F97>.W^1.\0$"W(M%)B(<6+\T+G1K"[SR$INL**KO%7SD\;J<'^/.YEC@ +M<.'3F7^A%%XB^H8_=JTA#+N/A[X`_1B"H"-A]RQ#?N +MW4.D7YVYT/;]E\+XN&[3-IDH$-(B\@F!-#T.WZ$D+VVV_3S_2OW%)!M7&MX@ +MT4NMQG`HTV:@04@.&>;!7F@GTU"MU/GIHGTGMK5R%D4FPWIMID#?UJUYO@)H +MOB?**1\`(UYLK^JU-^H/UBI%GM+/$>74B,__EB +M:LQ3BHX5!C9R*BT9(!LW]^ZXX.Z+D>Y]X_;Q=)```BS]T_*! +MT((W]M&3;DM;T`Z!L\G>5@7.2506[V"TD_Q',(&%GF5@VC1O80L=$)GHYS=6 +M`9[F`Z;$&V/G?B'GE)J7:ST9'B,RA:\A@R+]B`#,G6@Y#%AG?L),P=]C91PL +MJ!/9]3*DJ(J([G/[7N:N&!LIVG$)4G96HS1>U^^Q4,30(*JMRRRKXI[O_'.R +M._DP-;R&J75W"2[';%?/`I497'1G!F7#J)HZ*((*Z_R(^$AZ)=CA,.=.!HY: +M^,=\>+0F"3R6'?I$&)*$`2EC'O:&8$J?%QD;O'W>5Z\GOSTBIHRL'FS53^IA +MZA/5Z-OVQM]X!5!O412->;:$Z`2W)33*)`$\GHVDO:U"-O#34V;;)I`+RIN) +M_.QECG3/M:\4K-*\4.R/1=B?=9I+4I/+3"]@J+R+3<&SZBEFA_ECI^IC-U3[ +M,^O]RJVR\K.&1$.*6M?`>'\Z^1F1XSX=2&KXG9&5[&!#2K5]\Q#V=+%I9HLI?+1G^DX?:P;6WLX:+]3XHSBJ*0\)'"\6ES[P?N451" +MCY;QNAS=5O8EW0)Z^IXW0JL,>Y,(KX*`H28I\<:PT/F'R-ORM+,7OMV+$-I2 +M$YPZJZ-4.;=CR&..K#JA'Z +M%\9VUXQ@=^'AH.1QZ>M-)G&-`O5FWQ,>"7]0R+WS2=JVV$Y?TUEOB%TJ?!D_ +M/@72B>Q:(HIIBSSD<6A1(B>!B=I\)@JXV6<6V$44''\.O3PELK*BBBD#LD7U +M@.4['+GG;4;^-WRK7VMGJ:S9.P3J8Y$`]\=37/]9(+=57 +M9_T[1+*KQ"W/`S)B'(T>\R&'UK"NTW@I,7N]KIPDI4_JY]3-MO`O@(T@)F;;E +MC5Z-QWL_!;ZU#)X[554CTKUJ(?O!6[87WOB."N_1G]Z*-NHAHC==D3FDHM7< +M#6A@EV4T=;L`7>^^1])P*'BC-000H+`T^5DD`&4>R/C!_X@:R6;SG1URN^O[:+\ZJB,A05M%MH/^,T+7`_DZ+H7Q`%U?3"F3]>>YZU5 +M_)2=(;,]8,OW,L`898-!Q>X`_T2R.<,2)WZMCM'?SW[$D[%%K=(YU>*9G49SDU,0:9M[+'*W#I]'4#]1W! +M(8^2/Z3S8[TY!6#]VVX94AZ3WL)"N;_,@$Z,C_.%U?:XT.HNM#?;=?]0>!@0 +M%5$K'T2V3QZ2MY3U3R1$:EN-*]HD0K$WIE/%Q)B0?AFQB?/*GXZ?DP2O8TQC +MO/,_%D4_YC2:)JXARXL%7*B/]HC!BK`T9A*XBZ<9W[-4_OC-_1%ZF?+N(K%# +MJ=E1P*L1&/G06C@^".V#GY9C@NS#1;JU@^X%2FJM!;O]=$#8,L'0,:;0ZA;A +M7XB7NFOTA*2"C&UTV&.B^/`O,VFG1/"$QXUIZA3J[OR)`(]EY5,<`#AY)<2( +MWOKYG(6KRUR)"`+3LZ%]IG7&N[#=AW>:OK0VFO'=4HS>4L[LMB,%'6\@"OX" +MV3\`HIY]98A_T+!%3*@)#94`+/6JU2<4F_6+(SC]3A@1$1E\=J(%_G=0*GWD +MM)*B3:WLMP)P(0,3?>FH4#LXGTG1XQQ.BCH>;]=D>WR\;1(%.0^,6OVXI(M[ +MD4UH)!@S@6#/K":5>2AG19I(%!]_O[V#$&*RW5FJ`]%[DX`8N?"YGD97_B*$ +M:5]Z8+ZL/-UM[7;R'F'?1M_%K,"[6D^<85U8IUT1U,A,MULZ" +M8A#UFQTMS]#G:2^8Q(=PKSC#&Z1X-9:#IGM9=M>^"`6/Y1J;@R! +M1B15RI[(:CP`X%LU?A;XH6!GT;9.LS:UW_T_1K +M2!"XP:@);_!,#C4J+B\+]QH\1?)5Q?*G,1J4V!E+*W,D*'D\(T'VF4KFU]NC +M)S%'(M3_2R6K;[]$\D?'LRR/X$!H$V[??<+R+V[:*S6)S@<'X*4^DE +MGLAN4AD6#\WYSIP1<:6E-RH?>B>,$"LK$$*(JTM<(:FKC49!)=`'3^0[-/BS +M]EVV>`S2*^2_H]3L;Z!CLJE96P])'C,U"?\7;DC,&6JP%531,"IC_>T;-9;E +MN`8%[.?9/:?T7C+[ +M3<^#8&D!`#*8G3EZ"25^F%RIWX%70BQN3OKU@/[:BKXEO/M1L^@2D=\N'K>OK.2"#<+N'>/T4&I"!UQ]J#,_3XMM@=B'J&DOH2.V-<63R/YAV!G +M/O,=T9^ET0GL)K2&M:H814H%@NR[_#0RV,4R>3$]]N\6+&/WEZFL.X1G^H5M +M>T#KHO-=\S-(B-#P=W@::6%VK>ZG2^V/-'5F'".%2R6K"OSZ5$Y4JXJ:]@A5 +M3\&AU-]&QL:Z&=Z?U]2 +MSTR+97J5IW46JO!EVYMT-\]RG=UP3.9@*CO7Q`D6>UMZMX5=%<CEA'*#NHM>RFM:L$3(264488I+9(:I66!',GA[ +M=#II&YUK5642V)9$XZO[V>")&1.9)")M7F,&!C\CLSL,9+U!57%G'VI742!; +MM$`1^+3BJ-'->NLV@*7N-SM,Z:0H"E,-=HV&PFE!_]-3'ZL0*Q/0"6:HS0Y@ +MP!,O6@'?/.AG#,59:,L:;R;:2VI3("A,Z6Y!S7E=YF*L*OAX;)74`IOI??S4 +MM7+'2U;#US0)0'@/:7GCN;.^Z`K2'RV#`FS8D\XU/FS0RM((2SM\-_58#QQA +ML8BO&Y912>\7H1=I!,GN8.W^`H0B\4!WLS=KLG!Q$2KIG)UF;OT@/`^<5YB< +M-'01I1XJ(WJ"XV8KZOL$B/[=Z"$`0R]&3!<$!_Y,FS1[F-5=?:[ETN9KB*-S +ML_1'_IEW8[/S[T!7[&-0J?F%R&[1OIK@94@Z1,QNY]E)Q +MS?=VRZ!D&>LNS74;_A-8;Y!J^5[\>)LX+T>U9Q'4]/;I*U3:]C^U328&-9!!G`EE+>F`T% +MO^H1Y8Q5X0S51$K:ZF16Y-MSA`)X/A2D45EWT+;EK<6W>\.2O/10UGQ)K<2<[+"<8/J#50+,$X +M<'9F*%F%IWV@&?M-L;[(F. +MW[71/C9*H*)CN_M0O-A8T`@QU-?*%G=RKY>8"46$*EZM;8(DZ*+NEH0S)T4D +M\&K5QVG8$\+N4IFXCP.AXAQ&/ITEDHA"^J]+7JM)-,L(J/?8H2O5U,;1E#&' +MU6OYM#QC>]W@]VQ21/SS(T.F:"!P40?9K9;&GE/V!7_NCV%,;BT:$&B[EZH+ +M\$B;\SYA."/I6A$_3S-V.!L6=RX/5N0ZJ6M-NU?7`F;ZU1,[/LY0(3%*2;_Q +MHD[@K(1O=4=T?`(/2T4'#K!:L_HNB_G_]&C*AYN!#NV,)JL@-&>C!"J>63'M;QCZ8 +MG78F\9D5`-8I@-]7--7G1/L#/BWDV#AB#:TQ\[Z.'+"'YPDIE@QEKIW2:$]7 +M3/#<;C4IY6NO;;KFKKYK&:$HR)P8-F'A[Y*&M-281^$UAW2]?D"=,F@/$I,W +M4>-*9=7@/(1'F5L:LU[.=_L_LI<-#$>@W548+#)JJ4X.D:P8&#?U?:L+D!H_3LJXO$)X0:@E=G(D.B^)Y+ZFG!CR+2;3% +MP"08YF/`ETNICQTH'\)60:055^;MB#X%E6*)/]K$?;PN6G-XM%Y"" +MC02,2%'M!:4%(NQ?<;,6%L1P9,0YZ1/9C6ZHM!@/:U'#AG'CE*4%)@AZW890 +M*ZE#IW9KG)MT7,T_FC-741@0]*AOX&C$E( +M"5*,_S7I:,S?^3L5HER/*ID1]1LSI=6X+,NND4IRHO8=B'KWY5E'9%QB61>T`F$(9.>BJFWYNG&_?G[FB +M+#0Y'?7.2&6F#HV5^UI"S!AG&TEQ9:1.5L]-N=>KT=(#.**;XYS +M+IMN,`$L@*E1.18N<__^SSYP*UP`"-5E7ZS'.I23]NOXVU)(QD)^X8R(O@:Z +M%M+'./_&]/[A[AT6H+:2<%0`8YHL`6`6FJ(9]ZSL, +M5>I_&:+;\M`B>%%8[5%P/7*VL`?,M^'A&20-?>*-P!?70B+6BU/BJ`F0UP^Q +MB=C,@(X@*(2O4-6ZO-S"845TH72:MXE,RTBPIZ@(/_3!#VJ-9K,[MZ>F^-M4 +M!]"W_>;&I/*?OZ=#!G_VP$30@OGU'Q0^R2/&G\.!J.SLU/2U^&!)2CL/&[OZ +MR,[8ZYFO,6MUZQ;"C6"7AX(`A.VM,FV)A,JBZ9N3,P;>+_!=WL70:>V7[OUR +M$H_2:+@<:6.>)7D"(OH*E5.K""KW"/O+*J&"AH_&W)@!AX1^2+2_J3'=I^?6 +M4O^U!R/BM`<@(_TNV>0[;9Z^$-M_?6GL3%%A')7U_.BJ)L8,FW*BX3@V^(\1 +M5H04=@.X\'>=JS+D?1;>34)'K`+A&.43FK*I4)2OJ4FVTL1."[B&;\0B_'RE +M@0^0B(>46HX-WHLXP:!HG0%8@>45'28V!E%GTHCK%"@'W+U8NO"RCH_E`RY& +M"X"Q.(BYPS@/_N/C(%^G\T2L93O$PHL/6=%[" +M5R-5/42^3GHMZQ=^8@BE2_.=GMNP>INS*0VU;*Y(SPE/)ONYW)&U9>52Z$RZ +M04O*:]CM'\CO]YUA.%`*AK];$4>0#X!T0(#BG?GVT1).I)+CZ:ISX.^:VYF2 +M3AU>.76F/T$8U`PJ[R=5O^QP:HW7QUNRV0WXVFI'T?L&2S866>)I*[N +M2`V$KGW"7Y@>TQ$:QA`1NJ8)5,SP#K:0$"_XZ8$\+I=]Q2?=AQHEI@)+Z"<, +M*/+&"D%[;:?F>'%Z)[X*G=0(K1Q0I5665&UMKZ^G,8? +M2\\7;)X@136,L%+*".+E.WHGUE@UK[JQMNIXEN=_/1=_"5P$3XXW[0S#)"+G +M2D&S]9P%<%CAV?2MHYQB(I(:8(:ZE +MP$9B'Y`'7'*U-($E#E`FCI727-];H['.2*!\,\%9`*7V'G0R*/_$$.K,PF/A +M%4F]PR;%Y_LX@Q,]"UBD%>@&3RT!T3">IGL](N=I +M/AB4J8'F/7Q88S*LM=:#O7VF`,N9"3F?5?1^60Z^WZ[4@"R74(?U]6=#BH^R +MT/M9?U(8^L5`JIR'\V +M+AY"/\\-[1E<(SZ!949CJSKOU4_KM];M2!DXF^'C1@0&Q:/,Q^=F\/2!68$= +MW6S/DBLGWF,YX$I@R8:X&2[_PMYN",>_"!-NC!M_[=62R[/JFB@ZVW%Y77AD +MR'=1A[\[MGMM!;=0$Y`D4%L?C4.2QS5KY_E +MV;Q_QJU%V]?_FI(5T,N']JBHVUF@P>8?,V8^*O>GU>-$5`I%9OTM@^7RVHM_ +M0R5NM,!W$C?YK-EQ/K3XS9_2PY_L3X?VM1]_[DK6Q\*[^9Q9V'Z/G3$"S\$6 +M#;6ZEW877H2PZ45MD&`"HN1LT#/E4N/P4)-$:<8"G^ZHV*J/H`KZ92KDJ\=[:@A.S.:AV?/FQN>HU8*;>I?PS?7TPT`;&5G#IZJD'HUIOQ83@X23,2$6UU.7;.8.IW#&3O +MY,!O1$W)LD0"7`/X$_D"2U`'VD])K>]E3C9/_6\3FLM\2 !=7\;UII;+Z+ +M=]YQ:'"G!(&\^E'N>UE`!YC#RR'QQ^I8UK](.G<2\?!$@840+?+2 +M]&(#:3_S?(I^L&W[:2F$F4&N\U=53_5%R=RTHL4TBU)8R+<&R$%<9!&;35(!PLO4#0$ +MSL==TC=,TA?+QJ-0)T2@+V'`63>D^Q;[GI7_4]1+S*4YL17PUPTJSU6U&S$D +M/[A20;^1>%0(]K/96??#RCQDXA3RS0PW[8(9Y;F3J-VPT,!OC,FH-,G.2T]+ +M[SHP*?P96'^%"%8EVE88>`N7;Q/HV0KD`:ASV*8&-9CGF/Y`E*RG*#W#<70R +MYQJ\@]R1S`#^G%LY5CQ+EF`,E-^]B[TL^M/!]1R#QXHJ)(#[6!B%&(V6ZN[T!+U8':2U*)+FQ?ACV&^? +M8'P?=AEEC77>V5>LOY=]!8S^;W?5.89FZK6#7-OP?:)X=V1S49[]G+9&WR&W +MOV!:)),EM(W.K!A"&)&V4Z`W=I"]NEN)QJ0ACG:2]%ECH;9=ER$V$X_Z+?">LSQ*,N\$/T;<'U5.%R:$1&$(A)VF16S=WLX?.0#MY +MOA-8+Z)0>K!L2:1(]DYA`B[976&A#>=&!DYNM +M)DSU*&;L[K($6EP#;&Z3@!<_N+DQ!EG^N=\X7\5ANJPK/NQU +M,,_Z"GAY&GM8!ZJ85CA,\5[O+]8*/_=7`:DR1$2H,"WY"0UB_OB\\UM2];!@ +M!UTI^[E3KIYL^T_$+`[\U_GPY2!.JKKW9^=K5IL(R)4Z`&'F1).&U8V"1^X6 +M^71&1UV=AXA@$L3A<^,GREC@5*1]IB[=L9%W--=KD_,H^ZZ$[>(=X>%OR +M\+'D%8V6?>:/%UUKMP35W:7M&G/[(MV$(K.S/MAP/>6Z!S0TAV(J79E=U]+2@G*C +M[UO^H.(Z"UENWZ`>2I>$_^(S?LR(>]L5EP^Q*NKI4ANP;5FX93Z/D8_?9/9^ +M%.SW,JUK9"50P,V3W)R"E(%P+UI:>Z*SRT@%_%3^U<-5!@@GZ%Q*A@W5K">5M=XSW/$ +M)7*3K?RSB.)[;69SRR@K,-I-7*FVP/8N=&OWSZNZGUQI[E-E%,4Y.J4^$.LU +MX(5(OZ:OX9'WT0;;XC*2H3``8W?;[F`O9`L9G%=DWD0S +M?[\FA&F,X`\W>K0*KG;;][A>(HL$%&U]P6V$5W1MR[*:"36V]"-5;&L +M0I.@AU/_2%.0,!)!B;WJ7HZAJ%"O/=JZO,41,D6I.&>:2!&GRP67IZB!$V7! +ME+_J9E0K8:C#*BVG*3,Z/0 +M`?`V`81IM.$`[OY'5X\_:+,)2Y8MF8Y.9>K13/G"/107:&'`BS&_>Q) +MAY%>/+1(!5K=_)[?4C"K5R._"*X@/`A\V8F3\I[_I_-_*I[`3/J$497JJ1RJ +M=Q`^F^_T*#AO+;X1FXL.=F36^G?J*,GD1N:L3=?]?P)2/.D3MS>?4.TR,$F; +MW>5.C"$(;[%<`7R.83T%H]N+6A\.TXOU+$1H%G?N@6KXC2;7_D-G7W.F%#'; +M]KTMP.VR/_$E`XO,'H!*Q+:YZ$:__J0O!%_4`F>LCF,9=5?D*0H37<)[5RIA +MF.G0*?'UK[C654&I5_!\LD0'MPHM`+TV8OE4=/,Z]B^P4[^\*7(6$ZY)#:Q/ +MDZ]XW/J]W^4'>PBZ4NA8:Q7\V=1%DFYPR6G4Z+#!)_ +MLG/CQJ-_X`CGM5!54Y5>>K7(/^Y!0PS38_T0TA:5:MY:=%LO<2KX$P%*T4!; +M0#9W0&7;P,[Y_$;P?>%-S;,<=4#4!^=C*V6&D.W=!BECNU0*Q>\43ELBK,A=, +M1B0$F.>-[2$%DR+8TY.SDEE)<#!(;^GO'D=N5'ML#Z=0_YSCUTREH>TE>DD, +M>"NE^JF(G4UH(;,[;D:#A0P':?Q0-`KC95U@_Z;2C08/HXYL9Z"5P-%9I596 +M'K4;I?]Q+W',:RF0%3Y#+"H%RJX;+Q.BN'AG))&F)AWV25O5]Z&K4F1H=WK- +M&OZG@@_JNI-F[N;0IG2?R99>1-:_K9;OV(Z"!G[%E(76,+3:#`9*"Q4:I^A_ +M!BH!5BVUIAQ(NO@4TY>4LK>)/5$83C)=2?N#&DX?`&2GY#G$O;.6.*R6W]O- +M$BS`'FCU:JV!BF#K^_M67J\8L\UJ<<&VPE;[)N779[&LQ.AV?*T#)07B=J\\ +M=3ZLD&7ZEC=PQ>$(.?,!37C:*D*,5 +M<8FT;,^.S$#;KXR^7+QH6NG8DVR +M5!,L,_T"H'[RB[@W0X)CH\`0X:7YT6[H/G6_6I__,67L>"9FM93!J4JGN3N[ +M]D`LV#=X\C''0^<6[G*?ET-^HX3Y)+-MI=P*HM6G>3."V4SP*;JD:2NK.^ZI +M4AWJ/YQ@@A2H=`X:^TR_NSW<-^ +MFU+KE&XC7H=((.]CTMG`*!J>:O+.?[GGC4S=:+V5D=L-JI(6.YMNR*(X@NQ' +M$;*PGG8OHW%W637&Y(D##TU>W0("S8HG$Q8<,E!@8+-%XE:.F@BX%!$6DF8A1I9X[Y@A0T3QB]=`_7LGI4C@.J +M!F^`??6.N9_#._Z/"X]?XS]*K/'T#A"=]9:FS(R6>U:I')A\E>KQC"QCUJ!/ +M`%??;"!$RK'.+0?LX6+T?!7S&78>L$8CN2GIKN^N]#D73V+.^-P=[NGE[J,; +MNJVTH6O4N'[(.RV[890I?X;K[S-'3(YF=$R^"YT5"I$2.D]HXV59">"[K:B9 +M;G?P7N7B*3]],CP,MF+A55O'XVKY<_);KEMC^":+,M%7[P8V0E#=-]4T"3#D_0YBP1`O-`MS/+H%L<7#EF0:D34S2^0@ +M]/9E0*JD6'DM?->^:E!Z +M'X>W86PJ2N5CL1(QV)83?687.SW[!%O=*^TH$X@B +MT^Z:LW%0JG`C5VKI9H:L;O_**0]V5B* +MT!GY]LF-S=3.S"[O[R>HSOP"GLN%/1@BVN?N0`M4^1SH4D89`2VD-Z:G8\;G +M)>>EE+6A"#V8I?8RF!*U@SGV*P"7KGLY>UBE?@5.(]_J6`LL(^8S$+&\EXK[ +M0"C[50N'&$8H+W%8$:A61KOPUU:OR\2K9^>M"A*[XG&]5'.)*$0PP!7AJ:3Q +M2R[*Q#G/2?B;7*].M&7B%KPZ6]?-Z[-9BTAEQ7SA/YCB!O%/9\>C'4GTC6]< +M'?EW%\8V:I5CM48^8)7Q?!D*H+;>&HZMOT1E5XRGRKO5+I[6Z@9=X=MMLP]X +ML$#M4>,?K[:G?*FW#/$1)%P,8HUC+NM"B2,14[W_"I`6Q1N)JRKCCO=D+J_; +M/@J\"PW<17>)>IK^/-/+@Z>+J"=]^L%"3QUF:EQ#IY&H!U064:W]24P9\L:T +M>D.APJ3`0],P^8@83SY@Q%"?#N1F[-N(^;6[S_9*0J*$N_6$^-C*'=TQ7HWT +M%CG*?9;:RH^*+0J$TS8;[_\!P?!FFBL&?N5.<]C]US]1P8<7LE +M_"?&^M%W]ZO#6L$(FH6\H34!),8380Z'[W]$M$[>KMV*VL-2IDI^(_WA^2"B +M:^S9]_GYGM7(8W](S0-,P+A[E3@)_ZJM:/Z;[?>R.)77WA')D3)V^GO'=\A= +M8^3,#"5-Y3@)`DBE*1-UW$I0TSMK28F[HD[B(R7X;H^5?-02]P5QJL-:U=_L +MBW/KZVB\]MB!&.:N)NTU2&>]FSX4-Z_`O;O:&K*U@T4#R1QT@+#2<+E6?#[^ +M>G.)7_(85*Q4\OQUI0*R"DD(1WM5A,R8#X:^\2-EG];K::Q7SGAL1(V8K"T. +MEWYB[W!0\_C1L+`Y,X(I8(-9KI_R[$\QB7[<0(K?Y=7(4[H(S7%KD]'FR([H +M46'K-?*&]K(\1_OS$'3/6^Z=W`1>$O6W`>,$Z^7@;B#KF:77GUY0G+`_#+_9 +M#)G-8U4"=(YJ%-EB6RVB[O@Y0331;C_M-@UL'C=$S,UF'4R6#2=[]L+&8_G3 +M[!:2$%`=[@DG0$T.C2OJ+X-9H#Q=;_'%*CL3&FR74$LCA:DH$7,50:XHGN8O +ML`,GJ8"RU!=\[WWM$`ER9#]PP[#$O4#=;/1YY$.=OE4;%!^6MW)LC>+"-4\5 +MS\H/,V([=6!0AY+&I6$J67EK;%RI0+`0>OZ_M+YU*(L[5Y[-0(^MP%A*^ +MZ=TI.VZJIKT/1?A&G"E:%:5ON5IZ_Y9?*R-^1XA#KOTA8?;>1ID%@0?'.?8T +M$*@G7>ZVT](#B\@Z_X_Z6,PQR5@5C)YJ*CMN81LM +MD=3H8/;?#&WW?_P(9.Z))?A:3[/MK"(X$+DS3PZ"6RT`$$UURF,HMDYU07BY2X=Z8]WZZQ +M,M.G%8-8>F4`RF$T.3(O57[P>_??^7\B+(89D/6XQQ.*EN*(^U04RQC9KAYQ +M[R=75I%;*==%R<#$;D0$K1%[&N)LEUYS:'(",+-[XJD;9.8N=HAZ`S;["?>>,.,C5'Z3<^VJ]R: +MV43!R\*YJZCZ<^O!&;$<9O6&&:?`'[!4&6(2_9!THO(!?1!#P/T.P'9Q:?D_ +M_@\^2U7ZGQTR@^P^5VFUXMD%2MW6+;OT).W87>_^,?62YQ:$+767(:2!A'_H +MAFVS\&NBRW6_,T!_"O`/,-AI&?+T[O\(7_C,XY0<.QZ))>.):_HLB>"K@GBI +M".UA6Z&,&AO&'JU&AYXZZK31K'K=4;+(4F',-B.<&+(@51_ZV2X'`IY`*V)_ +MUTX23*-TO)7?,F"JR`8XFVR#23BH[K(1X"XZ=L5@K']V +MSR9X8Y%G[%G)%#K['[T"(BFR\'[[`^@.'H#C6XRY#7$W_F5GHU\!CG2LMUL3 +M=P +M+.]'*=>3P*Y*-]U/`.#YW&.2\.6\Z2"Z8S%Y:U)&`LCASTYA0OY-A@&"I4>J +M=4AT4N5MG:-[XQ*P[._CHU@%X"$_K9G<N^$,/YOG"PQ\I+15J1P%:B^* +M=5NL1+SX3"-_T85+8S;8D]HWLY<,I$"\*`VNL5;"0;WWTTNJ0Q##SNH)'MCG +M#K@A%"_20\$\`$5SQ9';A!I'13M+8D7P*.1_@D&%Z";FNN#Z)]B%/M0:`WQ] +M-'=39M'];Y`EI.D"OB246(EJ#;(;AM9D:D5]4'=H)>-)/L#5VUS!#2OEEI +MHVO&&Z"N6>M92B0X'O;P#_\>RBW,%+O,%3/I6,A@;K"ORN+'J8I'K8\SV +MJG=[]^X`!RSXTTZX9#%U$,VO2DZ"@.LU_+SB+5=>_%KE7\ +M2).!A0O;>MJ=I+H+TLL0Z/A)@!#\Q92FO;Y$YX-C-Y2[.9,])C3O,@X)S8-W +MVKL`_<`^.>E!,W[;-ECESU7B)EL%BR +M.A&@".LQ0WY("Y2"X>THM@8'[B3'`2$T]).")QV'9?Z2C=,=8_,AN6#[+HT9 +M>-@YWO`8TK]BMK>,;7BIU7SO'^H\OS+;:_AF>-8$CU$1.+>V/AB9E:M(Y-4>ZO>1-(O%&X*T5%RKF:M)C4RP$H +MU:C-EN#!\,Y*9H"]':?OLIM4'%^2_=<8BO(\$:E#WQ32P*F[?_GV'YL^-G(' +MGRP6U">!&2GA7Y[OS(/K8\.R',8Q4,!;/G#67$.%;A$*LW6VZM-2B +MQ@Y_,Q\<7?`"[4IID&]3[JJVWH][[QV&?S>6)F`C[D4N,L3?-C8`(^/CD3,_'- +M4+GT@$EWB1Q..%;4;^TQ"'P:=(:08&F76HOABXOQ&>EH8B9$@<;#.B8#G?1G +M&S03%#TS9KU)Y7&R*T9@`P?=7(`6D1%0YJ&6_E#GC%\0GYNL&C&"=?,0L@I_ +MX_Q[4T[)F#7K7_%1"HW6@<+#5ZSD7D03*G8M'&%,8N6\4_.V'8&P'U6C!O.ZON%G:Q$7P<@TX^%AF8("A@Q='D=9$_UTU5JZ +MG6IND1)K[>+C0J)7X-0)'YD33+M>)K/N/RJLP@Q?YP-54,&W:DJ'VELK0[SX +M7S%#T9P3V3:CQ\$W(G]'I(,8FPTU9/;"O.+%F,P9EQ`V+-H)%:P]#N.C/B\A +MRL4\HX9FQ?AW*1HB/Z-+H6&X*+>X"T%<9!;F11WX)P>G!=0==KM]>UFT5*'3[XK59H?M(N\;%-7!S6=(:W*130!.W/!M%@Z'`*QU]OW +MEO0X2!<(REP$I$8I[3TE^"+$!Q.S#EW,K51U"NA<:*(Z;Y;(0^A8D:OM24')>>_-!8=C5U\7E+2>0VIHPI'H*!A +MCX!9@8N.'F&EUO^M-8/,]QB+%=KI2"IQ0DT;*T%4N,3#[)$"FU/V=>Z@]UV] +MWV1T3%K--EPG@ON#2I/B7WG0'&= +M"Q&_;NC+#5@!LG!/IDC^%N>RRYJ4ON?^/6TU'S))-HN#EA8?`##,RCF@D*]^ +MQGLVWQ>B;"Q,Q+GU!M+]BLP)I_M2.E@8_S\_<1V)TE8GP.'7(U'MQ/3!$]!! +MP97B8J\'!F[K\AGN+STK\E%K.2!FH\XOHH.4)P<>H%V8+ +M``"G'F*Z5!](7?T>.4#R=W'T0_X'WL#M3#B +M*4H3)&&[\G@/V(,6J#$CTCX6&AAOE_\Y:(0%7!)MP?2BHL%S/#/^E.?P]56. +M:0`'(&\TB6Q02%F!9"4JP#('PB3GF>A.H$OQC1ITX?\`RFT +MUPU%W=_5)U+7,V(CVAGW(+N*L-F%I1;?_4EN4QI,(65+7HRJOXFSIMJ +MKLDXSJ#2$0Z=9'9;3N-W%7];Y)6P?DUZ,Y$"N;RF$Y)%&%`LTU5);DIX3F/! +MNF\\[0Q\O4:V!:;",UIF5`6+,+O_:#4`PH&=S&`FCN(A9THJ#!1`5\.]D"1E +MLF;9(P,FAR"`27(PN7L.S/X[\_5?'TRYK'UR>74:$G<*&D-B>U(]]IC(7XWQ +MZ'8>ULP`CU>;1.5$UM3!+2.`76+@%'SSE0V?L!)KB2G%:$:NH&3-3YYAD=RI +MYB3EBA\ZNAG>[6O00Q/(AR([8?#"?X871%='",T>=%B\`YTQW=<2)KJ*X^'' +MS."'@'"92F`'7I$0!C^Y#.X,0I:!5?=A%FIET$%;JIW>^')KL-&SE[XV;@>$ +M-Y&";J3&8!T-O))%PC?4$],)97P0J37L"0R\O@X.LADO<.R=/$8T^\5?$'$: +M_80X2^#9\2B*'>%!,YS*NN[V,E6_R.."V%V8T/VE.*V0M0#OKHGOV'`8G7 +M_*;328OGX4^EE,&!&3VTLK$RA"\`@[4N?P!C@P@Y5QLZ?K%-`=\FG7\LKSOG +M]2W12XIGC\3[45>!&))*T'&TTWY6$0P +MC+4BJ8`CM2YV!UW/AQM-LOX!W"HPSUO#5RC`YZG)1 +M+=MIK6=X_'AU43]`GK;32WZWR([5)&A](XU_^`%"=;D!4SF]L)#B.E&)AWI2 +MCJ-M$*\76/2\EQ.A11/[I)IJ)"O+?Q@$]I9@E\&>/O-FEDYPITTM_%0B':/] +M9=4T\K*=HD,=UR+`#U0%^607T#X,4`R?2L37[&=*P[57X!Y*KCQ+N_$L'=@6 +M;(\QN@N#%F6?T?@1KY:$R/=4;F9\.AJTFE^'D3N`+:YCQG; +MK!RDK6XEF)<1^CV%OZN_^8T(YOXTH9'.+,8_V8>'JQ*BM%J@H]VF(6)2DADU +M(WGD8R!N0KG9TY9KGH&:MG^$>S%,3#!ME=27@ZH9J8J;Z4V3..$[?A$E;F?V +M25>5=X07V>38:41-`7\%`C0'F!/Q=:A0GUWQ265'T]:QN)5OBT39?&"%:P4- +M&(=:01,PBV^XNZ@(!K"!=)&Q)ADTR6=Y/*040V=GO=??86P".\7^?`=872V]7BL`) +M1)EKYRTJ&R!BVSC^\W39(+`%*V4I/"U%KDM-+4]MC(WBG73;"7J=^,_I!J@&5;6RE4$_B%T?;1RM>#-CE3T3<`HG&D"97?#4,] +MM,*RD9'3:(SC$!SK4`D#"-L..4Q5+[^J.3SI36'%QJ3+&M@6JTL?KHPH7:XR +M00J0Z>9;NL0G93T%U",@$WT)DGER4,!5PRR114"I1?QYP+K*O@&_SA_2S]!L +M&7R=->L=\\I[90$[<2F,6&#'W+$%3YR6BD?Y%^:S-00J?GKM2YT;!MYIE;/, +M!FUPXK5#Z;D/TFY6C2X/@9>IG+5N_Z4+(9!Z:SQ6*Q_5.HB/?P(AO3MY1L65 +M>@>#!"XT'$(670KVHM+OV/JC2-RUH,?^'5GO#6P>-[+X_\O@;\R]&*<5*"5' +M*J=&\ALI@*.XZOE/D3`]=?8+`/'W.^Y69/J^L^R2@7\,2T7A4!A2-HN[QX+C[FBZ'$90`%@^@RF7J%1YQ#P1I3$ +M!Q,]^ZL6,K_TVX/4(LP`EN8G.0[B0GZN\JK2WW]N)8][=SY#'25J/+EJGWA<0N(3;T +MSU2\T%Q>(6EE91U%.B*'<^MNTX](!)0=X?(#208HM47'#OA:]!P)11W<:X22 +MOFO:8]#<2F`4%7<9'6]"WC&#_K\/PD:,%B?NU7'+2^J-#&+;Z)&_>`]UNE%6 +M7GVVGAUSMS>\`"2KIS>-!F%*0O!<&5.8TK*0548QB7YD)H[.39ZO9N3KO#KC +MX45Z#9.:15S.R[Z:Z:A&![G!=9!-9B&I'J=\%%%4)=Z3_:SMU_+ME6-#/(*0 +MP$*BU/D;[5D^]Q!DGUF"ONS*R(00H]J52DFA5;OP/!/8J>UM,5$!;1:P,@V7 +MW3_7A(M_U6W/Z"^BMZ%P,[P:9/#;*T=I]4^V[JL'=8_HNI(S"#F+'*'HU[;&2E5PAUH][+3='I/=;>DX4DW+<)@\BY<@(:9UV0J4 +M=)[L+#M_$VT"QE.O(NN$0UH +MVS<^@4L2G2:DD9IGJ&K+%$9C@KT>%PNRW&.'`V1O]H"O./NYZ9MY_A&O!E\] +M>IBL0SG!=6@(NPI0YK%[4"]=F.[,D?$6A*;68J$45WE8O$1<[A8M,3($8M8H +MBO-3@?:P^);]*;,M`:[VP6=//%,_W._J!N581>>ZS[I_:TZQ,<@M)0`0PW>4 +MABTVR4=FE&ET7D--G;\N[A*A8@P(.,@0 +M:I>7:IM&UUNH9DKXF&-&T>'3F#8$JUDW;]U`I$RU$0W*8[+"+1OOT\>/O!"/ +M3#@)^H\"#F1RH2\.!8D\C-/(Q2TUJ1JS]*I+7MYX*#!K,$'A/ZWFYAKT)Y2R +M'=0F5XM]4.B]HL-+JB%`W&E4>7>NR[[L10(?X=N>;'PPGS42%C7*%O#= +M.W!GD%CSWLBQG"(#EM5@`Q?54CD$[?M+[5*3<2*U3(^SJR4=<-7,(NDPW[YN +M`25##U!%YIS8@>2E\O#_>L8C]"O>$%:(XB&)(12CA>G!9`D +M[KU3C8U%VD)KS&A3]\.>D,C]JXEUBCV.^;D4K[4,C[S='KQ%X1N\FB#@5LO] +M>)BH&=J>$'*>EF:L#[NGJQ>I5"B+7_WS@9GR*%KG0UL)%\!UG$'YTOYM/N)= +MWY]HW&_L+;,2M:70_DKD43"[[ZCAV#R;Y6Y4Z/MS8B'.K3A"5F-TWZ\U,GJSUQ@`B,0TZA +M/YT]QE7=/(<)A/>T8(E#W30M'%((2<4CMPV"(-2U%=E!S=6>5TN9?Z+Z&TP2 +M=#25(L/;AC)9W8IRTY%Z@ZTT/BG@"*@#&$Z]J +M6E"%=CQ)4_5&3N1*Z6\IQXA\ID8F!=NV$EKW=%F+[<;C:*Z5G($B3Y,F+_M% +M9]>T<11)2T2]9B`XAT'N:"\T?[\7@Y?3/LYK*:^<76/_1D* +MZRG\>T\R=YB!Q6+1Y,_S,@40L.NN@BDJ +M:NJNH<(L^#"VEMP2#W'E/U(MNBJ;ZVL-\MW(@.C+8?7K"X8X06)'%:)R=.%H +MK9V&$9X25^B>X[Y@7>"@[(*9[P0E7M)P;3]FUHL,ZRKQ5>PKFFJV--40;0PJAPW-,F;_<0`EK2"?%3I0Z/)*XO +M7?M[/"_%8<64P7D:CA[H2YUJIB@#5`H0WJN7YG&'Z2?#0S'4*7IV?9>ZV'+& +M2`G#:I^.0`\Z8GDNWHZ^UU@Q'_"#+VS?4`4U.0HARK'&XCMO`>58AB+TL3.R +MMY8@AFX7Q`TW!+!E(^U9!34BL/ONLXH@37#(CWKII^IT;JQ(3@\?C\2<^P`A +MI=F\D'HA5V4KG:B0B#7P<2%KQJ)&VZ7_!^&1P#0B%T-=F'U%TF743Z.&X<:@ +M+1[J65V`RAX-[R#_Z?N>S(/(+!5V4.4JW7K0&R17L]%]J%!W?<9 +MC^YD@.'XS5)Z7H8-WI:37I'IT#YH-LE.9E+.^08Z[U!I6/QW6@.%#OS&VN2< +MA0L-)`SH%]4(`0YT%;::=)3G;H0/E,K78CV)$A@R->.'@Q*?[`#\L,LY?"./ +M?G(+3]]3-K`LV"D2]:*VV:9>Z);[3M;&&$MRQG%Y&WY64QFOU]^PC\0`S7^,BYW-^K/\H%[7*1IB64S-RTZV$=WZ^Q4I$BJ +M&L'AH.Q3!C[+8\=L$^GT(E31'6HQP)(TS5)C#E""8'H*)!F+_X_>!X8:^%,K +ME8S<#(S5O_2LZL-FJMOF;LYX-?'7483.0K8TY46W%6N3)#'`IXT4IB%"Y@>@ +M4U:19)`[8?&);.P'CB2R(Z\$T,$C:#YF +M-X+UI]HV]%0K)#;].I+I\F/JW4-Z;*;`J$)\^8E,?#[--BZ?=]+;[)`)G;7, +MPLE*?TZ+N^YP>-C/=-`Q?R#R%D9]SY>[M4Z$$5-0;=W,DJ*M_]LS>A``\,&Q +M@FGC9K[N`4_WCX9.2QP-"5\E38%X^DXMQ*O\MN0(WR?ZH98>,<9@<9V'N.:S +MM./?Y-W[['M2PY+B(U9L$R"'<$+_GL+U@'\Q)&Y,];=SE/)+?$G)BP5"`"M:TA>XBRS8+VB!'_N!^4<$)KIP>V<&&X<43!_=O:SY +MFK)2'J8#HL)PKDQ/+$_:M(/< +M-9\6Y29;J+J%QZYKS([=R-/7'6%,'`R&0UA[1\3+J'1D>LJ=37"F$A+E^>-= +M]KW@4Q,SF28)8EX]_CH_]:;I#,Z)"%63O`AJT>RU6Z:??_B^"\'1#P!R-II^ +MVS=-J`.DTKF%#=@D'<(CD@G3A2J$$'433PGH4S,.NF(K+ZR-`CS2(ES%^TFM +M&FF4$DO<4@4HUFCP08_3&.;\_U0G\O`OU"R)!'W>"#IR`3\[PXEP?$!"ME5Y +MY4F$OL+W"Q__QW)L0MW_-6#Z)LI,Z;\[;O=Y6!F2CK'*_2F6:[?J%[/D'@1S +MNS#M8//5(JMV9+-I]^QPN_YZZ1XR9&Y7^/\/[G0TG![LY*7`A+)Z?-99*UR< +MVHB(ZNPAPJ5V1N8_C`>0@@\TI>..75][+=(@A%.C):Z%H."O>5F4$\(>>%_O +M_WASA\QF0B\5'6"H*O@&S1NF$`:X(Y7[D76RY_9L],7WB(E^9,-4;LW9]U"" +M8G)_D[U#4-"Q3,/)1;70'E +MRNVF_^]PYYUX%I3-(1E0]W^4,G`,N(,Q)Z``+QBP.3.>LGY=#5C?8J'=+#M1 +MH?7)I5^*TYVE^TUC01C>+"1Q$%;Q5IX-8]?.CI_$PQT$K4+J_^))W=5S@8<" +M,%@5KZE<%7(N(@JIS\%.+5S'K[YE&KY,.C,:J.NKG^=(_1_HX?=ZP@9)Z![' +M&\OO]*7;_ATQSKZ2[R]J8ZU3+F`M!W89PB'IWG+9!+^\28>,OZ&,U6:#V^K> +M!1[;96=E>$AQ$%&;(`FV_]J#MX3LVB1TKF.-FX*00F$/;U.U.O20KG`QB6.* +M]8Y/-GNO4S&8O"/:_$6O+'^:ULO7RWM)$JB_`W?!^NVT9L(`?J<)./*>1O=#F:LNP-N4M/E`UO0^N)L`GX9D\NSSRU.U\[!F2AJ7%KOME,?8 +M8GJT,19*;1:1-+:7JIIEA-2>JFWD>.RBJ&#>,UG5L?"T=JEI6DPT[JQ$'H]5 +M`OX<,*I8)33'[/JI4CRKNUC0"#(X4*'M6P'&:Q%"AV=E$;1F(J?=PBC(4GZH +M>"/UICL@.MG`V:_S8)XM+*)_P.GH3-AGA95PY@>JA:E[F^5CV3@M8/`1@0K8 +M=E+R4W=`D5#DY(:B31V%.0(@*4,3;<]C4&L_XDE!D_=..@U/^.ZYEGSLMS8% +MJ46Z6JT&B(MK1>%H5-6)@=#\7R9%\NIG-9W[%$_??+WSI'*RC:V3`\&]BX!( +M[%NY#K*T_M)BC%]JT9"L;`W<==W;@0HAE45[34PHF*(2*1B7!*[W9Q9/J4_VW%B;)^4-8,BZX#]Y +M:XN'(,@F\R5AHP@%ADY[Q$=S$?QTYW?4G11+=Q+-K7GC_&?QX`;FLIE=27-> +MJ!>N/X03;`RQ5#Y5.DE3:NRQ>%LEQEWXT2%,T28P%C9E^C0WO#?7I!6>)PU? +MOVG"GG\FT^3!4A#H>-X)WX\<#N4LR2_[NPP\=>SGQ6+.LQI$5MI+&Y>%**FI +M$D%FMZ#FAX3F)R&Z"=0OA&5])=-1>9SQ&[ZM(D(?#JA8 +M;*75OC6'YK;*YUBU^`$-OVC[6==Q"*FO9^S +M2*KL<*X95.F5M?QN`C0(#[WV)9Q!0V"EITC5>M>A@2L;[".(VQ`J(1E!7T;Q +MY30'/X_W-"B?)F0&M*_-8/'^@MW2`Y,Y$#U\)#+BFZ?:/M, +M3*@8<1,M<:=,KE;I#<37O8@%Q(9:<*U),VOB1!%.Q6$&5!'BQ\J>"_0H3[$=^.8] +MDJI>_F-,,R5@X*^O&?E_3M[=N&SXD;+KZ(MWKS& +M6<-%%B_YFK$S,,NZ.[VHDEK)P=$O:F9-BP2G.,DC5#$:Q/W()O7RV6[C0[C& +MG\`.DQKY]YM'/J.,!I^5DJ/L5OIFM1J;14JU*X*[[!2_V/6K3S&M+TX6W/QG:]VH<6``"TZ5"ZW8G@<%(YBCF.ZL?T]+?$R[@YF5%_"HJJ"G)YMDSV(X0-=M$9B'/78=-W%WK[6^Y"?< +M>TVD/(\"FV\7$\Q+85#>)2]).+S>3F7`0&\6'5E\CQ)7!1"',%J(07F+J@[H +M:">W>(&G;18FT']$9V0[OQ12BU-08\R4LL@(``/@CQR`>]4$N<6&'_Z]MF)? +M>28FK,_6]74$Q6H*O+RD>TQCGY@ZMX#Q3H48?JP +M9H99JNU'(G-<=&IA^YW;H8`2B$+.XYNUY^"Y9#*9E,7(7A.]=C\3'`,,E(O" +M5>'1Z6H'PUU;I$C^^.1CH-$]',HV,(*)-ROC-J18;OD-YM4%PADYX]-#-5,0 +M6XJP"=8'T0X0@S?YA(53B=0 +MGZ[4,FO)?6P9D')WC[_#QZ::0*BRK7E3U<6^F-F#'QTDT6P(.G(UKLY`RH(2 +MPSML]R2X/_60L?C(!Y8!/M87D>9L!/DJK>UN0W`BH=!0LRI!^ZW:#2K0(%= +M[>I:TI9'?:-CVQ^@1M?;60+N<1Y.Y'#_EC:JS"QZHQ5K=FV()8_].3>SR'T)<9ZDZ&[STI +M8OQ@2/*%0T8UVG5;%0O*=7;HD#AC`9.L=J85"Q#G^[09E--YDCU>SK0RYL(\ +M@V_+_0"5ZHTYH40@]1=XC8H)[8_[2Y'K&`0F$7$^_R16 +M-!J"UZR>L<1)@SZM4/AOXC\=J',_MT@P/DT1J4KV+RYX4Q[/7[SY7`V`8V7H +MOQ(ZZ:+7ZRE)D$BCKM#AI2"0"O]I:L8R%#^'5M)1DZA0H%-MC;&)X;O7$4G`03W>^:Z=Y)Z. +M";"?I%S?[+F4!K5!@=9*4`Q^N^OP3MB("=(Q1-$$%B?'_I>(N/F$6OJ(;?)$ +M(**>ANPS%_.,\)H$^6E(RS/N`M2GC_,]P!QV,&HE`D=P^E^!?JLYL/T(V-RZ +MH9L*^,:!=1>'Q;9OM&QK3MU0%.92]#?/(S.P$""@K#K9'0J-G@,]47_>2!A, +MG@O@E;[UJ:J[\7F;XY;B.%-ZV!&OP`9VZ(=-=Q[V+.1J'>E-*W#(`A:"CQ,K +MI$104;AB22\A94Z0_T9O91R#?#+9'`5WQ/U:8\@[B^DS-&CZQ/<0J)J7)EL9 +M.&>3]<$452.1O8%O<;(#,)L#_SW"%MAJE[E\EW#^J):(7X@70JPR#.AU*ZIA +M"E#'W15,3]-OL5B3>:JA*DFRHK#6UH8<*4ON_3HH1U$,Y']0G-N\`..V4F +M+YGL:SR[PW/%,,OY&OJ]`4N9,ZHH3Z_+012SA.:=L3LG?Z`.U#4^8'VSVV1R +MLNEQP^8+JT#;,G)Y"O7QOH,<@1\L/,QL:PI*-=6R-,H=&4D[:P$;6-+HV+&/ +M2%.63).E%X`1LP2\1'SM=:5)(IRO)[,&I,MPE%-0Z8-PZ6Y83]P/6\Y-G(=_%(T+M&'7;1<@-)16P.C`CS; +MH/EL#EDY-0H>N?B#_R6"Q,P/.'45$G?D]J"-]`*`N+)*@H'SJ73..^,]CG\`\5\A:H!.-I0 +MB`(+"4E_Z&-FH*HRYRK]]936A[4]I:\X,B%0\8..YX<`R%7G.5AV +M4GX>6^#?CXK=+&1/;T)/$#)B> +MK5('YH%'@?BH68020M]$8ZB0]`>>-,``J0M+M +M2E/27KZ0!Z>QB4C,\8[0"A6O0/J`RS@TGZ@XC"K!?^=0 +M9F/$RZ>6RU]4-\"]_5:K>O)/(_1#]I#\P'23+_EEVSL^>1)M^-W"E>PF`7_JZBJBK^&4?""MQ+Z'R>_[&C#FHAW2) +M4+3O<9-00L274J^Q[R/2?IT@4Q@=V51O-YYN%S]_;>*L3('_B)5E]WJJUH-4HODK4H:3;(?50*18%L3BMC]4,3VK$DFPOI. +MN\H1;[2W!'S5"[@%WOV16CZ;?XLHM;5HS?XV@56#4R;FOG5GPLOS%%BG9;W( +MCGBR/B,5`Q[(;(#AJT.Y,^07_I,"KE^7I-2LE:WB=5*+U@KR[T=[2J5ZF&`T +M:_&@2^C6)*.>5Q,T%$3N6H0M6^B;8KB$'_.-?%FPJCJ**7T<;Z[X^3-/%LC/ +MW,$7O^7"5F:Z\7#D:\B9W<@?36-AJ#4DAF\LH$>`B@1T3Z:O(/S$!+PQ:/795+]>Z:CW)3ENB84;X"FQDT6LH(3;& +MM>,W"8Y:I-[2[`WT=NS"#Q#\TP&PJ1!!L=P)Q*M;#F;312_H[J`\7M'3Q9=# +M&1K58\%+V(LN*")TPD'KRL#F*%I?)ZZ]&=0X_5E]^AE`<%0$+YNZ^D?._`Y;,Y&=C7\D@+*N&MG.VIG$5AE"!"Q)H0J*<(=RT^GZIQUN`R\(#RHD9)RJT/ZE7'RC>NC+#"M8 +M`UR(U(M.(1Y[]482(+<'3%VF-+%-UUU[2IK2##MJHZ%X=:"LKBQ9<+AB'B29 +MN/^T:<_,\F%!%;EV1"ID+\X;HBY=N&\A'I'1EB3VF#=O]T2+#V3/61O-]<7- +M@DZ\%Y,YI370?98)R2%)#L[0Y(?OVU]A_*:M=(41N;G:*P')"TAX->@VK.T! +M07X*IBM6=^=98?F4V8$'/>P,GS)]@7B-8W!<_U6 +M?C`D-8:I.,>"+L[,'<_]F3R&?@"69;:QS#J?6P3KIV*F2$GM<>%3#,0&==0[ +MQ]?!/?2Q,<8=31D>DMNRAV,I^BI@^;&J&9X"*-X>GW6_^-O-C$LMC]4"EKWJ +M2GJSVV'A"'B4*;Y1]\#OMBLJNK=`"B(D@)\BT.+,ED)-)5E3,7C5D'IFO(D_ +M6LR.^[YNKX_*];RO8.]%2QX)FD(SV9P59=8F;N:[Z;SW +MV*Y(3#%R2+,6O8DKEV9&5H3@!=]M``'JHI3<\3/5@$?P@$*+:38Z..Y8DVN/ +MD>GH'+QF.270ME?Z%>!MJ[=WB`A^,0ZA2:FAQI<)/8)*TV7TF?_P7*DQD7\25#/6+JO%IT9OA.84X>40U$=UF +MA'9^30-W_,K)Y/!C/TTY2Z4"#`LK$^GH?%`WX3U)'9\/3N8A46\#[\U\>*I8 +M!44S75H9NO2+9Y)<:*!D9H$L*+HF<7"M9_&]1!1;%P&]#RH:LGGB'0,]+@R` +M%)DYN>ILA?Y^^H)?ILW-K/BZ%75Y:#D*C?OK>TG0OICS-,7Q-U09UF]-YU<" +MD.E$L5$Y+6)CJF*S"##S;"BZL)U:B]`?P9>NHT*H3^72N-SDPUWEJ4MZNDP9 +MK&N'`H[(P$7PHHMN--@M]_6W\:6";O7$0Z1S44O-XT9/`%+A<7^M\L:L%7>&$-`E"CU +M9+5!PI[#YK,.:2?=]K_4.ZCF?4'4D7(H`B?#)&I^0$$W''EXZ)"^649N'TI` +M"FPQ[R8U(V\:RE,*SCMI*?A8^%.FL;U[TY;B86<77KLM!$(\+.V#W40BDM$& +ML\0-:G!Z/>_I<1N[;JZ:2@_Y%;RS$7AD$FELS0`(+/?3?*_)@LE,ZY.8 +MHW'0\-:/R1C$S,[[@2/O*F7(_$D""H5X;"K:!L88"X<#B>@)**(X1.4W&>X/3/F5S*7SJZ#S+,"`"XP7SG&A\E^]$KVWZW8BZQ)*%U*UE+W?B,/?`<]SPJC`7J]^I +MKPE\+GE;B<_,_]6I$##J997ZV^"E +MO%ZXDPRFVC3X56T+(*&]3D&UFD.W1L8DV)/J.)O^` +MK]XVBKQ2D#M%LA`/FAW+YLNVG":HWFTAH))1QJ+EA8?2@B6T]4+PW:67<%(E"8LC$+"K +M(+GS'-G"[>2>1'"W?XOI?TQ'*\*5L^Y,J@R8-IM@%8@XC&N'$?33&'9)PCQW +M:U_8Y#-IB$W`\B@"AX&N$TC5L5"J=T5)%+@33A$%M6]2PLY\&[4N!:QDSUKT +M)$N+ILL8:'/VM8$#B3LF_"V/9R#,I!1>TA7)YB&8:+QSAB,UKH#=5?Q!\U$=#1X +M+6E:6F*N6,KS2[#NL)`FR2EKH?VN3A=W3[1YU]P'W%?ED#QCNJ<-ZVM&QS'( +MQ0^+85:JQ(:%WA)'WA$A='8_>)WWU)H2=(TPXQ>$'>"%%^V9<`Z)7K_*L#]/ +M5NT,()^C57W,^VIS_PA;H.K7^#`DC=&9)/H[J1DY]\-`@A1P@47 +MU-._F@<0)XA3";(_4PZ\4]8OAL?%/*0PN,5E*($:BY,(2U90RRC(@=1!F2[] +MYC[/""<,3/'4/C\V?8@*RG)CP(G#0V,A0'2[87DOO..^1"V^'UDL+6R4`XPU +M2D?LRN[T+#MH1[]8B\O+L50OZ)8MV[D733I"!`4:;>V*LT8BC11OLZ,,67!M +MM-/IZ,HQ;EF24P_(HT6<\$H8I2K@X7J>#EHKUMY8B'ID3"',37%&*-@T!ZIR +M2#2W"A%="9IKTH)>FJ/?A0:@$C;L6=YR%![Z?4;_T@QWS+CYB!4':B(JFHO$ +M&Z#E'KB5;\Z1_W!I]BXASO[+. +M)JN[[WD&7_SLPFR%WQOZVN'6H0O&ZFGHW,^8`>\_*:9!T2;GMWTUE-\Y^#UZ +M.],RTK\7@^MCTP!AG]TY?6KR?M^4_-^\[\`LC1GE];/LL(IU&]C[1SU4&%`H +M\7&:(0%68,/]$G?OQ;-7(P1^Q;54(=FZH#\>]M?QMAXN@U\425YL;;! +MF>>W'D%^)`:@T8W%V>9"0_@\4^\^89!@Z<&)+2KKCE'+^Z\M/TJ\]2NA11G_ +MSYKXKEG+]5K@J+&@X\$$??&V,?27IR,A651`(P;]*Y1O\@`LZD-3,DK`#9Y5 +M^8K\_CXZ=3F?X)SX[$4VP;7=\X[I@'IWPKE8,G/^5([[7\3SLZR^ +M&>5UQQ:)"L/V2'M'.J28&;HR\669L-8%QO=7X)NN1#<#<2UPS1MBJ72>\RH4 +MY["#GG,IXKT0KOEW4$^#O!O<*F#=Y7BXR!@SR&&M_L&&D$GL +M1XW_TG3YQ/^=OI=.$B\>_>.V'?+@./(ZF:>VPUXJ+'4_WFK[#`/R?U4D09YJ +MCHSI6Q\Y5/A#0I_1L+R)-B?=]3=6X!JDIU-Q#8F*"L*`S/_3'EH1$)KU0@FI +MXX\.YV6G^`*>E3Q'@IS%S)2I&F8=\$N5O,,HF\I/07!ZW>;)F^XG.[V=U$'" +M>'W/O%ARGDJ'PV(XU4"GZ]QYYLMXOB^@B1]HM))L]X/5-7$;Z,J)*S0[9\%6 +M6!YOL+'KGLJ6>&#M%SS5T`3(XS/MD04V(H@CO;."YT=P8->PZ5^V\6X$%M,/ +ME\WPG!,JT*41C#IZ^:$E*Y563HC-D@8A(U$&1C/&]0=9)W9<+__V'(\\R;9V05KEV=*K4KA1L?2\VXJ%@VD19X +M7?;Y)ZCWD7*19)A+,](,$-?CYL.J;K^?;WD(H>,W!?%$\[XVVC..*MXV&@B1)`J6- +M3!JJ0KZ3RH[.ZU!,1H2E?C[;OFH-O89W%Y,3?]:Q:>AT*7,^V`A"F!:&T5O7FBA5>Q +M,W)'JO!9J8MIFR*0[EE,QH_^_*T<)>);)85`UOHFJ$&K]-3/"'=Z!Q!]F9?& +MN,'SAS#L_K3[PU#=,UM[4IN0B9A\494@'W^KV3!<\Q9)^95_J6!U3`T'Q94! +MUIZJ0MCCFU[@H#BR/6,1(N(<=M_/W'=)W"7DN3=&3$D"GJE.9V^J;_`\-WQA#CL%>KMH6JK>6XLD4I%H&9YU@;%^3B82S1!G?CQ2 +M%&3O7+WDX_VR*NAP`YJ(!X"M:+6Y#V_3_C\J_U_3\("L(6OA=>[G50I>YF%[ +M1GDEF*P^IO'\U?;64)0?5\]+?D3ZO5,M=[Q>8J1<7/,)5).P=L*Z*5D60:ZD +M9.1/4IMUW$U0'N5>$G2Z7=J.:\1D$\AK@@MA!`_(AIO`Q4<\"3)T?#;$G876 +MVA+J>K[C59,IX.MXJ)G>FN)>E1FO/VT;O@SFL,I5R[$L:JEE#%N>XS+X5HR: +MT<<0S#;RK)PWQ3-SI.JK4@T0B;/"^TWKTWWU[-8[#Y4)H,,W#A($:2\D$'D\ +MZC]BQ3/]M!I!\T1+U(1M*&X_5T%'@M3F5'3*)VI*M=1$,=Y]%NNM]>9T7P+X:GX`?=]@J;%IP,S68ZWW*)*Z3.V:7KEBQ/H2Q@LKJEL/Q +ML1E2!]"M4*M.((.W2(7H0P$8^P*('"/C.-+UWKEV7V4[2^+&-HQT#4Z!X;9I9 +M3!T''M"2FO_P'R',?D'Z%55-8"K0%[0;G@R:N+MMDY$M.QWVQ#J)]P!-(F3; +M'H?-$50"@CK&;).UQOC02:`ZR?V!3SGKXTY^MC-=X +M^-6>:-_JMJ[MVUT;4GC@H7Y14R%2ME(??2B6T?F;: +MW?IVZU)6!J8C[6LT(-)&@-EG[=:>A8]#?S@&]4SP%BJ5J,,/JEWJBQ@-T)8X +MQ>A,$3F#;':J:*-WN,1$Y/P`&TU5T4@"^*Q/$V7"\U`#0P1)^6H;3I+VFV\_ +MY'Y\!%W1!>'@;UJ%+7:+8Y\M:.RS18&($\`FG:X09ZV>?Q1G#_2]*V]I1I77 +ME`.Y7MJ`X`6RN<;GN?XJ4G)?XHETK(0HUG45M=`*^2%7>T44FY@_[RSQD\[. +MUIMNG%>^%RQ$O610+7XGDV*?$+3SZ&_CL&F2SBG0JOZN3]WB3R$6U=QPK$KE +M1?=[NKBR4VN#/3'(OOV$85O;1H:F0T]'ZRXN=/^3]9U,D:\2I:,DX_3DL^"> +MJV69%;M57@"3#C_\9R4,R=NS57(>%VK$BB%;'>IC^&D"K]'>("XVO8@.-S2C +MYZ0M\+,-EP.\PG:;1M/<;(H0@-1Y"9??N@X]+LLO9\R>F=XR6?5V5.7(4PQ+#H'8O0`U0-'E4GRF^ +M!2I3'X:8Y6R]R-4UO[<=RK]\^SMER;981#IWL1FRN)8.C[<7&E\@*PLC68/,L!X[[\;_?Q/O +MS&&'>A7\.-LJZT\!&QTJ=;C.KA611+H8]H]9(?=O0TMD'M[GS9@M3(*UAH^3 +M%#6,T+R0&\GT!Z_.IVY/[V:2H/S5C$6%%!(#VG+1=`N#R+SH5<^>`73D(Z;B +M8]WG1/HDE.@W\3U/(S0R@UN[@TB<%>M@>L:XAR7<'-8\+,PXSIT +MJ8MK52XE#A'/".X!7`(CN&4YR$`Z9MN?ZV)LM$CH"P7A!HBV!JP)G<="+&+T +M0!MV'3`=R)*"A*QC1"#C8'@ED1FQX;'>-633O\/(D8RZV2+51=,!ZM'Y/=O< +M3T9X\ZVQ46H/;K8BFUJW:V7G),^*,]"*#Y="M"[54PS;+)"Y-W*)$J2QCM)W +MOP\;`/"\WH9*14ZRU)J)(O++LKTEJ"I!LL&)KF9FI8]RI[!R7E1)TC#]`-GF +M\K%@:4.PCV?`IK0V^:\Y#C[[P_6?#?-J[&Z=?[527`1*C8#IN^0$J-B-:&LQ +M9^?*<7705(`MY: +M8,@QBZ)(GVB_]?6!7V0,)BB<>F?<''P4:AM"O7)VG[*@DK<#V!!2@!-/Z%"N +MG);^%08%7_&^?JSG4?-U/'YI"I2IV'M$"_68TLOV\2J#J-;F5!Y7K?^GYK#W +MEBV%#BEBFI!5\\=GQ[!<#M\I1WNYKOKA^T_P+"KP?-HAP^S>C+FBD<"#%#1[ZD\+Q +MM,'3.\#9$/-%(0F.-XD.:.1]+\+(.CPP_U'%`<(T&O>"]AB#*94H%*W*0?7% +MQN9J220GR+CEU$K3T7VDNVMR3[$`P[_?M4!`A'!Y"V,EOO!`L1^5]+)L((WL +M->9_-\I:EQL[9%=?6-=AN;&P5:2663BN%PE.N001R8OMIN/T(GD8ZK,?H'-P +M.4C\Z<3@2&X%Z?@/!/,;G'Q/Y*7&=VIZ"B1<*]+E2HC&)6(A*,-A1E38@`=F +M[;DTPY0O724)Q-)N-<2C6_#:H-Y4MUFZM[H>IM^UHU7\>/V]A7NQKMI-$L,+PA\SZ"@G40%+DP>F;5=>YW,&PY3KN4P] +MEF@9,KY\7X-GO'8`+11(.5E-I'Z+-`'?^%-GC!D/L/5/(32*X0EE^T.I@AB< +MZH-Q]@-Y5HPL"-R.L&E>>LA3WE(.\FIX5UNO`:WGIC'\?MK],/:1!WWD7N2/=^3 +M8^.[JV`KVE\9Z%LU%M(OE;]2_]&,G::SU>PYJ.KLD(C;6^#T>`;F15&!N5&V +M[0CGM]K^)R02L4Z-X<.@0B\N8E-K'"^0+/_,B0F:#X[]<[RO:7B^O('2]46U +MD.&:9#;:R_'K9*C](VK!8(P`9C"&UZ??Q?M43(_?_4<]-O+LM*Y#SV8XP-F^ +MI_EF&[T^YJ"[GQR=N^S1ET*U>2+E2BC!)[OCP+K8(";G,?Q-2!9_\0J7`)`> +MA')(KVTJU6.^&Y6Z2+D@E>@36-\ +M#P+NQ=@3(U>7H!&1(BY-(W9_@!&9U\`D(^L +M2N=H=6-#D*9@?99[0UXCF&74,_'!\_/6,4=55YP#XD1#=E43M;JZ;D-IA":ANAKQM +M,D=)SKUT!1-?G=IZ\A(;69_C,AR!X`05CYF#K_A>UBG^/QK5H0R/=2%5P.N_ +M:OJXXEL;"WWXCW+CT0QU$3939!,W$M;DI+,\M@VE;*O";)H,TYKORA!F8[L) +MAB1K2/+-+D07S]ABFED',`:+F]44")_>9WM1- +M!L!]ZUVT)`U`<)B([[;XTH?I.R";(.]IS4-1#O*]?VC.V-! +MF%AJM7JQ1[4=EJ3M11?X6)`$U$)*[:TE^B)T5Z^S_NK!<"P;G.D[O(UNL)PR +M#].,K9R?WMFEK1.G>F7G5"N1S1V(&02%(7^V*49?]A$'%1(M-$:%O5WL6LW2 +M*:F0H_HPS:"12H=0(]^4%D-,E/^=SBS,)U?Z%4H$N2HAI+8;&WD6N/2T29%R +M[(+V);LV]TV$)62D($W-IE12@JMKZ)/2I6S0HG7YR>*1R\[]K)#O!%'J_!O: +M\X7B3.G:*)^Z"P'3YNC[JBL!(OYMIOA6T>>B+AB4[B81!+GLO1,^F4W-<2(LJ90>"X#H< +MO)+KX(U92(ZR_.&T$%,&PC]\QZ#GZO1&AG@=OH(/DO@%V&.",Q]@WCKV&1\2 +M)=X+A[2_X=&.C(#?VM63G$+J!W1]V]K,37@92FM1F!]MMKB"=K=)M%PD5QIJ%?^6>QTR9SB>5 +MG73"JJ[H"9M:QEXX_O$X$7[.T78+>Q%W!2;"Y,7DL$WJ"_@":00#$X)K6E!< +M&J])WCE,3A/4BV\L@N-\[=`4!18YKUH6XDR+(*_":&7]MH$-.O4F/IM,9SIM +MHA1P]\*]`/DJLA9MN6,6"?"`>>7!N>NY\O[K$4GX0?V6K*WH9G6*!+4*RIQ` +M>T(-%ICX"!3Q6"`AUGQ%\5>+(\I$!N'!+9-0.6*0]#7SA>L*L$(*MBDUT.:N +M4(\]3XXN==S92#3E^HW1NLD#KFO`[W[(MW<686"H]X;'%.Q'^$H?NVTXQPB;V+*L]T1P'`?FB3ILM-+;EO<4@^BPA"&]=F6I92K +MM'K`]A`K4!%`B&P.>B'69=9&:B!/!\12B33)811=LRA+TN3HO:.931Z@1.S" +M62A"KQ:O3)7_+)9\S`KA617QX_LJ/,!^&YJJO+@'>EN1B9_1]O#]$_>\SK9D +MYZ.QS2VB[H(FD'Z3,:B9T!5J5LG9L,S.*!Z$[U.:3OR^(A>%2>0BV@-X"PM` +MXL?HSEI>`?DDQZ:#8N;GT7(4E/&!L#TD9RLR-"-L@Z60U0"35D7[00]6(BDLUP)^NN8:SY6MED[9 +MX@TM[W/U-:\PO+04J?!JGRXFDH'/=7#H7&OAZ&7NV#LZ=QZ;ZY\H14CAQRWA +M"V_J!LWL]\=/[\[TR1U!'87O\Y*S^"AC(8=FKAOW!`N(QC +MA,C5.OM`ZB*CS-M9%4%X+-I14:]!,+:#"+FWN(5U))C!'(WOH`I")9&C4RDS +M<9`2DM*)]SD5CE,0\*\'/=Z\QZV818-!+)H:0MK+;UR_J*-MIL$.?(!F1I?K +MISW`OU12S4W]U7FP\F+!`4,-J:3`/]?B^=22.3Z\_C&?.L4\[)?0K;7'HORF +MD2:5N^%,Z=A&)=0Q*H52%A5TR)?>W9R$^!/D;3A1[*)L%*\SS1?L\!@:;VGN +M57ONL'#`CPGXBQS4']\E"-;<^NTZ[ZNO]5D(%F1RH0&$1<;O.?6'F]W)M(HH +M"MT6OJA20M6/(VEN52">'3C90XI7GC%A^7]TM$E,?(ZVE412DZK)*JN;V24. +M.!%--Q&57Y!VUD,T=W1^P>0(7C5-<1YFI?)#/H`5]3O):\6`,BU]L5&@XPR>!Y+YQ)LFL%&=I/ZP4<\=5O4=T8+)1 +MC/@#-4_V@*PRQ;FB(E+^UFE:7QX&A:P05AUUL<%'"F4(AWM!&+V8BVG'\>\( +MH-;'3Z)7>E]01,DW!BV:JY-BB]C4\V1W2K`UBMPIK%0YKOLWQD]=&4O<_%3N +MY>%BS;HD#'!K?S@*R`JC2&&W9+F&TL<51Z?=`70+0KQ0G%27$I$^[>-PP]G@ +M:BXWF?&3+%ZL\,,_.++P#%6_2%4.J1AJ,!>M6QF<'_BUDKS1.KFS-NEOA#?P +M>)*D.3GX#;/%6,)BR='36.)2$$RBAPESIQ<.Z4Y9+Z]OX#P92`3%-LN*&HB4 +M9$L/TO(HC1M#07JM3.8+8Y`G5;!!U%,P=7UY%DER`$2KPQQ#[D#9**=Y,!;F +MOK'-\!_"L.IL26++-5O`P,HKKZT[82E[6)#@.K%UJ'F6.GP8%];"K`1:^ECM +MNQW-(FHYZ[.\%%YP$L7%Z2DG\VX&J`D/<90UG.5`2Z5[/P-Z&U)`*P^QH8B_ +M1'$A+='71G56Z`=SK[XT3"ID77IRK=*?]/NC.$:^H/Y]CG_[OY +M+,SMUF,5F+^V*EG&4+?TW&Z*$V&0'0!X.F +M#GI-YP#3IY3TQQ@]:R`X,F^H-(/$1'``QGD!.7XMWWY,ZOO?)87-+$*U_/7$ +M7BV*LMJ[^T!`OQA52O]QNR-!5FVY2=?QQTG$JAOYK9UG5N((]SQSU*]Z,*^* +M4ZEGC8(NT8#!ST0I&UR.H^IEU2LT.M+,0NM>'RQ(-`EMX6*)\@;8I;O'_A(( +M]$RASAU,V)B2;K%5$^H]'B;; +MV+F^>#^6*5'&'PNV3II>MR=S8B3;JR^-=LE*0J#TMU.(")&0A5W=B>[CG(Y^ +ML(:O5HD/O&H]\$7J$TJ:]3E2RZZ>NJAVU(K!Y8U&9M&Q^J=L9\9454YM_1Q2 +MN_*=>6]#+!0@P'+E#/F+*.<^IW]"GTE#3>SZ#K:U[EP32OE*)+O@'.78V6:E +M,<$Z&!9@QQ27S2Y_A+M;PVT_2:VB9]'4,ZP(M/`+FVYI94>![EN"Y(_8EXL< +M]SR!)>_GU?I(/!JK;D-U"&FP?*%831O&^G6>ATK(I;0-9XC=W47'4OU&V",( +MU78@44&OV7I'9!;1LY9_`V8D3%^ZAQ`@+)2JY+B"FUCA`S@DU#(1K*R"=J$8 +MIG?C\A#D2\-=3?B>`P)?Y9(30"AL4$);LDG,[UA.:A7MH"[4X@;O_B"94R@9GF"U%%':#7[V.:R"RC'?V`U1Q88#?*\*Y +M6<=G_L%-2U\MJTFL#6$F885VFW9$2, +M>E0MH8(/8H:(??-#W]VP*+:_*2,52:ZF;"E/+*#6W!_-@^7(,QZZ?V`I:S[= +M",A=^J!A#MTCAMV&7T=6CQ]TCCO>G2][9A),8]OC#V$2X!V]4IDHX-1>%R8O +M/\<*GPPX!AI5M0%?:DNXS5N8;OSXR.0>T5%U1#:TASTW7W2CI'P9J0TZK,[" +MW1*LT[>ER1"_PQD):N]R#A0U+B_^B>D\O(T"Q9T;&!IC,KE+3L)T*&"8@T%1 +M'?SFEH.@)X3EB]16)YQ]P"QU>?)BKZ<%%T?T7QB;*S6 +MDS6">"+*QOF..V`G!5@CN)FQ##P\EH55LQ?%(=($'3@)[<+3>B)EW,&SF_.+ +M<`6E8L\">G;YDBO.K#*($J>:(83XG4:`F(T' +M(BB]-,_>W%U!.&326LSZH]<:G:"J,SU9EKQD'E+Q._]*^2W,T#A(%3^762IR +MW%D;F"%DN"V(7I&W.=+VAJ?C]YJWT61M;L[-.[&)@AP][I/O0!W0^U;B +MY,Y^I(#O\0!&_`3XY847!U/:N0UY"[X4TNAT/HP!IZ81IIF=MNIWNM:+GS[L\\3 +M?OMZMGA2?B)1W;EM +MMADX%[>6?!,D'H)*C?UMTD(;)`13Y[F:D/C4@HGER6>.J=P%KZ5Q[+P-.>%% +M4>*BFM!]K\OM/'^H26GFRK05%B[9_UCIN3RV/V?0TM;M,"E +MG]3Y0OH>/I*,=I]872CL7;F*M143'$J4NX.[WTBYV&S4G3%8K/,6JR;JE[R; +M8X1^CQ!M1KTCHF):"#.+U8/P_L?.:=QT&.X&(">/I],T'!K&XB76]3?ZHXG` +MJRWM-PB\*-&AK]+MQ@6^*X?YSD*TD)''W:1)\E*XA#DCIQ=/(`01"MFQ0,F> +M!Y(-?Y*S1TQ"4LN5D@$UJO^20)O7^"\X-X=;$<.U&'9L^19W4H.@Y(X46DU_ +MH@8"VH=8$Y_0-5"U_55)PR1V3I2=%&\9G$Q\UA`)!4MM75JG0E^+W?HG:-[2 +M&8&2_*HFW_/29\S]"Z)BQKF?Q'0Y3UEV(`U=8*_=$3ZG<+:C5'&*!!.#S +M(B.UL!N.CRF:$8:N42T73_O"!]Q"L23EKP`TNMH`BFO]W6K(L'U/8641IX;H +M47E'!5UZ=!$AC9WV'1X2!U2.XZ[:/>#0]]V97VAQ7H+GWS]6_Y$,&[FI55Q6 +MH6IFI0!;%YW+^-MBNI">[90V?ZOA-\Z+<*2_Y?D-P0Y$PE@H%M,61:(0,I%> +M"6%S20X_I\/X@D"6/%\_,L4$:)@:/AQ+2XT.."@S0:!!*$G2YB&P=B>S-M'4 +M60HV?0>_=W)V3+C#6M%20#^=PGF>N?&^A^?$'^#DG&P/M\`UJ2PNRF&0F#M? +M@=]8MKLWL:.+3);0.J\7M)SQ!.!AO]#_!98B8$O^LWJN.7#X'A+I%X*];ESC +M^@\/#(2<*U-:I4E;(WK1[+@KC03CB&4U)B';VYA&B^;N!]&\=#12?ZY="'K? +MTA5P:J-VRZJ@7=G;633M=3T&Y/9N?RG +MA/&%?I(IVI3*]0O]-]OH1W+G`7X!XZ8DS='G7N=3E/H71CWY!HDRY1:Z@P)! +M+``LQ-HY[.[R?B+*:-]#!^`L"$"W]1+)!HKHX07[/L8Z&'N[WG'IY$%6`_X+ +MU\^OZL0P>=6MJPU-XO[NK7.2-V4PE3I_J56&F"XG`@G/XFYY7-1K>&6CQ;LZ +MB`TPZ]P@K$C:#];A$/(4!NX[V\=MUR9OQTT((P,^Q?5+__SZG/XM]O6%AEX99)L4?0/R+;DX9*W!:)X)&%$ +MG8V$CA%"EA\X4T9'?I!K^/*)W.OECM24':_T]62\L*";1;BLIIJK#:+++#?] +MJ!PFT,&3Y0YFYPJ3I\H)S51;%&W]JG:<\A-.0T-J?E'`V`5J^?D'43Y])7Z? +M9Q.^[,#/6[4\Q89*+WACN][05=C_;>TQ[]E4%O:1^)>K)#J$0:_[;4VMOZ)= +MK_DS:GY+\/&@OVE3WK#NL4DXCW:K=QP]NS,EO?]R^OXBDJOL\I$(#\V?H084 +M+<80$DWGJ/&PM!.H]]UKR9E2L)V-^8V=\$O8.3145I-!B*=`E7VL1/^5!50 +M*$Q02;J5XDZ_^+5O:(<)?'E3106R=F5:@J52BQS,26BQ'/N!:;5')FHI*6?V +M7>2FLK^7O1S._[("LNX#DDDF@$4B:;G'Q'7^E[F+7TO)%JPY)G-U)N'S"C83 +MY'L^84R81).T\;0J9]:HMA-71[OW1!+$SBVOI"MB?$RL\P%#P+`.$GPI$4I@ +MKOQY/T_*@<[<90$!Y9;)?KHU[4%KO#H@R`,MA&&\`;.&\R@31[1&`G24\OB= +MO3PN$L(0MN--,$S@@]X(F98L6*OG)&V!_OZ(EL<@."-=#<4\K\`;=4TU0L)7<\WC]+0W_JC%5C7H!`K +MIED$)UL>*,:H_VC,X7#0?8<@0_W>!#- +M9//HFLU)L)*"$AI-DX395WS+\1[&<54C3HE-K)@E5NQ5&J-&:D3M*(U-6H3: +M2F,3*ZBJ*(W2(5I-=="BHJT5*R@EJ-@::M=VI.?.;%^O.VN?[N/.^Z?^>?@T(0-K^J<>SCOSE:/7AFWG77S +M_-W&B&;%)PN&%V[?/S3\;^/9WGTZ)W5L5.K^NL:;VY::5#2^1TS+$&VE;4%/ +M1G9BR;LV7=N`=_O*O_$0F[S@M>_>366$5JE?N68047BC>;DU(C +MXY__JM]A8?"`Z>UU-:X?O3XQ49H7/O]FRI_U$W\$3Y^QI6)KKQ<'\XOVM_:4 +MM\V?]&Q6G\I=V]?9>]FC>;6O^6E?&QP*"0H[>.;8M*BYG28'=&Q9IZ/G"D>3 +M^07GCE6;N.WC\)+\>\.>O]6/\WLTYF;^LF5YW49X--ER,GGOFHQ%+]8TB_TR +M;KM'ZV7]JA:ZCXH[WGJGXW+`YCN^F_0E]M!DGUJ!6:,6#1_0<.'%R(UO'ER; +MO;V9Z6.573E!K3_/7."[\LBO,DFC5>.3_G[.'?E%$53!9W-H8;:'Z.^OXR/> +MR]2#*QQ<,L/UKNG(\2>F+3P_^.KJA6<>IR?8PSI=Z+#Q?&)$WPH[]T9>^;*^ +MS)GTN:/K?#ZLO*7LY.O^Y]-+S^#`J^M.?YA8U_ +M]GY98X5^V/8&W4K?:C^\>L")WW[[!][V3>DG5+Q]XC$O0ZA86;_TY;)U5K[T +M#QB=433AUR?U^K"*:_JTNMG\6]"I69[15VNLZMW\[+0>):G>;K[N*2635B]K +MN/"4MH'"M;9OSTG^_4J.'?`I6CQ,;_S';?.*%=X%S;I6C@W_/,1]^[WB3S_' +M=9TUMG_^5?^V)S\F3ETZ.:SXJ>SC<;=^)UKJ+(/,1T:.'+ZOH&Z![JS\5:A> +MF11[>,CHM;?+UF%,0O&#H`/3`B;4%+K]='LQ\79$U2Y=:]9. +M['9,6!([H%_/2C<'O<]59(R_<[=VCRJB]MD5AK>3"/>T^]2N:'5DK[C7KR[F +M?>U0]^N=UT,\SO23/*@E[7OO;NKK;FV[M*C:MM+=2>%K0_>,RU[P;OVG\X:; +MI_O$Y2Q:7G'8)4M`AT$-7;HZ<_+/E._R0^=^=>^AEST#?`G=<^\.]\A<7M3]L8<3:IB8^6D8M&!X)J; +MXFK/VA2R*4/6VL,Z1>=QKVGCS??>#3P4'U>^G=?-SLKU]0,B@E_D;:TW,28] +MNUKT>7ORZNV;E[_MGD +MT??"DH8_"E\OF=Y54._&K64=$APH2@FM' +M'!V[;T'N_:3RS2ZNI/V*R-"C/O/F](3+IT^5?%46+FZ@'A4;87CGJ7,_MZ=ORJD3EAQJFOSU:.NW1W1<%R[;*TD]C$I9V;+[IW]->O^--FH/Y^X$-\GA46?S.TFFK_UT?VSUP+XSXCZ4[[MQ +M1XUV6^JO>S/-RQ-#*IV77!BW^Q#Z5=3+C7_]PM'N:VETNOL>_OX +M5R_QR#=E\YJN+[^F40.=WX.QX8%&^>+9JWKWOSBTZ%"S/9N"NB7XO3FW?_0L +M_^[S31>J"GI477+CA<_2@P7M7H[;V7Q8C3W]2P_I]K1ZUN=\/^%[@:^I=:"B +MYJA+\\IU_,?*<_4M95>'#O;O/MQ[:^3:)/6XSMHUM2>D-%^=V6G6:%=)C;6) +M16<3&OFF+JO?Q/W6+LOJ1&O74UU2#[CFCMDR*7"<]EC)=8_%B[H5_F-.1,,5 +MO=*F&(*>=OO\?&_G>[)NT\9YMG/O;SA[O.W9DY6F+NC3K_A+KHZ-+97H(!UJ.5^TP)JN6YL<*?C8.J:<8O +MBAQ_.6K(A]K5NG]H^F+KO-M/WR3_W5+V@Q9VOYS-$@C[V,<;EYS]5M\A-.0TK7>PYI_O3-5W$'9*N +M=JR_J>?$C./*>;8Y*>KGR1$I':_47G-B6YF6LUZ]=ZRS-QDQ^7M:[?*'Y]]. +M?O]FY=".FH![H957WJYY(-SP-,[U.B'DN=[5^FJG*CJ5\UOCI^\]!QX332CO +M:#*R\R.G*""@Q="\@4UE,:/*#HM>DA&^,.;EXK:%%O]^S.>]Q^LI`9W;-P6[N(8/B+\:T +M*RP:URG<_7>.MZUT@XX17;NLFY34O-1%[[D]JHY*T[Y(6_1D_L3@-AV\_+]M +MNCI^RZGFP_[TS&DOZIR<$VP6]!K[4G;[_H''*1>6SVN>E"T9^/QC0:^%[;YY +MMM=K$YYU2Q?DQ9>_*S5G>S]DV:-N!]7'LSK)UI3U#O'%'1 +MLYFZMS^/C!M^;_3T(\:_JVZ&?+E0SN)SN;(E.[A/P2_QOF8R6?>@@^&/9>_> +M1.X[D7)^P>!22UZ>>;33VY[9[W*']*N_:]VOY[]WV:RKZ[=$^,V=.N/N8<62 +M1IW*'[K]Z6JG>X&9:Y]]2!4VW?'B88_Z\4UB/!>4C?Q=.>-M8LLVVP2"7.LA +MCS[W+W486V'3";?2Z0<65GJLDJVQN +MLX?:WE3=(VV?F3"[^/O^.Q_.=5_GL7:GLF/$/H)I^U;MFB&AO<16<_KQN6FN;>ROUC28?5C@8O3[U;>]NE_":9 +MM$RP_MC4W#*+]Q=X_Z/\U54K>K[^8O@KMWU>[KQQ;_/FTN-J5/_0?\*!4FTO +M_,J>.O?=[&+CU67'"^-NM%PRP67^>^3'\&XSRQ38EC]\-:3BW],#0\:(.BT/ +MM_ZS7XLI.5ZS-];XGCTH)U&PJ,''S5'5UMRN5BR\^J+5>,G3`Y$M]O5\UE8E +MG5&W=]B;:Q6/9(G&_%S=+RAOA,^$ZA][_VQ1O=GM"YXUNB:F3NZVQTW1 +MJ]+C^7=S^R^I;[CNO[>*2]NF@O>C3L*B0E6[S1WF]BKR/G@Q.OA^W80S&4,V +MWP^3K#HPI^]Z/\WK80^'M)BW>_'=&;:'H4V?'EXP]?V=\_>\X_MX1'@O#[GU +MPM(I\4?#^E%/Y^R?WZC+@/SLJS>>5^TY84T;9>FUA2DEC<=%_'-0R]KSH[^6 +M>==G]_M7<16:#"TYFO:]0_+\"\^KO2DNOT8C>-NZ\I`ABPYD;!H1]VIJ6WMU!T5O60!C0;WR//[8=?S!?/)=$M)47C +MC^JDXL"#Y4MJULNZWGM)W9IK=\UK]>ONY7K5:_IG;DM2[VI5T^U[:./X'Q<+ +M\Z9G_:/W#8=?U[7KGC3=?=4JNN7UYLR5R#J3;&,V>GQ/UDU(;U-^(P.W\QS<[:,7CWLCU>3C:++22^"!W3[.;9=[??W +MM]Y5 +MF5Y?Q;E%]H7I\N9MPY[-V?V\7-V?IR^+NCS\JJIVN<'W9=EAMXHV3`MYVG?R +MDOG>=>YWFN"8]F7LN?%C5VPSW#N3?ZWQJ^9UO?<^RFNT95E*Y.H1?MG&DU7< +MGF;D#+_7;.Z&^]NCG3_[K2U[;U1NC>$[N\E4FS.O&?6KWW=(;M^P7E:OI4T' +MC#8WC:P]P.V:)/.+2?U@6\['Z;=F5A_V[LA&8\GH6<>3_U[:GW*_NELUIO`=@6.#P%JQ8[4)C[UKUS> +MY3F^Q/`[:+5OHO-0C&!"K&+TAMZ/7N\*?KNR$E),4US +MY94;K>N4\>#"86^C6-QN5<"$"D-3HJ;LR?\L2-CHNW[_R#R/[A&1$ZMV+?RP +M9%K584M#?Y6\?*SM-ZS)S^6E^YJBJH>XVOI]]9[H&Y$R1S'[L61>[-"C*\M& +M7"^K6+35_X-D[E;1T>;JOW_ZKKGX:4#SB*7QLU*%HAJZ08_.=I^V,O7#R2KN +MPL$)JZLV3#P5LV.BRZ?QY4F;QI0Z=K%IN<6YH=7^<=AWZ`JOQ5;#SL>;/E?Y +M9KUWMW#C./?JY?N+9X6TO9;99:IZ^KGB4/^TXP\?5&SY?EV2V=3L[?!R>]84 +M?%Z^_E7'BRM.'XH8/#MSP`_K\,.CIXCJ_=[8\*_7H/EEXQ9%S[\2]6!'3#7S +MG]@75\1-G@[OD+)OP9Z&JGW/E_1N_-:SXI:M[<<=?L5.RA6F+&RTO3>]W//;8J[\WE`8)5@DZ5"YZVEA;5JE6]G +M+YY0[HI'Q,7R"?%U-S2^/L3Z\9AD9./X*A&2B-?V?GXMC/-3[ZJ'K0L-^UKV+KJSSFN.=6[S%ELOJK^UKFG_Q&36ZO7_G8V\R**A36G[W=_,S\(^NS*[VC?5^KP?F-GI;OI<\K?.SQ=L"BLK5 +MR\N*Z!^SJXDVNJ!E__%/K_U#?+?D;DELKM_U)I,KU6PY(K55C1&Y]<*:IV4> +MS'NQJZ]I[G?YA14_.E>;,;WD?;\;,[0?UKJFV7>W3%QT*\E;<667R-^V>ES, +MQVMWF_SSWMX:&]94'O+PT8=HC^Y#4YPS*SQT2[G6,?Z1QE[HN_Y[>(6U-;PG +MYHSM6#'*O"ZJ[ZA2K?MLZK%JS@SM*8\&3_3?9[3Y\4_3M_R*D[9\]*PF6OQD +MK\C?WB>XH+%B;,B?B_=#'D^Z4%+F=9(MWN/YJ^L[%KZO<;_]Z,ZVHR:AAU0[ +M3G?S_<`_$]?EUIK1O-1;K^'/RA3M_7!:OOSPHF?G;_^/JOP +MH\'9B\ZPK;*_T^K4*)S<\K&\CK#<%8>MR_%SSM@KVU8\.I"_8/+YM[^O+7LT +MPK?*L@M;VH^8E3OIY*/7(=TZN.UL=OQ%U>TM%@?T^_O/IZ,:?UBW,^'L[LW) +M==K>>STE/'E5]-DO;T-1SD27G4N2PF<8S[X^*[:%C*R;5J'IE+2GC;IZ*!_\<7]IW-R_W:IW?CN'AI_UV;,R>5_" +MH:+:^V]7E78_.F9]U6'*K4ND7I*E[SIDOVRD;#[LZ(LUI;.JM:K^P+;%;Y;S +MON]VQ3=%Q-?V\XI]YJPT??4I>R)PH/^5\DNWCAL8H[[5^.N:`V/LS1N_<DS.ZSK)&+[SQ=D]]QJ.C![W12KK_ZR!HFU>\:.ITTSOBB=>NGW\ +MQBEER^G.EN91;3<-SY_4OZ#&Z_&OZH1,.OU/5[G92S4!UL-M?TZ9E]%W8ZLJ +M<8,:KA`O:NHX$15=UU4M,'WZ"W6V^6GI15?W'=T0IRKSKGOOXQNG5YQ[J?F8 +M@2[_(,'\CA.J]A'\'%:\X_:7EJE=^VK7=E-/;QL[PB.T4M'FY;E>,QQW_"IY +M5WD9VK1"ZZ7OA'UO[6C78J2^5X0\I/-TD\\Q[U[9BU_O6T +M#*\6-P*WW)V=:0E-41Y<8-_PW.!7?T^?X+I1RYT^5RT5QHQMZ-5XP9PI^5NZ +M[*[Z^JK[Z.B>XUO65$KF1J:TS=='A$54K*VXFEIF06:I]\4!Z4WZ'>J7EE[/ +M=W["T,MOGG4,T:A.S*[\IEJM`W^/]HA+4]0+*2X(;EW^8&W=N!:_&K_:/VS@ +MC9--RQ>M#^X\W3@M(+7]E+REI:_$M/\S.GK'_ +M;%7S5L7WK=K5J5>O:KA_9FK3"[N4/\9\K[Y0_B-&>7-ZO[-Y-]ZU:+)V>;IS +MMRY0>*N1K/<5W:B*ML3T31_K1KK_<_FQ4AN<:UL_/+FPK,?2O?[?%P^(<-/I +M+\3/?+VC<-F%TN$5)9V]8_PS.@Y:.W==5GZ-4JKSPAY=[ZS3QFY^^22J[9(V +M_J4^?_OGD<-;MM0>\J?[SS&B]](*P0NM26,O%GG<#RP_^4)NBN%6U>CPYT^_ +MMUYX7=.W_:"@+T<;5K5+6\8?N)GUY.'$^)?%,RIUVN*5\'%FT=B+O>37?5X] +MJWM]>[GVXA67ES3R_3K]_>P&Z_4CP@8&I!B^-MT[N>#;GSH#:K@[>I0==&[I +MCJ_;5$._YM?9_^UMS2,O']VZ$;WL?OR($=K5F2?C[3\S#MH:-"MN&[O='%JG +MW[+LXE&K8SKN;/`F;7-\C%!_;TIRLNW1BJRU;TL&-*G=/_*&JY^30+\,K^QR;?7SX#U\M*NFX\ +MF-.T\KMJ'9>>:&[<.?AV0],9^[9/]N"8X`[+Q]6HE5DU=.^`^*CU=ZY\F;MT +M^>_#O0/3.L6NLGV*RXW,7Z]L6NKWST9O]&T>=#STQ?CU +MNWW5BNYI&O@P(>O!E/U^2;6Z9_5J677ZAD=+?FGREO[[G_/+:0VD +MP]H/.5NZFV]0]>XKHOP>RJ[ZKF\Q5Y&S-V!>O_LS5PXY]ZEL_0FF4J7\;O0I\,'@E\0KP'<#+B9>!_USQ?WUW +MXJ78#UY"O!C[P8N(%V(_>`'QKESH]_A?_S/W__L/N=`/WDF\(Q?ZP=N)M^5" +M/W@K\1;L_\?_^DSBS=@/WD2\$?O!&XC78S]X'?%:[*_POWX9\1KL!Z\F7H7] +MX)7$*[`?O)QX&?8+8/_$2[$?O(1X,?:#%Q$OQ'[P`N)=.=!?'O:?0_:?`_W@ +MG<0[!WQ6NPO"_LG +M7H/]X-7$J[`?O))X!?:#EQ,OP_XRL'_BI=@/7D*\&/O!BX@78C]X`?&N;.AW +MA_UGD_UG0S]X)_&.;.@';R?>E@W]X*W$6["_-.R?>#/V@S<1;\1^\`;B]=@/ +M7D>\%OO=8/_$:[`?O)IX%?:#5Q*OP'[P@';R7>@OVN__69Q)NQ +M'[R)>"/V@S<0K\=^\#KBM=C_]W_],N(UV`]>3;P*^\$KB5=@/W@Y\3+L_^?_ +M^N[$2[$?O(1X,?:#%Q$OQ'[P`N)=%NC_`_NWD/U;H!^\DWB'!?K!VXFW6:`? +MO)5X"_;_AOT3;\9^\";BC=@/WD"\'OO!ZXC78O\OV#_Q&NP'KR9>A?W@E<0K +ML!^\G'@9]O^$_1,OQ7[P$N+%V`]>1+P0^\$+B'>=A?X?L/^S9/]GH1^\DWC' +M6>@';R?>=A;ZP5N)MV#_=]@_\6;L!V\BWHC]X`W$Z[$?O(YX+?9_@_T3K\%^ +M\&KB5=@/7DF\`OO!RXF78?]7V#_Q4NP'+R%>C/W@1<0+L1^\@'A7%O07P_ZS +MR/ZSH!^\DWA'%O2#MQ-ORX)^\%;B+=C_!?9/O!G[P9N(-V(_>`/Q>NP'KR-> +MB_V?8?_$:[`?O)IX%?:#5Q*OP'[P"/V +M@S<0K\=^\#KBM=C_'O9/O`;[P:N)5V$_>"7Q"NP'+R=>AOWO8/_$2[$?O(1X +M,?:#%Q$OQ'[P`N)=IZ&_"/9_FNS_-/2#=Q+O.`W]X.W$VTY#/W@K\1;L?PO[ +M)]Z,_>!-Q!NQ'[R!>#WV@]<1K\7^-[!_XC78#UY-O`K[P2N)5V`_>#GQ,NQW +MPOZ)EV(_>`GQ8NP'+R)>B/W@!<2[3D%_(>S_%-G_*>@'[R3><0KZP=N)MYV" +M?O!6XBW8_QKV3[P9^\&;B#=B/W@#\7KL!Z\C7HO]KV#_Q&NP'[R:>!7V@U<2 +MK\!^\'+B9=C_$O9/O!3[P4N(%V,_>!'Q0NP'+R#>E0G]+V#_F63_F=`/WDF\ +M(Q/ZP=N)MV5"/W@K\1;L?P[[)]Z,_>!-Q!NQ'[R!>#WV@]<1K\7^9[!_XC78 +M#UY-O`K[P2N)5V`_>#GQ,NQ_"OLG7HK]X"7$B[$?O(AX(?:#%Q#ORH#^)[#_ +M#++_#.@'[R3>D0']X.W$VS*@'[R5>`OV.V#_Q)NQ'[R)>"/V@S<0K\=^\#KB +MM=C_&/9/O`;[P:N)5V$_>"7Q"NP'+R=>AOT%L'_BI=@/7D*\&/O!BX@78C]X +M`?&ND]#_"/9_DNS_)/2#=Q+O.`G]X.W$VTY"/W@K\1;L?PC[)]Z,_>!-Q!NQ +M'[R!>#WV@]<1K\7^![!_XC78#UY-O`K[P2N)5V`_>#GQ,NRWP_Z)EV(_>`GQ +M8NP'+R)>B/W@!<2[3D#_?=C_";+_$]`/WDF\XP3T@[<3;SL!_>"MQ%NP_Q[L +MGW@S]H,W$6_$?O`&XO78#UY'O!;[\V'_Q&NP'[R:>!7V@U<2K\!^\'+B9=A_ +M%_9/O!3[P4N(%V,_>!'Q0NP'+R#>E0[]=V#_Z63_Z=`/WDF\(QWZP=N)MZ5# +M/W@K\1;LSX/]$V_&?O`FXHW8#]Y`O![[P>N(UV+_;=@_\1KL!Z\F7H7]X)7$ +M*[`?O)QX&?;?@OT3+\5^\!+BQ=@/7D2\$/O!"XAWI4'_3=A_&ME_&O2#=Q+O +M2(-^\';B;6G0#]Y*O`7[;;!_XLW8#]Y$O!'[P1N(UV,_>!WQ6NR_`?LG7H/] +MX-7$J[`?O))X!?:#EQ,OP_[KL'_BI=@/7D*\&/O!BX@78C]X`?&N5.B_!OM/ +M)?M/A7[P3N(=J=`/WDZ\+17ZP5N)MV#_5=@_\6;L!V\BWHC]X`W$Z[$?O(YX +M+?9?@?T3K\%^\&KB5=@/7DF\`OO!RXF78;\5]D^\%/O!2X@78S]X$?%"[`2;PC!?K!VXFWI4`_>"OQ%NR_!/LGWHS]X$W$&[$? +MO(%X/?:#UQ&OQ?Z+L'_B-=@/7DV\"OO!*XE78#]X.?$R[+\`^R=>BOW@)<2+ +ML1^\B'@A]H,7$.\Z#OWG8?_'R?Z/0S]X)_&.X]`/WDZ\[3CT@[<2;\'^7-@_ +M\6;L!V\BWHC]X`W$Z[$?O(YX+?;GP/Z)UV`_>#7Q*NP'KR1>@?W@Y<3+L#\; +M]D^\%/O!2X@78S]X$?%"[`2;PC&?K!VXFW)4,_ +M>"OQ%NRWP/Z)-V,_>!/Q1NP';R!>C_W@=<1KL?\L[)]X#?:#5Q.OPG[P2N(5 +MV`]>3KP,^[-@_\1+L1^\A'@Q]H,7$2_$?O`"XEUFZ#\#^S>3_9NA'[R3>(<9 +M^L';B;>9H1^\E7@+]I^&_1-OQG[P)N*-V`_>0+P>^\'KB-=B_RG8/_$:[`>O +M)EZ%_>"5Q"NP'[R<>!GV9\+^B9=B/W@)\6+L!R\B7HC]X`7$NY*@/P/VGT3V +MGP3]X)W$.Y*@'[R=>%L2](.W$F_!_I.P?^+-V`_>1+P1^\$;B-=C/W@=\5KL +M/P'[)UZ#_>#5Q*NP'[R2>`7V@Y<3+\/^=-@_\5+L!R\A7HS]X$7$"[$?O(!X +MUS'H3X/]'R/[/P;]X)W$.XY!/W@[\;9CT`_>2KP%^U-A_\2;L1^\B7@C]H,W +M$*_'?O`ZXK78GP+[)UZ#_>#5Q*NP'[R2>`7V@Y<3+\/^X[!_XJ78#UY"O!C[ +MP8N(%V(_>`'QKD3H3X;])Y+])T(_>"?QCD3H!V\GWI8(_>"MQ%NPWPS[)]Z, +M_>!-Q!NQ'[R!>#WV@]<1K\7^)-@_\1KL!Z\F7H7]X)7$*[`?O)QX&?8?@_T3 +M+\5^\!+BQ=@/7D2\$/O!"XAW)4!_(NP_@>P_`?K!.XEW)$`_>#OQM@3H!V\E +MWH+]";!_XLW8#]Y$O!'[P1N(UV,_>!WQ6NP_"OLG7H/]X-7$J[`?O))X!?:# +MEQ,OPWX3[)]X*?:#EQ`OQG[P(N*%V`]>0+SK*/0?@?T?)?L_"OW@G<0[CD(_ +M>#OQMJ/0#]Y*O`7[XV'_Q)NQ'[R)>"/V@S<0K\=^\#KBM=@?!_LG7H/]X-7$ +MJ[`?O))X!?:#EQ,OP_Y8V#_Q4NP'+R%>C/W@1<0+L1^\@'B7"?IC8/\FLG\3 +M](-W$N\P03]X._$V$_2#MQ)OP?[#L'_BS=@/WD2\$?O!&XC78S]X'?%:[#\$ +M^R=>@_W@U<2KL!^\DG@%]H.7$R_#_FC8/_%2[`!%Q`NQ'[R`>-<1 +MZ#\(^S]"]G\$^L$[B7<<@7[P=N)M1Z`?O)5X"_8;8?_$F[$?O(EX(_:#-Q"O +MQW[P.N*UV!\%^R=>@_W@U<2KL!^\DG@%]H.7$R_#_@.P?^*EV`]>0KP8^\&+ +MB!=B/W@!\:YXZ-\/^X\G^X^'?O!.XAWQT`_>3KPM'OK!6XFW8'\D[)]X,_:# +M-Q%OQ'[P!N+UV`]>1[P6^_?!_HG78#]X-?$J[`>O)%Z!_>#EQ,NPWP#[)UZ* +M_>`EQ(NQ'[R(>"'V@Q<0[XJ#_KVP_SBR_SCH!^\DWA$'_>#MQ-OBH!^\E7@+ +M]D?`_HDW8S]X$_%&[`=O(%Z/_>!UQ&NQ?P_LGW@-]H-7$Z_"?O!*XA78#UY. +MO`S[PV'_Q$NQ'[R$>#'V@Q<1+\1^\`+B7;'0OQOV'TOV'PO]X)W$.V*A'[R= +M>%LL](.W$F_!_C#8/_%F[`=O(MZ(_>`-Q.NQ'[R.>"WV[X+]$Z_!?O!JXE78 +M#UY)O`+[P"?QCACH!V\GWA8#_>"MQ%NP7P_[)]Z,_>!-Q!NQ'[R!>#WV@]<1K\7^[;!_ +MXC78#UY-O`K[P2N)5V`_>#GQ,NS?!OLG7HK]X"7$B[$?O(AX(?:#%Q#O.@S] +M6V'_A\G^#T,_>"?QCL/0#]Y.O.TP](.W$F_!_BVP?^+-V`_>1+P1^\$;B-=C +M/W@=\5KLWPS[)UZ#_>#5Q*NP'[R2>`7V@Y<3+\-^'>R?>"GV@Y<0+\9^\"+B +MA=@/7D"\ZQ#T;X+]'R+[/P3]X)W$.PY!/W@[\;9#T`_>2KP%^S?"_HDW8S]X +M$_%&[`=O(%Z/_>!UQ&NQ?P/LGW@-]H-7$Z_"?O!*XA78#UY.O`S[U\/^B9=B +M/W@)\6+L!R\B7HC]X`7$NZ*A?QWL/YKL/QKZP3N)=T1#/W@[\;9HZ`=O)=Z" +M_6MA_\2;L1^\B7@C]H,W$*_'?O`ZXK78OP;V3[P&^\&KB5=A/W@E\0KL!R\G +M7H;]JV'_Q$NQ'[R$>#'V@Q<1+\1^\`+B70>A?Q7L_R#9_T'H!^\DWG$0^L'; +MB;<=A'[P5N(MV*^%_1-OQG[P)N*-V`_>0+P>^\'KB-=B?RCLGW@-]H-7$Z_" +M?O!*XA78#UY.O`S[5\+^B9=B/W@)\6+L!R\B7HC]X`7$NXS0OP+V;R3[-T(_ +M>"?Q#B/T@[<3;S-"/W@K\1;L#X']$V_&?O`FXHW8#]Y`O![[P>N(UV+_R?>`WV@U<3K\)^\$KB%=@/7DZ\#/L7P?Z)EV(_>`GQ8NP'+R)> +MB/W@!<2[#D#_0MC_`;+_`]`/WDF\XP#T@[<3;SL`_>"MQ%NP?P'LGW@S]H,W +M$6_$?O`&XO78#UY'O!;[@V'_Q&NP'[R:>!7V@U<2K\!^\'+B9=@_'_9/O!3[ +MP4N(%V,_>!'Q0NP'+R#>M1_ZY\'^]Y/][X=^\$[B'?NA'[R=>-M^Z`=O)=Z" +M_1K8/_%F[`=O(MZ(_>`-Q.NQ'[R.>"WVSX7]$Z_!?O!JXE78#UY)O`+[P"?QCDCH!V\G +MWA8)_>"MQ%NP/PCV3[P9^\&;B#=B/W@#\7KL!Z\C7HO]@;!_XC78#UY-O`K[ +MP2N)5V`_>#GQ,NQ7P_Z)EV(_>`GQ8NP'+R)>B/W@!<2[]D'_+-C_/K+_?=`/ +MWDF\8Q_T@[<3;]L'_>"MQ%NP?R;LGW@S]H,W$6_$?O`&XO78#UY'O!;[9\#^ +MB==@/W@U\2KL!Z\D7H']X.7$R[!_.NR?>"GV@Y<0+\9^\"+BA=@/7D"\RP#] +MTV#_!K)_`_2#=Q+O,$`_>#OQ-@/T@[<2;\'^J;!_XLW8#]Y$O!'[P1N(UV,_ +M>!WQ6NR?`OLG7H/]X-7$J[`?O))X!?:#EQ,OP_[)L'_BI=@/7D*\&/O!BX@7 +M8C]X`?&NO=`_"?:_E^Q_+_2#=Q+OV`O]X.W$V_9"/W@K\1;L5\'^B3=C/W@3 +M\4;L!V\@7H_]X'7$:[$_`/9/O`;[P:N)5V$_>"7Q"NP'+R=>AOT38?_$2[$? +MO(1X,?:#%Q$OQ'[P`N)=$=`_`?8?0?8?`?W@G<0[(J`?O)UX6P3T@[<2;\%^ +M?]@_\6;L!V\BWHC]X`W$Z[$?O(YX+?:/A_T3K\%^\&KB5=@/7DF\`OO!RXF7 +M8;\2]D^\%/O!2X@78S]X$?%"[`Z`?O)UX +MVQ[H!V\EWH+]8V'_Q)NQ'[R)>"/V@S<0K\=^\#KBM=@_!O9/O`;[P:N)5V$_ +M>"7Q"NP'+R=>AOVC8?_$2[$?O(1X,?:#%Q$OQ'[P`N)=X=`_"O8?3O8?#OW@ +MG<0[PJ$?O)UX6SCT@[<2;\'^D;!_XLW8#]Y$O!'[P1N(UV,_>!WQ6NP?`?LG +M7H/]X-7$J[`?O))X!?:#EQ,OPWX_V#_Q4NP'+R%>C/W@1<0+L1^\@'C7;N@? +M#OO?3?:_&_K!.XEW[(9^\';B;;NA'[R5>`OV*V#_Q)NQ'[R)>"/V@S<0K\=^ +M\#KBM=@_#/9/O`;[P:N)5V$_>"7Q"NP'+R=>AOU#8?_$2[$?O(1X,?:#%Q$O +MQ'[P`N)=8=`_!/8?1O8?!OW@G<0[PJ`?O)UX6QCT@[<2;\'^P;!_XLW8#]Y$ +MO!'[P1N(UV,_>!WQ6NSWA?T3K\%^\&KB5=@/7DF\`OO!RXF78;\<]D^\%/O! +M2X@78S]X$?%"[`"/V@S<0K\=^\#KBM=@_`/9/O`;[P:N)5V$_>"7Q"NP'+R=> +MAOW]8?_$2[$?O(1X,?:#%Q$OQ'[P`N)=.Z'?!_:_D^Q_)_2#=Q+OV`G]X.W$ +MVW9"/W@K\1;L[P?[)]Z,_>!-Q!NQ'[R!>#WV@]<1K\7^OK!_XC78#UY-O`K[ +MP2N)5V`_>#GQ,NSO`_LG7HK]X"7$B[$?O(AX(?:#%Q#OV@']O6'_.\C^=T`_ +M>"?QCAW0#]Y.O&T'](.W$F_!?AGLGW@S]H,W$6_$?O`&XO78#UY'O!;[O6'_ +MQ&NP'[R:>!7V@U<2K\!^\'+B9=C?"_9/O!3[P4N(%V,_>!'Q0NP'+R#>I8?^ +MGK!_/=F_'OK!.XEWZ*$?O)UXFQ[ZP5N)MV"_%^R?>#/V@S<1;\1^\`;B]=@/ +M7D>\%OM[P/Z)UV`_>#7Q*NP'KR1>@?W@Y<3+L+\[[)]X*?:#EQ`OQG[P(N*% +MV`]>0+QK._1W@_UO)_O?#OW@G<0[MD,_>#OQMNW0#]Y*O`7[N\+^B3=C/W@3 +M\4;L!V\@7H_]X'7$:['_7X73J=+3``!&48LA[!"VL$/8`F&Q:!0:]>NJSO0% +M:MC",M55U56=Z0M45U5'1>7P1?VX\\%/_VE_PW__PW_X_?X,?@!O\#WP9_P+?X8 +M_,'^AO^#W]F/WP:_L1^_#GYE/[X+?FG_:_X/?FX_?A;\F?WXK\%_L1__.?A/ +M]K_B_^`;^_%U\)7]^#+XPG[\N>"G/_2_Y/\_X7_\'C\&/^`7^#[X$[[%'X,_ +MV/^"_X/?V8_?!K^Q'[\.?F4_O@M^:?]S_@]^;C]^%OR9_?BOP7^Q'_\Y^$_V +MU_P??&,_O@Z^LA]?!E_8CS\7_/2;_F?\_SO\C]_CQ^`'_`+?!W_"M_AC\`?[ +MG_)_\#O[\=O@-_;CU\&O[,=WP2_M?\+_P<_MQ\^"/[,?_S7X+_;C/P?_R?[' +M_!]\8S^^#KZR'U\&7]B//Q?\U-'_B/^[\#]^CQ^#'_`+?!_\"=_BC\$?['_( +M_\'O[,=O@]_8CU\'O[(?WP6_M/\!_P<_MQ\_"_[,?OS7X+_8C_\<_"?[[_-_ +M\(W]^#KXRGY\&7QA/_Y<\-,O^N_Q_Z_P/WZ/'X,?\`M\'_P)W^*/P1_LK_@_ +M^)W]^&WP&_OQZ^!7]N.[X)?VW^7_X.?VXV?!G]F/_QK\%_OQGX/_9/\=_@^^ +ML1]?!U_9CR^#+^S'GPM^^DG_;?[_&?['[_%C\`-^@>^#/^%;_#'X@_VW^#_X +MG?WX;?`;^_'KX%?VX[O@E_;?Y/_@Y_;C9\&?V8__&OP7^_&?@_]D?\G_P3?V +MX^O@*_OQ9?"%_?ASP4\_Z+_!_S_"__@]?@Q^P"_P??`G?(L_!G^P_SK_![^S +M'[\-?F,_?AW\RGY\%_S2_FO\'_S__'_`0$$!@`!"<'E#``'"P$``@,$`0@!"@$`#,$P +M\L$P\@`("@%Y<_C^```%`1D!`!$=`&(`8P!J`"T`80!R`&T`-@`T`"X`8@!I +/`&X````5!@$`(("D@0`` +` +end diff --git a/libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z.uu b/libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z.uu new file mode 100644 index 000000000000..61afc1f6da5c --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z.uu @@ -0,0 +1,124 @@ +This archive was created by extracting hw-arm64 from test_read_format_7zip_deflate_arm64.7z.uu +and hw-gnueabihf from test_read_format_7zip_lzma2_arm.7z.uu and then using 7zip 26.02 to create +an archive using two different bcj filters for these files: + +rm -f libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z +7z a -m0=ARM64 -m1=Deflate -mhc=off -ms=off libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z hw-arm64 -y +7z a -m0=ARM -m1=Deflate -mhc=off -ms=off libarchive/test/test_read_format_7zip_deflate_arm_arm64.7z hw-gnueabihf -y + +begin 644 test_read_format_7zip_deflate_arm_arm64.7z +M-WJ\KR<<``1HP7@(`Q,```````">`````````/2RI_CM66<($S$43J[7VKKW +M'G4/M&Y%47'4O2?J#\_3GGI:V])>W0L';L6)J*B@B#\$-[@'N!!107\H*&[< +MJ.!>]67<)3WKP%_^,/#UY;OW\I*\)-@]@`!8@593A.DR1G:(^ +M!B\AA9G^8D%`1896\*`MP,?U"HAK!0$5&3X@!I_4Q@06"","H>O[T(H@=^+E +M"P/Z@5ZNOPDF$.VO'S5'U(]&ZD7-6'I2/3TY?LRH4"H>:HRX_^(\!IU[#42W +M+M]O9,:NGQG7:S@]VFGQQ[7?"P3M'E) +MZ(](10&E)5X%T(/F1#'LLA_CTD]T\9HN>Y1(6RFD:1">D5K*TI.6-EXW8_!D +MY"1=&V7&]*@YQ4#ZB'C20L2(1*T9ZMRC:_L.6J-0$R?7N`G2N@[HJ46,I#': +M3%E&X_%")237(Y'/@)6!07Q'IA35%9!Z$OA`) +M"^X.D5##`R+![C&1`5AC;>L]*831,_^L8X_5._4>JG]7NPR%!91Y?^EEYOV#E^O*(527Q*7!YH?%@YL? +M>AHLOZ,$5SV"E7&_8J8^?IC)/)T%-K7(FNF2IWN>;I/"4\-3PG/:>ZYXNU\= +M[-E3NG.B,SJQ"6PV`H8!JI%W2)=`]TGA0+>IX7)EIX1O%,+/MN*NA;/+6&"; +M`#0@(&4.Y.V>MQOQ7F[2N$,!L*RZQ]]]:KCTF,S[&=#:$QE?=UQE:KALPG^L +M33DH4([-S3H@*P)./T(G9AV8O1OMFC2\Q7`O*=DI\W[42U2U="=_\V.=9AU# +M)W8C%GL2=S(W6@-O"\C'96$N:W#9E$L/ESXNBW-9D,L"7%;GLBB7M;A4N"S- +M98#+O%2*-,:(1N/!B?%D-/+/M6W'U\S,/WE/_4__T__T/_U/_]/_]#_]3<*P +MMSZELG-*/4!^?OXI`+(6Z,KPO7HY?@XKR_?LY3E_^RT3WX_9.3.JL+W11?ZV'YL$?LG8KQ%YJ"SNG(/]5(8='J!RKL/SYCPW'W1X?BK]JLT+4%G+:_."5#9P>"$JA_ML +M7IC*,0XOPL^0-B]*Y16'%V-CZ_#BS+Z2S4NXS_6L?0XOQ2-N\](N7B;K7*^B +MUYD`C[_38\KY'0"M0?2?K"YBWT3FHK]TQ018/'@J!%RT%X&W,J[ZB:J5Y*^J +MQ!7N?["D#XKQHJLT*,:#GF@:Y/"_WV5?5[)OGB,>3:3VMY5J*9TGRW)O= +MWVJ"YQR/*Z[QN"*-1T"L+_I&OB=Q3-:;R]\K:7UAX!^E]87Y?!'OS_S4_WF) +M$_U%B<,=3=:]5UV)5P%T%)R6781YAHJ22,BIA'1 +M1HTPX9927"UJUGAM)+DS)%>7D;@V.AH?H4>UB!5/IC0]/0F-C$-)PS(BH0:Y +M+O=NWZZ'U[M2I?\T5TP%38T0A484S([>]0KW67>RS#05U\;HL0AQ +MW[4W*")F3$NGC`@";_!C^Q15(WY9#$TC@?C%S2Z_0Y:JIW%%*)2:/-[21X"T +MDDR.L7-F#'PD4"@6MXS0Z%@Z-")MPAV^&>&/VK7O6H],*ZH;HZ?&H%!D<@S\ +M40E>F&:"D4R9\5@6T4"7-*+$CF4248M4:,*O94R"7Q)*4,5I8$/M^O4,&9/, +MR"04,L:PV0'FSL1@YBQ/*]?'FR.APC@X8@X@9"@$TW(\3"'F3K>LI#DB;<$D +M_O-4#H#I.U_^5B.2O4RK`O)0._%-I5K6.Y:E9I+=:LP0S&$7!KR#O:]"]WT, +MI:6_))6C%]^3*W0_R+"%WVEZ`/9WE$'2]Y.HPK`B1S^&`3*\WF\*0W&I7H5C +M'-%SN^(>A@:\7BSU8PH@P,N$/0QM7?WP`^9PN_9T'\I0B]N5D.R6\F:;XT\'+D\+>=V25F +MH^QOB`5<=GLD?V-\#&J.>H\`"M(QYM_D"KKL.,X!"G&[*Q4!/[&[ZLPA_@VQ +M(/]^Z!JWF[1]XAQX$9PWR>'OKK!C$[YP[O@]XGX]?/_D+TQT/\[3YRS/][7, +MKH9LQ_&6^4K8ST-@U]CMSXFQ2)'"9$S8^N\MK:,`]2?2:=BXU59^]%?$9?>B +M-,QE_*/==^S57XA,41S`\7/G#DO^Y%\H_V97(?E/_L<8?X9":+W1F+6#J;&C +MV9$-97A2-ID(#TBBD`OY_V +MW-^>WSV_<\_<>^[,H95K5P4,0SE,=5M9O7#O2C^L\QW]K*R3FZ_ZRG&T&J6L +M84%W7/?XP_#'/LYU=-W\@!RZ1YG9'PU/[*5ZUM['C4Z%<[37>J(D!SL:OIC3 +MUST:\-<%=-U%J\Z.AB\6[%"-SF6#NC7:\W6/*Y0_!G7<\#';;/U_N4X.->)B +MI6.7NHU2UUO]NT$Z;K*N]Y?[LD?66XW5YS`]E6R:GFJ>FDJV[&V;&H]GMN^: +M.V=::WK:S,J:ANEG'%V_67V<.'G"\3-7VN_NW;KXQL&%`UI_-NP)ZC48E3'N +MGJAS[K[;_I^I^JD[O6KEA]M/U*6?[9`:[.M*A:3V[X]UIJ-9[*QW?%DBV2VM\5C.Y(M\51R?T+%F]*9K+(& +M6<]BKHJN71-9'ILU;>8\]]_9.Z6',BD]B9;,TF,HWKEJ?2+8G&>%,J +M(1/NW)UNT9>(58;6'%@1D$]BVLVL/"_)3/7LKS/YT[VM.S]/YVY)WZJ**/]^ +M,X-Z_^L-<-:7KVZ,0MF?;ZQSWKN`SOO[K_1X0_+RY^IP\_[OD\^>O/=]^N') +M]_7DK^I\(&A]RJJ;3K[+_'>UF07$%NT^!GT>*@(TORH<"XE]98I]GG7LJY2Y5S=O^U]!]Y +M^N^DW^GI=TH_L+3:_R3]AJ7NW&-4[ME8:1]-=?V]&2YVCE?Y3[*N#AFK?D2+ +MIIP+2CLHZSPJSEA51NT[:( +M6O"M7[A8.O#HJG?];^3S.'-VC%-WW\N\UC97V\;FVZ5626VUIOH,G>=GZ'V[ +M*Y%*I4/[TIE4LS)&F8OFZ^^J<[_+Y0T2OTILECBZ7"Y;WQE;)+9+O"'QIK7_ +M)#[VO(?&_DTJV#;6&-5_G)T;*6V%S#''.KFL;JR5FR(M)[D9GM^(L+ZF\WL3 +MLMXY:U]*;K5=.S!T/GC!C)P,K'_W]OE`I\Y:TW<9X[QSHZ59Z_L@N4F>NM7O +MW@Y4```````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````/"G +M'6L)<2((HC6SG\3__R\2U(,*"9OU+X(;_X(_-`HBVDPRLYO19+),)II5#QX$ +M]:"(>%@$0="#@@=/XE%O'A0\"EY4U)."@A]4--8DE=GI265W#^)IWM)YZ==5 +MU=TU-;V9"1$B1(@0(4*$"!$B1(@0(4*,"GLC4`>Q0MTX\7CBAQUUGD#],V0_ +MA_I7?M<]YU$_2CR7>)#&YP?&O_ZI%FOC*M300?J=MCIW-N:G\;'4OTH\CG@V +M\0R0T=987U6IL4K]"O%T__81LXA[.F3];+N\[D?$8P+S+P#9[E>UOC^%I#_4 +MWTOQJMYX'9^HOX[&?U"_'?XOGG>,CF_B=1T.Z0BPB-*5V+9IT[K8D@.9LN64 +M8\GE\>ZN[N5=W^7:M6OC?_"JNWPR"K=\!# +M5N_$.N?T"%Y_3H_"7E8?`V=9?2S6,:>/@X>L/AZ>L_H$Z.GD](EX#G#Z)+C" +MZI/A-JM/@5>L/M4[5V1]&@RR^G2XR>HS\/[G])GXJ3+Z+)C,ZK,A"*5V;WZN +M@@0Z%Y@Z&>_Z^/08Z3,!07ENCC,%OD08GKA-/ +M9ZY[G)_7B_.D19P+@3@K1EC_E<#Z-V!3&3U%^B,VGS/I_!E"FNPAL)YCV!1? +M/2C^.$P=GG!M&/T&Q=_+74>FSN]B4TCGZB$:9>(P=?MDV/JCHKE?#_D+`_ADVA709?)R7KCWJP?OZ+2"8\^0[V3^O*LW[8LZW"8J;_68L +M0ST&S;CHZLRYL17UR4S^TZ@OA&:<(?O9`7NKIL]LBE-VUZDVWW>G6\0Y[^K, +M.J^UV-HS??%_1;Y>>S:^\Z?]L;OAA;VKUJL\Y)K[SOW5I#^KH7]QQ9Y +M_D%Q&G6[A/0_M*^@_5B5S\-BUBUM8(A]'*A,(`N +MOIY`2TV;]X'8 +MMG//QM1.L6?KUOU;TB*=VKAS"WJ(O)G)BI*CV8XH:*;5LVWGCHV;1'=B^0H0 +M.]*[Q-!^T[LVN;M-:YF\@8Z94HG\W##9BN;N1LN;IPPO1G(U"$/7'`UJ.R4G +MP])K+NY`/0)V^@I%RQ=.+Q5%3K-TG$G+%&U'"KEC#UKJIB7*)4.'_K)3DH8Q +M/@B:Q;=,<+>'`FZ#TE7;'[N[6K8!$J6!@J-ED!V[SKG&-]-"GWY(6$7'2/19 +MY42F;.;UN*F3E-JX(^[656TLIY5RD-`'+(Q78XQ2'SEAV"6S:$D=@6.VD==< +M0_K6GW?<*4W\=(P*?KKIQ+%B+;D)(T>EDM/MH1YZ>#53]VA\Q\!:P8($5JS[8QS^!>9A4WRGUAK58QJ7H8",1=@B/O^T*O/BIO\Y,E8%_"NJ +MS+$1_#=C^X;/;`W_057F^:1W>,]!,G;3,ZWJ/??*_(GT3FQM/O_9Q`=)5[WG +M:)FOCI"_H]BJOO6O:Y=Y>F#]:H"/8_OC\]_;+G.7;_T*L_]3E-.&_]EVF1_Y +MYV?V?X[\-WK/\3)7?/XS&/_+M*Y.[[V'S+-'N/X7&_XMGJ/[0<9DD'$MX-_3 +M*?-Z!23T@(SK`?\S$9FGC[#^FX'[[TK$8]F_1?W>"?C?CLB\?H3Y'P3\7T5D +MOM=Z?J\^)F)KD]]+(?/VT0`_Q39IR)]^QR./TO^%=V]Z[T>(Z?Y5J/X]/[D. +M7M/^?>^MB!OU,_S\[V5_7R85FF?X_'T@KZ-![Q11S_X[QUB[C8;3NOTP=?OU36OB_&5NW_`G#^_\%`00&``() +MB-Z*)0`'"P(``@,$`0@$`P,%`0$``@,$`0@!"@$`#)Y\GGS!X!+!X!(`"`H! +MX<1>-935E]X```4"&0P````````````````1+0!H`'<`+0!G`&X`=0!E`&$` +M8@!I`&@`9@```&@`=P`M`&$`<@!M`#8`-````!02`0```TX94)S9`3)5M-E? +1M]D!%0H!`""`_8$@@/V!```` +` +end diff --git a/libarchive/test/test_read_format_7zip_deflate_armthumb.7z.uu b/libarchive/test/test_read_format_7zip_deflate_armthumb.7z.uu new file mode 100644 index 000000000000..b7bd75501f64 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate_armthumb.7z.uu @@ -0,0 +1,46 @@ +The payload is 96 KiB of ARM Thumb BL instruction pairs, every one of which +the ARMTHUMB filter converts. It was generated by running the filter's decode +step over a buffer of identical encoded branches, so that 7-Zip's encoder turns +it back into that constant buffer and the archive stays small: + +def decode(buf, ip=4): + b = bytearray(buf) + i = 0 + while i + 4 <= len(b): + if (b[i+1] & 0xF8) == 0xF0 and (b[i+3] & 0xF8) == 0xF8: + addr = ((b[i+1] & 7) << 19) | (b[i] << 11) \ + | ((b[i+3] & 7) << 8) | b[i+2] + addr = ((((addr << 1) - (ip + i)) & 0xFFFFFFFF) >> 1) + b[i+1] = 0xF0 | ((addr >> 19) & 7) + b[i] = (addr >> 11) & 0xFF + b[i+3] = 0xF8 | ((addr >> 8) & 7) + b[i+2] = addr & 0xFF + i += 2 + i += 2 + return bytes(b) + +open('thumb_payload.bin', 'wb').write(decode(bytes([0, 0xF0, 0, 0xF8]) * 24576)) + +Being larger than the reader's 64 KiB uncompressed buffer, it also covers the +carry-over of bytes the filter cannot convert until the next chunk arrives. + +The archives were then created with 7zip 26.03: + +7z a -m0=ARMT -m1=Deflate -mhc=off -mtm=off test_read_format_7zip_deflate_armthumb.7z thumb_payload.bin -y +7z a -m0=ARMT -m1=LZMA -mhc=off -mtm=off test_read_format_7zip_lzma1_armthumb.7z thumb_payload.bin -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archives. + +begin 644 test_read_format_7zip_deflate_armthumb.7z +M-WJ\KR<<``3O%M9_@@````````!N`````````#-#2#?LPS$-`````B#[I_7T +MM`ALI)FJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ +MJJJJJJJJJJJJJJJJJJJJJJJJJF\?CFD````8!LV_XITU`@FJJJJJJJJJJJJJ +MJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJI[`00&``$)@((`!PL!``(#!`$( +M!`,#!P$!``S!`(#!`(``"`H!%7`HI```!0$9#P```````````````````!$E +M`'0`:`!U`&T`8@!?`'``80!Y`&P`;P!A`&0`+@!B`&D`;@```!4&`0`@@*2! +"```` +` +end diff --git a/libarchive/test/test_read_format_7zip_deflate_delta.7z.uu b/libarchive/test/test_read_format_7zip_deflate_delta.7z.uu new file mode 100644 index 000000000000..5e4b00438f64 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate_delta.7z.uu @@ -0,0 +1,61 @@ +The payload is 256 KiB whose byte differences at distance 4 compress well, +so that the archive stays small, generated by this small python program: + +open('delta_payload.bin', 'wb').write( + bytes((i * i + (i >> 8)) & 0xff for i in range(256 * 1024))) + +Being several times larger than the reader's 64 KiB uncompressed buffer, it +covers the Delta history being carried across calls to decompress(). + +The archive was then created with 7-Zip 26.03: + +7zz a -m0=Delta:4 -m1=Deflate -mhc=off -mtm=off test_read_format_7zip_deflate_delta.7z delta_payload.bin -y + +begin 664 test_read_format_7zip_deflate_delta.7z.uu +M-WJ\KR<<``3UV:A93@<```````!>`````````+OYY3_LSQ%7&```1M$Z9[#9 +M<#@<#H?#X7`X#,,P#,,P#,,P#,,P#,O*^A$?O<[E=Q[<@\-/7[Y^^_[CYZ_? +M?_[^^W]T?')Z=GYQ>75]W0_[AKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W +M?O[EKW=^_N6O=W[^Y:]W?O[EKW?^=_;GD`8``(!AF'_7%W&TI'@9*/_SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL_ +M__/7.S__\]<[/__SUSL___/7.S__\]<[/__SUSL__]B?0QH```"&8?Y=7\31 +MDN)EH,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/ +M7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O_/S/7^_\_,]?[_S\SU_O8W\. +M:0```!B&^7=]$4=+BI>!\O,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^]\_,_?[WS\S]_O?/S/W^] +M\_,_?[WSK_TYI@$`@&$8QI_U0/2*YCO*8?[EKW=^_N6O=W[^Y:]W?O[EKW=^ +M_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^ +M_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^_N6O=W[^Y:]W?O[EKW=^ +M_N6O=W[^Y:_W[_X#`00&``$)ATX`!PL!``(#!`$((0,!`P$`#,0``,0````( +M"@$;QDD*```%`1D`$24`9`!E`&P`=`!A`%\`<`!A`'D`;`!O`&$`9``N`&(` +0:0!N````%08!`""`M($````` +` +end diff --git a/libarchive/test/test_read_format_7zip_deflate_ia64.7z.uu b/libarchive/test/test_read_format_7zip_deflate_ia64.7z.uu new file mode 100644 index 000000000000..0088d4e5ea28 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_deflate_ia64.7z.uu @@ -0,0 +1,69 @@ +The payload is 96 KiB of IA-64 instruction bundles whose three slots all hold +a branch that the IA64 filter converts. It was generated by running the +filter's decode step over a buffer of identical encoded bundles, so that +7-Zip's encoder turns it back into that constant buffer and the archive stays +small: + +branch_table = bytes([ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 4,4,6,6,0,0,7,7, + 4,4,0,0,4,4,0,0]) + +def decode(buf, ip=0): + b = bytearray(buf) + for i in range(0, len(b) & ~15, 16): + mask = branch_table[b[i] & 0x1F] + bit_pos = 5 + for slot in range(3): + if (mask >> slot) & 1: + byte_pos, bit_res = bit_pos >> 3, bit_pos & 7 + instr = int.from_bytes(b[i+byte_pos:i+byte_pos+6], 'little') + norm = instr >> bit_res + if ((norm >> 37) & 0x0F) == 5 and ((norm >> 9) & 7) == 0: + addr = ((norm >> 13) & 0x0FFFFF) \ + | (((norm >> 36) & 1) << 20) + addr = ((((addr << 4) - (ip + i)) & 0xFFFFFFFF) >> 4) + norm &= ~(0x8FFFFF << 13) + norm |= (addr & 0x0FFFFF) << 13 + norm |= (addr & 0x100000) << (36 - 20) + instr = (instr & ((1 << bit_res) - 1)) | (norm << bit_res) + b[i+byte_pos:i+byte_pos+6] = \ + (instr & 0xFFFFFFFFFFFF).to_bytes(6, 'little') + bit_pos += 41 + return bytes(b) + +# Template 0x16 makes all three slots branches; each holds a branch +# instruction with a zero address field. +bundle = 0x16 +for slot in range(3): + bundle |= (0x05 << 37) << (5 + 41 * slot) + +open('ia64_payload.bin', 'wb').write(decode(bundle.to_bytes(16, 'little') * 6144)) + +Being larger than the reader's 64 KiB uncompressed buffer, it also covers the +carry-over of bytes the filter cannot convert until the next chunk arrives. +IA64 has the largest carry-over of any of our filters, up to 15 bytes. + +The archives were then created with 7zip 26.03: + +7z a -m0=IA64 -m1=Deflate -mhc=off -mtm=off test_read_format_7zip_deflate_ia64.7z ia64_payload.bin -y +7z a -m0=IA64 -m1=LZMA -mhc=off -mtm=off test_read_format_7zip_lzma1_ia64.7z ia64_payload.bin -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archives. + +begin 644 test_read_format_7zip_deflate_ia64.7z +M-WJ\KR<<``2^89XYR27J7U($*/OB5Y)?[W??=?7?W77IW63YXQ!!1$(`E/O`6X-2Q +M,C,]@/+M&II81KA>H"*Z-P:-0`"E_;:>&T,BB^6M>JA=EO!N;`Y8%!R[++:3 +MJ@-/&<^E!0N)[Z6E53D6077;CK3Q6!4S?:P)BY.IOS-$UDZD=E>IW=4F++X6 +M+&3[Q4^O;:0\-PX"+/HICGEJ*/CY&*[4C6BL*')V8Y%=`/R\6-T^#M?WG7X9 +M(UK(CD/GI#:S.N7_[J2\"U;'LJ@&G_\L5C1]5"%K#.W?YGJ7? +M;2BIOS*>!X[0LFIZZ,;0%?3@A1+Z]='5PH./ERBG%BX'^9.@C>E@963S1@Y` +MB+HU#G.&K!LP)6MIQ,07RG"6EI:3VF(5X&SK2TW[LV@W` +M81-&0D75U=E:SE#U"2,')C-I=8(\,ZF:>5XYM*=1&WWH)Y"V"BC=MBA.UFW9 +M$<`]&:;<"93&-GT!&R\^FKY)^_LDQ\^B[3['\1TI_[P9RR>L00]:/)M^2/5% +M.X_.QR*^>.@_%O'%\^4JYY5T5-]<+-T#!U0ZL6H&MQXY!32Y?+[QH(<;X^L.(6[@$V3[< +MY+5/>CJ1NIT>)AO7!D`-9;'?.# +MP).UCZG&ZRU73)^"8-5.Y,>KM;&6X!TX.$-4(L*J%=I9H4;@98OGH/J1=2U# +M)[ZV;//NJUES8#_2OK$RUF_MHQE1I4S&7@,SCLQQ5)/)3,?@@HR>5`C?R->G +ML=77A4*A&XX5A$.Y]Y-O\3@@+!6$1E5$Q)GS[>+70H',N7(":(5C!Y>/;,E< +MBE8-#EHIKO='*Y<'4\]'KU86@]&JX+_\E__R7_[+?_FG1:#8EF*06U\U!^P^ +MHP%@UUF-N/R&@%UO->;RWW\M9##>%>DZQ]+WV>MD9N%5D2:74:Q$L3[%.OS: +MV_+/7G>9$J)8FV(YBO4H#BAC^15^UN_7%"MP]3?EVO>Y8+9/H-17FAY#RRM8 +M^5:Y--V/YG^BZ3+P=Z1`Q=X&E%EH[S-_26(#!_8.MILX,Y\V\L%P5ZF+%.K4 +MI6N>I+NUIPR(C$.J9-,&A*%X95N]3$?[-ZU+-@Q37;(A*..G65VR7:"";W', +M+=;B.=U\F*6FXZI&GE.I?#*<#8&?$+RWR(I>O`BV>?'H=]*3]X.[GGP9NGGQ +M`7#.DR]'QMG-EP=C//D*8(4G7Y'$JYNOA,;QY_:C)E\%[`IX\57!,4^^&KCA +MR5?"UPU9.OC>>SF\>S/^C%UP7EO7@ZV]U\?14Q*\#YW.4UX=WQN0'QUU/_5 +M.7X[X>NXXG9O"?XHX=WEGRK!7T5\#='-W\9\T7O`>MT\*>'G>=P/=IP[9U$O +ML#X>EW:L_N<2_59)).6[VE5/]#ZO:BQZGWOU$$F[7.5$1=)4MXR/ZT98RJ"# +M+GFF!@UY-FBU\'?_BY(968&SLP`5G3/RLV9)<>"/X[`L?MRD9.#N9 +MF2DGH6)D]!R4\PM!/)/*)E5#5:20MP8^CM.@K.OR(JBF#7T1F*7+*14J^51J +M$3(I2D&D:3"JB052'.D,&1<9.1@.'C4(0@#'C!L],&PQ'#1@V/Q`;# +M"9'HB,$H9]"449&1PP8B`W0X"@,F3\X`G4PGV" +M..!G3P=A=/QXRR%5D0T94>,'129$8'1P;-@H3$,NB;7,JG`BEX$).:V0D\;1 +MB%:T-,SG5`45ET8WJQNS&2V-*F]%3CP'.,>82&-F+D>+\SC[9%61[Z:SWSO6 +ME'*+4H8\$Z&AFYBPGH@/62"E,X8JS4[GI9EY#1UG:PJE(M%AG7`PDKR$G$L` +M25F41N411*68.?-5/:=ETDP"HCQ=32FJ!QDE!T +M)P6DK*XZ,8/]=)Z=L"-.R"DMCBK.H$+-(E'W`0E%;TI-(XXL[R39,'1M9MY` +MT?Y'I!']CQ+!][_W./_;K+1$5SG;WOU=I)7KOX65'IS]29'%X`_L!Z'K`UK3 +M6_9W18I<_67V/H*5473/(]J+%1;/T62`ZI3G]B>3J*IH[[-87/:#_IM.]RR6 +M?3\_B]4Y_T4.Y](]D)4>XV+WZZ]1PKYV`Q.K"=^W_P8!!`8``0F(NX6*J`]\W5V'E1<]9A +M1&J5D`#J!0K[IET\(=96D87RO?!\YQ>LH!0''871$*/B-@-70EJ0`;\!OE9C +MF-^[@!DI0LA7Z5P`#.!8`&65OAKG.CZ"F_H1?0DF"U1TA(*8JEQ>/>U8FBB]^E)QG>MQ_LLWD%-Y +MVI_%$>)09X;WO&$[MP([#E"AI +M4U,3VSH@/S*]#&EGP37GQ_#W*+P:2"&0(O=$S'+O'75NH+-'ZI&29W^I\?.Y +M`$T4YPC[ETIN9KTL\>"^-]*+([,!RK2%12BUU!TRF!5:8B?]<3K'1/*E5"22 +M231Q(@S6P/`GO*EAHOGEC)B5=%X;>B*\-D*4#KGX(2,,P4)Q\PQR/ +MAC>SH1S1_FUK7"#5\]0&QR9`];@`YS*UH9Q@P^Y/XD+S/4D;L(F1;G@QAN$Z +M`QL?8J-REA]%UT.SO^UK`ND3CQ&)8*I(!67M^R.W;)M:2*87.WX/@U,P#S=P +M38^J!/1-'@*D;)S6V0+;M#H)UE4'5FE9!H'P>(#5%8?]G@1KK1=0T/I7 +MPO9"$.C'+Y*:=Y&UUE9P/A8[FIZ,2X.41=T*OJ%\+#Z3T'N.+_F>E8N\%\A$ +M^6#@_:$H,'4<;FPSQ!A2*LD';Q,\M5X>0]Q1_<';1]DAU:A%FPBL?=0.+X7N +M4)K[T!2B2%1AM+%5TZ]-]@$P!X,.==5*,Y8J#5VTCQ"8AF\VQQ(I;AU+H*E' +M6#B`*7:\@=0/Z8Z]@U@(`#KK9G(_JNWW-L/6/4(\(_AR!LEBVR67>._AXF<5 +MG>YX_N7MX??+9(:A;PFGR7:FL8"KE^A:DY`7>]"DXZ/;%V+W4YPB:7YYMHM$ +MWC:N*U%1_0Q+%(EN\$K^D6YR&>R>2U$D6S>VU1:67CN@?`6"N,I,__M-9RCY +MG:2^R;CX&Y*^&C@3"@M.*GB029R4L&#-T%"5U8O@>URQ+]\Q,`?PUG;-Z#7G +M4KXN=&6!9IA8QW5ZP,B:;1RM=.,3O"3I'Y?A0L5\FM$R*,_>;U!R(:DCO@@D +MFN:).\!>ABW>@C:MM]]`%6=%2M`QYJ@/\T1LMA\$+A^Y_"*".QA508O)%=!VN&;%T!X7/"%_2 +MYH2;YZI,.F2`5TE[T[<^9CTL!46`/&F\BEZW@$)9/9"E@?!*_;2/05LFE,TN +M9"N%RA)*+ZE&\VQA+0C7X)<'-OM!>5#B^RJK+\\8E!(S?C^.5XR.1 +M>+3WUN:.05!_8_3T;+(8J*T'F\W +MYJ!:$L!I[#)B?Z<9=\0,\`Y@FZ0*TC1`],NQ!?\+D:OW7]%JRN@*6O`=OI@( +MX<')%-4[:;8?0/G_1@6V4:\H`=T%(58;EH6&J#8S(&"'C4FO1QN0=`S5SL>2 +M[+SU#RL'LD=?9KD(5&'P1O2P"8-45XH6R!K^[O]6,*:?#?3V,L'W2L:W!5#R +MC4SPG&3,R$D![$5^A8.R!Y5N@LNC),LU3:6IXV1MJ3,&>TRM8 +MAU?HMH:I=$[5K#T8H3IT,PIF$Z<2Z?/[>L-*?$:[3VZUHKB.!$5#UDC"(*#5 +M"4Q#B8=FB:G?@P?9.DSNF(:J='^+PZ^,0==7ZS_OR"&%`LDVBA2$T:LUERR4 +M:8ZV^Z4HCYVGT#->J45`;BN1-8/CQ06.UQ/,CRU^G/1]3%!202QP<:=#`!$. +M-X\`!RQ:FVEOB:G+%3ME?%64/BJ'/Q2/\-/1#U7'+B+(TPNRNZKCGA`*+(HE +M?RR=_R?D=O1\RA(H6G>,\YER*9R55)S+B#24FYW3PN_%'5;^+^0S%$"0Y:T' +MW!`XR^GRC[5C1VM?;2MQ$:@7\SA,C<:__(]+UIUJ#P,*`;XZSV[.T3$5Y0*" +MS6,M[AX..(T9N+#NF6%7TLDYPTHBBX?H[+L"`#%0`00&``$)A[H`!PL!``(C +M`P$!!5T`(```!`,#!0$!``R>?)Y\``@*`>'$7C4```4!&0L````````````` +J`!$;`&@`=P`M`&<`;@!U`&4`80!B`&D`:`!F````&0`5!@$`((#M@0`` +` +end diff --git a/libarchive/test/test_read_format_7zip_lzma1_arm64.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_arm64.7z.uu new file mode 100644 index 000000000000..dcb180849b1b --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_arm64.7z.uu @@ -0,0 +1,65 @@ +This archive was created by extracting hw-arm64 from test_read_format_7zip_lzma2_arm64.7z.uu, +chmodding it to 0775 and then creating the archive with 7zip 26.02: + +rm -f libarchive/test/test_read_format_7zip_lzma1_arm64.7z +7z a -m0=ARM64 -m1=LZMA -mhc=off -mtm=off libarchive/test/test_read_format_7zip_lzma1_arm64.7z hw-arm64 -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archive. + +begin 644 test_read_format_7zip_lzma1_arm64.7z +M-WJ\KR<<``3\)J+6;`@```````!>`````````%D.A!0`/Y%%A&@]B:;:BN&& +M(J+"URDYX#ZEJ=1:R[LP;*R_8L2:_5F_1<^DV<8-2,=3[&X?U.=W*%Q!?DN> +MB^),$C`%""HM.8#>=`#!HGG`);CB +MW],\(2H/P+,[60V#BSUD@SPX?+]7UE`-G%X8-3UI(38JH=H'>(C4.BHYW!+W +MO_VP/@H5V?EN8ESQEC<5V/WV[($3WNDOW%0OV?,5D(VX$W,9F]X6$@+,!DJ9 +MMCLG?+981]+>4RH&\+O6N:Z[J\UUAP1\;YX=FQG04]%"*WE/:)&N7]/;*`CB +M"$1/1LN;6/@^--#D_P_@^=I7&LU`$C*&]O)XY16O/YBAT\L2Q!M6Z,^,_"DV +M_%G"4[;]`8E`Y>_4'B-G'SBG0\P#T504(#V)73,/E2#B3I7=S".'T?4S]U]0 +M5SC_GJUAE*!NK%8\&=\',!%JIL5J>;6'O[D[_5JVE7>4\(D] +MJBL8$[^!T?<88]&GF&D!J76?C\!TV:R8*BQ,2%*"NX6^-J!7;)H[,VL2_VSH +MEIU&_VR#)=O81/7B?&1://2C9_O[-3*X(-,H(QI$N8$/CSMR0_PUF+&[-\:Y +MPQ-V_/\D'K<+P1?:NF3DC@(W0X+$X,HG8*-0_+3@`07IT`9`RZO>=4MI;EB= +MPHN\-U*#KN5B^JX'^8EFYG-3'H?CITKDVRN+'.(ABHXGW.51@<$JTX]Z-:0Y +M]$8M))->TGEHM">WLYXA/P.=F-)"H)_M7B5FK,I=7R&:5F=A^H:\"U +M3HG2Y>^C\3P&?0,F/>+S1<<,$W5!DW#+5:DT!&H>R]*HYAL;3=`9G-6=ZO*O +M/-T<0GY[;.-GK`F4@EZ(HL.JR%:.V(7[VY:.`?"47U/P;N",R)G"%MX4UG$8 +M8O@.`;_;(1W&"==@<5,SMBD.R7>ITI@G5OR14O5NB?*E!["2KU6:H+P#/KA. +M)R4]DLK9W^<7&2RL#EF40DU1*VS`"A%7D5^"M`OB.0W5*_<#5K7*R'D05`R: +M?G3Q$`L;B5>KD\B"A%!FAV9F[BA2=I^AWD%J7`WFL7T,T%?%VI^;]0`QABMO +M4"3N3&-",I^)1E`Z61GN*(E7I&+P'2/Z$W,Q4^P3@1\Z01D"VYZ!Z]*!@]@/ +M<"ZSHHN2'Z66_/Q1';U/W@:5/JY).$#*$JYEYOXW:DD&\7,"\NUZ*R7JO?D$ +M*$NX)#AFK5F&$ZS`,HKM4&XRP,+WL+W:X2X98SA`L."UXPSOZY< +M".O^LP->K,4?Z-"*L#Z[P#"4,UW54G>W0KX/!21G-DW010]NZ54"T?"[+_BD +MNGN7B2'-)K$-,Q;L3<*/`/+A_EM-CG,CZT>TOP.1(>#99CO&/97=B[##0N[? +MU\)&U'1(]0CFGJ2>AXP_3RLI*,:V)B$I-<88"C66?>!/I0-YKR[,,(`W)51U +MA>T26X/6.,4;@!NGC*;0VLT(3KS2Z>(RX'MC'$C+I1BBR!)!)C1*!AZ7_:5Q +M_RI!L!3"6M0;8&NO0V2HYVA,"18X1IW+/I9?$B,06BS=Z#B<$J,U-GI\,NTS +M)(%MEW#FI%Q,1ZU30VDFO38M=_@*<2-HT`H>8VF^;V6!0,8*T'5/S(?V9HD% +MDO;K^EOY8MB'_7J6P=+7MGT:AAH>E977IC9\\<$?0X0CKH-,^Y5$PG(L?(8V +MYY=8"QMC)#MW)JL9?(`E$Q-65D_!\FG\A]NOX"3KECRR&I/I,-_LY&!L^F*E +M(.H&^C3[CL?KK2\S60UD3W[E,,NZ1Y^*&S@5U6J#BIL,-/Q?VP9<,5*R?(?< +M6J69T=K\E=)Y6T0$(RM!_S!38A6'S"CCI=F<`'?6/*5KS2L7ON'B:!)QKWUW +M%-N6`)K7@<="%O@E[$=W9+1;A,+'*KT@O# +M)$%J/;W$T,J!'NA?2G7F7GK88KT1(K\Q52<<(!S6;SKP4H#K%(SYK0`B,Y-/&7\3]CZ/'(0@61%II0 +MK.Q,&R`2$K'#I8'B(J0'\20($ONEJ2AXW;-'V3AP;9ME^L7JBW46!+9%;M7. +MG(>CLF;A8*'@X"+832%9%WXO^K*>?/(]*..3\@$$!@`!"8AL``<+`0`"(P,! +M`05=`(`!``$*`0`,P>`2P>`2``@*`935E]X```4!&0P````````````````1 +@$P!H`'<`+0!A`'(`;0`V`#0````9`!4&`0`@@/V!```` +` +end diff --git a/libarchive/test/test_read_format_7zip_lzma1_armthumb.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_armthumb.7z.uu new file mode 100644 index 000000000000..2e10966a6694 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_armthumb.7z.uu @@ -0,0 +1,45 @@ +The payload is 96 KiB of ARM Thumb BL instruction pairs, every one of which +the ARMTHUMB filter converts. It was generated by running the filter's decode +step over a buffer of identical encoded branches, so that 7-Zip's encoder turns +it back into that constant buffer and the archive stays small: + +def decode(buf, ip=4): + b = bytearray(buf) + i = 0 + while i + 4 <= len(b): + if (b[i+1] & 0xF8) == 0xF0 and (b[i+3] & 0xF8) == 0xF8: + addr = ((b[i+1] & 7) << 19) | (b[i] << 11) \ + | ((b[i+3] & 7) << 8) | b[i+2] + addr = ((((addr << 1) - (ip + i)) & 0xFFFFFFFF) >> 1) + b[i+1] = 0xF0 | ((addr >> 19) & 7) + b[i] = (addr >> 11) & 0xFF + b[i+3] = 0xF8 | ((addr >> 8) & 7) + b[i+2] = addr & 0xFF + i += 2 + i += 2 + return bytes(b) + +open('thumb_payload.bin', 'wb').write(decode(bytes([0, 0xF0, 0, 0xF8]) * 24576)) + +Being larger than the reader's 64 KiB uncompressed buffer, it also covers the +carry-over of bytes the filter cannot convert until the next chunk arrives. + +The archives were then created with 7zip 26.03: + +7z a -m0=ARMT -m1=Deflate -mhc=off -mtm=off test_read_format_7zip_deflate_armthumb.7z thumb_payload.bin -y +7z a -m0=ARMT -m1=LZMA -mhc=off -mtm=off test_read_format_7zip_lzma1_armthumb.7z thumb_payload.bin -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archives. + +begin 644 test_read_format_7zip_lzma1_armthumb.7z +M-WJ\KR<<``29"L"!60````````!N`````````%@-F&\``#P?&PA;6-"Z`.4E +M0#42S+`O1N%WW>1[UCHF*RJ=ASI]D105;M^AL;"MA@(CW;VT&0!L`&\`80!D`"X`8@!I`&X````5!@$` +&(("D@0`` +` +end diff --git a/libarchive/test/test_read_format_7zip_lzma1_ia64.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_ia64.7z.uu new file mode 100644 index 000000000000..a201a15685e3 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_ia64.7z.uu @@ -0,0 +1,66 @@ +The payload is 96 KiB of IA-64 instruction bundles whose three slots all hold +a branch that the IA64 filter converts. It was generated by running the +filter's decode step over a buffer of identical encoded bundles, so that +7-Zip's encoder turns it back into that constant buffer and the archive stays +small: + +branch_table = bytes([ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 4,4,6,6,0,0,7,7, + 4,4,0,0,4,4,0,0]) + +def decode(buf, ip=0): + b = bytearray(buf) + for i in range(0, len(b) & ~15, 16): + mask = branch_table[b[i] & 0x1F] + bit_pos = 5 + for slot in range(3): + if (mask >> slot) & 1: + byte_pos, bit_res = bit_pos >> 3, bit_pos & 7 + instr = int.from_bytes(b[i+byte_pos:i+byte_pos+6], 'little') + norm = instr >> bit_res + if ((norm >> 37) & 0x0F) == 5 and ((norm >> 9) & 7) == 0: + addr = ((norm >> 13) & 0x0FFFFF) \ + | (((norm >> 36) & 1) << 20) + addr = ((((addr << 4) - (ip + i)) & 0xFFFFFFFF) >> 4) + norm &= ~(0x8FFFFF << 13) + norm |= (addr & 0x0FFFFF) << 13 + norm |= (addr & 0x100000) << (36 - 20) + instr = (instr & ((1 << bit_res) - 1)) | (norm << bit_res) + b[i+byte_pos:i+byte_pos+6] = \ + (instr & 0xFFFFFFFFFFFF).to_bytes(6, 'little') + bit_pos += 41 + return bytes(b) + +# Template 0x16 makes all three slots branches; each holds a branch +# instruction with a zero address field. +bundle = 0x16 +for slot in range(3): + bundle |= (0x05 << 37) << (5 + 41 * slot) + +open('ia64_payload.bin', 'wb').write(decode(bundle.to_bytes(16, 'little') * 6144)) + +Being larger than the reader's 64 KiB uncompressed buffer, it also covers the +carry-over of bytes the filter cannot convert until the next chunk arrives. +IA64 has the largest carry-over of any of our filters, up to 15 bytes. + +The archives were then created with 7zip 26.03: + +7z a -m0=IA64 -m1=Deflate -mhc=off -mtm=off test_read_format_7zip_deflate_ia64.7z ia64_payload.bin -y +7z a -m0=IA64 -m1=LZMA -mhc=off -mtm=off test_read_format_7zip_lzma1_ia64.7z ia64_payload.bin -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archives. + +begin 644 test_read_format_7zip_lzma1_ia64.7z +M-WJ\KR<<``1L%#D`7@````````!N`````````%RKRPT`"P`SA1AJW>YGQ7^X +M5]G3R.&+&W,AE$:\^83QQOXZ*[;FYZ5W&K5B +M.SO-WAP+QAS=/%O)!<4.GKC!_V1Q$6;T/7-ST!MO.9\,+P```00&``$)7@`' +M"P$``B,#`0$%70"``0`$`P,$`0$`#,$`@,$`@``("@'4SK.@```%`1D*```` +M`````````!$C`&D`80`V`#0`7P!P`&$`>0!L`&\`80!D`"X`8@!I`&X````9 ++`!4&`0`@@*2!```` +` +end diff --git a/libarchive/test/test_read_format_7zip_lzma1_powerpc.7z.uu b/libarchive/test/test_read_format_7zip_lzma1_powerpc.7z.uu new file mode 100644 index 000000000000..5336e65caf74 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_lzma1_powerpc.7z.uu @@ -0,0 +1,58 @@ +This archive was created by extracting hw-powerpc from test_read_format_7zip_lzma2_powerpc.7z.uu, +chmodding it to 0775 and then creating the archive with 7zip 26.02: + +rm -f libarchive/test/test_read_format_7zip_lzma1_powerpc.7z +7z a -m0=PPC -m1=LZMA -mhc=off -mtm=off libarchive/test/test_read_format_7zip_lzma1_powerpc.7z hw-powerpc -y + +-mtm=off keeps the result reproducible: without it the stored mtime +changes on every regeneration. The test does not check the mtime. + +And then uuencoding the resulting archive. + +begin 644 test_read_format_7zip_lzma1_powerpc.7z +M-WJ\KR<<``1@#9(6.@<```````!B`````````&C_.8,`/Y%%A&@TB@D*0&*N +MI,NQR,0S-H]24V>HUJ)S8G8?UTBL>Q5G.4VRG`G +MX;NVN!)QZV9EE-86\EX/-AGSD8"!_9OCIVXS85?#W'27\3@+6C5[NF\X^*^Y +M;RXP>O=WDX`WFTS!C`ZZ+4=K-.AWX[@*@)]I$[S64O;XZLAMD8<%,2 +M5+2ZFN6C=QD/7BQW"%!J\Q]5:/P\JOWIO1&Z)XJPAH]RU>Q=R:'"1.D[?\\0 +M9!A4R'EED]]!$(`P\VJ>\*'QH^,1[W<'/KHPYR\>YQ>)7;$Q1$2QT;Y'@(N5 +MGF.QDBD^[&@!Z7!)8T:A8G>LO#(-C]Q]RQ+RP8)+;A49<3:R@[<.6QP1PPEA +M<]\\J&[Z4Q.3D4JF=[9(>PZ;PH1=9X$OB2+-JWD+(MY@9)+.RBE\-(YGOM"( +MP#:$I2]OP+Z`!T:_-7`>%UH]^X%9I;=?%F'`?%@SZGPK7U7P.NY1O^(M\S$T +MG0-!QBLO4NVWI'9=V=59:L$<)=R(B=><7E`EHU>+9U:R60*?H+;L:IXHASE[EX="AL9^\TE8<]4\'P<.8 +MX63DC$%-'EF2+XYRU/NJEAQ8^I(QUY>=IH']H+>E?4H15L`[6A??YQ%.OP=9 +MFA1\3D^6X28B!N^S0C/K(YJNUH-"Z();:A$'BXD)LNBZ4M;S-:$X1#"0H?_EN"/R^EE'3BUH(JL&GR=V\DK6^W&Q4&"6)M)";27.MU5=J:`5'\[QRGVNDF%% +M,496/=".O2\%0/:L/DYAK$@8N&Q-MB\(B,I&D44;Z$"5OEOZ&+[3P<6Y""AA +M;7[-PHA'VM!.P?%2&UY&RV1PV8A%_E\W>C?4AZ$."9483$_WT@>?P +M9Q@;):Q=!M6*6%B<=I/2O&&TR;`%8C,>AU);W2__21.`.E&3R*"#F/E^)UDT +M^O^">DN`G0X?YMDA0(VD##Q!=#AJ2_GU(R]"BT\9=G]06IVXB*N>D._HN.P# +M'34_+GN3+-R$,309TH/LS@S@U\MI$#2K3\^7&&MXU4$XN0"Q\X9"@"F6:AQACT&WF>-H#X#L62S\U$Z^I@#R%"^PAU[*_S%['0[X +M>],*)X36#OJ=S>JW>URI18]C)/IPC>%O$O87NK;/*'LSFF`&O=UO +MN5U$^V>E]4BYF!=EJ=>W>!L!))2)WXN[6.R9KY6OYB_%D\LB@UJKV$X?$3X!__S,A$L(9TJ;'"/_KQD_`[B8%4@:*M>W7> +M7:^#T%>UD!F&2U9MH!,&+NCL8_C0R1,V,<09?&_#YQ,:=OX`P`>Z$F2Q3)V# +MBA^O*^SVX_H(":;Y1R\E%.2")3UKK[GS4LK?/)MTMK9O,;C_&^1Z]YV_L72P +M)(`*4@G2BS"T#+0MFF+[2@%^L.Y+G;/_G;O3U]9V>BO?AA(@YY1T(PZ_RW(H +M!:>:2"-VPA#,?7A`^W<*16&L'FSQ0>S*^?^%4M.0J!D;_PJV@M9UX^RVHWG( +M^4T*27L?-?N_,&Z9_L+B`5FC2$7OSN:!R!T>@:PS.!&G%]<=TRUWO$"BL"1/ +M8WVCYVY[)O0#BGT7+'4)(8$#PDBKX[G8`5IV^HOM);AWVK;N2=?O6Z(82J?P +MP3)M":X^V\^4P/`JLI6D3E(0Y,&R?38!2J\A?)$FA9N-)=C.8TM7;NY.1S63 +MC),^$)LC$@<'B:M(DW@!)1#8/",9(Z%V@<#;N=,QJ>GQ\7@6``$$!@`!"85VNZVMMC.M1:6A'_@4B7D:$-^A7OC]^75)8S5[+EBS'O"X%U.;JIP3!?I?] +M\-&D0*0_8\P?X^=GN)?^"&<8F?3+:T? +M=0[F$8D0,>.@/W@?.H_*3R#+OM.JM\::8JT52"_:`"T^IFT#HJ&EG?=B5!4E +MX?D"/N"[9/+^W956RS)T4MN,MPSU'BH$'6>.O!%>MM[Q1M\*?-:,ZA.LC4S*%X\>2A`A!MM\`L +M.#S5]OZSD.^1E^WI&^?6F,*]*G."0CI6H8W.9"#Z_6ZN<@;G-2.=T][M[&X3 +M["?7[V$_E354K.RAM%W%]K=@-LG+<"*P\S=2`=&JS5Y(DY7CT!03HW%SJ900 +M'-CH(?!5OE0H$4K#Z(2OI<]5&X]"W)0TM&W$$F3GQ;,]!=OAC50%0^&+<+E; +MOMQ$NM:OT+PBPC.UIS`9V##GY2Q!&\VMUPK.(]I:2^O=\&*_+P2`SQ;RDC)U +M?8.'V.H3CD<%HEJFBY@;9B.?>RSO`Z*LF3IYHBM#W"L')Q8P*'5$I:[H>M4C +MA0P,'R?M%_JJIKVCEK0L=U<&@>F8]L'_HK6W*/B7\WE^E8NL5+I?\#G/4 +MK>2*9==_\M[[L0]5+C",0H-Q]@"S>,%VY+S&9RV_!NCCNRF8KO6(28T%N->@ +MQV&8E^W3$.1)I=N?7?EJJSC9/2,Q6"RQ1D!EYW_2]['@E-]V66+=X+Y:#/L9 +M;[2A/3DB2BM[%M73D]L=*T8RO&7\@Z7?RP]2SH52\,/YJ[5R5U%+\C@I[JTT +M!7[J)502-;,3_4CD1LZ/[*M()M=BD%P8[$]0H=F[=E:3WCZZPVGSD>(O_"VN +MIFCO;2\>S;\4GE8,#'FKL+O'E999,FO+U7<$4N)[F +ML%?263W@TVD]/=,)<[Z9USX_P:N+(U@S[()YQ\?5,[P*IF0`EX<@^.GX!Z\? +M!M76Q]9KJ.5J2VHC579C,X1RIE<60O#5%:9',_*B24>2\8 +M3;TIM&K.XR'DB*?&[E[R*%\:Q[[G[7`KF`F5F"GRR;%3/96U:,#>O*5M +M0,2^<..K%T*'C#.7>S8QPK;]>GJ20ZB]&P#K+\XL)Z!%).]9:1])R\1H[/]I +M:U!ZX)^D+H-_B%48^]$&^>W09_2LD?(7X_,?`I%F(ZYPQVGI(><\)S;)H(Z3 +M?+J.>U1-^.1=Y_!_[\AU;E=5CTIR`9EED2T]PYY@&97N0+EW$`C550K)B)UG +M(Z5[<"$Y]:/>WF)'F4:'`\G^6@>.C_Q&S$7X(#A#:\HHDPNA3&Q[GN._G\F. +M-KJD/R9IWQ#JQ3DBJX.RC+-W#,%&5$-W^>:=^63U;.?UHG"+IEJR_PVSJ\"11HX +M4^R3--6FN`]3E9[S\+$U!X0VOR)N1;HN#Y_Y&M2WYN`X)<(>`&W02SN#@M8> +M+=04++7&-!FD76*=WO:L99[,FZL>O#5R7EMF +M=ZWCA#?>]5C)I=_E1H^SDQX72:*)?L)AVKW"Z=S\8$7;;"1R0H515I@-MLZ( +M)+]B88&)P^C^S;8D62W$"V.Z-GU"DI\TBOGK&8BW*H4LOMG])),VD\436(;$ +MT\J.LQ[-]\&@2@!_UAZ\'\6,*A"R[6_QUV7%5=0M40X<0!#PYXZ'Z":.-1>U +M'DD=JHOL]XD5B5K[/^D;$VQ6YD\E(L)O2SX*B?/\!KG%A>^4)"'P9O\TIJ'O +MF%R%G7)H99X0_6_YDG$`RV4+ZNU**RUAOWUCW'6S(%)KCB&FT*J*`&0U;``B +MUN4KQ<01(`"X)ED'5'57KXT,%'-!\.D71?[:@&6;[$X$SBATYB;"7RH6L$VO +MJWCPADUR@>/DE]Y+FY'CP8*DV8XJO<;,.8,$EX:32RB_\T]13VFQ<+#:+Z2/ +M"T_B-``!!`8``0F'=``'"P$``B,#`0$%70`P```!"P$`#*$HH2@`"`H!YR3M +J]P``!0$1%P!H`'<`+0!R`&D`'?U2E3"\\>M,//F#7G:326?5K[;;;]`;4RZL+2"YK0&S"%^;]B,"%"( +M0GP=@^)F!P+MPN8?ZGF#H5_,+&A0807$T$%4KK'"1QQ6,C0D'WN!T^Z!IL,$ +MDSM(19N5@1CZ +MK\:&ZK(]+$OQ-2^U_$M?X/3SY1>(F58)3"#>U7&3"_5L.P<&"X7W?GBRV'3A +M:F6EBI.++/^^YY^IGOLA#!5=FWM/.>D;RG:N*C!EF$;WX*0Y#7%Z8MA]H%&9 +M/$0N?$=6B+$;F/E&*"QS?A*T2;^.%2B1*5&IB=5`"2-9Z9P%Z*6EB,'M;HP=TQ4LM1J2-<1R^TNU&)(0+:'"P28#SFB^)5=8+7OR8Y(JA2>F +M&M]@>`;6SO`M>HN,-._W"9.X%R_FQ/=5?#6"3WP;0)*[F5(7XU1>"[P@6XU2 +M:(M:BHMB3^8EM"LF<^@,1+S]$G+36<7G^.)2 +M`"WBLK;#WN.8&["49-'TU9`)-S9KL[_5JR\7E()[1[DU>>>9NCGR66%-YC3):OM@/=DR-S``E0?X-:7>UD2EB" +M8/$UF&Q0YLX:6&IS>7$T2F5V?'H:$2EK%_EC]&74@0>T4EW:USS=G)F[,@KD^$(Q['P01JJU@';K. +ML2<87%F&Z6926+[I=JQ9Y.5;!0CYQ]JM_/M2*W3-'EL@0OG=4SWX*60).X#+ +M*FS?M3OPQ+TN7ZH//DMF0I`3#O\0D_AQ>%GX"\W_E2A&#ZG\?-[[FC`N5L"/ +MA?.#@K0Y)T%3R4A#=Y3<@4!#EL9W^*M74%D@AK^/:C5;08O#`;FJH68D3!D$C +M*`@"S@"7Z(K$QYMRF4'D9^C[44ARV4(OC(Y6$1L`@X]==+0YOB.@M*E6`179 +M?2E[ZMF[PY$'N17/D2WH=Y&_\2TO620)3L7JUPDNS%Y6&<8F:P';WXWA^-5D +MH&*!9,`OVA&>SOGI%>6A[;I']6_CG(-&7#*']M-W`JQ+]D4:A26=Q"J@T!': +M)]W!4TD9GK>5>JD-Z5(07?-5B;4$N6&JUIF&Q)G4$:][JC3V:]H$/R]V&D,G"GBP`V["I55P<$->8"C$3'KTT-2E/OH<'#WOU`X4X=I"5_WMR.L%; +MD:K0BF;,N@WM8F(%&58GD8O.7"A>62B/C]#.,MK5]J`,\\W?&9(ZW!_[$0S0U'E$[;^`5MMR-EG7,[J +M5G=2-T6#HA/-7NF#]Q8`)."O+TS)J+LG*VHCM?*^7P5E4L:TCS-9:6TE*AN0 +MZIE+HC8EDHA(4C3:`!_4C]SXI$(,D%D[3D(Z1I&7E?S1_K?764K.R22.>0[M +MPKB`'X#=BCKT,"C/18R-*U=4,[BH![QR`HRN0,-<*1]%O[PI$ZD;4C,"<`HM +M4_A^!]@55PWC<%?=)8>UG$YD04.L/!!P>NC>*5]_P%J.SP0]>%Y29.LNBVJY +M4L"Y>\>PQ"TB#<[Z)65+R%!S5VI/`;Q*'T8/`VI)CK6`,N21#[K*%..WS#_K +M4:PA&]9Z6/VO`K*FCB4N127"+)KG3]&]AP\C7>?/`TZ'?&W/GG'=0*2O'_6Y +MQ?,%&']M3!0#,1Z^]E0_(%VOZX6ZO]&AY*1Q.XDZ&J,`P`K8G6L^;#6[(&5Z +M"]25@J)F\(J7KTD7?B.@LCA8MG&U"[)UND`RIPR,FZ_\;5'%CSJ>%6,@I)%F +M!$2A$:R29-''1-G1&AT:4+EB4*@%JV2KV6);JPGWL7YQ(YU!**I5^!MWT>98 +M5=1L5%3D?#1KQ>P=NNCP)+ICQA%U#2-FI@>(V0Y'/U;BMW%_L$:6/]3D/D:Z +M$Z:I5?=VDHN^L)HI4%O--)M!Q.M^?>(*:1ZP:!/>,M_I4Q=?;5?\0Y!.KQ

OF$(K88HV-T)O2MM%02@U>&7<``00& +M``$)A\8`!PL!``(C`P$!!5T``!@`!`,#"`4!``S06!'06!$`"`H!339;:P`` +M!0$9"0```````````!$7`&@`=P`M`',`<`!A`'(`8P`V`#0````9`!4&`0`@ +%@/V!```` +` +end diff --git a/libarchive/test/test_read_format_7zip_null_client_next.7z.uu b/libarchive/test/test_read_format_7zip_null_client_next.7z.uu new file mode 100644 index 000000000000..4c6179a7ec8e --- /dev/null +++ b/libarchive/test/test_read_format_7zip_null_client_next.7z.uu @@ -0,0 +1,19 @@ +begin 644 test_read_format_7zip_null_client_next.7z +M35J0 , $ __\ +@ 0 +M @ X?N@X M G-(;@!3,TA5&AI !T &0 4 +5"@$ (W&,=.EOV@$5!@$ ( $ , +` +end diff --git a/libarchive/test/test_read_format_7zip_ppmd_arm64.7z.uu b/libarchive/test/test_read_format_7zip_ppmd_arm64.7z.uu new file mode 100644 index 000000000000..a16427aeff4c --- /dev/null +++ b/libarchive/test/test_read_format_7zip_ppmd_arm64.7z.uu @@ -0,0 +1,62 @@ +The payload is synthetic, generated by the script below: mostly ARM64 +nops, with a branch instruction every 16th and 8th word and a little +incompressible noise, so that both the ARM64 and the x86 filter have +something to convert and PPMd still compresses it down to a few +hundred bytes. + + python3 gen.py 13066 ppmd-arm64.bin + + import struct, sys, zlib + n = int(sys.argv[1]) + state = 0x2545f491 + out = bytearray() + while len(out) < n: + i = len(out) // 4 + pc = len(out) + if i % 16 == 0: # arm64 bl + w = 0x94000000 | (((0x40000 - pc) >> 2) & 0x03ffffff) + elif i % 16 == 8: # x86-ish call + w = 0x000000e8 | ((pc << 8) & 0xffffff00) + elif i % 64 == 20: # a little noise + state = (state * 1103515245 + 12345) & 0xffffffff + w = state + else: + w = 0xd503201f # nop + out += struct.pack('@,```````!:`````````'SX(E\```0M;#H=)%=2Q,FP +MRUM<&54NI4YJ?$5,YI9J??+\>`+J,TAD@_Z&VD4MNZ%S';Z[GX>)KQWRVIWS +M--&6% +M?_SN/D%+Y>J6X*X#SF+ML&J>A7IZS-Y<2L>]WE(5FB!VI#3X4I+$Z?8B&&^^ +M5`;?&`F3M0(UW4NV4QEL,36M`22B9,8%05CNC@@I5\+;B3;AH?_+/<4Z!K'? +M9KNG@>X'`UX,N8T8#]\+/!KS&9GB*U6_2GLNW$A%$52;_/L*?N/'\00=`XDK +M'#P(3G@-P]0 +MW\WJ@=R`.D1(5&YK&)!L%LEGBJ"^`/%IO19^@&9$J@.$:];D^L?6!X)4)<>C +M1,.%I6STY&H^K7LP-7.`^_AOQE(-!,7&@$P1UJK$ANU-)?_!<(Z<@6-6:N1& +MQN$&E-F=S733+0^F.I::I&51*XM^1V#U\$X+_V**PL(QE&:O@VUY.0-6H9)U\ZL[RONG +M#'^:Y5:"*\T/:Q>.TY*!D^-.D<%8S,>;9CCF/@H^;$88^C[">_VH=C@4<]]P7J\J[EMG?S"L'.&.Y$)IMD[59!/ +MQ@8FE]6:Y/#@E08^S+[&Q$P'1EVGT;X)TRK$\B_<+SA@PW?JD/L'H-,#*/<9 +M/]F*QCK!UFPAV)"45#IHZZS@7B\KU184&%Y'AVY8:TSE\WC9E;D8^?BO^NKD +MC'KXU2`]8I7'.T/,%F-SY-W&F[T:96>X9DGJ,*"RP+D8_(?^?PI; +MR3`U$@U!(D)-=0CT%Q7-">"[`5'<'$_\4CCO`CI/-5.5ZRK*7$HW.NUAA"TK +MIK$UCK!4UY7N)3'RU0#SO='H#K#;?`$$!@`!"8-Z``<+`0`"(P,$`04&```$ +M``$*`0`,LPJS"@`("@%GO(I[```%`1$?`'``<`!M`&0`+0!A`'(`;0`V`#0` +6+@!B`&D`;@```!D`%08!`""`I($````` +` +end diff --git a/libarchive/test/test_read_format_7zip_ppmd_bcj.7z.uu b/libarchive/test/test_read_format_7zip_ppmd_bcj.7z.uu new file mode 100644 index 000000000000..a9ce2d477a3e --- /dev/null +++ b/libarchive/test/test_read_format_7zip_ppmd_bcj.7z.uu @@ -0,0 +1,47 @@ +The payload is synthetic, generated by the script below: mostly ARM64 +nops, with a branch instruction every 16th and 8th word and a little +incompressible noise, so that both the ARM64 and the x86 filter have +something to convert and PPMd still compresses it down to a few +hundred bytes. + + python3 gen.py 2000 ppmd-bcj.bin + + import struct, sys, zlib + n = int(sys.argv[1]) + state = 0x2545f491 + out = bytearray() + while len(out) < n: + i = len(out) // 4 + pc = len(out) + if i % 16 == 0: # arm64 bl + w = 0x94000000 | (((0x40000 - pc) >> 2) & 0x03ffffff) + elif i % 16 == 8: # x86-ish call + w = 0x000000e8 | ((pc << 8) & 0xffffff00) + elif i % 64 == 20: # a little noise + state = (state * 1103515245 + 12345) & 0xffffffff + w = state + else: + w = 0xd503201f # nop + out += struct.pack('AQ +M[%KK7=ZS+E5NZ? +MQH"0EC#>%)3.H&H`6#I6A!5$_9"V9?D?,'%XX)?Q-2@TMD>X>3,9)Z05IBRY^U$;":J'FS@5T,F:D?7_CR.2=S?V92V +M.@D%A+-\_69)]9&9.U-!YZMSXH@``00&``$)@-8`!PL!``(C`P0!!08```$` +M!`,#`0,!``R'T(?0``@*`8_71R(```4!&0L``````````````!$;`'``<`!M +A`&0`+0!B`&,`:@`N`&(`:0!N````&0`5!@$`(("D@0`` +` +end diff --git a/libarchive/test/test_read_format_7zip_ppmd_small_block.7z.uu b/libarchive/test/test_read_format_7zip_ppmd_small_block.7z.uu new file mode 100644 index 000000000000..26e0839bd435 --- /dev/null +++ b/libarchive/test/test_read_format_7zip_ppmd_small_block.7z.uu @@ -0,0 +1,103 @@ +begin 644 test_read_format_7zip_ppmd_small_block.7z +M-WJ\KR<<``0QLF?X-1$````````C`````````#)PBBD`!".]=[XQT +M.ZO+@2"J4&_UD +MM_W=LL'"Y5IT:G-DH5_!F@)DZSH$[=DJII=P_[?TN+/M_L=PIUH.1T/83*]A +M410V3@.?0Y(?27"2KR-2!AB!*4]/7^6XK-TK\4!SMD1B^;,CT=M&)<_=.5-7Y^2[BS^&$MKH,(/XBLFB6RGY-D+%[&&=-U>".8*;H +MMR\DR'?X^Q='_D!X+/!6_9U_\P\*N/@+A+'(I?@KM/JFK0\3E9X+"JQ3`-9* +M6,.R'S)!_5:+`@P*L7_S)MMJ;N)6@C-,I5TEAOEDY?_\W0QH;* +M.W<;BT'?47>):-]Z%BLJ=0P:J[&=W@XJ(S$5.F*"F3LDPXZ)X +M)!>J4'_$^W\=[_@DOCW#`T\V[E72I)1.)KSH:WX37EWB53)!^T\-(D\HFF%! +MR-F41L55>)[BR:L#%$R+O,0']RJ>3]J)CHSKX^/"=GQ*@_8(':IAGT"DR36T +M?,/`HO`G6)R!404MD83!1DPO<;CVKUKM:5[J_GEHC+P7I1<(ZZH[U\?MRG@\ +MJ1<%C>7>8N*AZ\%MRJI5#,'CX/:'N,E:-Q3]XBR0S@T@9S^-7SX4$&2#8?J>L<]1C7*;DN]OK_ +MQ4=*^.(FD98,2%_:/E1;AEC.$V+U7`JY_?*T[5OE-J0_@_A0Y^3AS1S.%%QO +M1I-C*7J,AM`JP24=LLP20RUK>7033,=$BK1&$=$`&CKN1EG)+'%^;MWGW_M> +M5PL-A-0*@4=Y30I(A^62(W4+HMIT62_D'SQIA[E,]MVFO.$TL4U,"Q@_V,-1 +M8U*\/"$'N]G!-S054-*]W$-J]X"XAOWXJY2>Z@%*<;(WF\QH#R0-4GRBLKG.^XE$F3B=9%'`XQ$K&CS7A1 +M)<(2O[/G8Z#MPC%4W/X@6Y65(P3A7DU/75;XUJN20I7(2$=UW-5TKU^CUB#Z +M&6!9DP@[A-61$'S!)'=MET3T/;9G2#H]O)E04+\WR4QJYS#K*1(UL'&DRN@B +M;^#M7__._92*6BMZ3*?S%;%'-5OR_#:Y6LRA"J/[J!=\7XCVG0\.[9'%@KMR +M%#-J>13$#N(.;5K1^PIL_-34E?&"",:#`3^P02&"\"Z;"25 +MS*HDSYY0N2)GN!*@ATJ(8X>%/FEH<.<>J]&UB53R;B7F1+-T!RJ/HGP4(.C6 +M-#W>4?=.?-$J!4V;7$\WR^7X-E4G@BY,[:F4W7 +M,!4`_M\EP.NP1V,-;7%V5H]ZH)KDQ2:%V"U2+$/`-W4FO!VY!-(BF!E!DU97 +M!0]#IT;;?]:(!,VE=_ZKCV41(,;Z;O^A8GGBQCF9?TF&=7EVD!Z!8^)]$R$$ +ME]=M#C![1_C>KMPI<@$J4RRIYGS?L@R+_RY$8I9NZ`WF +MDW/>W%\.C8U>E\.3KJ>A$<*="D>=I)$WE/F@/=8\T,&7/[575!+#U%5)\"]; +ML+^`HB@H+B5KYH^=LXR!]0P5M%3Z@2M$#0OE._8$<7_%24#Y`0\0*RI%PO.P +M",&_I-"=9,,4G.6S"H8/K>+-"EBFZP!4UE'AUZZAYQI'*YMK_/JXTG-/HE'% +M@T='-O7WQ+6"+;LRRU +M47\%+R<97791YB=\)FV*-*D#98$"_#P/IBXPF?Q-0`34V85>C_,UA3UC?57;(2KTC%9?)'HA8-*I +M2ZVZ_&PQ)/SIM9W%0>1Z`#Q\DF,K=XAQIR)V;FSI5>2R-H!OP-';+"7NM*SF +MSIY64*>[,C""X'#ZXB_9OGGAY./%(_"=CTM2#0!K']1TYN=HGW9P6:NPF[FN6J<,N^6DD@1 +M%!>WE%>P3`6[6G@=6N;.RLF084ILFH*KN<1N@@]*(9%6L%`X8^L`-_$'-?CTIO5$42JP+L.4$811QV+.PT8H[<,E*S_\^.FLO21*&52S:0(M*X)S^%"@;.LFJ^KN$ +MJ(-0+)8`10IE(TZ"Y69">"^5D,ZR)#!M\-&'QDQDKI.FDR_4($WLUI'KYWO7 +MU:@I@_.+4&)3KI;E:06$<@ZQ1U&[51,OT-1>X(AJ$_]_/P^KJ'*8+.WR!!.N.[5.?L8TBT!+8#J2*9[D>5S6R.GO46.CM@/-5 +MB;&^L`1'1C[QN;5>[/9ZT4X7W9(+W^R?;IBND'8S*P-PTMC%;JAYVH& +M.T/Y=2,-^SY&6.]"")N33S!&AU#6X;YHA9R@U930O5;['27$+%Z/LC)?$U@] +M`"O!#=X&Q_O]\2X\/!7QJ^F>*2E%G53=(9%%[?B2K-PF!N!04^SA_OE#+`EY +MT,STVL[2U/`K%@.;^Z+Q62AT',CN\IJ(B6"PAO1(IMSA?$@G";KI'CO5+$AE;7A#FWOUJ5'>F;KK!;2AZ +MZ/&F7[G,U3+RRE^0H9\_?,OF9X;H;`Q8'30F\2)>K@W([(P--CGI-S1?Q;$R5R +MV&0,9*7A*9=K4B9G0:^J]CJJ\^DWF+,+RVNYRLLZFC:HX<5@C"-``H;.GD+/ +M]A8^>S''"`>'@]6?,M/)F.>WG\+6N6#JE'EJ.W$:QDV'*G5AGS!TUV%?FSFY +M3M&6788U$O]_^9!*1HZ2??N!"`)0D+!@M),I+"W)BW-BRC.FH%L9\`N&"MEE +MY'0B)OCBA$\\!3C/_.U8SSZ$_N;K2$4ME&,*2Z"RE8?:R-)[[K`3U]HY,[H8R5IF@[-FBVQCENK +M%S`0U-/[.+0@MG;EY0XVOLU05B`\8CX/[>Q.>4`Z2&E+&VI6"LPV8>J:JX6GN&8H..ET7`(O]M]/PN@B,QA!Y6-A(<>.=L1Q7)?\&S#O$W<^Z\T&- +M6(N\ST*HE@!3<)Z6)6)-&AJ\9MC$?VC\J3XJDB#* +M'?R[SS`+07/:J9ZQ^(V"OP.XSU +MG0[9I4EOVFR6*'PJES(I,?@::2U9Q`OJW2\9#^0V:^EH0#D/1"`I"==$GTLV +M_G87%TQ36\]$J_7/S+A]_PZ5IM2P->)*/,7(.L;D:B_:WRU]ZW".S*0M+&L/ +MK?G=-YTK&`$W\$J>.@ZM&5`"C6HL'$0,H+`D3*,P!:UV\*Y;[Q+E29DRES`A +M..O-R/R;R))]G3_JC7L,2/9(L1>MYR3%GQZ&H)$@;L63%`_B')\7WH3!(RN? +M\P+S[*37>C8)**Y[!1,`5;OH,;2XG7C>[/EN':O:JU&EQ5]Y)(1#*?+&.3[% +MDX_NZF52/YNX>T@C?YLPT]S*"ZODT2-3"5B<916\TYAVC3_/%JF+::36)Q&" +MV0[BG=U#,U&'?TH4W/+Z'X]0=;L;G\N'_^C`J0`)!#A(02C!^ +MW+4YS9(.S>+HF!__#K=Z>XO%"A?K$0+KZ*,#6VJ8RRJ@81?>8JMK,.V_VE(? +MG8Q;R*KX%MU@5Q3$C7\G]EG;1S+E=\UW""X6+T>5YG\?H_M +M'(LN92(W3SJZLQAL>(0N%)%%D88M!63&YQEU8U&OD776PZLFGR!X:&L +M_KAA-LU59RGA6I!/$'-]"G3^,`& +MY"E?+O>3FJVA86HZN!T#>1J"49V697/G&ED3%3=>4'=C(Z?`J(+!*1BGQBQ5 +M`>%J&0F2$;T)\Y9K]%F92=`@?00?8>^4%I0OL`ZB,VV&:)8ZEA)(\<4]1X=WXOG.+!5 +M@0Z/.`>U?4;M)5JJC5;;R/HNSB9>?E:G[7-'SXS>+3&!E56Y)8=8.&Z[U)IP +MV*3@B7L/KJ#?-R;C@U`V7F)O7*W.UT67(&:@?XS\0L8WO,@&K>B]IYK4`/D+ +M"=:"MZ?J7"^W`E#!99B$T&^)T7[8!(Z7_F9VB)N[3(Q<"<.FM(Y!Z:`>9N#]7@M&27)-/^LWXO +MF#)X)[59T*79@.S$KHLJ!`9_WD,;A=2QV%SL_^M=AJ&OS/CE'8A4GP();?U" +M7,*`Z8#%P,WV28>22T#2)L"`9;(P"8W#.AGX=!LZVA2O;,R_P9]7<3!$[;/A +M/I/E:PX$%!I7^:QDE(/%"S6"]0X^72TDL```%P:0K@$)@(<`!PL!``$C`P$! +1!5T`$```#(#>"@&"6SL>```` +` +end diff --git a/libarchive/test/test_read_format_ar.c b/libarchive/test/test_read_format_ar.c index 6e279ec134d9..a58a878c53ad 100644 --- a/libarchive/test/test_read_format_ar.c +++ b/libarchive/test/test_read_format_ar.c @@ -40,16 +40,6 @@ DEFINE_TEST(test_read_format_ar) assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_filename(a, reffile, 7)); - /* Filename table. */ - assertA(0 == archive_read_next_header(a, &ae)); - assertEqualString("//", archive_entry_pathname(ae)); - assertEqualInt(0, archive_entry_mtime(ae)); - assertEqualInt(0, archive_entry_uid(ae)); - assertEqualInt(0, archive_entry_gid(ae)); - assertEqualInt(0, archive_entry_size(ae)); - assertEqualInt(archive_entry_is_encrypted(ae), 0); - assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); - /* First Entry */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("yyytttsssaaafff.o", archive_entry_pathname(ae)); @@ -86,7 +76,7 @@ DEFINE_TEST(test_read_format_ar) /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); - assertEqualInt(4, archive_file_count(a)); + assertEqualInt(3, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } diff --git a/libarchive/test/test_read_format_cab.c b/libarchive/test/test_read_format_cab.c index 0adf24c7c67f..b82988cc889a 100644 --- a/libarchive/test/test_read_format_cab.c +++ b/libarchive/test/test_read_format_cab.c @@ -179,10 +179,8 @@ verify(const char *refname, enum comp_type comp) struct archive_entry *ae; struct archive *a; char buff[128]; - char zero[128]; size_t s; - memset(zero, 0, sizeof(zero)); extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); @@ -221,10 +219,10 @@ verify(const char *refname, enum comp_type comp) goto finish; } assertEqualInt(sizeof(buff), rsize); - assertEqualMem(buff, zero, sizeof(buff)); + assertMemoryFilledWith(buff, sizeof(buff), 0); } assertEqualInt(33000 - s, archive_read_data(a, buff, 33000 - s)); - assertEqualMem(buff, zero, 33000 - s); + assertMemoryFilledWith(buff, 33000 - s, 0); } /* Verify regular file1. */ @@ -279,14 +277,12 @@ verify2(const char *refname, enum comp_type comp) struct archive_entry *ae; struct archive *a; char buff[128]; - char zero[128]; if (comp == MSZIP && archive_zlib_version() == NULL) { skipping("Skipping CAB format(MSZIP) check for %s", refname); return; } - memset(zero, 0, sizeof(zero)); extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); @@ -344,9 +340,7 @@ verify3(const char *refname, enum comp_type comp) { struct archive_entry *ae; struct archive *a; - char zero[128]; - memset(zero, 0, sizeof(zero)); extract_reference_file(refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); diff --git a/libarchive/test/test_read_format_cab_filename.c b/libarchive/test/test_read_format_cab_filename.c index 8c0d17042896..675d1de6cd87 100644 --- a/libarchive/test/test_read_format_cab_filename.c +++ b/libarchive/test/test_read_format_cab_filename.c @@ -35,7 +35,7 @@ test_read_format_cab_filename_CP932_eucJP(const char *refname) /* * Read CAB filename in ja_JP.eucJP with "hdrcharset=CP932" option. */ - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("ja_JP.eucJP locale not available on this system."); return; } @@ -92,7 +92,7 @@ test_read_format_cab_filename_CP932_UTF8(const char *refname) /* * Read CAB filename in en_US.UTF-8 with "hdrcharset=CP932" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_cab_lzx_invalid_history.c b/libarchive/test/test_read_format_cab_lzx_invalid_history.c new file mode 100644 index 000000000000..1cfcb15c7071 --- /dev/null +++ b/libarchive/test/test_read_format_cab_lzx_invalid_history.c @@ -0,0 +1,45 @@ +#include "test.h" + +DEFINE_TEST(test_read_format_cab_lzx_invalid_history) +{ + const char *invalid = "test_read_format_cab_lzx_invalid_history.cab"; + const char *control = "test_read_format_cab_lzx_literal_control.cab"; + struct archive *a; + struct archive_entry *ae; + char buff[2]; + + extract_reference_file(invalid); + extract_reference_file(control); + + /* A match cannot refer to history that has not been produced yet. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, invalid, 10240)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("x", archive_entry_pathname(ae)); + assertEqualInt(2, archive_entry_size(ae)); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_data(a, buff, sizeof(buff))); + assertEqualInt(-1, archive_errno(a)); + assertEqualString("LZX decompression failed (-25)", + archive_error_string(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + /* The corresponding literal-only stream remains valid. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, control, 10240)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("x", archive_entry_pathname(ae)); + assertEqualInt(2, archive_entry_size(ae)); + assertEqualIntA(a, sizeof(buff), + archive_read_data(a, buff, sizeof(buff))); + assertEqualMem("\0\0", buff, sizeof(buff)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_cab_lzx_invalid_history.cab.uu b/libarchive/test/test_read_format_cab_lzx_invalid_history.cab.uu new file mode 100644 index 000000000000..a6bc436c9e1f --- /dev/null +++ b/libarchive/test/test_read_format_cab_lzx_invalid_history.cab.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_cab_lzx_invalid_history.cab +M35-#1@````!V`````````"P``````````P$!``$`````````/@````$``P\" +M````````````````````>```````,``"```0(@`````````"`````````,``"```0(@`````````"``,"RXLY<,(L+`.@HL6+&#-JW,BQH\>/`&"(A&&# +M!HV0(T>B3`EC)?.& +M#E&.2YL^G4JUJM6K6+-6G1$#Q!$G55Q`E#BGC(LV8>#TH"$C!XT<,&+LKTN"O#AE>P8B/Z=1.FC6`[:<;02=-&@=;+F#-KWLRY +ML^?/H$-KABR9LNC3(%.6/$FS9>N9+V&SM`EC[DX0+5'K_FD4J1RE3)UFC2I\ +2M_'CR+->2ce_offset + file->ce_size > iso9660->volume_size + * + * is wrapping uint64 arithmetic, so an entry choosing + * ce_offset + ce_size == logical_block_size brought the sum to exactly 0 and + * the test passed. The entry was accepted and queued at an offset that never + * matches current_position, so read_CE() silently never processed it. + * + * 0xFFFFFFFF is the only location for which this worked. An earlier check + * already rejects ce_offset + ce_size > logical_block_size, so for a location + * that sign-extends to -k the wrapped sum is small only when k is 1 and + * ce_offset + ce_size is exactly logical_block_size; every other location with + * its top bit set left the sum near 2^64, which the bound test rejected. + * + * The reference image is a directory entry carrying exactly that "CE": + * location 0xFFFFFFFF, ce_offset 0, ce_size 2048. + * + * Carrying the location as uint32_t is what fixes it: the offset becomes + * 0x7FFFFFF800, nothing wraps, and the bound test rejects the entry the way it + * was always meant to. (The checked helpers register_CE() now uses make the + * bound explicit, but their overflow branch cannot trigger for any value these + * fields can hold: location is at most 2^32-1 and logical_block_size is read + * with archive_le16dec, so the product stays below 2^48 and the sum below + * 2^49.) + */ +DEFINE_TEST(test_read_format_iso_rockridge_ce_overflow) +{ + const char *refname = "test_read_format_iso_rockridge_ce_overflow.iso.Z"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* The malformed "CE" is parsed while reading the root directory, so + * the very first header read must fail. Before the fix the entry was + * accepted and this returned ARCHIVE_OK. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assertEqualString("Invalid parameter in SUSP \"CE\" extension", + archive_error_string(a)); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_iso_rockridge_ce_overflow.iso.Z.uu b/libarchive/test/test_read_format_iso_rockridge_ce_overflow.iso.Z.uu new file mode 100644 index 000000000000..b4cc23c8ec84 --- /dev/null +++ b/libarchive/test/test_read_format_iso_rockridge_ce_overflow.iso.Z.uu @@ -0,0 +1,15 @@ +begin 644 test_read_format_iso_rockridge_ce_overflow.iso.Z +M'YV0``(*'$BPH,&#"!,J7,BPH<.'$"-*G$BQHL6+&#-JW,BQH\>/($.*'$FR +MI,F3*%.J7,FRIO8,.*'4NVK-FS:-.J7//JW/'D"-+GDRYLN7+F#-KWLRY +ML^?/H$.+'DVZM.G3J%.K7LVZM>O7L&/+GDV[MNW;N'/KWLV[M^_?P(,+'TZ\ +MN/'CR),K7\Z\N?/GT*-+GTZ]NO7KV+-KW\Z]N_?OX,.+'T^^O/GSZ-.K7\^^ +MO?OW\./+GT^_OOW[^//KW\^_O_^_`0Q!!`PPQ!```"`DJ.""##;HX(,01@CA +M01@45*%'!P(00(8'(H!`0!\*]*$$#HD`@`4%H1@BB`#HL>%``@3$88;_U0@; +MC3;FJ...?/TC(($&\BCDD$06:>212":IY)),-NGDDU!&*>645%9IY9589JGE +MEEQVZ>678(8IYIADJN;"B2D"L**:+;XH4(P:RGC@%%`<$(`O[P``!1$%'&@B +MB@2I.-"'+N((YXP!^`#`!04Q:E"A,,HH9Q!#%,%!`/]DJFFF"*VY9IF@ABKJ ++J*26:NJIJ*:JJJH` +` +end diff --git a/libarchive/test/test_read_format_iso_rockridge_cl_re_control.iso.Z.uu b/libarchive/test/test_read_format_iso_rockridge_cl_re_control.iso.Z.uu new file mode 100644 index 000000000000..7b78b67d7ba7 --- /dev/null +++ b/libarchive/test/test_read_format_iso_rockridge_cl_re_control.iso.Z.uu @@ -0,0 +1,18 @@ +begin 644 test_read_format_iso_rockridge_cl_re_control.iso.Z +M'YV0 (*'$BPH,&#"!,J7,BPH<.'$"-*G$BQHL6+&#-JW,BQH\>/($.*'$FR +MI,F3*%.J7,FRIO8,.*'4NVK-FS:-.J7//JW/'D"-+GDRYLN7+F#-KWLRY +ML^?/H$.+'DVZM.G3J%.K7LVZM>O7L&/+GDV[MNW;N'/KWLV[M^_?P(,+'TZ\ +MN/'CR),K7\Z\N?/GT*-+GTZ]NO7KV+-KW\Z]N_?OX,.+'T^^O/GSZ-.K7\^^ +MO?OW\./+GT^_OOW[^//KW\^_O_^_ 0Q!! PPQ! " DJ.""##;HX()#/.$$ +M%5(\P42%0RPA11)$'%'$@P\>E$%!(WIT( !G'@@ @@ H$!!"DB0T 0#B0" +M!07AV*) +?J1@ $#"1"0BB?^9R1;,L @@PTPY #DP1&22 22[9Y)-22BE2 +MD4=VZ>67?/TC(($&@FGFF6BFJ>::N1V(XY!LQBGGG'36:2=B7.+(Y9U\]NGG +MGX &*NB@A!9JZ*&()JKHHHPVZNBC?>EP8XX [!A0CS\&"2>*!TX!Q0$!^/(. +M %)(44"*4&#!0 #M!#%0$.W8^.9 .@Z$*9 ""8GBD"G" , %!0%K::4 ^(AK +M0+JJB( <JNFNG 9@ @ 8%%1ON^,>"T"RO HA11$$[,GMP 07;/#!87E;+T'WVIKO +HNT12FRX -@)+D+ .&PLQKP&H )'&!:DX0! [Q("NM0BGK/+*++?L$P + +end diff --git a/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.c b/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.c new file mode 100644 index 000000000000..7b9586d783ff --- /dev/null +++ b/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.c @@ -0,0 +1,101 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 iPuppeteer - Red Centipede Security + * 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 "test.h" + +/* + * A malformed Rock Ridge ISO whose "CL" (child link) entries reparent + * directories into a cycle of "parent" pointers in which no node carries + * the "RE" (relocated entry) flag. rede_add_entry() walks that parent + * chain looking for the "RE" ancestor; before the fix the walk had no + * termination guard, so the cyclic chain spun forever (CWE-835, 100% CPU). + * The reader must instead reject the image with ARCHIVE_FATAL while + * enumerating headers. + */ +DEFINE_TEST(test_read_format_iso_rockridge_cl_re_cycle) +{ + const char *refname = "test_read_format_iso_rockridge_cl_re_cycle.iso.Z"; + struct archive *a; + struct archive_entry *ae; + int r; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* + * Read headers until the cyclic CL/RE structure is reached. If the + * guard is missing this loop never returns (the test times out); with + * the guard the reader stops with ARCHIVE_FATAL. + */ + while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) + archive_read_data_skip(a); + + /* The cyclic CL/RE chain must be reported as a fatal error. */ + assertEqualIntA(a, ARCHIVE_FATAL, r); + assert(archive_errno(a) != 0); + assert(archive_error_string(a) != NULL); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* + * Control: a well-formed Rock Ridge ISO that also uses the rr_moved / "RE" + * relocation machinery but contains no cycle. It must continue to parse + * cleanly to EOF -- i.e. the cycle guard in rede_add_entry() must not + * over-reject legitimate deep Rock Ridge relocation. + */ +DEFINE_TEST(test_read_format_iso_rockridge_cl_re_control) +{ + const char *refname = + "test_read_format_iso_rockridge_cl_re_control.iso.Z"; + struct archive *a; + struct archive_entry *ae; + int r, entries = 0; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { + entries++; + archive_read_data_skip(a); + } + + /* Benign relocation image parses to completion, not to an error. */ + assertEqualIntA(a, ARCHIVE_EOF, r); + assert(entries > 0); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.iso.Z.uu b/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.iso.Z.uu new file mode 100644 index 000000000000..f91a0fab4e83 --- /dev/null +++ b/libarchive/test/test_read_format_iso_rockridge_cl_re_cycle.iso.Z.uu @@ -0,0 +1,22 @@ +begin 644 test_read_format_iso_rockridge_cl_re_cycle.iso.Z +M'YV0 (*'$BPH,&#"!,J7,BPH<.'$"-*G$BQHL6+&#-JW,BQH\>/($.*'$FR +MI,F3*%.J7,FRIO8,.*'4NVK-FS:-.J7//JW/'D"-+GDRYLN7+F#-KWLRY +ML^?/H$.+'DVZM.G3J%.K7LVZM>O7L&/+GDV[MNW;N'/KWLV[M^_?P(,+'TZ\ +MN/'CR),K7\Z\N?/GT*-+GTZ]NO7KV+-KW\Z]N_?OX,.+'T^^O/GSZ-.K7\^^ +MO?OW\./+GT^_OOW[^//KW\^_O_^_ 0Q!! PPQ! " DJ.""##;HX()#7%$$ +M#C/4, 034A0Q1!87%D'#@P\>Q$%!(WIT( !G'@@ @@ H$!!"DB0T 0#B0" +M!07AV*) +?J1@ $#"1"0BB?^9R1;,L @@PTPY #DP1&22 22[9Y)-22BE2 +MD4=VZ>67?/TC(($&@FGFF6BFJ>::N1V(XY!LQBGGG'36:2=B7.+(Y9U\]NGG +MGX &*NB@A!9JZ*&()JKHHHPVZNBC?>EP8XX [!A0CS\&"2>*!TX!Q0$!^/(. +M %)(44"*4&#!0 #M!#%0$.W8^.9 .@Z$*9 ""8GBD"G" , %!0%K::4 ^(AK +M0+JJB( <JNFNG 9@ @ 8%%1ON^,>"T"RO J1(0'R J!!00/C:^R[1 [Q;\ ;%-2P +MP>0B:VX 4"S,[<489ZSQQAIY6R]!]]J:+\*\4ILN #8"2Y"P(A]<+KPI!N!M +M!@71#+&^_'(:!;K634(_&[E=L1'OH+V[U7P6K`"MB0:^U'P' +M>_GQ-]6ELRA<+`H7])LEH,6@S:&42^R@X5B5T4A8(7<"VLD#0.:3I#+C79#H +M89787[6>E7[B.D/*>@(YZMDJ`/1YU;D3)"*?=N/-'2(3IY)6T@Q +MD$2+TG#,31K:]M]:RU@#K5#K!@Z6"$P,=?8(170&M?_A@34`QB`'I*4AF-QZ +MU:J58#SK\&=LRD15?L#"/!3@IPCN\%%7;8[+B8O)F\LMEJE#MD]5321(B31E +M=N>.#O<[-?3O^NUU,_GO%_O9/$L)H0N0^;SB_'HDM*.:Y]K-](>/LOK%M2T/ +MF!ULKRG$GNB*I*`X.?&$79&#O@$TZ59B531\DQQ$J%=/-H*\T'SECQG&KP*0 +MW+-O)C$`(_8M;&@U+2X!``!G`@``X63F'``!"D5D+7-T87)T=7`1;D$```$( +M8Y>1I6)XYX`[+C_"T``'!8*AA+%09'&M.T;RAW6WN`AXY;N]UE6+A?J`6M)M +MRPE`)@D]+&_([GR7B@S`&$)/.H67[]\"[5M[VU2!(VH.T,&M?`>P,""L(!C/ +MWB0EMGYB$.(DZ#GX[I^EQJMT\U*,BU5R153[E6DRM*!\R3_7D +MM]SK64ZM)N=+8\F\QXBR%LZ$(>Q6/^\1=Y%XW]*83N:][>/\$H()1`)-"HIL +M)C-:V*CBNX9O(9OAC!B@<^Q;/BVX9L2,<^[ZZ3V*<%J`;&9>C_F$,XSM74I< +M:LR&D>`\!>(>*V?>?U$CJ@$RPQ.-G7_;!JX3[A2K2"">+QK-MC)!]*ST2<+W +M6^FW_@DYHWMG-1V"I;B*=?R]N?0E$O"!M`2WAXW@&4$M;&@P+1$````````` +>FF$M7``!````00P``D5M<'1Y1&ER_P4```/@```` +` +end diff --git a/libarchive/test/test_read_format_lha_filename.c b/libarchive/test/test_read_format_lha_filename.c index 22c303aa4b3e..af2396ce6764 100644 --- a/libarchive/test/test_read_format_lha_filename.c +++ b/libarchive/test/test_read_format_lha_filename.c @@ -35,7 +35,7 @@ test_read_format_lha_filename_CP932_eucJP(const char *refname) /* * Read LHA filename in ja_JP.eucJP. */ - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("ja_JP.eucJP locale not available on this system."); return; } @@ -98,7 +98,7 @@ test_read_format_lha_filename_CP932_UTF8(const char *refname) /* * Read LHA filename in en_US.UTF-8. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_lha_filename_utf16.c b/libarchive/test/test_read_format_lha_filename_utf16.c index f700ec3c3ebb..1c6966cba295 100644 --- a/libarchive/test/test_read_format_lha_filename_utf16.c +++ b/libarchive/test/test_read_format_lha_filename_utf16.c @@ -35,7 +35,7 @@ test_read_format_lha_filename_UTF16_UTF8(const char *refname) /* * Read LHA filename in en_US.UTF-8. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_mtree_size.c b/libarchive/test/test_read_format_mtree_size.c new file mode 100644 index 000000000000..032615a39560 --- /dev/null +++ b/libarchive/test/test_read_format_mtree_size.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2026 krishna28238-arch + * 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 "test.h" + +/* + * Verify how the size keyword interacts with the checkfs option. + * + * Like the other metadata keywords (uid, gid, mode, mtime, ...), an + * explicit size in the specification must win over the value found on + * disk; the on-disk size is only a fallback for entries without a + * size keyword. The nochange keyword inverts this and forces the + * on-disk values to be used. + */ +static void +verify(const char *spec, int expected_size) +{ + struct archive_entry *ae; + struct archive *a; + char buf[16]; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_set_options(a, "mtree:checkfs")); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, spec, strlen(spec))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(archive_entry_pathname(ae), "x"); + assertEqualInt(expected_size, archive_entry_size(ae)); + assertEqualIntA(a, expected_size, + archive_read_data(a, buf, sizeof(buf))); + assertEqualIntA(a, 0, archive_read_data(a, buf, sizeof(buf))); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_mtree_size) +{ + /* The file referenced by the specifications is 10 bytes long. */ + assertMakeFile("file", 0644, "0123456789"); + + /* An explicit size must override the on-disk file size. */ + verify("#mtree\n" + "x type=file size=3 contents=file\n", 3); + + /* Without a size keyword, the on-disk file size is used. */ + verify("#mtree\n" + "x type=file contents=file\n", 10); + + /* The nochange keyword forces the on-disk file size. */ + verify("#mtree\n" + "x type=file size=3 nochange contents=file\n", 10); +} diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c index b4b16c66ec9c..44830a4cf1ea 100644 --- a/libarchive/test/test_read_format_rar.c +++ b/libarchive/test/test_read_format_rar.c @@ -205,7 +205,7 @@ DEFINE_TEST(test_read_format_rar_unicode_UTF8) struct archive_entry *ae; struct archive *a; - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -344,8 +344,11 @@ DEFINE_TEST(test_read_format_rar_unicode_CP932) struct archive_entry *ae; struct archive *a; - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c index 01881c1dd3d6..863326a5b744 100644 --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -154,6 +154,37 @@ DEFINE_TEST(test_read_format_rar5_stored) EPILOGUE(); } +DEFINE_TEST(test_read_format_rar5_zip_in_rar) +{ + /* Regression test for issue #2249. This archive begins with the + * RAR5 signature but stores an uncompressed (method "store") Zip + * file as one of its members. The Zip bidder used to outscore the + * RAR5 bidder on such archives (its seekable end-of-central-directory + * bid of 32 beat the RAR5 signature bid of 30), so auto-detection + * misread the whole thing as a Zip and reported the embedded Zip's + * entries instead of the RAR members. With the RAR5 signature bid + * raised to 64 the reader must pick RAR5 and list the real members. */ + + const char *expected[] = { + "payload/inner.zip", + "payload/real_after.txt", + "payload", + }; + size_t i; + + PROLOGUE("test_read_format_rar5_zip_in_rar.rar"); + + for(i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) { + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_FORMAT_RAR_V5, archive_format(a)); + assertEqualString(expected[i], archive_entry_pathname(ae)); + } + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + DEFINE_TEST(test_read_format_rar5_compressed) { const int DATA_SIZE = 1200; @@ -253,6 +284,35 @@ DEFINE_TEST(test_read_format_rar5_multiple_files_solid) EPILOGUE(); } +DEFINE_TEST(test_read_format_rar5_multiarchive_first_volume_only) +{ + /* Regression test: opening only the first volume of a multivolume + * RAR5 set must not silently return a truncated file. The first + * entry's data is flagged 'split after', i.e. it continues in the + * following volumes; when those are not supplied, reading the data + * has to fail rather than report a clean end of file after a short + * read, which used to silently hand back corrupted data. */ + char buf[16384]; + la_ssize_t r; + + PROLOGUE("test_read_format_rar5_multiarchive.part01.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString( + "home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", + archive_entry_pathname(ae)); + + /* Drain the entry's data. It must terminate with a fatal error, + * not a clean 0-length end of file. */ + do { + r = archive_read_data(a, buf, sizeof(buf)); + } while(r > 0); + + assertEqualInt(ARCHIVE_FATAL, r); + + EPILOGUE(); +} + DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all) { const char* reffiles[] = { @@ -912,6 +972,50 @@ DEFINE_TEST(test_read_format_rar5_owner) EPILOGUE(); } +DEFINE_TEST(test_read_format_rar5_owner_name_toolong) +{ + /* GH #3066: a crafted HEAD_FILE declares an EX_UOWNER owner user name + * whose length is far larger than the extra field that contains it. + * The reader used to pass that length straight to read_ahead(), which + * attempted a multi-terabyte allocation. It must reject the header + * instead of trying to satisfy the bogus length. */ + PROLOGUE("test_read_format_rar5_owner_name_toolong.rar"); + + assertA(archive_read_next_header(a, &ae) < 0); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_group_name_toolong) +{ + /* Companion to test_read_format_rar5_owner_name_toolong: the crafted + * HEAD_FILE declares an EX_UOWNER owner group name whose length + * (0x8000000000000) is far larger than the extra field that contains + * it. The length must be validated in 64 bits: 32-bit platforms used + * to truncate it to zero and accept the header instead of rejecting + * it. */ + PROLOGUE("test_read_format_rar5_group_name_toolong.rar"); + + assertA(archive_read_next_header(a, &ae) < 0); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_extra_data_size_toolong) +{ + /* A crafted HEAD_FILE declares a file-header extra-area size + * (edata_size) far larger than the 2 MB header cap. That value was + * used, unbounded, as the ceiling for the EX_UOWNER owner-name length, + * so parse_file_extra_owner() passed an attacker-chosen length straight + * to read_ahead(), attempting a multi-terabyte allocation. The reader + * must reject the oversized extra-area size instead. */ + PROLOGUE("test_read_format_rar5_extra_data_size_toolong.rar.Z"); + + assertA(archive_read_next_header(a, &ae) < 0); + + EPILOGUE(); +} + DEFINE_TEST(test_read_format_rar5_symlink) { const int DATA_SIZE = 5; @@ -1614,3 +1718,33 @@ DEFINE_TEST(test_read_format_rar5_seek_data_unsupported) EPILOGUE(); } + +DEFINE_TEST(test_read_format_rar5_redir_varint_2byte) +{ + /* + * A crafted RAR5 whose EX_REDIR symlink target is exactly 128 bytes. + * That makes read_var_sized() encode the length as a 2-byte varint + * (0x80 0x01) instead of the 1-byte form assumed by the old "+1" code. + * With the old code extra_data_size was decremented by only target_size+1 + * (129) instead of target_size+2 (130), leaving it 1 too large. + * process_head_file_extra() would then loop back and attempt to parse a + * phantom extra field from the one byte of padding that follows, which + * returns ARCHIVE_EOF. With the fix, extra_data_size reaches exactly 0 + * and the loop exits cleanly. + */ + char expected_target[129]; + memset(expected_target, 'a', 128); + expected_target[128] = '\0'; + + PROLOGUE("test_read_format_rar5_redir_varint_2byte.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("link.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString(expected_target, archive_entry_symlink(ae)); + assertEqualInt(128, (int)strlen(archive_entry_symlink(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} diff --git a/libarchive/test/test_read_format_rar5_extra_data_size_toolong.rar.Z.uu b/libarchive/test/test_read_format_rar5_extra_data_size_toolong.rar.Z.uu new file mode 100644 index 000000000000..b56e50507941 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_extra_data_size_toolong.rar.Z.uu @@ -0,0 +1,18 @@ +begin 644 test_read_format_rar5_extra_data_size_toolong.rar.Z +M'YV04L+(":'A0 Q33,D#'@( ![K=!@PR @ *"+%X$O8,.*'4NVK-FS:-.J7//JW/'D"-+GDRYLN7+F#-KWLRY +ML^?/H$.+'DVZM.G3J%.K7LVZM>O7L&/+GDV[MNW;N'/KWLV[M^_?P(,+'TZ\ +MN/'CR),K7\Z\N?/GT*-+GTZ]NO7KV+-KW\Z]N_?OX,.+'T^^O/GSZ-.K7\^^ +MO?OW\./+GT^_OOW[^//KW\^_O___ 8HX( $%FC@@0@FJ.""##;HX(,01BCA +MA!16:.&%&&:HX88<=NCAAR"&*.*())9HXHDHIJCBBBRVZ.*+,,8HXXPTUFCC +MC3CFJ...//;HXX] !BGDD$06:>212":IY)),-NGDDU!&*>645%9IY9589JGE +MEEQVZ>678(8IYIADEFGFF6BFJ>::;+;IYIMPQBGGG'36:>>=>.:IYYY\]NGG +MGX &*NB@A!9JZ*&()JKHHHPVZNBCD$8JZ:245FKII9AFJNFFG';JZ:>@ABKJ +MJ*26:NJIJ*:JZJJLMNKJJ[#&*NNLM-9JZZVXYJKKKKSVZNNOP 8K[+#$%FOL +ML<@FJ^RRS#;K[+/01BOMM-16:^VUV&:K[;;<=NOMM^"&*^ZXY)9K[KGHIJON +MNNRVZ^Z[\,8K[[STUFOOO?CFJ^^^_/;K[[\ !RSPP 07;/#!"">L\,(,-^SP +=PQ!'+/'$%%=L\<489ZSQQAQW[/''((69;#0(# @$` +0 0%B 60 .?FR@0(% ` +` +end diff --git a/libarchive/test/test_read_format_rar5_group_name_toolong.rar.uu b/libarchive/test/test_read_format_rar5_group_name_toolong.rar.uu new file mode 100644 index 000000000000..f21750dc09fe --- /dev/null +++ b/libarchive/test/test_read_format_rar5_group_name_toolong.rar.uu @@ -0,0 +1,5 @@ +begin 644 test_read_format_rar5_group_name_toolong.rar +M4F%R(1H'`0#%&C,R`P$``%,'IAX5`@$+`0`0``$!80H&`H"`@("`@(`$6@`` +"```` +` +end diff --git a/libarchive/test/test_read_format_rar5_header_size_wraparound.c b/libarchive/test/test_read_format_rar5_header_size_wraparound.c new file mode 100644 index 000000000000..9f8c2ab304dd --- /dev/null +++ b/libarchive/test/test_read_format_rar5_header_size_wraparound.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2026 krishna28238-arch + * 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 "test.h" + +/* + * Regression test for a size_t wraparound in process_base_block(). + * + * The size vint of a base block can decode to values close to SIZE_MAX, + * because read_var_sized() does not bound the decoded value. Computing + * hdr_size = raw_hdr_size + hdr_size_len then wraps around to a small + * number, which passes the 2 MB sanity check. The reader accepted the + * block and desynced; on the 22-byte archive below, the unpatched reader + * consumed the size vint as a whole block and reported ARCHIVE_EOF. + * + * The patched reader bounds raw_hdr_size before the addition and rejects + * the block up front. + */ +DEFINE_TEST(test_read_format_rar5_header_size_wraparound) +{ + /* RAR5 signature, CRC32 of the first nine 0xFF bytes, and a size + * vint that decodes to SIZE_MAX (0xFF * 9, 0x01). */ + static const uint8_t data[] = { + 0x52,0x61,0x72,0x21,0x1a,0x07,0x01,0x00, + 0x90,0x18,0x20,0xeb,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0x01 + }; + + struct archive *a; + struct archive_entry *ae; + + a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_rar5(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, + data, sizeof(data))); + + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assert(archive_error_string(a) != NULL); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_rar5_owner_name_toolong.rar.uu b/libarchive/test/test_read_format_rar5_owner_name_toolong.rar.uu new file mode 100644 index 000000000000..780c279376db --- /dev/null +++ b/libarchive/test/test_read_format_rar5_owner_name_toolong.rar.uu @@ -0,0 +1,5 @@ +begin 644 test_read_format_rar5_owner_name_toolong.rar +M4F%R(1H'`0#%&C,R`P$``)8[*R<5`@$+`0`0``$!80H&`8"`@("`@(`$6@`` +"```` +` +end diff --git a/libarchive/test/test_read_format_rar5_redir_overflow.c b/libarchive/test/test_read_format_rar5_redir_overflow.c new file mode 100644 index 000000000000..0f7cc8c9eb93 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_redir_overflow.c @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2026 susen325 + * 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 "test.h" + +/* + * A crafted RAR5 archive with a symlink entry whose target_size varint + * is pathologically large. In parse_file_extra_redir(), subtracting + * target_size and varint_len from *extra_data_size before validating + * the bounds of target_size triggers a signed integer overflow in + * int64_t arithmetic. The fix moves the bounds checks before the + * arithmetic so the malformed archive is rejected with ARCHIVE_FATAL. + */ +DEFINE_TEST(test_read_format_rar5_redir_overflow) +{ + struct archive *a; + struct archive_entry *ae; + + extract_reference_file( + "test_read_format_rar5_redir_overflow.rar"); + a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_rar5(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, + "test_read_format_rar5_redir_overflow.rar", 10240)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_rar5_redir_overflow.rar.uu b/libarchive/test/test_read_format_rar5_redir_overflow.rar.uu new file mode 100644 index 000000000000..945ee9906a1c --- /dev/null +++ b/libarchive/test/test_read_format_rar5_redir_overflow.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_redir_overflow.rar +M4F%R(1H'`0!3@"KT`P+G(D&:``!@4```*B!V559!]&!8VPQ\<8P8,?'&+++& +M,99,8,%EEED,?&RRR8QBR0GQD'96-A4%!07___________\````````````` +*`````/__`P4$```` +` +end diff --git a/libarchive/test/test_read_format_rar5_redir_varint_2byte.rar.uu b/libarchive/test/test_read_format_rar5_redir_varint_2byte.rar.uu new file mode 100644 index 000000000000..415872945fb6 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_redir_varint_2byte.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_redir_varint_2byte.rar +M4F%R(1H'`0#%&C,R`P$``%DA0:R:`0(#AP$`````(`$(;&EN:RYT>'2%`04! +M`(`!86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A +M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A +M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86$Y^;*! +#`@4` +` +end diff --git a/libarchive/test/test_read_format_rar5_truncated_htime.c b/libarchive/test/test_read_format_rar5_truncated_htime.c new file mode 100644 index 000000000000..135922f765b1 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_truncated_htime.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2026 krishna28238-arch + * 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 "test.h" + +/* + * Regression test for ignored parse_htime_item() failures in + * parse_file_extra_htime(). + * + * The HTIME extra record of the FILE header below declares a unix mtime, + * ctime and atime, but the archive ends right after the mtime and ctime + * values, so reading the atime fails with ARCHIVE_EOF. The unpatched + * code ignored that failure: the extra area accounting then went + * negative and the reader reported a misleading ARCHIVE_FATAL + * "unsupported structure of file header extra data". The patched code + * propagates the EOF instead. + */ +DEFINE_TEST(test_read_format_rar5_truncated_htime) +{ + /* RAR5 archive with a FILE header ("a") whose HTIME extra record is + * truncated inside the atime field. */ + static const uint8_t data[] = { + 0x52,0x61,0x72,0x21,0x1a,0x07,0x01,0x00, + 0x01,0xa1,0xc1,0x64,0x17,0x02,0x03,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x61, + 0x00,0x03,0x0f,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 + }; + + struct archive *a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_rar5(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, + data, sizeof(data))); + + struct archive_entry *ae; + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_rar5_varint_near_eof.c b/libarchive/test/test_read_format_rar5_varint_near_eof.c new file mode 100644 index 000000000000..8377654bf43e --- /dev/null +++ b/libarchive/test/test_read_format_rar5_varint_near_eof.c @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2026 Gaurav Jadhav + * 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 "test.h" + +/* + * Regression test for GitHub issue #3484. + * + * read_var() always calls read_ahead(a, 10, &p), requiring a full 10-byte + * lookahead before decoding a varint, even though RAR5 varints are + * self-terminating (a byte with the MSB clear ends the value, usually + * within 1-3 bytes). In this single-entry archive the FILE header's extra + * area size is encoded as a 1-byte varint that sits fewer than 10 bytes + * from the end of the archive (ENDARC immediately follows, with no + * padding), which is a legal layout. read_ahead(a, 10, &p) can't satisfy + * the full 10-byte request, so read_var() returned 0 even though the + * varint's bytes were fully present and valid, and every caller treated + * that failure identically to legitimate end-of-archive: the entry was + * silently dropped and archive_read_next_header() returned ARCHIVE_EOF. + */ +DEFINE_TEST(test_read_format_rar5_varint_near_eof) +{ + /* Minimal single-entry RAR5 archive: signature, main archive + * header, a FILE header for entry "a" (empty, stored) whose extra + * area size varint sits right up against the trailing ENDARC + * block, then ENDARC itself. */ + static const uint8_t data[] = { + 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00, + 0xc5, 0x1a, 0x33, 0x32, 0x03, 0x01, 0x00, 0x00, + 0x1a, 0xc7, 0x20, 0x59, 0x0d, 0x02, 0x03, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x61, + 0x01, 0x64, 0x00, + 0x39, 0xf9, 0xb2, 0x81, 0x02, 0x05, 0x00 + }; + + struct archive *a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_rar5(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, + data, sizeof(data))); + + struct archive_entry *ae; + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("a", archive_entry_pathname(ae)); + + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_rar5_zip_in_rar.rar.uu b/libarchive/test/test_read_format_rar5_zip_in_rar.rar.uu new file mode 100644 index 000000000000..107133f23e51 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_zip_in_rar.rar.uu @@ -0,0 +1,11 @@ +begin 644 test_read_format_rar5_zip_in_rar.rar +M4F%R(1H' 0 SDK7E"@$%!@ % 0& @ "(/\@F+0(#"Z4!!*4!("[A<86 1 +M<&%Y;&]A9"]I;FYE S2# =A.D +M%X2[-NGU(*VLHG0+KG"V2*8G'[N !.<8_P#]"N862-?B!S("21<6" MZ9)%% +MTVBZV)'AQM$KN^1=)?"S='BE065(5;'A*4?V#3!1,W-FC,/.O2@LY@F 4L,) +4I7)B8@ OXAG]JG_U,0]>P! !P +` +end diff --git a/libarchive/test/test_read_format_tar_filename.c b/libarchive/test/test_read_format_tar_filename.c index 42081ef1935d..7affec9068ea 100644 --- a/libarchive/test/test_read_format_tar_filename.c +++ b/libarchive/test/test_read_format_tar_filename.c @@ -46,8 +46,11 @@ test_read_format_tar_filename_KOI8R_CP866(const char *refname) * Read filename in ru_RU.CP866 with "hdrcharset=KOI8-R" option. * We should correctly read two filenames. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.866") && - NULL == setlocale(LC_ALL, "ru_RU.CP866")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.866") && +#endif + !setCheckedLocale("ru_RU.CP866", "\x80", L'\x0410')) { skipping("ru_RU.CP866 locale not available on this system."); return; } @@ -164,7 +167,7 @@ test_read_format_tar_filename_KOI8R_UTF8(const char *refname) * Read filename in en_US.UTF-8 with "hdrcharset=KOI8-R" option. * We should correctly read two filenames. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -275,8 +278,11 @@ test_read_format_tar_filename_KOI8R_CP1251(const char *refname) * Read filename in CP1251 with "hdrcharset=KOI8-R" option. * We should correctly read two filenames. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_tar_pax_global_header.c b/libarchive/test/test_read_format_tar_pax_global_header.c new file mode 100644 index 000000000000..c19ec8f27216 --- /dev/null +++ b/libarchive/test/test_read_format_tar_pax_global_header.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2026 alb3e3 + * 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 "test.h" + +/* + * A pax global header supplies defaults for every following member, which + * we do not implement. An attribute that we would otherwise have honored + * therefore cannot be ignored, and the archive is rejected. + */ +DEFINE_TEST(test_read_format_tar_pax_global_header) +{ + const char *name = "test_read_format_tar_pax_global_header.tar"; + struct archive_entry *ae; + struct archive *a; + + extract_reference_file(name); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, name, 10240)); + + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae)); + assertEqualString( + "Unsupported pax global extended header attribute 'path'", + archive_error_string(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* + * A global header that carries only attributes we ignore everywhere is + * harmless and must still be readable. star always emits such a header + * for its "exustar" format, so rejecting these would break well-formed + * archives. + */ +DEFINE_TEST(test_read_format_tar_pax_global_header_ignorable) +{ + const char *name = + "test_read_format_tar_pax_global_header_ignorable.tar"; + char data[6]; + struct archive_entry *ae; + struct archive *a; + + extract_reference_file(name); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, name, 10240)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("member.txt", archive_entry_pathname(ae)); + assertEqualInt(6, archive_entry_size(ae)); + assertEqualIntA(a, 6, archive_read_data(a, data, sizeof(data))); + assertEqualMem(data, "hello\n", 6); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_tar_pax_global_header.tar.uu b/libarchive/test/test_read_format_tar_pax_global_header.tar.uu new file mode 100644 index 000000000000..5f5ffcf0b7a0 --- /dev/null +++ b/libarchive/test/test_read_format_tar_pax_global_header.tar.uu @@ -0,0 +1,72 @@ +begin 644 test_read_format_tar_pax_global_header.tar +M<&%X7V=L;V)A;%]H96%D97( +M +M # P,# V-#0 ,# P,# P, P,# P,# P # P,# P,# P,#(S +M # P,# P,# P,# P # Q,3,W,@ @9P +M +M !U'0* +M +M +M +M +M +M +M +M +M +M +M ')A=RYT>'0 +M +M P +M,# P-C0T # P,# P,# ,# P,# P, P,# P,# P,# P,0 P,# P,# P,# P +M, P,#F4] +M,C`*```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````&UE;6)E'0` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````P +M,#`P-C0T`#`P,#`P,#``,#`P,#`P,``P,#`P,#`P,#`P-@`Q,S4P,#`P,#`P +M,``P,3$U-S(`(#`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````=7-T87(`,#!R;V]T```````````````` +M`````````````````````')O;W0````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````:&5L;&\*```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +,```````````````` +` +end diff --git a/libarchive/test/test_read_format_ustar_filename.c b/libarchive/test/test_read_format_ustar_filename.c index 7896ff4ec9ff..a603d6c4c486 100644 --- a/libarchive/test/test_read_format_ustar_filename.c +++ b/libarchive/test/test_read_format_ustar_filename.c @@ -35,7 +35,7 @@ test_read_format_ustar_filename_eucJP_UTF8(const char *refname) /* * Read eucJP filename in en_US.UTF-8 with "hdrcharset=eucJP" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -89,8 +89,11 @@ test_read_format_ustar_filename_CP866_KOI8R(const char *refname) /* * Read CP866 filename in ru_RU.KOI8-R with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.20866") && - NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.20866") && +#endif + !setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("ru_RU.KOI8-R locale not available on this system."); return; } @@ -144,7 +147,7 @@ test_read_format_ustar_filename_CP866_UTF8(const char *refname) /* * Read CP866 filename in en_US.UTF-8 with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -199,8 +202,11 @@ test_read_format_ustar_filename_KOI8R_CP866(const char *refname) /* * Read KOI8-R filename in ru_RU.CP866 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.866") && - NULL == setlocale(LC_ALL, "ru_RU.CP866")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.866") && +#endif + !setCheckedLocale("ru_RU.CP866", "\x80", L'\x0410')) { skipping("ru_RU.CP866 locale not available on this system."); return; } @@ -255,7 +261,7 @@ test_read_format_ustar_filename_KOI8R_UTF8(const char *refname) /* * Read KOI8-R filename in en_US.UTF-8 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -310,8 +316,11 @@ test_read_format_ustar_filename_eucJP_CP932(const char *refname) /* * Read eucJP filename in CP932/SJIS with "hdrcharset=eucJP" option. */ - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932 locale not available on this system."); return; } @@ -364,8 +373,11 @@ test_read_format_ustar_filename_CP866_CP1251(const char *refname) /* * Read CP866 filename in CP1251 with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -426,7 +438,10 @@ test_read_format_ustar_filename_CP866_CP1251_win(const char *refname) /* * Read CP866 filename in CP1251 without "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia")) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (NULL == setlocale(LC_ALL, "Russian_Russia")) +#endif + { skipping("Russian_Russia locale not available on this system."); return; } @@ -475,8 +490,11 @@ test_read_format_ustar_filename_KOI8R_CP1251(const char *refname) /* * Read KOI8-R filename in CP1251 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_warc.c b/libarchive/test/test_read_format_warc.c index be14b88b1a29..fe3063773548 100644 --- a/libarchive/test/test_read_format_warc.c +++ b/libarchive/test/test_read_format_warc.c @@ -202,3 +202,61 @@ DEFINE_TEST(test_read_format_warc_truncated_body) assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } + +DEFINE_TEST(test_read_format_warc_uri_nul) +{ + const char reffile[] = "test_read_format_warc_uri_nul.warc"; + struct archive_entry *ae; + struct archive *a; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, reffile, 10240)); + + /* Entry cannot be parsed */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); + + /* Verify closing and resource freeing */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_warc_future_date) +{ + static const char warc[] = + "WARC/1.0\r\n" + "WARC-Type: resource\r\n" + "WARC-Date: 2039-01-01T00:00:00Z\r\n" + "WARC-Target-URI: file://test.txt\r\n" + "Content-Length: 0\r\n" + "\r\n" + "\r\n\r\n"; + struct archive_entry *ae; + struct archive *a; + + if (sizeof(time_t) < 8) { + skipping("This test requires a 64-bit time_t"); + return; + } + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, warc, sizeof(warc) - 1U)); + + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(2177452800LL, archive_entry_mtime(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_warc_uri_nul.warc.uu b/libarchive/test/test_read_format_warc_uri_nul.warc.uu new file mode 100644 index 000000000000..61243a244e73 --- /dev/null +++ b/libarchive/test/test_read_format_warc_uri_nul.warc.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_warc_uri_nul.warc +M5T%20R\Q+C`-"E=!4D,M5'EP93H@&%R(0`<``$`````````R`````````%W`````'B<7Z`<,[50#>91F%.O7FX3J5G!US^3!'"X>G\[6#YZC";Y3[;91-?L^ +M#,9?.W4YGS8'==05/FG6**%/.!K+M1G28Z71DV,](I1$6:8TI7N$@NC"P+K9 +M[W8(!2MD^OZU[*]RTPW"2AC&,;+DDY4PFA?GN21^S.HHB^5.T319TY,D=PB] +ML&RBS$Q.P6IVCSQOV4VR?!2!*5D,)/05:'\*M/\(0%E2(>1"&%R(0`<``$`````````H0`````````\`````'C:L[&OR,U1*$LM*L[,S[-5 +M,M0S4%)(S4O.3\G,2[=5"@UQT[50LK>SJ4@LLK,IR4^VL]&'D"`!`$ND$R1' +M1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T=' +M1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T='1T=' +)1T='1T='1T=' +end diff --git a/libarchive/test/test_read_format_xar_toc_premature_stream_end_control.xar.uu b/libarchive/test/test_read_format_xar_toc_premature_stream_end_control.xar.uu new file mode 100644 index 000000000000..057cb37c3cfb --- /dev/null +++ b/libarchive/test/test_read_format_xar_toc_premature_stream_end_control.xar.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_xar_toc_premature_stream_end_control.xar +M>&%R(0`<``$`````````/0`````````\`````'C:L[&OR,U1*$LM*L[,S[-5 +L,M0S4%)(S4O.3\G,2[=5"@UQT[50LK>SJ4@LLK,IR4^VL]&'D"`!`$ND$R0` +end diff --git a/libarchive/test/test_read_format_zip_7075_utf8_paths.c b/libarchive/test/test_read_format_zip_7075_utf8_paths.c index 136ddaa5388e..16ff72c11e01 100644 --- a/libarchive/test/test_read_format_zip_7075_utf8_paths.c +++ b/libarchive/test/test_read_format_zip_7075_utf8_paths.c @@ -74,7 +74,7 @@ DEFINE_TEST(test_read_format_zip_utf8_paths) extract_reference_file(refname); - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_zip_ambiguous_timestamps.c b/libarchive/test/test_read_format_zip_ambiguous_timestamps.c new file mode 100644 index 000000000000..11912cd7981b --- /dev/null +++ b/libarchive/test/test_read_format_zip_ambiguous_timestamps.c @@ -0,0 +1,321 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +/* + * Regression tests for the disambiguation of a raw 32-bit ZIP timestamp + * whose high bit is set, in the three extra fields that carry them: + * 0x000D (PKWARE Unix), 0x5455 (Info-Zip "UT" extended timestamp), and + * 0x5855 (Info-Zip "UX", old version). + * + * Such a value is ambiguous: read as signed, it is a pre-1970 date (as + * far back as 1901-12-13); read as unsigned, it is a post-2038 date (as + * far forward as 2106-02-07). Per + * https://github.com/libarchive/libarchive/issues/3550, the signed + * reading is preferred unless it would land before 1960 - no real file + * should predate computing itself by that much - in which case the + * unsigned reading is used instead. + * + * Each archive below has a single entry ("test.txt") probing this cutoff + * from both sides, plus the canonical halfway value 0x80000000 (which + * used to always resolve to 1901-12-13, the oldest date the old, + * sign-extension-only code could produce). + */ + +/* 0x000D "PKWARE Unix Extra Field": AcTime, ModTime, UID, GID. + * atime = 0xed30087f (one second before the 1960 cutoff -> unsigned + * reading, 2096-02-06 06:28:15 UTC). + * mtime = 0x80000000 (the halfway value -> unsigned reading, + * 2038-01-19 03:14:08 UTC, not the old 1901-12-13). */ +static const unsigned char archive_pkware_unix_ambiguous[] = { +/* --- archive_pkware_unix_ambiguous: local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000d */ + 0x0d, 0x00, /* extra field ID = 0x000d */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x7f, 0x08, 0x30, 0xed, /* atime -> 3979348095 (unsigned) */ + 0x00, 0x00, 0x00, 0x80, /* mtime -> 2147483648 (unsigned) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- archive_pkware_unix_ambiguous: central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000d */ + 0x0d, 0x00, /* extra field ID = 0x000d */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x7f, 0x08, 0x30, 0xed, /* atime -> 3979348095 (unsigned) */ + 0x00, 0x00, 0x00, 0x80, /* mtime -> 2147483648 (unsigned) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- archive_pkware_unix_ambiguous: end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x46, 0x00, 0x00, 0x00, /* size of central directory = 70 */ + 0x39, 0x00, 0x00, 0x00, /* offset of central directory = 57 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ambiguous_timestamp_pkware_unix) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_pkware_unix_ambiguous, + sizeof(archive_pkware_unix_ambiguous))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + if (sizeof(time_t) <= sizeof(int32_t)) { + /* time_t this narrow can't hold a post-2038 date, so + * to_time_t() always sign-extends here instead of + * picking the unsigned reading; see the matching + * comment in archive_read_support_format_zip.c. */ + assertEqualInt(-2147483648LL, archive_entry_mtime(ae)); + assertEqualInt(-315619201LL, archive_entry_atime(ae)); + } else { + assertEqualInt(2147483648LL, archive_entry_mtime(ae)); + assertEqualInt(3979348095LL, archive_entry_atime(ae)); + } + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x5455 "UT" extended timestamp: Flags, ModTime, AcTime, CrTime. + * mtime = 0xed300880 (exactly the 1960 cutoff -> signed reading, + * 1960-01-01 00:00:00 UTC). + * atime = 0xed300881 (one second past the cutoff -> signed reading, + * 1960-01-01 00:00:01 UTC). + * ctime = 0xffffffff (-1 as signed -> 1969-12-31 23:59:59 UTC, an + * ordinary near-1970 value, still handled correctly through the new + * helper). */ +static const unsigned char archive_ut_ambiguous[] = { +/* --- archive_ut_ambiguous: local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x11, 0x00, /* extra field length = 17 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5455 */ + 0x55, 0x54, /* extra field ID = 0x5455 */ + 0x0d, 0x00, /* extra field size = 13 */ + 0x07, /* flags: mtime, atime, ctime all present */ + 0x80, 0x08, 0x30, 0xed, /* mtime -> -315619200 (signed) */ + 0x81, 0x08, 0x30, 0xed, /* atime -> -315619199 (signed) */ + 0xff, 0xff, 0xff, 0xff, /* ctime -> -1 (signed) */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- archive_ut_ambiguous: central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x11, 0x00, /* extra field length = 17 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5455 */ + 0x55, 0x54, /* extra field ID = 0x5455 */ + 0x0d, 0x00, /* extra field size = 13 */ + 0x07, /* flags: mtime, atime, ctime all present */ + 0x80, 0x08, 0x30, 0xed, /* mtime -> -315619200 (signed) */ + 0x81, 0x08, 0x30, 0xed, /* atime -> -315619199 (signed) */ + 0xff, 0xff, 0xff, 0xff, /* ctime -> -1 (signed) */ +/* --- archive_ut_ambiguous: end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x47, 0x00, 0x00, 0x00, /* size of central directory = 71 */ + 0x3a, 0x00, 0x00, 0x00, /* offset of central directory = 58 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ambiguous_timestamp_ut) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ut_ambiguous, + sizeof(archive_ut_ambiguous))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(-315619200LL, archive_entry_mtime(ae)); + assertEqualInt(-315619199LL, archive_entry_atime(ae)); + assertEqualInt(-1LL, archive_entry_ctime(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x5855 "UX" (old): AcTime, ModTime, UID, GID. + * atime = 0xed300880 (exactly the 1960 cutoff -> signed reading, + * 1960-01-01 00:00:00 UTC). + * mtime = 0x80000000 (the halfway value -> unsigned reading, + * 2038-01-19 03:14:08 UTC). */ +static const unsigned char archive_ux_ambiguous[] = { +/* --- archive_ux_ambiguous: local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5855 */ + 0x55, 0x58, /* extra field ID = 0x5855 */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x80, 0x08, 0x30, 0xed, /* atime -> -315619200 (signed) */ + 0x00, 0x00, 0x00, 0x80, /* mtime -> 2147483648 (unsigned) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- archive_ux_ambiguous: central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x21, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5855 */ + 0x55, 0x58, /* extra field ID = 0x5855 */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x80, 0x08, 0x30, 0xed, /* atime -> -315619200 (signed) */ + 0x00, 0x00, 0x00, 0x80, /* mtime -> 2147483648 (unsigned) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- archive_ux_ambiguous: end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x46, 0x00, 0x00, 0x00, /* size of central directory = 70 */ + 0x39, 0x00, 0x00, 0x00, /* offset of central directory = 57 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ambiguous_timestamp_ux) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ux_ambiguous, + sizeof(archive_ux_ambiguous))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + if (sizeof(time_t) <= sizeof(int32_t)) { + /* time_t this narrow can't hold a post-2038 date, so + * to_time_t() always sign-extends here instead of + * picking the unsigned reading; see the matching + * comment in archive_read_support_format_zip.c. */ + assertEqualInt(-2147483648LL, archive_entry_mtime(ae)); + } else { + assertEqualInt(2147483648LL, archive_entry_mtime(ae)); + } + assertEqualInt(-315619200LL, archive_entry_atime(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_dos_mtime_precedence.c b/libarchive/test/test_read_format_zip_dos_mtime_precedence.c new file mode 100644 index 000000000000..9274cee86e9a --- /dev/null +++ b/libarchive/test/test_read_format_zip_dos_mtime_precedence.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +/* + * Regression tests for the Local Header's legacy DOS mtime NOT clobbering + * a more precise mtime already established from an extra field. Unlike + * test_read_format_zip_ntfs_precedence.c (which places the winning extra + * field in the Local Header, alongside a losing coarser one), each archive + * here places its timestamp extra field *only* in the Central Directory - + * a Local Header is allowed to omit an extra field the Central Directory + * carries. That means the seekable reader's second, redundant call to + * process_extra() on the Local Header's own (here absent) extra data can't + * mask a broken guard by re-deriving the correct mtime a second time: this + * is the one scenario where the DOS-clobber guard at + * zip_read_local_file_header() actually does the work, so it's the one + * that must be tested directly. + * + * Each archive's Local Header carries a deliberately-wrong DOS time/date + * (DOS_MIN_TIME, 1980-01-01) that must NOT end up as the entry's mtime; + * the Central-Directory-only extra field gives the correct value: + * mtime = 1700000000 (with .100000000 sub-second precision for NTFS) + */ + +/* 0x000A "NTFS", Central Directory only. */ +static const unsigned char archive_dos_mtime_precedence_ntfs[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (wrong-on-purpose: DOS_MIN_TIME) */ + 0x21, 0x00, /* last mod file date (wrong-on-purpose: DOS_MIN_TIME) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x00, 0x00, /* extra field length = 0 (no extra field here) */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" placed in the Central Directory ONLY - + * the Local Header above has none: this is the scenario the + * DOS-clobber guard exists for. */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (unused: 0x000a "NTFS" wins) */ + 0x21, 0x00, /* last mod file date (unused: 0x000a "NTFS" wins) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x24, 0x00, /* extra field length = 36 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (only copy: must set mtime) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime -> 1700000000.100000000 */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime -> 1700000100.200000000 */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> 1700000200.300000000 (-> birthtime) */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x5a, 0x00, 0x00, 0x00, /* size of central directory = 90 */ + 0x29, 0x00, 0x00, 0x00, /* offset of central directory = 41 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_dos_mtime_precedence_ntfs) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_dos_mtime_precedence_ntfs, + sizeof(archive_dos_mtime_precedence_ntfs))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(100000000L, archive_entry_mtime_nsec(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(200000000L, archive_entry_atime_nsec(ae)); + assert(archive_entry_birthtime_is_set(ae)); + assertEqualInt(1700000200LL, archive_entry_birthtime(ae)); + assertEqualInt(300000000L, archive_entry_birthtime_nsec(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x000D "PKWARE Unix", Central Directory only. */ +static const unsigned char archive_dos_mtime_precedence_pkware_unix[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (wrong-on-purpose: DOS_MIN_TIME) */ + 0x21, 0x00, /* last mod file date (wrong-on-purpose: DOS_MIN_TIME) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x00, 0x00, /* extra field length = 0 (no extra field here) */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000d "PKWARE Unix" placed in the Central Directory ONLY - + * the Local Header above has none: this is the scenario the + * DOS-clobber guard exists for. */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (unused: 0x000d "PKWARE Unix" wins) */ + 0x21, 0x00, /* last mod file date (unused: 0x000d "PKWARE Unix" wins) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000d "PKWARE Unix" (only copy: must set mtime) */ + 0x0d, 0x00, /* extra field ID = 0x000d */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0xf1, 0x53, 0x65, /* atime -> 1700000100 */ + 0x00, 0xf1, 0x53, 0x65, /* mtime -> 1700000000 */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x46, 0x00, 0x00, 0x00, /* size of central directory = 70 */ + 0x29, 0x00, 0x00, 0x00, /* offset of central directory = 41 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_dos_mtime_precedence_pkware_unix) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_dos_mtime_precedence_pkware_unix, + sizeof(archive_dos_mtime_precedence_pkware_unix))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(1001, archive_entry_uid(ae)); + assertEqualInt(1002, archive_entry_gid(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x5455 "UT", Central Directory only. */ +static const unsigned char archive_dos_mtime_precedence_ut[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (wrong-on-purpose: DOS_MIN_TIME) */ + 0x21, 0x00, /* last mod file date (wrong-on-purpose: DOS_MIN_TIME) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x00, 0x00, /* extra field length = 0 (no extra field here) */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5455 "UT" placed in the Central Directory ONLY - + * the Local Header above has none: this is the scenario the + * DOS-clobber guard exists for. */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (unused: 0x5455 "UT" wins) */ + 0x21, 0x00, /* last mod file date (unused: 0x5455 "UT" wins) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x11, 0x00, /* extra field length = 17 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5455 "UT" (only copy: must set mtime) */ + 0x55, 0x54, /* extra field ID = 0x5455 */ + 0x0d, 0x00, /* extra field size = 13 */ + 0x07, /* flags: mtime, atime, ctime all present */ + 0x00, 0xf1, 0x53, 0x65, /* mtime -> 1700000000 */ + 0x64, 0xf1, 0x53, 0x65, /* atime -> 1700000100 */ + 0xc8, 0xf1, 0x53, 0x65, /* ctime -> 1700000200 */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x47, 0x00, 0x00, 0x00, /* size of central directory = 71 */ + 0x29, 0x00, 0x00, 0x00, /* offset of central directory = 41 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_dos_mtime_precedence_ut) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_dos_mtime_precedence_ut, + sizeof(archive_dos_mtime_precedence_ut))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(1700000200LL, archive_entry_ctime(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x5855 "UX" (old), Central Directory only. */ +static const unsigned char archive_dos_mtime_precedence_ux[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (wrong-on-purpose: DOS_MIN_TIME) */ + 0x21, 0x00, /* last mod file date (wrong-on-purpose: DOS_MIN_TIME) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x00, 0x00, /* extra field length = 0 (no extra field here) */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5855 "UX" (old) placed in the Central Directory ONLY - + * the Local Header above has none: this is the scenario the + * DOS-clobber guard exists for. */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time (unused: 0x5855 "UX" (old) wins) */ + 0x21, 0x00, /* last mod file date (unused: 0x5855 "UX" (old) wins) */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x10, 0x00, /* extra field length = 16 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x5855 "UX" (old) (only copy: must set mtime) */ + 0x55, 0x58, /* extra field ID = 0x5855 */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0xf1, 0x53, 0x65, /* atime -> 1700000100 */ + 0x00, 0xf1, 0x53, 0x65, /* mtime -> 1700000000 */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x46, 0x00, 0x00, 0x00, /* size of central directory = 70 */ + 0x29, 0x00, 0x00, 0x00, /* offset of central directory = 41 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_dos_mtime_precedence_ux) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_dos_mtime_precedence_ux, + sizeof(archive_dos_mtime_precedence_ux))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(1001, archive_entry_uid(ae)); + assertEqualInt(1002, archive_entry_gid(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_empty.c b/libarchive/test/test_read_format_zip_empty.c new file mode 100644 index 000000000000..eeaf8a767176 --- /dev/null +++ b/libarchive/test/test_read_format_zip_empty.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +/* + * A minimal, valid, zero-entry ZIP archive: just a bare End-Of-Central- + * Directory record, no local file headers, no central directory entries. + * 22 bytes total - the smallest a ZIP file can be. This puts the EOCD + * signature at the very start (offset 0) of the seekable bidder's search + * buffer, which used to be skipped by an off-by-one in its loop bound. + */ +static const unsigned char empty_zip[] = { + 'P', 'K', 5, 6, + 0, 0, 0, 0, /* Disk numbers. */ + 0, 0, 0, 0, /* Number of entries (this disk / total). */ + 0, 0, 0, 0, /* Size of central directory. */ + 0, 0, 0, 0, /* Offset of central directory. */ + 0, 0, /* Comment length. */ +}; + +DEFINE_TEST(test_read_format_zip_empty) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, empty_zip, sizeof(empty_zip))); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_filename.c b/libarchive/test/test_read_format_zip_filename.c index 4f8c95581ef8..9f83e33fe23e 100644 --- a/libarchive/test/test_read_format_zip_filename.c +++ b/libarchive/test/test_read_format_zip_filename.c @@ -35,7 +35,7 @@ DEFINE_TEST(test_read_format_zip_filename_CP932_eucJP) /* * Read CP932 filename in ja_JP.eucJP with "hdrcharset=CP932" option. */ - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("ja_JP.eucJP locale not available on this system."); return; } @@ -95,7 +95,7 @@ DEFINE_TEST(test_read_format_zip_filename_CP932_UTF8) /* * Read CP932 filename in en_US.UTF-8 with "hdrcharset=CP932" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -175,7 +175,7 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_eucJP) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("ja_JP.eucJP locale not available on " "this system."); return; @@ -250,7 +250,7 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_UTF8) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -342,8 +342,11 @@ DEFINE_TEST(test_read_format_zip_filename_CP866_KOI8R) /* * Read CP866 filename in ru_RU.KOI8-R with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.20866") && - NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.20866") && +#endif + !setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("ru_RU.KOI8-R locale not available on this system."); return; } @@ -399,7 +402,7 @@ DEFINE_TEST(test_read_format_zip_filename_CP866_UTF8) /* * Read CP866 filename in en_US.UTF-8 with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -455,8 +458,11 @@ DEFINE_TEST(test_read_format_zip_filename_KOI8R_CP866) /* * Read KOI8-R filename in ru_RU.CP866 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.866") && - NULL == setlocale(LC_ALL, "ru_RU.CP866")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.866") && +#endif + !setCheckedLocale("ru_RU.CP866", "\x80", L'\x0410')) { skipping("ru_RU.CP866 locale not available on this system."); return; } @@ -512,7 +518,7 @@ DEFINE_TEST(test_read_format_zip_filename_KOI8R_UTF8) /* * Read KOI8-R filename in en_US.UTF-8 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -568,8 +574,11 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_KOI8R) /* * Read UTF-8 filename in ru_RU.KOI8-R with "hdrcharset=UTF-8" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.20866") && - NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.20866") && +#endif + !setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("ru_RU.KOI8-R locale not available on this system."); return; } @@ -632,8 +641,11 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_CP866) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia.866") && - NULL == setlocale(LC_ALL, "ru_RU.CP866")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia.866") && +#endif + !setCheckedLocale("ru_RU.CP866", "\x80", L'\x0410')) { skipping("ru_RU.CP866 locale not available on this system."); return; } @@ -697,7 +709,7 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_UTF8_ru) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -747,8 +759,11 @@ DEFINE_TEST(test_read_format_zip_filename_CP932_CP932) /* * Read CP932 filename in CP932/SJIS with "hdrcharset=CP932" option. */ - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932 locale not available on this system."); return; } @@ -808,8 +823,11 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_CP932) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932 locale not available on this system."); return; } @@ -883,8 +901,11 @@ DEFINE_TEST(test_read_format_zip_filename_CP866_CP1251) /* * Read CP866 filename in CP1251 with "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -946,7 +967,10 @@ DEFINE_TEST(test_read_format_zip_filename_CP866_CP1251_win) /* * Read CP866 filename in CP1251 without "hdrcharset=CP866" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia")) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (NULL == setlocale(LC_ALL, "Russian_Russia")) +#endif + { skipping("Russian_Russia locale not available on this system."); return; } @@ -996,8 +1020,11 @@ DEFINE_TEST(test_read_format_zip_filename_KOI8R_CP1251) /* * Read KOI8-R filename in CP1251 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -1055,8 +1082,11 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_CP1251) * because the file name in the sample file is UTF-8 and * Bit 11 of its general purpose bit flag is set. */ - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -1127,7 +1157,7 @@ DEFINE_TEST(test_read_format_zip_filename_KOI8R_UTF8_2) /* * Read filename in en_US.UTF-8 with "hdrcharset=KOI8-R" option. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_read_format_zip_lzma_alone_large_size.c b/libarchive/test/test_read_format_zip_lzma_alone_large_size.c new file mode 100644 index 000000000000..ca1b7b0d4899 --- /dev/null +++ b/libarchive/test/test_read_format_zip_lzma_alone_large_size.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2026 susen325 + * 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 "test.h" + +/* + * The local header of an lzma (zipx) entry claims a compressed size of + * about 4 GiB while the archive is under 2 KiB. Skipping the trailing + * compressed bytes must not request a read-ahead buffer of that size. + */ +DEFINE_TEST(test_read_format_zip_lzma_alone_large_size) +{ + const char *refname = "test_read_format_zip_lzma_alone_large_size.zip"; + struct archive *a; + struct archive_entry *ae; + char buf[1024]; + la_ssize_t s; + int r; + + extract_reference_file(refname); + a = archive_read_new(); + assert(a != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK || + r == ARCHIVE_WARN) { + while ((s = archive_read_data(a, buf, sizeof(buf))) > 0) + ; + assert(s != ARCHIVE_FATAL); + } + assertEqualIntA(a, ARCHIVE_EOF, r); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_lzma_alone_large_size.zip.uu b/libarchive/test/test_read_format_zip_lzma_alone_large_size.zip.uu new file mode 100644 index 000000000000..8c71b3416683 --- /dev/null +++ b/libarchive/test/test_read_format_zip_lzma_alone_large_size.zip.uu @@ -0,0 +1,45 @@ +begin 644 test_read_format_zip_lzma_alone_large_size.zip +M4$L#!`H#`````.$#)%$````````````````,````7VENA79\GCDS+I*=O.=!+"5(),_1O%5^1102P,$"@,`````]0,D +M40```````````````"P```!?:6YS=&%L;&1I7-I00]!0!=````!``1B$)'BB,SPW````7VEN7-I7-I7-I7-I birthtime) + * while the coarser field carries different, wrong-on-purpose values + * (1600000000/1600000100/1600000200) that must NOT show up in the + * resulting archive_entry - if they do, the ordering-independence + * guarantee is broken. + */ + +/* 0x000A "NTFS" then 0x000D "PKWARE Unix": AcTime, ModTime, UID, GID. */ +static const unsigned char archive_ntfs_precedence_pkware_unix[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x34, 0x00, /* extra field length = 52 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x000d "PKWARE Unix" (coarser; must NOT win) */ + 0x0d, 0x00, /* extra field ID = 0x000d */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x34, 0x00, /* extra field length = 52 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x000d "PKWARE Unix" (coarser; must NOT win) */ + 0x0d, 0x00, /* extra field ID = 0x000d */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x6a, 0x00, 0x00, 0x00, /* size of central directory = 106 */ + 0x5d, 0x00, 0x00, 0x00, /* offset of central directory = 93 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ntfs_precedence_pkware_unix) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ntfs_precedence_pkware_unix, + sizeof(archive_ntfs_precedence_pkware_unix))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(100000000L, archive_entry_mtime_nsec(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(200000000L, archive_entry_atime_nsec(ae)); + assert(archive_entry_birthtime_is_set(ae)); + assertEqualInt(1700000200LL, archive_entry_birthtime(ae)); + assertEqualInt(300000000L, archive_entry_birthtime_nsec(ae)); + assertEqualInt(1001, archive_entry_uid(ae)); + assertEqualInt(1002, archive_entry_gid(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x000A "NTFS" then 0x5455 "UT": Flags, ModTime, AcTime, CrTime. */ +static const unsigned char archive_ntfs_precedence_ut[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x35, 0x00, /* extra field length = 53 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x5455 "UT" (coarser; must NOT win for m/atime) */ + 0x55, 0x54, /* extra field ID = 0x5455 */ + 0x0d, 0x00, /* extra field size = 13 */ + 0x07, /* flags: mtime, atime, ctime all present */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0xc8, 0x10, 0x5e, 0x5f, /* ctime -> 1600000200, unaffected */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x35, 0x00, /* extra field length = 53 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x5455 "UT" (coarser; must NOT win for m/atime) */ + 0x55, 0x54, /* extra field ID = 0x5455 */ + 0x0d, 0x00, /* extra field size = 13 */ + 0x07, /* flags: mtime, atime, ctime all present */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0xc8, 0x10, 0x5e, 0x5f, /* ctime -> 1600000200, unaffected */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x6b, 0x00, 0x00, 0x00, /* size of central directory = 107 */ + 0x5e, 0x00, 0x00, 0x00, /* offset of central directory = 94 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ntfs_precedence_ut) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ntfs_precedence_ut, + sizeof(archive_ntfs_precedence_ut))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(100000000L, archive_entry_mtime_nsec(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(200000000L, archive_entry_atime_nsec(ae)); + assert(archive_entry_birthtime_is_set(ae)); + assertEqualInt(1700000200LL, archive_entry_birthtime(ae)); + assertEqualInt(300000000L, archive_entry_birthtime_nsec(ae)); + /* UT's ctime is POSIX ctime, a separate field the NTFS precedence + * logic doesn't touch either way: it applies normally. */ + assertEqualInt(1600000200LL, archive_entry_ctime(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* 0x000A "NTFS" then 0x5855 "UX" (old): AcTime, ModTime, UID, GID. */ +static const unsigned char archive_ntfs_precedence_ux[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x34, 0x00, /* extra field length = 52 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x5855 "UX" (coarser; must NOT win) */ + 0x55, 0x58, /* extra field ID = 0x5855 */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x34, 0x00, /* extra field length = 52 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x74, 0x78, 0x74, /* "test.txt" */ +/* extra field 0x000a "NTFS" (processed FIRST: must still win) */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x40, 0x42, 0x7c, 0xc6, 0x47, 0x17, 0xda, 0x01, /* Mtime (precise) */ + 0x80, 0x4e, 0x26, 0x02, 0x48, 0x17, 0xda, 0x01, /* Atime (precise) */ + 0xc0, 0x5a, 0xd0, 0x3d, 0x48, 0x17, 0xda, 0x01, /* Ctime -> birthtime */ +/* extra field 0x5855 "UX" (coarser; must NOT win) */ + 0x55, 0x58, /* extra field ID = 0x5855 */ + 0x0c, 0x00, /* extra field size = 12 */ + 0x64, 0x10, 0x5e, 0x5f, /* atime -> 1600000100 (must be ignored) */ + 0x00, 0x10, 0x5e, 0x5f, /* mtime -> 1600000000 (must be ignored) */ + 0xe9, 0x03, /* uid = 1001 */ + 0xea, 0x03, /* gid = 1002 */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x6a, 0x00, 0x00, 0x00, /* size of central directory = 106 */ + 0x5d, 0x00, 0x00, 0x00, /* offset of central directory = 93 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ntfs_precedence_ux) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ntfs_precedence_ux, + sizeof(archive_ntfs_precedence_ux))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("test.txt", archive_entry_pathname(ae)); + assertEqualInt(1700000000LL, archive_entry_mtime(ae)); + assertEqualInt(100000000L, archive_entry_mtime_nsec(ae)); + assertEqualInt(1700000100LL, archive_entry_atime(ae)); + assertEqualInt(200000000L, archive_entry_atime_nsec(ae)); + assert(archive_entry_birthtime_is_set(ae)); + assertEqualInt(1700000200LL, archive_entry_birthtime(ae)); + assertEqualInt(300000000L, archive_entry_birthtime_nsec(ae)); + assertEqualInt(1001, archive_entry_uid(ae)); + assertEqualInt(1002, archive_entry_gid(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_ntfs_timestamp.c b/libarchive/test/test_read_format_zip_ntfs_timestamp.c new file mode 100644 index 000000000000..93dba1dbf907 --- /dev/null +++ b/libarchive/test/test_read_format_zip_ntfs_timestamp.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +/* + * A minimal ZIP archive with a single empty-ish entry ("ntfs.txt") whose + * local and central extra data both carry an 0x000A "NTFS" extra field + * (reserved(4) + Tag(1)/Size(24): Mtime, Atime, Ctime as 8-byte Windows + * FILETIME values), and no 0x5455 "UT" field. This exercises reading + * that field directly, rather than through libarchive's own writer, + * since libarchive doesn't write 0x000A. + * + * The three FILETIME values encode, respectively: + * Mtime: 2024-03-15T10:20:30.123456000Z + * Atime: 2024-03-16T11:21:31.234567000Z + * Ctime: 2024-03-14T09:19:29.345678000Z (Windows creation time, which + * is why it must map to archive_entry_birthtime(), not ctime) + */ +static const unsigned char archive_ntfs_timestamp[] = { +/* --- local file header --- */ + 0x50, 0x4b, 0x03, 0x04, /* local file header signature */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x24, 0x00, /* extra field length = 36 */ + 0x6e, 0x74, 0x66, 0x73, 0x2e, 0x74, 0x78, 0x74, /* "ntfs.txt" */ +/* extra field 0x000a "NTFS" */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x80, 0x41, 0xfd, 0x67, 0xc2, 0x76, 0xda, 0x01, /* Mtime FILETIME */ + 0x46, 0x3a, 0x98, 0x18, 0x94, 0x77, 0xda, 0x01, /* Atime FILETIME */ + 0x8c, 0x25, 0x95, 0xb7, 0xf0, 0x75, 0xda, 0x01, /* Ctime -> birthtime */ + 0x68, 0x69, 0x0a, /* "hi\n" */ +/* --- central directory header --- */ + 0x50, 0x4b, 0x01, 0x02, /* central directory header signature */ + 0x14, 0x00, /* version made by: 2.0, MS-DOS */ + 0x14, 0x00, /* version needed to extract: 2.0 */ + 0x00, 0x00, /* general purpose bit flag */ + 0x00, 0x00, /* compression method: stored */ + 0x00, 0x00, /* last mod file time */ + 0x00, 0x00, /* last mod file date */ + 0x7a, 0x7a, 0x6f, 0xed, /* CRC-32 = 0xed6f7a7a */ + 0x03, 0x00, 0x00, 0x00, /* compressed size = 3 */ + 0x03, 0x00, 0x00, 0x00, /* uncompressed size = 3 */ + 0x08, 0x00, /* file name length = 8 */ + 0x24, 0x00, /* extra field length = 36 */ + 0x00, 0x00, /* file comment length */ + 0x00, 0x00, /* disk number start */ + 0x00, 0x00, /* internal file attributes */ + 0x00, 0x00, 0xa4, 0x81, /* external file attributes: -rw-r--r-- */ + 0x00, 0x00, 0x00, 0x00, /* relative offset of local header = 0 */ + 0x6e, 0x74, 0x66, 0x73, 0x2e, 0x74, 0x78, 0x74, /* "ntfs.txt" */ +/* extra field 0x000a "NTFS" */ + 0x0a, 0x00, /* extra field ID = 0x000a */ + 0x20, 0x00, /* extra field size = 32 */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x01, 0x00, /* NTFS attribute tag 1 */ + 0x18, 0x00, /* NTFS attribute 1 size */ + 0x80, 0x41, 0xfd, 0x67, 0xc2, 0x76, 0xda, 0x01, /* Mtime FILETIME */ + 0x46, 0x3a, 0x98, 0x18, 0x94, 0x77, 0xda, 0x01, /* Atime FILETIME */ + 0x8c, 0x25, 0x95, 0xb7, 0xf0, 0x75, 0xda, 0x01, /* Ctime -> birthtime */ +/* --- end of central directory record --- */ + 0x50, 0x4b, 0x05, 0x06, /* end of central directory signature */ + 0x00, 0x00, /* number of this disk */ + 0x00, 0x00, /* disk with start of central directory */ + 0x01, 0x00, /* central directory entries on this disk */ + 0x01, 0x00, /* total central directory entries */ + 0x5a, 0x00, 0x00, 0x00, /* size of central directory = 90 */ + 0x4d, 0x00, 0x00, 0x00, /* offset of central directory = 77 */ + 0x00, 0x00, /* .ZIP file comment length */ +}; + +DEFINE_TEST(test_read_format_zip_ntfs_timestamp) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, archive_ntfs_timestamp, + sizeof(archive_ntfs_timestamp))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + assertEqualString("ntfs.txt", archive_entry_pathname(ae)); + + assert(archive_entry_mtime_is_set(ae)); + assertEqualInt(1710498030, archive_entry_mtime(ae)); + assertEqualInt(123456000, archive_entry_mtime_nsec(ae)); + + assert(archive_entry_atime_is_set(ae)); + assertEqualInt(1710588091, archive_entry_atime(ae)); + assertEqualInt(234567000, archive_entry_atime_nsec(ae)); + + /* NTFS "Ctime" is Windows creation time, so it must map to + * birthtime, not to POSIX ctime (which ZIP has no source for + * here, so it stays unset by this extra field). */ + assert(archive_entry_birthtime_is_set(ae)); + assertEqualInt(1710407969, archive_entry_birthtime(ae)); + assertEqualInt(345678000, archive_entry_birthtime_nsec(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} diff --git a/libarchive/test/test_read_format_zip_unset_times.c b/libarchive/test/test_read_format_zip_unset_times.c new file mode 100644 index 000000000000..de396e9f97d2 --- /dev/null +++ b/libarchive/test/test_read_format_zip_unset_times.c @@ -0,0 +1,362 @@ +/*- + * Copyright (c) 2026 François Degros + * 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 "test.h" + +/* + * A ZIP entry only has a modification time (in the legacy DOS fields) by + * default. Its access time, creation time and so on come from optional + * extra fields. When no extra field supplies atime, or ctime, the reader + * must leave it unset rather than report it as set to the Unix epoch. + * + * Each test case builds a ZIP file with a single entry and the given extra + * field data, and checks which timestamps of the entry are set. Every case + * is run with both the seekable reader and the streaming reader. + */ + +#define MTIME 1700000000LL +#define ATIME 1700000100LL +#define CTIME 1700000200LL + +static void +put16(unsigned char *p, unsigned v) +{ + p[0] = (unsigned char)(v & 0xff); + p[1] = (unsigned char)((v >> 8) & 0xff); +} + +static void +put32(unsigned char *p, unsigned long v) +{ + put16(p, (unsigned)(v & 0xffff)); + put16(p + 2, (unsigned)((v >> 16) & 0xffff)); +} + +static void +put64(unsigned char *p, uint64_t v) +{ + put32(p, (unsigned long)(v & 0xffffffffUL)); + put32(p + 4, (unsigned long)((v >> 32) & 0xffffffffUL)); +} + +/* A Windows FILETIME, i.e. 100ns ticks since 1601-01-01. */ +static uint64_t +filetime(long long secs, long nsecs) +{ + return ((uint64_t)secs + 11644473600ULL) * 10000000ULL + nsecs / 100; +} + +/* 0x5455 "UT": flags tell which of mtime (1), atime (2), ctime (4) follow. */ +static size_t +make_ut(unsigned char *p, int flags) +{ + size_t n = 5; + + put16(p, 0x5455); + p[4] = (unsigned char)flags; + if (flags & 1) { + put32(p + n, MTIME); + n += 4; + } + if (flags & 2) { + put32(p + n, ATIME); + n += 4; + } + if (flags & 4) { + put32(p + n, CTIME); + n += 4; + } + put16(p + 2, (unsigned)(n - 4)); + return (n); +} + +/* 0x5855 "UX": atime and mtime. */ +static size_t +make_ux(unsigned char *p) +{ + put16(p, 0x5855); + put16(p + 2, 8); + put32(p + 4, ATIME); + put32(p + 8, MTIME); + return (12); +} + +/* 0x000D "PKWARE Unix": atime, mtime, uid, gid. */ +static size_t +make_pkware_unix(unsigned char *p) +{ + put16(p, 0x000d); + put16(p + 2, 12); + put32(p + 4, ATIME); + put32(p + 8, MTIME); + put16(p + 12, 1000); + put16(p + 14, 1000); + return (16); +} + +/* 0x000A "NTFS": a zero FILETIME stands for a time that isn't there. */ +static size_t +make_ntfs(unsigned char *p, int has_mtime, int has_atime, int has_btime) +{ + put16(p, 0x000a); + put16(p + 2, 32); + put32(p + 4, 0); + put16(p + 8, 1); + put16(p + 10, 24); + put64(p + 12, has_mtime ? filetime(MTIME, 100000000L) : 0); + put64(p + 20, has_atime ? filetime(ATIME, 200000000L) : 0); + put64(p + 28, has_btime ? filetime(CTIME, 300000000L) : 0); + return (36); +} + +/* + * Builds a ZIP file made of a single stored entry named "test.txt". The + * Local Header gets lh_extra, and the Central Directory gets cd_extra. + * Returns the size of the ZIP file. + */ +static size_t +build_zip(unsigned char *zip, const unsigned char *lh_extra, size_t lh_len, + const unsigned char *cd_extra, size_t cd_len) +{ + static const char name[] = "test.txt"; + static const char data[] = "hi\n"; + const size_t name_len = sizeof(name) - 1; + const size_t data_len = sizeof(data) - 1; + size_t n = 0, cd_offset, cd_size; + + /* Local file header. */ + put32(zip + n, 0x04034b50UL); + put16(zip + n + 4, 20); /* version needed to extract */ + put16(zip + n + 6, 0); /* general purpose bit flag */ + put16(zip + n + 8, 0); /* compression method: stored */ + put16(zip + n + 10, 0); /* last mod file time */ + put16(zip + n + 12, 0x21); /* last mod file date: 1980-01-01 */ + put32(zip + n + 14, 0xed6f7a7aUL); /* CRC-32 of the data */ + put32(zip + n + 18, (unsigned long)data_len); + put32(zip + n + 22, (unsigned long)data_len); + put16(zip + n + 26, (unsigned)name_len); + put16(zip + n + 28, (unsigned)lh_len); + n += 30; + memcpy(zip + n, name, name_len); + n += name_len; + memcpy(zip + n, lh_extra, lh_len); + n += lh_len; + memcpy(zip + n, data, data_len); + n += data_len; + + /* Central directory header. */ + cd_offset = n; + put32(zip + n, 0x02014b50UL); + put16(zip + n + 4, 20); /* version made by */ + put16(zip + n + 6, 20); /* version needed to extract */ + put16(zip + n + 8, 0); /* general purpose bit flag */ + put16(zip + n + 10, 0); /* compression method: stored */ + put16(zip + n + 12, 0); /* last mod file time */ + put16(zip + n + 14, 0x21); /* last mod file date: 1980-01-01 */ + put32(zip + n + 16, 0xed6f7a7aUL); + put32(zip + n + 20, (unsigned long)data_len); + put32(zip + n + 24, (unsigned long)data_len); + put16(zip + n + 28, (unsigned)name_len); + put16(zip + n + 30, (unsigned)cd_len); + put16(zip + n + 32, 0); /* file comment length */ + put16(zip + n + 34, 0); /* disk number start */ + put16(zip + n + 36, 0); /* internal file attributes */ + put32(zip + n + 38, 0x81a40000UL); /* -rw-r--r-- */ + put32(zip + n + 42, 0); /* offset of local header */ + n += 46; + memcpy(zip + n, name, name_len); + n += name_len; + memcpy(zip + n, cd_extra, cd_len); + n += cd_len; + cd_size = n - cd_offset; + + /* End of central directory record. */ + put32(zip + n, 0x06054b50UL); + put16(zip + n + 4, 0); /* number of this disk */ + put16(zip + n + 6, 0); /* disk with the central directory */ + put16(zip + n + 8, 1); /* entries on this disk */ + put16(zip + n + 10, 1); /* total entries */ + put32(zip + n + 12, (unsigned long)cd_size); + put32(zip + n + 16, (unsigned long)cd_offset); + put16(zip + n + 20, 0); /* comment length */ + n += 22; + return (n); +} + +/* Which timestamps of the entry are expected to be set, and their values. */ +struct expected { + int atime_set; + long long atime; + long atime_nsec; + int ctime_set; + long long ctime; + int btime_set; +}; + +static void +check_entry(struct archive_entry *ae, const struct expected *e) +{ + /* The DOS time is always there. */ + assert(archive_entry_mtime_is_set(ae)); + + assertEqualInt(e->atime_set, archive_entry_atime_is_set(ae) != 0); + if (e->atime_set) { + assertEqualInt(e->atime, archive_entry_atime(ae)); + assertEqualInt(e->atime_nsec, archive_entry_atime_nsec(ae)); + } + + assertEqualInt(e->ctime_set, archive_entry_ctime_is_set(ae) != 0); + if (e->ctime_set) + assertEqualInt(e->ctime, archive_entry_ctime(ae)); + + assertEqualInt(e->btime_set, archive_entry_birthtime_is_set(ae) != 0); +} + +/* + * Reads the given ZIP file with the seekable reader, or with the streaming + * reader, and checks the timestamps that are set on its only entry. + */ +static void +check_zip(const unsigned char *zip, size_t size, int seekable, + const struct expected *e) +{ + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (seekable) + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_seekable(a)); + else + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_zip_streamable(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, zip, size)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("test.txt", archive_entry_pathname(ae)); + check_entry(ae, e); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +/* Runs a test case with both readers. */ +static void +check_both_readers(const unsigned char *lh_extra, size_t lh_len, + const unsigned char *cd_extra, size_t cd_len, const struct expected *e) +{ + unsigned char zip[512]; + size_t size; + + size = build_zip(zip, lh_extra, lh_len, cd_extra, cd_len); + assert(size <= sizeof(zip)); + check_zip(zip, size, 1, e); + check_zip(zip, size, 0, e); +} + +DEFINE_TEST(test_read_format_zip_unset_times) +{ + unsigned char extra[64]; + size_t n; + struct expected e; + + memset(extra, 0, sizeof(extra)); + + /* No extra field at all: only mtime is set. */ + memset(&e, 0, sizeof(e)); + check_both_readers(extra, 0, extra, 0, &e); + + /* "UT" with mtime only. */ + n = make_ut(extra, 1); + check_both_readers(extra, n, extra, n, &e); + + /* "UT" with mtime and atime. */ + n = make_ut(extra, 3); + e.atime_set = 1; + e.atime = ATIME; + check_both_readers(extra, n, extra, n, &e); + + /* "UT" with mtime and ctime, but no atime. */ + n = make_ut(extra, 5); + e.atime_set = 0; + e.ctime_set = 1; + e.ctime = CTIME; + check_both_readers(extra, n, extra, n, &e); + + /* "UT" with all three times. */ + n = make_ut(extra, 7); + e.atime_set = 1; + e.atime = ATIME; + check_both_readers(extra, n, extra, n, &e); + + /* "UX" supplies atime, but not ctime. */ + n = make_ux(extra); + e.ctime_set = 0; + check_both_readers(extra, n, extra, n, &e); + + /* "PKWARE Unix" supplies atime, but not ctime. */ + n = make_pkware_unix(extra); + check_both_readers(extra, n, extra, n, &e); + + /* "NTFS" with a time in each of its three slots. */ + n = make_ntfs(extra, 1, 1, 1); + e.atime_nsec = 200000000L; + e.btime_set = 1; + check_both_readers(extra, n, extra, n, &e); + + /* "NTFS" without atime: that slot is a zero FILETIME. */ + n = make_ntfs(extra, 1, 0, 1); + e.atime_set = 0; + check_both_readers(extra, n, extra, n, &e); + + /* "NTFS" with mtime only. */ + n = make_ntfs(extra, 1, 0, 0); + e.btime_set = 0; + check_both_readers(extra, n, extra, n, &e); +} + +/* + * The seekable reader reads the extra field of the Central Directory before + * that of the Local Header. A time that only the Central Directory has is + * still set, and a time that neither has remains unset. + */ +DEFINE_TEST(test_read_format_zip_unset_times_central_directory_only) +{ + unsigned char extra[64]; + unsigned char zip[512]; + size_t n, size; + struct expected e; + + memset(&e, 0, sizeof(e)); + e.atime_set = 1; + e.atime = ATIME; + e.ctime_set = 1; + e.ctime = CTIME; + + n = make_ut(extra, 7); + size = build_zip(zip, extra, 0, extra, n); + check_zip(zip, size, 1, &e); + + /* The streaming reader never sees the Central Directory. */ + memset(&e, 0, sizeof(e)); + check_zip(zip, size, 0, &e); +} diff --git a/libarchive/test/test_read_format_zip_winzip_aes_large.c b/libarchive/test/test_read_format_zip_winzip_aes_large.c index e24b69a032ad..a4fed18f8460 100644 --- a/libarchive/test/test_read_format_zip_winzip_aes_large.c +++ b/libarchive/test/test_read_format_zip_winzip_aes_large.c @@ -159,7 +159,6 @@ test_winzip_aes_large(const char *refname, const char *compression_name) DEFINE_TEST(test_read_format_zip_winzip_aes256_large) { - // Deflate is always supported, no need to test for it test_winzip_aes_large("test_read_format_zip_winzip_aes256_large.zip", NULL); } @@ -176,7 +175,11 @@ DEFINE_TEST(test_read_format_zip_winzip_aes256_large_lzma) DEFINE_TEST(test_read_format_zip_winzip_aes256_large_ppmd) { +#ifdef HAVE_LIBZ test_winzip_aes_large("test_read_format_zip_winzip_aes256_large_ppmd.zip", NULL); +#else + skipping("zlib not available"); +#endif } DEFINE_TEST(test_read_format_zip_winzip_aes256_large_xz) diff --git a/libarchive/test/test_read_format_zip_zipx_encrypted.c b/libarchive/test/test_read_format_zip_zipx_encrypted.c index f07e1e5c3129..a0a431fb3eec 100644 --- a/libarchive/test/test_read_format_zip_zipx_encrypted.c +++ b/libarchive/test/test_read_format_zip_zipx_encrypted.c @@ -257,8 +257,22 @@ DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted_streaming) DEFINE_TEST(test_read_format_zip_ppmd8_aes256_streaming) { const char *refname = "test_read_format_zip_ppmd8_aes256_streaming.zipx"; + struct archive *a; size_t used; char buff[600]; + + /* Check if running system has cryptographic functionality. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + if (ARCHIVE_OK != archive_write_set_options(a, + "zip:encryption=aes256")) { + skipping("This system does not have cryptographic library"); + archive_write_free(a); + return; + } + archive_write_free(a); + extract_reference_file(refname); FILE *f = fopen(refname, "rb"); used = fread(buff, 1, sizeof(buff), f); diff --git a/libarchive/test/test_read_set_format.c b/libarchive/test/test_read_set_format.c index 0b656b1682f5..9756fca59df3 100644 --- a/libarchive/test/test_read_set_format.c +++ b/libarchive/test/test_read_set_format.c @@ -102,18 +102,13 @@ DEFINE_TEST(test_read_set_format) DEFINE_TEST(test_read_set_wrong_format) { const char reffile[] = "test_read_format_zip.zip"; - struct archive_entry *ae; struct archive *a; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); - assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR)); - assertA(0 == archive_read_append_filter(a, ARCHIVE_FILTER_NONE)); - assertA(0 == archive_read_open_filename(a, reffile, 10240)); - - /* Check that this actually fails, then close the archive. */ - assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_set_format(a, ARCHIVE_FORMAT_CPIO)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_append_filter(a, ARCHIVE_FILTER_NONE)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open_filename(a, reffile, 10240)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } @@ -154,27 +149,16 @@ DEFINE_TEST(test_read_append_filter) DEFINE_TEST(test_read_append_wrong_filter) { - struct archive_entry *ae; struct archive *a; - int r; assert((a = archive_read_new()) != NULL); - assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR)); - r = archive_read_append_filter(a, ARCHIVE_FILTER_XZ); - if (r == ARCHIVE_WARN && !canXz()) { - skipping("xz reading not fully supported on this platform"); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); - return; - } - assertEqualInt(ARCHIVE_OK, + assertEqualIntA(a, ARCHIVE_OK, + archive_read_set_format(a, ARCHIVE_FORMAT_RAW)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_append_filter(a, ARCHIVE_FILTER_UU)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open_memory(a, archive, sizeof(archive))); - assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN)); - if (r == ARCHIVE_WARN && canXz()) { - assertEqualIntA(a, ARCHIVE_WARN, archive_read_close(a)); - } else { - assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); - } - assertEqualInt(ARCHIVE_OK,archive_read_free(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } DEFINE_TEST(test_read_append_lzop_filter) @@ -404,9 +388,9 @@ DEFINE_TEST(test_read_append_filter_program) DEFINE_TEST(test_read_append_filter_wrong_program) { - struct archive_entry *ae; struct archive *a; -#if !defined(_WIN32) || defined(__CYGWIN__) +#if (!defined(_WIN32) || defined(__CYGWIN__)) && \ + !(defined(__ANDROID__) && __ANDROID_API__ < 24) FILE * fp; int fd; fpos_t pos; @@ -420,7 +404,8 @@ DEFINE_TEST(test_read_append_filter_wrong_program) return; } -#if !defined(_WIN32) || defined(__CYGWIN__) +#if (!defined(_WIN32) || defined(__CYGWIN__)) && \ + !(defined(__ANDROID__) && __ANDROID_API__ < 24) /* bunzip2 will write to stderr, redirect it to a file */ fflush(stderr); fgetpos(stderr, &pos); @@ -429,16 +414,15 @@ DEFINE_TEST(test_read_append_filter_wrong_program) #endif assert((a = archive_read_new()) != NULL); - assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_TAR)); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAW)); assertEqualIntA(a, ARCHIVE_OK, archive_read_append_filter_program(a, "bunzip2 -q")); - assertEqualIntA(a, ARCHIVE_OK, + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_open_memory(a, archive, sizeof(archive))); - assertA(archive_read_next_header(a, &ae) < (ARCHIVE_WARN)); - assertEqualIntA(a, ARCHIVE_WARN, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); -#if !defined(_WIN32) || defined(__CYGWIN__) +#if (!defined(_WIN32) || defined(__CYGWIN__)) && \ + !(defined(__ANDROID__) && __ANDROID_API__ < 24) /* restore stderr */ if (fp != NULL) { fflush(stderr); diff --git a/libarchive/test/test_read_truncated_filter.c b/libarchive/test/test_read_truncated_filter.c index fc8ddf51b73b..38f90496fb6d 100644 --- a/libarchive/test/test_read_truncated_filter.c +++ b/libarchive/test/test_read_truncated_filter.c @@ -174,3 +174,93 @@ DEFINE_TEST(test_read_truncated_filter_xz) { test_truncation("xz", archive_write_add_filter_xz, canXz()); } + +/* + * Check that we generate an error message when the 8 byte trailer of a gzip + * file is incomplete, whether or not the beginning of the file was + * decompressed before the error was found. + */ +static void +test_gzip_truncated_trailer(size_t datasize) +{ + struct archive_entry *ae; + struct archive *a; + char *buff, *data, tmp[1024]; + size_t buffsize, used, n; + int r, failed; + + buffsize = datasize + 100000; + assert(NULL != (buff = malloc(buffsize))); + assert(NULL != (data = malloc(datasize))); + if (buff == NULL || data == NULL) { + free(buff); + free(data); + return; + } + fill_with_pseudorandom_data(data, datasize); + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + if (archive_write_add_filter_gzip(a) != ARCHIVE_OK) { + skipping("gzip writing not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + free(buff); + free(data); + return; + } + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used)); + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_pathname(ae, "file"); + archive_entry_set_size(ae, datasize); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + assertEqualIntA(a, (int)datasize, + (int)archive_write_data(a, data, datasize)); + archive_entry_free(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + for (n = 1; n <= 8; n++) { + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_raw(a)); + if (archive_read_support_filter_gzip(a) != ARCHIVE_OK) { + skipping("gzip reading not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + break; + } + + /* Read everything, and find out which call fails. */ + failed = 0; + r = archive_read_open_memory(a, buff, used - n); + if (r != ARCHIVE_OK) { + failed = 1; + } else if (archive_read_next_header(a, &ae) != ARCHIVE_OK) { + failed = 1; + } else { + while ((r = (int)archive_read_data(a, tmp, + sizeof(tmp))) > 0) + ; + failed = (r < 0); + } + + failure("%d bytes missing, %d bytes of data", (int)n, + (int)datasize); + assertEqualInt(1, failed); + failure("%d bytes missing, %d bytes of data", (int)n, + (int)datasize); + assert(NULL != archive_error_string(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + } + + free(data); + free(buff); +} + +DEFINE_TEST(test_read_truncated_filter_gzip_trailer) +{ + /* Small enough to be decompressed when opening the archive... */ + test_gzip_truncated_trailer(100); + /* ...and large enough to be decompressed in several blocks. */ + test_gzip_truncated_trailer(200000); +} diff --git a/libarchive/test/test_ustar_filename_encoding.c b/libarchive/test/test_ustar_filename_encoding.c index d230eeddd4d3..8823fdee2276 100644 --- a/libarchive/test/test_ustar_filename_encoding.c +++ b/libarchive/test/test_ustar_filename_encoding.c @@ -33,7 +33,7 @@ DEFINE_TEST(test_ustar_filename_encoding_UTF8_CP866) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -74,7 +74,7 @@ DEFINE_TEST(test_ustar_filename_encoding_KOI8R_UTF8) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if (!setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("KOI8-R locale not available on this system."); return; } @@ -115,7 +115,7 @@ DEFINE_TEST(test_ustar_filename_encoding_KOI8R_CP866) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if (!setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("KOI8-R locale not available on this system."); return; } @@ -156,8 +156,11 @@ DEFINE_TEST(test_ustar_filename_encoding_CP1251_UTF8) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "Russian_Russia") && - NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Russian_Russia") && +#endif + !setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -201,7 +204,7 @@ DEFINE_TEST(test_ustar_filename_encoding_ru_RU_CP1251) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if (!setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("CP1251 locale not available on this system."); return; } @@ -236,12 +239,15 @@ DEFINE_TEST(test_ustar_filename_encoding_ru_RU_CP1251) */ DEFINE_TEST(test_ustar_filename_encoding_Russian_Russia) { - struct archive *a; + struct archive *a; struct archive_entry *entry; char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "Russian_Russia")) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (NULL == setlocale(LC_ALL, "Russian_Russia")) +#endif + { skipping("Russian_Russia locale not available on this system."); return; } @@ -276,7 +282,7 @@ DEFINE_TEST(test_ustar_filename_encoding_EUCJP_UTF8) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("eucJP locale not available on this system."); return; } @@ -316,7 +322,7 @@ DEFINE_TEST(test_ustar_filename_encoding_EUCJP_CP932) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("eucJP locale not available on this system."); return; } @@ -356,8 +362,11 @@ DEFINE_TEST(test_ustar_filename_encoding_CP932_UTF8) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932/SJIS locale not available on this system."); return; } diff --git a/libarchive/test/test_write_disk_appledouble.c b/libarchive/test/test_write_disk_appledouble.c index f4e4bb8af96d..50c890555f48 100644 --- a/libarchive/test/test_write_disk_appledouble.c +++ b/libarchive/test/test_write_disk_appledouble.c @@ -41,24 +41,28 @@ // strips the 'Guest' information from the string to help ensure // consistent results on different machines. // -static char _acl_temp[256]; -static const char * -clean_acl(const char *acl) { - char *p, *q; - if (strlen(acl) >= sizeof(_acl_temp)) - return acl; +static char * +acl_clean(char *acl) +{ + char *end, *p, *q; - strcpy(_acl_temp, acl); - p = strstr(_acl_temp, ":Guest:"); - if (p != NULL) { - fprintf(stderr, "Shortening: %s\n", p + 1); - memmove(p + 1, p + 6, strlen(p + 6) + 1); - q = strstr(p + 2, ":"); - fprintf(stderr, "Shortening: %s\n", q); - memmove(p + 2, q, strlen(q) + 1); - return _acl_temp; + end = strchr(acl, '\0') + 1; + /* find occurrence of Guest user */ + while ((p = strstr(acl, ":Guest:")) != NULL) { + /* replace user name with empty string */ + p++; /* now points to user name */ + q = p + 5; /* points to colon after user name */ + memmove(p, q, end - q); + end -= q - p; /* shorten */ + p++; /* points to colon after user name */ + /* find colon after group name */ + if ((q = strchr(p, ':')) != NULL && q > p) { + /* replace group name with empty string */ + memmove(p, q, end - q); + end -= q - p; /* shorten */ + } } - return _acl_temp; + return (acl); } static int @@ -111,6 +115,7 @@ DEFINE_TEST(test_write_disk_appledouble) struct archive_entry *ae; struct stat st; acl_t acl; + char *acl_text; extract_reference_file(refname); @@ -161,13 +166,15 @@ DEFINE_TEST(test_write_disk_appledouble) assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork")); failure("'%s' should have decompfs xattr", "file3"); assertEqualInt(1, has_xattr("file3", "com.apple.decmpfs")); - assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED))); - assertEqualString(clean_acl(acl_to_text(acl, NULL)), + assert((acl = acl_get_file("file3", ACL_TYPE_EXTENDED)) != NULL); + acl_text = acl_clean(acl_to_text(acl, NULL)); + assertEqualString(acl_text, "!#acl 1\n" "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:::deny:read\n" "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n" ); - if (acl) acl_free(acl); + acl_free(acl); + free(acl_text); /* Test ._file3. */ failure("'file3' should be merged and removed"); assertFileNotExists("._file3"); @@ -220,13 +227,15 @@ DEFINE_TEST(test_write_disk_appledouble) assertEqualInt(0, has_xattr("file3", "com.apple.ResourceFork")); failure("'%s' should not have decmpfs", "file3"); assertEqualInt(0, has_xattr("file3", "com.apple.decmpfs")); - assert(NULL != (acl = acl_get_file("file3", ACL_TYPE_EXTENDED))); - assertEqualString(clean_acl(acl_to_text(acl, NULL)), + assert((acl = acl_get_file("file3", ACL_TYPE_EXTENDED)) != NULL); + acl_text = acl_clean(acl_to_text(acl, NULL)); + assertEqualString(acl_text, "!#acl 1\n" "user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000C9:::deny:read\n" "group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000050:admin:80:allow:write\n" ); - if (acl) acl_free(acl); + acl_free(acl); + free(acl_text); /* Test ._file3. */ failure("'file3' should be merged and removed"); assertFileNotExists("._file3"); diff --git a/libarchive/test/test_write_disk_secure_physicaldrive.c b/libarchive/test/test_write_disk_secure_physicaldrive.c new file mode 100644 index 000000000000..6b4555ee2ec0 --- /dev/null +++ b/libarchive/test/test_write_disk_secure_physicaldrive.c @@ -0,0 +1,94 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 EndlssNightmare + * + * 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 "test.h" + +/* + * Verify that \\.\PhysicalDriveX paths are correctly rejected + * by archive_write_disk on Windows. + * + * Before the fix, the PhysicalDrive validation contained a copy-paste + * bug where array indices p[10] through p[16] were all written as p[9]. + * Since the conditions are ANDed and p[9] cannot simultaneously equal + * 7 different characters, the check could never match — making the + * PhysicalDrive blocking code dead code. + */ +DEFINE_TEST(test_write_disk_secure_physicaldrive) +{ +#if !defined(_WIN32) || defined(__CYGWIN__) + skipping("Windows-specific PhysicalDrive path test"); +#else + struct archive *a; + struct archive_entry *ae; + + /* Create an archive_write_disk object. */ + assert((a = archive_write_disk_new()) != NULL); + + /* + * Test 1: \\.\PhysicalDrive0 should be rejected. + * Note: we intentionally do NOT set ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS + * because we are testing the device-path-specific check, not the + * broader absolute path check. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(ae, L"\\\\.\\PhysicalDrive0"); + archive_entry_set_mode(ae, S_IFREG | 0777); + archive_entry_set_size(ae, 0); + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* + * Test 2: \\.\PhysicalDrive9 should also be rejected. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(ae, L"\\\\.\\PhysicalDrive9"); + archive_entry_set_mode(ae, S_IFREG | 0777); + archive_entry_set_size(ae, 0); + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* + * Test 3: Case-insensitive — \\.\physicaldrive0 should be rejected. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(ae, L"\\\\.\\physicaldrive0"); + archive_entry_set_mode(ae, S_IFREG | 0777); + archive_entry_set_size(ae, 0); + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* + * Test 4: Mixed case — \\.\PHYSICALDRIVE5 should be rejected. + */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname_w(ae, L"\\\\.\\PHYSICALDRIVE5"); + archive_entry_set_mode(ae, S_IFREG | 0777); + archive_entry_set_size(ae, 0); + assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae)); + archive_entry_free(ae); + + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); +#endif +} diff --git a/libarchive/test/test_write_filter_compress.c b/libarchive/test/test_write_filter_compress.c index 6a0c022c48e1..c27ee33d52e8 100644 --- a/libarchive/test/test_write_filter_compress.c +++ b/libarchive/test/test_write_filter_compress.c @@ -94,3 +94,83 @@ DEFINE_TEST(test_write_filter_compress) free(data); free(buff); } + +/* + * A write callback that fails after it has accepted a threshold number + * of bytes, modelling a full disk or a broken pipe in the middle of the + * compressed output. + */ +struct failing_write_state { + size_t fail_after; + size_t written_so_far; +}; + +static ssize_t +failing_write(struct archive *a, void *client_data, const void *buff, + size_t length) +{ + struct failing_write_state *state = client_data; + + (void)a; + (void)buff; + state->written_so_far += length; + if (state->written_so_far > state->fail_after) + return (-1); + return ((ssize_t)length); +} + +/* + * Regression test: if the client write callback fails while the compress + * (.Z) filter is flushing a full output buffer, the filter must not keep + * writing past the end of that buffer. Before the fix, output_byte() + * left the buffer offset at buffer_size on a failed flush and + * output_code() issued further output_byte() calls without checking the + * return value, overflowing the heap buffer by one byte. The body is + * incompressible so the filter emits output steadily and is guaranteed + * to hit a flush while the sink is failing. + */ +DEFINE_TEST(test_write_filter_compress_failed_write) +{ + struct failing_write_state state; + struct archive *a; + struct archive_entry *ae; + size_t datasize = 2 * 1024 * 1024; + unsigned char *data; + size_t i; + uint32_t seed = 0x12345; + + assert(NULL != (data = malloc(datasize))); + for (i = 0; i < datasize; i++) { + seed = seed * 1103515245u + 12345u; + data[i] = (unsigned char)(seed >> 16); + } + + state.written_so_far = 0; + state.fail_after = 70000; + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_compress(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open(a, &state, NULL, failing_write, NULL)); + + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "file"); + archive_entry_set_size(ae, datasize); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_perm(ae, 0644); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* + * The write must fail cleanly (no out-of-bounds access). We do + * not care exactly which call reports it, only that nothing writes + * past the compress buffer; an ASan/UBSan build would abort here + * before the fix. + */ + archive_write_data(a, data, datasize); + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + free(data); +} diff --git a/libarchive/test/test_write_filter_lz4.c b/libarchive/test/test_write_filter_lz4.c index 12417b6034a3..d33413855b88 100644 --- a/libarchive/test/test_write_filter_lz4.c +++ b/libarchive/test/test_write_filter_lz4.c @@ -410,3 +410,57 @@ XXXDEFINE_TEST(test_write_filter_lz4_block_dependence_hc) test_options("lz4:block-dependence,lz4:compression-level=9"); } */ + +/* + * An archive with no entries whose format writes nothing at + * close (mtree, shar) closes the lz4 filter without any data + * having been written through it. The close used to compute + * the stream checksum of a stream descriptor that had never + * been written, dereferencing a null checksum state. + */ +DEFINE_TEST(test_write_filter_lz4_empty_archive) +{ + struct archive_entry *ae; + struct archive *a; + char *buff; + size_t buffsize, used1; + int r; + + buffsize = 2000000; + assert(NULL != (buff = malloc(buffsize))); + + /* Write an empty mtree archive through the lz4 filter. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a)); + r = archive_write_add_filter_lz4(a); + if (r != ARCHIVE_OK) { + assertEqualInt(ARCHIVE_WARN, r); + skipping("lz4 writing not supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + free(buff); + return; + } + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used1)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* The result is a valid empty lz4 stream: the 7-byte stream + * descriptor, the 4-byte end mark and the 4-byte stream + * checksum. */ + failure("empty lz4 stream is %d bytes", (int)used1); + assertEqualInt(15, used1); + assertEqualMem(buff, "\x04\x22\x4d\x18", 4); + + /* Read it back. */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_lz4(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_empty(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_memory(a, buff, used1)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + free(buff); +} diff --git a/libarchive/test/test_write_format_ar.c b/libarchive/test/test_write_format_ar.c index 9c6cd5ecdb05..5a7ebc924fe1 100644 --- a/libarchive/test/test_write_format_ar.c +++ b/libarchive/test/test_write_format_ar.c @@ -110,11 +110,6 @@ DEFINE_TEST(test_write_format_ar) assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assertEqualInt(0, archive_entry_mtime(ae)); - assertEqualString("//", archive_entry_pathname(ae)); - assertEqualInt(0, archive_entry_size(ae)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualString("abcdefghijklmn.o", archive_entry_pathname(ae)); diff --git a/libarchive/test/test_write_format_cpio_newc.c b/libarchive/test/test_write_format_cpio_newc.c index 786b5d739b4d..5f72b3082068 100644 --- a/libarchive/test/test_write_format_cpio_newc.c +++ b/libarchive/test/test_write_format_cpio_newc.c @@ -42,6 +42,24 @@ is_hex(const char *p, size_t l) return (1); } +static void +write_newc_entry(struct archive *a, const char *path, int64_t dev, int64_t ino, + int nlink) +{ + struct archive_entry *entry; + + assert((entry = archive_entry_new()) != NULL); + archive_entry_set_pathname(entry, path); + archive_entry_set_mode(entry, AE_IFREG | 0644); + archive_entry_set_size(entry, 0); + archive_entry_set_dev(entry, dev); + archive_entry_set_ino64(entry, ino); + archive_entry_set_nlink(entry, nlink); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, entry)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); + archive_entry_free(entry); +} + /* * Detailed verification that cpio 'newc' archives are written with * the correct format. @@ -205,3 +223,48 @@ DEFINE_TEST(test_write_format_cpio_newc) free(buff); } + +DEFINE_TEST(test_write_format_cpio_newc_large_inode) +{ + struct archive *a; + char *buff; + const char *first, *again, *other, *small_inode, *different_device; + size_t buffsize = 10000; + size_t used; + + buff = malloc(buffsize); + assert(buff != NULL); + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_cpio_newc(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, buffsize, &used)); + + write_newc_entry(a, "first", 12, INT64_C(0x100000001), 2); + write_newc_entry(a, "again", 12, INT64_C(0x100000001), 2); + write_newc_entry(a, "other", 12, INT64_C(0x200000001), 2); + write_newc_entry(a, "small", 12, 1, 1); + write_newc_entry(a, "dev13", 13, INT64_C(0x100000001), 2); + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + /* Each entry is a 110-byte header plus a padded six-byte name. */ + first = buff + 6; + again = buff + 116 + 6; + other = buff + 232 + 6; + small_inode = buff + 348 + 6; + different_device = buff + 464 + 6; + assertEqualMem(first, again, 8); + assert(memcmp(first, other, 8) != 0); + assert(memcmp(first, small_inode, 8) != 0); + assert(memcmp(first, different_device, 8) != 0); + assert(memcmp(other, small_inode, 8) != 0); + assert(memcmp(other, different_device, 8) != 0); + assert(memcmp(small_inode, different_device, 8) != 0); + assert(memcmp(first, "00000000", 8) != 0); + assert(memcmp(other, "00000000", 8) != 0); + assert(memcmp(small_inode, "00000000", 8) != 0); + assert(memcmp(different_device, "00000000", 8) != 0); + + free(buff); +} diff --git a/libarchive/test/test_write_format_iso9660_bugs.c b/libarchive/test/test_write_format_iso9660_bugs.c index 24f4d3734e63..39879fe44300 100644 --- a/libarchive/test/test_write_format_iso9660_bugs.c +++ b/libarchive/test/test_write_format_iso9660_bugs.c @@ -27,6 +27,28 @@ #include +struct iso9660_capture { + unsigned char data[64 * 1024]; + size_t used; +}; + +static la_ssize_t +capture_iso9660(struct archive *a, void *client_data, const void *buffer, + size_t length) +{ + struct iso9660_capture *capture = client_data; + size_t copy = length; + + (void)a; + if (copy > sizeof(capture->data) - capture->used) + copy = sizeof(capture->data) - capture->used; + if (copy > 0) { + memcpy(capture->data + capture->used, buffer, copy); + capture->used += copy; + } + return ((la_ssize_t)length); +} + /* * Replay a fuzzer binary through the ISO9660 writer, matching the protocol * in fuzzers/custom/fuzz_writer_iso9660.cc. @@ -247,6 +269,43 @@ DEFINE_TEST(test_write_format_iso9660_duplicate_identifier_truncation) free(buff); } +DEFINE_TEST(test_write_format_iso9660_boot_image_size_overflow) +{ + static const unsigned char expected_volume_size[] = { + 0x21, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x21 + }; + struct iso9660_capture capture = {{0}, 0}; + struct archive_entry *entry; + struct archive *a; + const size_t pvd = 16 * 2048; + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_iso9660(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_option(a, NULL, "boot", "boot.img")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_option(a, NULL, "boot-type", "no-emulation")); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_set_option(a, NULL, "pad", NULL)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open(a, &capture, NULL, capture_iso9660, NULL)); + + assert((entry = archive_entry_new()) != NULL); + archive_entry_copy_pathname(entry, "boot.img"); + archive_entry_set_mode(entry, AE_IFREG | 0644); + archive_entry_set_size(entry, INT32_MAX); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, entry)); + archive_entry_free(entry); + + /* The missing file body fails after the volume descriptors are written. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); + assert(capture.used >= pvd + 0x58); + assertEqualMem(capture.data + pvd, "\001CD001\001", 7); + assertEqualMem(capture.data + pvd + 0x50, expected_volume_size, + sizeof(expected_volume_size)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); +} DEFINE_TEST(test_write_format_iso9660_symlink) { diff --git a/libarchive/test/test_write_format_iso9660_rr_moved_notdir.c b/libarchive/test/test_write_format_iso9660_rr_moved_notdir.c new file mode 100644 index 000000000000..a8f996638355 --- /dev/null +++ b/libarchive/test/test_write_format_iso9660_rr_moved_notdir.c @@ -0,0 +1,133 @@ +/*- + * Copyright (c) 2026 krishna28238-arch + * 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 "test.h" + +/* + * Directories nested deeper than eight levels are relocated into the + * rr_moved directory when the archive is closed (Rock Ridge, RRIP + * 4.1.5). A user-supplied entry called "rr_moved" is adopted as the + * relocation target. When that entry is a regular file, relocated + * subtrees used to be attached to a non-directory entry, which made + * the writer produce a broken image while reporting success and leak + * the whole relocated subtree. + */ +DEFINE_TEST(test_write_format_iso9660_rr_moved_notdir) +{ + struct archive *a; + struct archive_entry *ae; + unsigned char *buff; + size_t buffsize = 256 * 2048; + size_t used; + + buff = malloc(buffsize); + assert(buff != NULL); + if (buff == NULL) + return; + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_iso9660(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); + + /* A regular file called rr_moved at the root of the archive. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "rr_moved"); + archive_entry_set_size(ae, 0); + archive_entry_set_mode(ae, S_IFREG | 0644); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* A path nested more than eight levels deep. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, + "d1/d2/d3/d4/d5/d6/d7/d8/d9/d10/d11/d12/file.txt"); + archive_entry_set_size(ae, 4); + archive_entry_set_mode(ae, S_IFREG | 0644); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + assertEqualIntA(a, 4, archive_write_data(a, "body", 4)); + archive_entry_free(ae); + + /* Relocating into the file cannot work; the close has to say so + * instead of writing a broken image and leaking the tree. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); + if (archive_error_string(a) != NULL) + assert(NULL != strstr(archive_error_string(a), + "rr_moved")); + + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + free(buff); +} + +/* + * A user-supplied rr_moved directory is still adopted and receives + * the relocated subtrees. + */ +DEFINE_TEST(test_write_format_iso9660_rr_moved_dir) +{ + struct archive *a; + struct archive_entry *ae; + unsigned char *buff; + size_t buffsize = 256 * 2048; + size_t used; + + buff = malloc(buffsize); + assert(buff != NULL); + if (buff == NULL) + return; + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_iso9660(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_in_last_block(a, 1)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used)); + + /* A directory called rr_moved supplied by the caller. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "rr_moved"); + archive_entry_set_size(ae, 0); + archive_entry_set_mode(ae, S_IFDIR | 0755); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + + /* A path nested more than eight levels deep. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, + "d1/d2/d3/d4/d5/d6/d7/d8/d9/d10/d11/d12/file.txt"); + archive_entry_set_size(ae, 4); + archive_entry_set_mode(ae, S_IFREG | 0644); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + assertEqualIntA(a, 4, archive_write_data(a, "body", 4)); + archive_entry_free(ae); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + + free(buff); +} diff --git a/libarchive/test/test_write_format_mtree.c b/libarchive/test/test_write_format_mtree.c index 907e291f2f55..b937617c290c 100644 --- a/libarchive/test/test_write_format_mtree.c +++ b/libarchive/test/test_write_format_mtree.c @@ -269,3 +269,34 @@ DEFINE_TEST(test_write_format_mtree_no_leading_dotslash) /* Use /set keyword with directory only */ test_write_format_mtree_sub2(1, 1); } + +DEFINE_TEST(test_write_format_mtree_nlink) +{ + struct archive_entry *ae; + struct archive *a; + size_t used; + + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_write_open_memory(a, buff, sizeof(buff) - 1, &used)); + + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_pathname(ae, "nlink-unset"); + archive_entry_set_mode(ae, AE_IFREG | 0644); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_set_pathname(ae, "nlink-one"); + archive_entry_set_nlink(ae, 1); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_set_pathname(ae, "nlink-two"); + archive_entry_set_nlink(ae, 2); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + buff[used] = '\0'; + assert(strstr(buff, "nlink=0") == NULL); + assert(strstr(buff, "nlink=1") == NULL); + assert(strstr(buff, "nlink=2") != NULL); +} diff --git a/libarchive/test/test_write_format_mtree_preset_digests.c b/libarchive/test/test_write_format_mtree_preset_digests.c index 43a7e14e97ed..9b35f6a42f9a 100644 --- a/libarchive/test/test_write_format_mtree_preset_digests.c +++ b/libarchive/test/test_write_format_mtree_preset_digests.c @@ -50,7 +50,12 @@ DEFINE_TEST(test_write_format_mtree_digests_no_digests_set_no_data) size_t used = 0; struct archive *a; struct archive_entry *entry; + +#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) \ + || defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) \ + || defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512) struct expected_digests ed; +#endif #ifdef ARCHIVE_HAS_MD5 assertEqualInt(ARCHIVE_OK, archive_md5_init(&expectedMd5Ctx)); @@ -139,7 +144,12 @@ DEFINE_TEST(test_write_format_mtree_digests_no_digests_set_empty_data) size_t used = 0; struct archive *a; struct archive_entry *entry; + +#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) \ + || defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) \ + || defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512) struct expected_digests ed; +#endif #ifdef ARCHIVE_HAS_MD5 assertEqualInt(ARCHIVE_OK, archive_md5_init(&expectedMd5Ctx)); @@ -229,9 +239,14 @@ DEFINE_TEST(test_write_format_mtree_digests_no_digests_set_non_empty_data) size_t used = 0; struct archive *a; struct archive_entry *entry; - struct expected_digests ed; char *data = "abcd"; +#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) \ + || defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) \ + || defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512) + struct expected_digests ed; +#endif + #ifdef ARCHIVE_HAS_MD5 assertEqualInt(ARCHIVE_OK, archive_md5_init(&expectedMd5Ctx)); assertEqualInt(ARCHIVE_OK, archive_md5_update(&expectedMd5Ctx, data, 4)); diff --git a/libarchive/test/test_write_format_pax.c b/libarchive/test/test_write_format_pax.c index 44cce2f8d2b4..adbcec840954 100644 --- a/libarchive/test/test_write_format_pax.c +++ b/libarchive/test/test_write_format_pax.c @@ -38,6 +38,20 @@ DEFINE_TEST(test_write_format_pax) int64_t offset, length; char long_slashes[300 + 1]; + /* Only archive_write_free() is valid once the archive is fatal. */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax(a)); + archive_write_fail(a); + assertEqualIntA(a, ARCHIVE_FATAL, + archive_write_open_memory(a, buff2, sizeof(buff2), &used)); + assert((ae = archive_entry_new()) != NULL); + archive_entry_set_pathname(ae, "test"); + archive_entry_set_filetype(ae, AE_IFREG); + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + buff = malloc(buffsize); /* million bytes of work area */ assert(buff != NULL); diff --git a/libarchive/test/test_write_format_pax_align.c b/libarchive/test/test_write_format_pax_align.c index 0ca0c81062a4..5703e8c3f0c3 100644 --- a/libarchive/test/test_write_format_pax_align.c +++ b/libarchive/test/test_write_format_pax_align.c @@ -129,10 +129,15 @@ write_archive(char *buff, size_t buffsize, size_t *used, int gzip) assert((a = archive_write_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax_restricted(a)); - if (gzip) - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_gzip(a)); - else - assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a)); + if (gzip) { + int r; + + r = archive_write_add_filter_gzip(a); + if (r != ARCHIVE_WARN) + assertEqualIntA(a, ARCHIVE_OK, r); + } else + assertEqualIntA(a, ARCHIVE_OK, + archive_write_add_filter_none(a)); /* Small blocks so the writer doesn't tail-pad past our offsets. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 512)); assertEqualIntA(a, ARCHIVE_OK, @@ -178,10 +183,15 @@ verify_archive(const char *buff, size_t used, int gzip) assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a)); - if (gzip) - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_gzip(a)); - else - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_none(a)); + if (gzip) { + int r; + + r = archive_read_support_filter_gzip(a); + if (r != ARCHIVE_WARN) + assertEqualIntA(a, ARCHIVE_OK, r); + } else + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_filter_none(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used)); diff --git a/libarchive/test/test_write_format_raw.c b/libarchive/test/test_write_format_raw.c index bd887ff86c94..eccaeea3cd5e 100644 --- a/libarchive/test/test_write_format_raw.c +++ b/libarchive/test/test_write_format_raw.c @@ -92,7 +92,7 @@ test_format(int (*set_format)(struct archive *)) assertEqualMem(err, "Raw format only supports one entry per archive", 47); archive_entry_free(ae); - assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Create a new archive */ @@ -111,7 +111,7 @@ test_format(int (*set_format)(struct archive *)) assertEqualMem(err, "Raw format only supports filetype AE_IFREG", 43); archive_entry_free(ae); - assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); free(buff); diff --git a/libarchive/test/test_write_format_shar_empty.c b/libarchive/test/test_write_format_shar_empty.c index b4dbce957523..8873aca9b908 100644 --- a/libarchive/test/test_write_format_shar_empty.c +++ b/libarchive/test/test_write_format_shar_empty.c @@ -51,3 +51,40 @@ DEFINE_TEST(test_write_format_shar_empty) failure("Empty shar archive should be exactly 0 bytes, was %zu.", used); assert(used == 0); } + +DEFINE_TEST(test_write_format_shar_invalid_pathname) +{ + struct archive *a; + struct archive_entry *ae; + char buff[2048]; + size_t used; + int dump, empty; + + for (dump = 0; dump <= 1; dump++) { + for (empty = 0; empty <= 1; empty++) { + assert((a = archive_write_new()) != NULL); + if (dump) + assertEqualInt(ARCHIVE_OK, + archive_write_set_format_shar_dump(a)); + else + assertEqualInt(ARCHIVE_OK, + archive_write_set_format_shar(a)); + assertEqualInt(ARCHIVE_OK, archive_write_open_memory(a, + buff, sizeof(buff), &used)); + + assert((ae = archive_entry_new()) != NULL); + if (empty) + archive_entry_set_pathname(ae, ""); + archive_entry_set_filetype(ae, AE_IFREG); + archive_entry_set_perm(ae, 0644); + archive_entry_set_size(ae, 0); + assertEqualIntA(a, ARCHIVE_FAILED, + archive_write_header(a, ae)); + archive_entry_free(ae); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualInt(ARCHIVE_OK, archive_write_free(a)); + assertEqualInt(0, used); + } + } +} diff --git a/libarchive/test/test_write_format_xar.c b/libarchive/test/test_write_format_xar.c index 40d42291a2ac..43b3776761b9 100644 --- a/libarchive/test/test_write_format_xar.c +++ b/libarchive/test/test_write_format_xar.c @@ -310,7 +310,7 @@ DEFINE_TEST(test_write_format_xar) { /* xar mandates the use of UTF-8 XML; if we cannot * use UTF-8, perhaps we should not write xar. */ - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } diff --git a/libarchive/test/test_zip_filename_encoding.c b/libarchive/test/test_zip_filename_encoding.c index 1cb394547066..3634a06ee71c 100644 --- a/libarchive/test/test_zip_filename_encoding.c +++ b/libarchive/test/test_zip_filename_encoding.c @@ -33,7 +33,7 @@ DEFINE_TEST(test_zip_filename_encoding_UTF8) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } @@ -124,7 +124,7 @@ DEFINE_TEST(test_zip_filename_encoding_KOI8R) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { + if (!setCheckedLocale("ru_RU.KOI8-R", "\xE1", L'\x0410')) { skipping("KOI8-R locale not available on this system."); return; } @@ -224,7 +224,7 @@ DEFINE_TEST(test_zip_filename_encoding_ru_RU_CP1251) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ru_RU.CP1251")) { + if (!setCheckedLocale("ru_RU.CP1251", "\xC0", L'\x0410')) { skipping("Russian_Russia locale not available on this system."); return; } @@ -267,7 +267,10 @@ DEFINE_TEST(test_zip_filename_encoding_Russian_Russia) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "Russian_Russia")) { +#if defined(_WIN32) && !defined(__CYGWIN__) + if (NULL == setlocale(LC_ALL, "Russian_Russia")) +#endif + { skipping("Russian_Russia locale not available on this system."); return; } @@ -336,7 +339,7 @@ DEFINE_TEST(test_zip_filename_encoding_EUCJP) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) { + if (!setCheckedLocale("ja_JP.eucJP", "\xA4\xA2", L'\x3042')) { skipping("eucJP locale not available on this system."); return; } @@ -435,8 +438,11 @@ DEFINE_TEST(test_zip_filename_encoding_CP932) char buff[4096]; size_t used; - if (NULL == setlocale(LC_ALL, "Japanese_Japan") && - NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { + if ( +#if defined(_WIN32) && !defined(__CYGWIN__) + NULL == setlocale(LC_ALL, "Japanese_Japan") && +#endif + !setCheckedLocale("ja_JP.SJIS", "\x82\xA0", L'\x3042')) { skipping("CP932/SJIS locale not available on this system."); return; } diff --git a/libarchive_fe/lafe_explicit_bzero.c b/libarchive_fe/lafe_explicit_bzero.c new file mode 100644 index 000000000000..6e50d1650a44 --- /dev/null +++ b/libarchive_fe/lafe_explicit_bzero.c @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(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 "lafe_platform.h" +#ifndef HAVE_EXPLICIT_BZERO + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_WINDOWS_H +#include +#endif + +#include "lafe_explicit_bzero.h" + +void +explicit_bzero(void *b, size_t len) +{ +#ifdef HAVE_MEMSET_EXPLICIT + memset_explicit(b, 0, len); +#elif defined(_WIN32) || defined(__CYGWIN__) + SecureZeroMemory(b, len); +#else + volatile char *p = (volatile char *)b; + + while (len--) + *p++ = 0; +#endif +} +#endif diff --git a/libarchive_fe/lafe_explicit_bzero.h b/libarchive_fe/lafe_explicit_bzero.h new file mode 100644 index 000000000000..e67f5873f702 --- /dev/null +++ b/libarchive_fe/lafe_explicit_bzero.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2026 Tobias Stoeckmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LAFE_EXPLICIT_BZERO_H_INCLUDED +#define LAFE_EXPLICIT_BZERO_H_INCLUDED + +#include "lafe_platform.h" + +#ifndef HAVE_EXPLICIT_BZERO +#include +void explicit_bzero(void *b, size_t len); +#endif + +#endif /* LAFE_EXPLICIT_BZERO_H_INCLUDED */ diff --git a/libarchive_fe/lafe_setmode.c b/libarchive_fe/lafe_setmode.c new file mode 100644 index 000000000000..3a14bf90c444 --- /dev/null +++ b/libarchive_fe/lafe_setmode.c @@ -0,0 +1,464 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dave Borman at Cray Research, Inc. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include "lafe_platform.h" +/* + * Include archive_platform.h only for S_I* mode macros. + * Undefine __LIBARCHIVE_BUILD again: libarchive.so can be + * linked dynamically, which turns private functions inaccessible. + */ +#include "archive_platform.h" +#undef __LIBARCHIVE_BUILD + +#include +#include +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef SETMODE_DEBUG +#include +#endif + +#include "lafe_setmode.h" + +#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ +#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ + +typedef struct bitcmd { + char cmd; + char cmd2; + mode_t bits; +} BITCMD; + +#define CMD2_CLR 0x01 +#define CMD2_SET 0x02 +#define CMD2_GBITS 0x04 +#define CMD2_OBITS 0x08 +#define CMD2_UBITS 0x10 + +static BITCMD *addcmd(BITCMD *, mode_t, mode_t, mode_t, mode_t); +static void compress_mode(BITCMD *); +#ifdef SETMODE_DEBUG +static void dumpmode(BITCMD *); +#endif + +/* + * Given the old mode and an array of bitcmd structures, apply the operations + * described in the bitcmd structures to the old mode, and return the new mode. + * Note that there is no '=' command; a strict assignment is just a '-' (clear + * bits) followed by a '+' (set bits). + */ +mode_t +lafe_getmode(const void *bbox, mode_t omode) +{ + const BITCMD *set; + mode_t clrval, newmode, value; + + set = (const BITCMD *)bbox; + newmode = omode; + for (value = 0;; set++) + switch(set->cmd) { + /* + * When copying the user, group or other bits around, we "know" + * where the bits are in the mode so that we can do shifts to + * copy them around. If we don't use shifts, it gets real + * grundgy with lots of single bit checks and bit sets. + */ + case 'u': + value = (newmode & S_IRWXU) >> 6; + goto common; + + case 'g': + value = (newmode & S_IRWXG) >> 3; + goto common; + + case 'o': + value = newmode & S_IRWXO; +common: if (set->cmd2 & CMD2_CLR) { + clrval = + (set->cmd2 & CMD2_SET) ? S_IRWXO : value; + if (set->cmd2 & CMD2_UBITS) + newmode &= ~((clrval<<6) & set->bits); + if (set->cmd2 & CMD2_GBITS) + newmode &= ~((clrval<<3) & set->bits); + if (set->cmd2 & CMD2_OBITS) + newmode &= ~(clrval & set->bits); + } + if (set->cmd2 & CMD2_SET) { + if (set->cmd2 & CMD2_UBITS) + newmode |= (value<<6) & set->bits; + if (set->cmd2 & CMD2_GBITS) + newmode |= (value<<3) & set->bits; + if (set->cmd2 & CMD2_OBITS) + newmode |= value & set->bits; + } + break; + + case '+': + newmode |= set->bits; + break; + + case '-': + newmode &= ~set->bits; + break; + + case 'X': + if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH)) + newmode |= set->bits; + break; + + case '\0': + default: +#ifdef SETMODE_DEBUG + (void)printf("getmode:%04o -> %04o\n", omode, newmode); +#endif + return (newmode); + } +} + +#define ADDCMD(a, b, c, d) \ + if (set >= endset) { \ + ptrdiff_t setdiff = set - saveset; \ + BITCMD *newset; \ + setlen += SET_LEN_INCR; \ + newset = realloc(saveset, setlen * sizeof(BITCMD)); \ + if (newset == NULL) \ + goto out; \ + set = newset + setdiff; \ + saveset = newset; \ + endset = newset + (setlen - 2); \ + } \ + set = addcmd(set, (mode_t)(a), (mode_t)(b), (mode_t)(c), (d)) + +#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) + +void * +lafe_setmode(const char *p) +{ + int serrno; + char op, *ep; + BITCMD *set, *saveset, *endset; + mode_t mask, perm, permXbits, who; + long perml; + int equalopdone; + unsigned int setlen; + + if (!*p) { + errno = EINVAL; + return (NULL); + } + + /* + * Get a copy of the mask for the permissions that are mask relative. + * Flip the bits, we want what's not set. + */ + umask(mask = umask(0)); + mask = ~mask; + + setlen = SET_LEN + 2; + + if ((set = malloc(setlen * sizeof(BITCMD))) == NULL) + return (NULL); + saveset = set; + endset = set + (setlen - 2); + + /* + * If an absolute number, get it and return; disallow non-octal digits + * or illegal bits. + */ + if (isdigit((unsigned char)*p)) { + errno = 0; + perml = strtol(p, &ep, 8); + if (*ep) { + errno = EINVAL; + goto out; + } + if (errno == ERANGE && (perml == LONG_MAX || perml == LONG_MIN)) + goto out; + if (perml & ~(STANDARD_BITS|S_ISVTX)) { + errno = EINVAL; + goto out; + } + perm = (mode_t)perml; + ADDCMD('=', (STANDARD_BITS|S_ISVTX), perm, mask); + set->cmd = 0; + return (saveset); + } + + /* + * Build list of structures to set/clear/copy bits as described by + * each clause of the symbolic mode. + */ + equalopdone = 0; + for (;;) { + /* First, find out which bits might be modified. */ + for (who = 0;; ++p) { + switch (*p) { + case 'a': + who |= STANDARD_BITS; + break; + case 'u': + who |= S_ISUID|S_IRWXU; + break; + case 'g': + who |= S_ISGID|S_IRWXG; + break; + case 'o': + who |= S_IRWXO; + break; + default: + goto getop; + } + } + +getop: if ((op = *p++) != '+' && op != '-' && op != '=') { + errno = EINVAL; + goto out; + } + if (op == '=') + equalopdone = 0; + + who &= ~S_ISVTX; + for (perm = 0, permXbits = 0;; ++p) { + switch (*p) { + case 'r': + perm |= S_IRUSR|S_IRGRP|S_IROTH; + break; + case 's': + /* If only "other" bits ignore set-id. */ + if (!who || who & ~S_IRWXO) + perm |= S_ISUID|S_ISGID; + break; + case 't': + /* If only "other" bits ignore sticky. */ + if (!who || who & ~S_IRWXO) { + who |= S_ISVTX; + perm |= S_ISVTX; + } + break; + case 'w': + perm |= S_IWUSR|S_IWGRP|S_IWOTH; + break; + case 'X': + permXbits = S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'x': + perm |= S_IXUSR|S_IXGRP|S_IXOTH; + break; + case 'u': + case 'g': + case 'o': + /* + * When ever we hit 'u', 'g', or 'o', we have + * to flush out any partial mode that we have, + * and then do the copying of the mode bits. + */ + if (perm) { + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (op == '=') + equalopdone = 1; + if (op == '+' && permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + ADDCMD(*p, who, op, mask); + break; + + default: + /* + * Add any permissions that we haven't already + * done. + */ + if (perm || (op == '=' && !equalopdone)) { + if (op == '=') + equalopdone = 1; + ADDCMD(op, who, perm, mask); + perm = 0; + } + if (permXbits) { + ADDCMD('X', who, permXbits, mask); + permXbits = 0; + } + goto apply; + } + } + +apply: if (!*p) + break; + if (*p != ',') + goto getop; + ++p; + } + set->cmd = 0; +#ifdef SETMODE_DEBUG + (void)printf("Before compress_mode()\n"); + dumpmode(saveset); +#endif + compress_mode(saveset); +#ifdef SETMODE_DEBUG + (void)printf("After compress_mode()\n"); + dumpmode(saveset); +#endif + return (saveset); +out: + serrno = errno; + free(saveset); + errno = serrno; + return NULL; +} + +static BITCMD * +addcmd(BITCMD *set, mode_t op, mode_t who, mode_t oparg, mode_t mask) +{ + switch (op) { + case '=': + set->cmd = '-'; + set->bits = who ? who : STANDARD_BITS; + set++; + + op = '+'; + /* FALLTHROUGH */ + case '+': + case '-': + case 'X': + set->cmd = op; + set->bits = (who ? who : mask) & oparg; + break; + + case 'u': + case 'g': + case 'o': + set->cmd = op; + if (who) { + set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) | + ((who & S_IRGRP) ? CMD2_GBITS : 0) | + ((who & S_IROTH) ? CMD2_OBITS : 0); + set->bits = (mode_t)~0; + } else { + set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; + set->bits = mask; + } + + if (oparg == '+') + set->cmd2 |= CMD2_SET; + else if (oparg == '-') + set->cmd2 |= CMD2_CLR; + else if (oparg == '=') + set->cmd2 |= CMD2_SET|CMD2_CLR; + break; + } + return (set + 1); +} + +#ifdef SETMODE_DEBUG +static void +dumpmode(BITCMD *set) +{ + for (; set->cmd; ++set) + (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n", + set->cmd, set->bits, set->cmd2 ? " cmd2:" : "", + set->cmd2 & CMD2_CLR ? " CLR" : "", + set->cmd2 & CMD2_SET ? " SET" : "", + set->cmd2 & CMD2_UBITS ? " UBITS" : "", + set->cmd2 & CMD2_GBITS ? " GBITS" : "", + set->cmd2 & CMD2_OBITS ? " OBITS" : ""); +} +#endif + +/* + * Given an array of bitcmd structures, compress by compacting consecutive + * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', + * 'g' and 'o' commands continue to be separate. They could probably be + * compacted, but it's not worth the effort. + */ +static void +compress_mode(BITCMD *set) +{ + BITCMD *nset; + int setbits, clrbits, Xbits, op; + + for (nset = set;;) { + /* Copy over any 'u', 'g' and 'o' commands. */ + while ((op = nset->cmd) != '+' && op != '-' && op != 'X') { + *set++ = *nset++; + if (!op) + return; + } + + for (setbits = clrbits = Xbits = 0;; nset++) { + if ((op = nset->cmd) == '-') { + clrbits |= nset->bits; + setbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == '+') { + setbits |= nset->bits; + clrbits &= ~nset->bits; + Xbits &= ~nset->bits; + } else if (op == 'X') + Xbits |= nset->bits & ~setbits; + else + break; + } + if (clrbits) { + set->cmd = '-'; + set->cmd2 = 0; + set->bits = clrbits; + set++; + } + if (setbits) { + set->cmd = '+'; + set->cmd2 = 0; + set->bits = setbits; + set++; + } + if (Xbits) { + set->cmd = 'X'; + set->cmd2 = 0; + set->bits = Xbits; + set++; + } + } +} diff --git a/libarchive_fe/lafe_setmode.h b/libarchive_fe/lafe_setmode.h new file mode 100644 index 000000000000..efd2861d21bb --- /dev/null +++ b/libarchive_fe/lafe_setmode.h @@ -0,0 +1,41 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Dave Borman at Cray Research, Inc. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef LAFE_SETMODE_H_INCLUDED +#define LAFE_SETMODE_H_INCLUDED + +mode_t lafe_getmode(const void *set, mode_t mode); +void *lafe_setmode(const char *mode_str); + +#endif diff --git a/tar/CMakeLists.txt b/tar/CMakeLists.txt index 39600360776b..395e7cf70aeb 100644 --- a/tar/CMakeLists.txt +++ b/tar/CMakeLists.txt @@ -20,7 +20,11 @@ IF(ENABLE_TAR) write.c ../libarchive_fe/lafe_err.c ../libarchive_fe/lafe_err.h + ../libarchive_fe/lafe_explicit_bzero.c + ../libarchive_fe/lafe_explicit_bzero.h ../libarchive_fe/lafe_platform.h + ../libarchive_fe/lafe_setmode.c + ../libarchive_fe/lafe_setmode.h ../libarchive_fe/line_reader.c ../libarchive_fe/line_reader.h ../libarchive_fe/passphrase.c diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index 0062789e27e9..726ccb937c77 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -185,6 +185,16 @@ Specify the block size, in 512-byte records, for tape drive I/O. As a rule, this argument is only needed when reading from or writing to tape drives, and usually not even then as the default block size of 20 records (10240 bytes) is very common. +.It Fl Fl b64encode +(c mode only) +Encode the resulting archive with base64, after any compression. +In extract or list modes, this option is ignored. +Note that this +.Nm tar +implementation recognizes base64-encoded archives automatically when +reading archives. +This option cannot be combined with +.Fl Fl uuencode . .It Fl C Ar directory , Fl Fl cd Ar directory , Fl Fl directory Ar directory In c and r mode, this changes the directory before adding the following files. @@ -261,7 +271,8 @@ on Linux, the default is Use the provided group id number. On extract, this overrides the group id in the archive; the group name in the archive will be ignored. -On create, this overrides the group id read from disk; +On create, this overrides the group id read from disk +or copied from an archive; if .Fl Fl gname is not also specified, the group name will be set to @@ -287,6 +298,15 @@ can be either a group name or numeric id. See the .Fl Fl gname option for details. +.It Fl Fl grzip +(c mode only) +Compress the resulting archive with +.Xr grzip 1 . +In extract or list modes, this option is ignored. +Note that this +.Nm tar +implementation recognizes grzip compression automatically when reading +archives. .It Fl H (c and r modes only) Symbolic links named on the command line will be followed; the @@ -378,6 +398,13 @@ In extract or list modes, this option is ignored. Note that this .Nm tar implementation recognizes lz4 compression automatically when reading archives. +.It Fl Fl lzip +(c mode only) +Compress the archive with lzip-compatible compression before writing it. +In extract or list modes, this option is ignored. +Note that this +.Nm tar +implementation recognizes lzip compression automatically when reading archives. .It Fl Fl zstd (c mode only) Compress the archive with zstd-compatible compression before writing it. @@ -421,6 +448,15 @@ is run in x mode as root. Currently supported only for pax formats .Po including "pax restricted", the default tar format for .Nm bsdtar Pc +.It Fl Fl mode Ar permissions +Modify the file mode bits. +On create, this takes the on-disk mode and modifies it before storing +in the archive. +On extract, the mode from the archive is modified before writing to disk. +Permissions can be absolute (an octal number) or symbolic. +See +.Xr chmod 1 +for more information and examples of both types. .It Fl Fl mtime Ar date (c, r, u modes only) Set the modification times of added files to the specified date. @@ -450,7 +486,7 @@ Honor the nodump file flag by skipping this file. .It Fl Fl nopreserveHFSCompression (x mode only) Mac OS X specific (v10.6 or later). Do not compress extracted regular files -which were compressed with HFS+ compression before archived. +which were compressed with HFS+ compression before being archived. By default, compress the regular files again with HFS+ compression. .It Fl Fl null (use with @@ -644,10 +680,10 @@ and zpaq (best, extremely slow). .It Cm lrzip:compression-level A decimal integer from 1 to 9 specifying the lrzip compression level. .It Cm lz4:compression-level -A decimal integer from 1 to 9 specifying the lzop compression level. +A decimal integer from 1 to 9 specifying the lz4 compression level. .It Cm lz4:stream-checksum Enable stream checksum. -This is by default, use +This is enabled by default, use .Cm lz4:!stream-checksum to disable. .It Cm lz4:block-checksum @@ -969,12 +1005,24 @@ the name is not verified against the system user database. Pipe the input (in x or t mode) or the output (in c mode) through .Pa program instead of using the builtin compression support. +.It Fl Fl uuencode +(c mode only) +Encode the resulting archive with +.Xr uuencode 1 , +after any compression. +In extract or list modes, this option is ignored. +Note that this +.Nm tar +implementation recognizes uuencoded archives automatically when reading +archives. +This option cannot be combined with +.Fl Fl b64encode . .It Fl Fl owner Ar name Ns Op : Ns Ar uid Use the provided user, if .Ar uid is not provided, .Ar name -can be either an username or numeric id. +can be either a username or numeric id. See the .Fl Fl uname option for details. @@ -1397,7 +1445,7 @@ complain about the null padding when decompressing an archive created by .Nm , although they still extract it correctly. .Pp -The compression and decompression is implemented internally, so +The compression and decompression are implemented internally, so there may be insignificant differences between the compressed output generated by .Pp diff --git a/tar/bsdtar.c b/tar/bsdtar.c index fbc0627a83b9..e91efa4bec52 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -56,6 +56,7 @@ #include "bsdtar.h" #include "lafe_err.h" +#include "lafe_setmode.h" #if ARCHIVE_VERSION_NUMBER < 4000000 && !defined(_PATH_DEFTAPE) // Libarchive 4.0 and later will NOT define _PATH_DEFTAPE @@ -512,6 +513,14 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_MAC_METADATA; bsdtar->flags |= OPTFLAG_MAC_METADATA; break; + case OPTION_MODE: /* GNU tar */ + free(bsdtar->file_mode); + bsdtar->file_mode = lafe_setmode(bsdtar->argument); + if (bsdtar->file_mode == NULL) + lafe_errc(1, 0, + "Invalid file mode: %s", + bsdtar->argument); + break; case 'n': /* GNU tar */ bsdtar->flags |= OPTFLAG_NO_SUBDIRS; break; @@ -1023,6 +1032,7 @@ main(int argc, char **argv) #endif cset_free(bsdtar->cset); passphrase_free(bsdtar->ppbuff); + free(bsdtar->file_mode); if (bsdtar->return_value != 0) lafe_warnc(0, diff --git a/tar/bsdtar.h b/tar/bsdtar.h index 29c67fc08295..9ab01044991e 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -47,6 +47,7 @@ struct bsdtar { const char *gname; /* --gname */ int uid; /* --uid */ const char *uname; /* --uname */ + void *file_mode; /* --mode */ const char *passphrase; /* --passphrase */ int mode; /* Program mode: 'c', 't', 'r', 'u', 'x' */ char symlink_mode; /* H or L, per BSD conventions */ @@ -81,15 +82,9 @@ struct bsdtar { */ struct archive *diskreader; /* for write.c */ struct archive_entry_linkresolver *resolver; /* for write.c */ - struct archive_dir *archive_dir; /* for write.c */ - struct name_cache *gname_cache; /* for write.c */ char *buff; /* for write.c */ size_t buff_size; /* for write.c */ - int first_fs; /* for write.c */ struct archive *matching; /* for matching.c */ - struct security *security; /* for read.c */ - struct name_cache *uname_cache; /* for write.c */ - struct siginfo_data *siginfo; /* for siginfo.c */ struct substitution *substitution; /* for subst.c */ char *ppbuff; /* for util.c */ }; @@ -186,6 +181,7 @@ enum { OPTION_ZSTD, OPTION_MTIME, OPTION_CLAMP_MTIME, + OPTION_MODE, }; int bsdtar_getopt(struct bsdtar *); @@ -194,7 +190,7 @@ int edit_pathname(struct bsdtar *, struct archive_entry *); void edit_mtime(struct bsdtar *, struct archive_entry *); int need_report(void); int pathcmp(const char *a, const char *b); -void safe_fprintf(FILE * ARCHIVE_RESTRICT, const char * ARCHIVE_RESTRICT fmt, ...) __LA_PRINTF(2, 3); +void safe_fputs(const char * ARCHIVE_RESTRICT s, FILE * ARCHIVE_RESTRICT); void set_chdir(struct bsdtar *, const char *newdir); void tar_mode_c(struct bsdtar *bsdtar); void tar_mode_r(struct bsdtar *bsdtar); diff --git a/tar/cmdline.c b/tar/cmdline.c index 309be312c948..6495b1eea675 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -95,6 +95,7 @@ static const struct bsdtar_option { { "lzma", 0, OPTION_LZMA }, { "lzop", 0, OPTION_LZOP }, { "mac-metadata", 0, OPTION_MAC_METADATA }, + { "mode", 1, OPTION_MODE }, { "modification-time", 0, 'm' }, { "mtime", 1, OPTION_MTIME }, { "newer", 1, OPTION_NEWER_CTIME }, diff --git a/tar/read.c b/tar/read.c index a50c7747d87a..639428f54f36 100644 --- a/tar/read.c +++ b/tar/read.c @@ -58,6 +58,7 @@ #include "bsdtar.h" #include "lafe_err.h" +#include "lafe_setmode.h" struct progress_data { struct bsdtar *bsdtar; @@ -127,8 +128,8 @@ progress_func(void *cookie) archive_file_count(a), (uintmax_t)uncomp); } if (entry != NULL) { - safe_fprintf(stderr, "Current: %s", - archive_entry_pathname(entry)); + fputs("Current: ", stderr); + safe_fputs(archive_entry_pathname(entry), stderr); fprintf(stderr, " (%jd bytes)\n", (intmax_t)archive_entry_size(entry)); } @@ -278,6 +279,12 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); + if (bsdtar->file_mode) { + mode_t m = archive_entry_mode(entry); + m = lafe_getmode(bsdtar->file_mode, m); + archive_entry_set_mode(entry, m); + } + /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more @@ -307,8 +314,7 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) - safe_fprintf(out, "%s", - archive_entry_pathname(entry)); + safe_fputs(archive_entry_pathname(entry), out); else list_item_verbose(bsdtar, out, entry); fflush(out); @@ -342,14 +348,15 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) if (bsdtar->verbose > 1) { /* GNU tar uses -tv format with -xvv */ - safe_fprintf(stderr, "x "); + fputs("x ", stderr); list_item_verbose(bsdtar, stderr, entry); fflush(stderr); } else if (bsdtar->verbose > 0) { /* Format follows SUSv2, including the * deferred '\n'. */ - safe_fprintf(stderr, "x %s", - archive_entry_pathname(entry)); + fputs("x ", stderr); + safe_fputs(archive_entry_pathname(entry), + stderr); fflush(stderr); } @@ -361,9 +368,12 @@ read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) - safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); - safe_fprintf(stderr, ": %s: %s", - archive_error_string(a), + safe_fputs( + archive_entry_pathname(entry), + stderr); + fputs(": ", stderr); + safe_fputs(archive_error_string(a), stderr); + fprintf(stderr, ": %s", strerror(archive_errno(a))); if (!bsdtar->verbose) fprintf(stderr, "\n"); diff --git a/tar/subst.c b/tar/subst.c index 62191a6304a0..7ceedd1ef2bd 100644 --- a/tar/subst.c +++ b/tar/subst.c @@ -49,13 +49,29 @@ init_substitution(struct bsdtar *bsdtar) subst->first_rule = subst->last_rule = NULL; } +static int +is_escaped(const char *start, const char *delim) +{ + int escaped = 0; + + if (*delim == '\\') + return (0); + + while (delim > start && delim[-1] == '\\') { + delim--; + escaped = !escaped; + } + + return (escaped); +} + void add_substitution(struct bsdtar *bsdtar, const char *rule_text) { struct subst_rule *rule; struct substitution *subst; - const char *end_pattern, *start_subst; - char *pattern; + const char *end_pattern, *q, *start_subst; + char *pattern, *p; int r; if ((subst = bsdtar->substitution) == NULL) { @@ -98,17 +114,29 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) free(pattern); start_subst = end_pattern + 1; - end_pattern = strchr(start_subst, delim); - if (end_pattern == NULL) - lafe_errc(1, 0, "Invalid replacement string \"%s\": " - "missing closing delimiter '%c' after replacement", - rule_text, delim); + do { + end_pattern = strchr(end_pattern + 1, delim); + if (end_pattern == NULL) + lafe_errc(1, 0, "Invalid replacement string \"%s\": " + "missing closing delimiter '%c' after replacement", + rule_text, delim); + } while (is_escaped(start_subst, end_pattern)); rule->result = malloc(end_pattern - start_subst + 1); if (rule->result == NULL) lafe_errc(1, errno, "Out of memory"); - memcpy(rule->result, start_subst, end_pattern - start_subst); - rule->result[end_pattern - start_subst] = '\0'; + p = rule->result; + q = start_subst; + while (q < end_pattern) { + if (*q == '\\' && delim != '\\') + q++; + if (q == end_pattern) + lafe_errc(1, 0, "Invalid replacement string \"%s\": " + "missing closing delimiter '%c' after replacement", + rule_text, delim); + *p++ = *q++; + } + *p = '\0'; /* Defaults */ rule->global = 0; /* Don't do multiple replacements. */ diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index 5edb07074b92..8be89ffcc6e4 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -27,6 +27,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_extract_tar_lzo.c test_extract_tar_xz.c test_extract_tar_zstd.c + test_edit_pathname_hardlink.c test_format_newc.c test_help.c test_leading_slash.c @@ -59,6 +60,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_lz4.c test_option_lzma.c test_option_lzop.c + test_option_mode.c test_option_mtime.c test_option_n.c test_option_newer_than.c diff --git a/tar/test/test_basic.c b/tar/test/test_basic.c index a74ed0a13591..3593cee7a601 100644 --- a/tar/test/test_basic.c +++ b/tar/test/test_basic.c @@ -76,7 +76,8 @@ run_tar(const char *target, const char *pack_options, assertMakeDir(target, 0775); /* Use the tar program to create an archive. */ - r = systemf("%s cf - %s %s >%s/archive 2>%s/pack.err", testprog, pack_options, flist, target, target); + r = systemf("%s cf - --uid=0 --gid=0 %s %s >%s/archive 2>%s/pack.err", + testprog, pack_options, flist, target, target); failure("Error invoking %s cf -%s", testprog, pack_options); assertEqualInt(r, 0); diff --git a/tar/test/test_copy.c b/tar/test/test_copy.c index d1263dc7928f..283c984395ed 100644 --- a/tar/test/test_copy.c +++ b/tar/test/test_copy.c @@ -323,8 +323,8 @@ copy_ustar(void) /* * Use the tar program to create an archive. */ - r = systemf("%s cf archive --format=ustar -C ../original f d l m s >pack.out 2>pack.err", - testprog); + r = systemf("%s cf archive --uid=0 --gid=0 --format=ustar " + "-C ../original f d l m s >pack.out 2>pack.err", testprog); failure("Error invoking \"%s cf archive --format=ustar\"", testprog); assertEqualInt(r, 0); @@ -354,7 +354,7 @@ DEFINE_TEST(test_copy) create_tree(); /* Create sample files in "original" dir. */ /* Test simple "tar -c | tar -x" pipeline copy. */ - copy_basic("", "default"); + copy_basic("--uid=0 --gid=0", "default"); /* Same, but constrain to ustar format. */ copy_ustar(); diff --git a/tar/test/test_edit_pathname_hardlink.c b/tar/test/test_edit_pathname_hardlink.c new file mode 100644 index 000000000000..5f9bac70d8e8 --- /dev/null +++ b/tar/test/test_edit_pathname_hardlink.c @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 lilu + * All rights reserved. + */ +#include "test.h" + +/* + * An entry whose hardlink target reduces to the empty string after + * stripping the leading '/' is skipped by edit_pathname(). This used to + * happen silently: the entry vanished from the output and bsdtar exited 0, + * so a caller could not detect the incomplete result. The entry must now be + * named in a diagnostic and must cause a non-zero exit status. + */ +DEFINE_TEST(test_edit_pathname_hardlink) +{ + const char *reffile = "test_edit_pathname_hardlink.tar.Z"; + char *p; + size_t s; + const char *expected_errmsg = + "Skipping ./hardlink: hardlink target becomes empty " + "after removing leading '/'"; + + extract_reference_file(reffile); + + /* Transforming the archive must report the dropped entry and fail. */ + assert(0 != systemf("%s -cf out.tar @%s >test.out 2>test.err", + testprog, reffile)); + + /* out.tar must list ./a.txt and must not list the dropped ./hardlink. */ + assertEqualInt(0, systemf("%s -tf out.tar >list.out 2>list.err", + testprog)); + p = slurpfile(&s, "list.out"); + assert(p != NULL); + assert(strstr(p, "a.txt") != NULL); + assert(strstr(p, "hardlink") == NULL); + free(p); + + /* The diagnostic must name the entry and the reason. */ + if (assertFileExists("test.err")) { + p = slurpfile(&s, "test.err"); + assert(strstr(p, expected_errmsg) != NULL); + free(p); + } +} diff --git a/tar/test/test_edit_pathname_hardlink.tar.Z.uu b/tar/test/test_edit_pathname_hardlink.tar.Z.uu new file mode 100644 index 000000000000..9988015d048a --- /dev/null +++ b/tar/test/test_edit_pathname_hardlink.tar.Z.uu @@ -0,0 +1,8 @@ +begin 644 test_edit_pathname_hardlink.tar.Z +M'YV0+EZ$<4$'#QT "!,J7,BPH<.'$"-*G(@0AD4;-&B &"Q(XR-'BV"#.E1 +M(\F0(&#$@"'C1@T *2G*G$FSIDT =>;0"2.'(XR;0(/Z]#B2J-"C2),J72HS +MC%,%3*-*G4JUJM6K6+-JWB;]V>3'GR +MK/'D"-+GDRYLN7+F#-KWLRY +!*P + +end diff --git a/tar/test/test_option_P_upper.c b/tar/test/test_option_P_upper.c index 69810a304318..2c86ea8b5f43 100644 --- a/tar/test/test_option_P_upper.c +++ b/tar/test/test_option_P_upper.c @@ -20,7 +20,7 @@ DEFINE_TEST(test_extract_tar_absolute_paths) int r; // Create an absolute path for a test file inside testworkdir. - char *entry_suffix = "/tar-noabs"; + char *entry_suffix = "/work/tar-noabs"; size_t entry_suffix_length = strlen(entry_suffix); size_t testworkdir_length = strlen(testworkdir); size_t temp_absolute_file_name_length = testworkdir_length + entry_suffix_length; @@ -28,6 +28,10 @@ DEFINE_TEST(test_extract_tar_absolute_paths) assertEqualInt(snprintf(temp_absolute_file_name, temp_absolute_file_name_length + 1, "%s%s", testworkdir, entry_suffix), temp_absolute_file_name_length); + // Use a directory owned by the unprivileged test user. + assertMakeDir("work", 0755); + assertChdir("work"); + #if defined(_WIN32) && !defined(__CYGWIN__) // I'm unsure how to specify paths with spaces for the test invocation on windows. // Adding quotes doesn't seem to work. We should find a way to escape these paths, @@ -55,21 +59,26 @@ DEFINE_TEST(test_extract_tar_absolute_paths) UNLINK(temp_absolute_file_name); // Extracting the archive without -P / --absolute-paths should strip leading drive letter or slash - r = systemf("%s -xf test.tar 2>test.err", testprog); + // inside its own extraction directory. + assertMakeDir("without-P", 0755); + assertChdir("without-P"); + r = systemf("%s -xf ../test.tar 2>test.err", testprog); assertEqualInt(r, 0); assertFileNotExists(temp_absolute_file_name); // Check that the mangled path exists. #if defined(_WIN32) && !defined(__CYGWIN__) assertFileExists(temp_absolute_file_name + 3); // Skip the drive letter, colon and slash. - UNLINK(temp_absolute_file_name + 3); #else assertFileExists(temp_absolute_file_name + 1); // Skip the slash. - UNLINK(temp_absolute_file_name + 1); #endif - // Extracting the archive with -P / --absolute-paths should create the file. - r = systemf("%s --absolute-paths -xf test.tar", testprog); + // Extracting the archive with -P / --absolute-paths in a separate directory should + // create the file without creating a mangled path. + assertChdir(".."); + assertMakeDir("with-P", 0755); + assertChdir("with-P"); + r = systemf("%s --absolute-paths -xf ../test.tar", testprog); assertEqualInt(r, 0); assertFileExists(temp_absolute_file_name); diff --git a/tar/test/test_option_T_upper.c b/tar/test/test_option_T_upper.c index e3c5bc2d30cd..349ec056aa67 100644 --- a/tar/test/test_option_T_upper.c +++ b/tar/test/test_option_T_upper.c @@ -70,8 +70,8 @@ DEFINE_TEST(test_option_T_upper) /* Use -x -T to dearchive the files */ if (!assertMakeDir("test1", 0755)) return; - systemf("%s -x -f test1.tar -T filelist -C test1" - " > test1b.out 2> test1b.err", testprog); + assertEqualInt(0, systemf("%s -x -f test1.tar -T filelist -C test1" + " > test1b.out 2> test1b.err", testprog)); assertEmptyFile("test1b.out"); assertEmptyFile("test1b.err"); @@ -88,15 +88,15 @@ DEFINE_TEST(test_option_T_upper) } /* Use -r -T to add more files to the archive. */ - systemf("%s -r -f test1.tar --null -T filelist2 > test2.out 2> test2.err", - testprog); + assertEqualInt(0, systemf("%s -r -f test1.tar --null -T filelist2 > test2.out 2> test2.err", + testprog)); assertEmptyFile("test2.out"); assertEmptyFile("test2.err"); /* Use -x without -T to dearchive the files (ensure -r worked) */ if (!assertMakeDir("test3", 0755)) return; - systemf("%s -x -f test1.tar -C test3" - " > test3.out 2> test3.err", testprog); + assertEqualInt(0, systemf("%s -x -f test1.tar -C test3" + " > test3.out 2> test3.err", testprog)); assertEmptyFile("test3.out"); assertEmptyFile("test3.err"); /* Verify the files were extracted.*/ @@ -113,8 +113,8 @@ DEFINE_TEST(test_option_T_upper) /* Use -x -T to dearchive the files (verify -x -T together) */ if (!assertMakeDir("test2", 0755)) return; - systemf("%s -x -f test1.tar -T filelist -C test2" - " > test2b.out 2> test2b.err", testprog); + assertEqualInt(0, systemf("%s -x -f test1.tar -T filelist -C test2" + " > test2b.out 2> test2b.err", testprog)); assertEmptyFile("test2b.out"); assertEmptyFile("test2b.err"); /* Verify the files were extracted.*/ diff --git a/tar/test/test_option_b.c b/tar/test/test_option_b.c index 2b2225de6531..cc9feb4b4105 100644 --- a/tar/test/test_option_b.c +++ b/tar/test/test_option_b.c @@ -6,7 +6,7 @@ */ #include "test.h" -#define USTAR_OPT " --format=ustar" +#define USTAR_OPT " --uid=0 --gid=0 --format=ustar" DEFINE_TEST(test_option_b) { diff --git a/tar/test/test_option_gid_gname.c b/tar/test/test_option_gid_gname.c index 38220772dc12..2a51d0ee4051 100644 --- a/tar/test/test_option_gid_gname.c +++ b/tar/test/test_option_gid_gname.c @@ -14,11 +14,11 @@ DEFINE_TEST(test_option_gid_gname) assertUmask(0); assertMakeFile("file", 0644, "1234567890"); - /* Create archive with no special options. */ + /* Create archive with simple uid/gid options. */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", - testprog)); + systemf("%s cf archive1 --uid=0 --gid=0 --format=ustar file " + ">stdout1.txt 2>stderr1.txt", testprog)); assertEmptyFile("stdout1.txt"); assertEmptyFile("stderr1.txt"); reference = slurpfile(&s, "archive1"); @@ -26,8 +26,9 @@ DEFINE_TEST(test_option_gid_gname) /* Again with both --gid and --gname */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive2 --gid=17 --gname=foofoofoo --format=ustar file >stdout2.txt 2>stderr2.txt", - testprog)); + systemf("%s cf archive2 --uid=17 --gid=17 " + "--gname=foofoofoo --format=ustar file >stdout2.txt " + "2>stderr2.txt", testprog)); assertEmptyFile("stdout2.txt"); assertEmptyFile("stderr2.txt"); data = slurpfile(&s, "archive2"); @@ -39,8 +40,8 @@ DEFINE_TEST(test_option_gid_gname) /* Again with just --gname */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive4 --gname=foofoofoo --format=ustar file >stdout4.txt 2>stderr4.txt", - testprog)); + systemf("%s cf archive4 --uid=0 --gid=0 --gname=foofoofoo " + "--format=ustar file >stdout4.txt 2>stderr4.txt", testprog)); assertEmptyFile("stdout4.txt"); assertEmptyFile("stderr4.txt"); data = slurpfile(&s, "archive4"); @@ -53,8 +54,8 @@ DEFINE_TEST(test_option_gid_gname) /* Again with --gid and force gname to empty. */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive3 --gid=17 --gname= --format=ustar file >stdout3.txt 2>stderr3.txt", - testprog)); + systemf("%s cf archive3 --uid=17 --gid=17 --gname= " + "--format=ustar file >stdout3.txt 2>stderr3.txt", testprog)); assertEmptyFile("stdout3.txt"); assertEmptyFile("stderr3.txt"); data = slurpfile(&s, "archive3"); @@ -63,6 +64,20 @@ DEFINE_TEST(test_option_gid_gname) assertEqualMem(data + 297, "\0", 1); free(data); + /* Again with --gid and --gname, copying entries from archive1 */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive5 --uid=17 --gid=17 --gname=foofoofoo " + "--format=ustar @archive1 >stdout5.txt 2>stderr5.txt", + testprog)); + assertEmptyFile("stdout5.txt"); + assertEmptyFile("stderr5.txt"); + data = slurpfile(&s, "archive5"); + /* Should force gid and gname fields in ustar header. */ + assertEqualMem(data + 116, "000021 \0", 8); + assertEqualMem(data + 297, "foofoofoo\0", 10); + free(data); + /* TODO: It would be nice to verify that --gid= by itself * will look up the associated gname and use that, but * that requires some system-specific code. */ diff --git a/tar/test/test_option_group.c b/tar/test/test_option_group.c index 8d3cc651362a..aa4b33f7a808 100644 --- a/tar/test/test_option_group.c +++ b/tar/test/test_option_group.c @@ -15,11 +15,11 @@ DEFINE_TEST(test_option_group) assertUmask(0); assertMakeFile("file", 0644, "1234567890"); - /* Create archive with no special options. */ + /* Create archive with simple uid/gid options. */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", - testprog)); + systemf("%s cf archive1 --uid=0 --gid=0 --format=ustar file " + ">stdout1.txt 2>stderr1.txt", testprog)); assertEmptyFile("stdout1.txt"); assertEmptyFile("stderr1.txt"); reference = slurpfile(&s, "archive1"); @@ -27,8 +27,8 @@ DEFINE_TEST(test_option_group) /* Create archive with --group (numeric) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive2 --group=17 --format=ustar file >stdout2.txt 2>stderr2.txt", - testprog)); + systemf("%s cf archive2 --uid=0 --gid=0 --group=17 --format=ustar " + "file >stdout2.txt 2>stderr2.txt", testprog)); assertEmptyFile("stdout2.txt"); assertEmptyFile("stderr2.txt"); data = slurpfile(&s, "archive2"); @@ -40,8 +40,8 @@ DEFINE_TEST(test_option_group) /* Again with --group (name) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive3 --group=foofoofoo --format=ustar file >stdout3.txt 2>stderr3.txt", - testprog)); + systemf("%s cf archive3 --uid=0 --gid=0 --group=foofoofoo " + "--format=ustar file >stdout3.txt 2>stderr3.txt", testprog)); assertEmptyFile("stdout3.txt"); assertEmptyFile("stderr3.txt"); data = slurpfile(&s, "archive3"); @@ -53,8 +53,8 @@ DEFINE_TEST(test_option_group) /* Again with --group (name:id) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive4 --group=foofoofoo:17 --format=ustar file >stdout4.txt 2>stderr4.txt", - testprog)); + systemf("%s cf archive4 --uid=0 --gid=0 --group=foofoofoo:17 " + "--format=ustar file >stdout4.txt 2>stderr4.txt", testprog)); assertEmptyFile("stdout4.txt"); assertEmptyFile("stderr4.txt"); data = slurpfile(&s, "archive4"); diff --git a/tar/test/test_option_ignore_zeros.c b/tar/test/test_option_ignore_zeros.c index 453e8a0907e6..a50e3e99b1fb 100644 --- a/tar/test/test_option_ignore_zeros.c +++ b/tar/test/test_option_ignore_zeros.c @@ -9,7 +9,9 @@ static int make_files(void) { - int ret; + char *pa, *pb, *pc; + size_t sa, sb; + int ret = 0; assertMakeDir("in", 0755); assertMakeDir("out", 0755); @@ -18,14 +20,28 @@ make_files(void) assertMakeFile("in/c", 0644, "c"); assertEqualInt(0, systemf("%s cf a.tar -C in a", testprog)); assertEqualInt(0, systemf("%s cf b.tar -C in b", testprog)); - /* An archive formed with cat, and readable with --ignore-zeros. */ - ret = systemf("cat a.tar b.tar > ab-cat.tar"); - if (ret != 0) { - skipping("This test requires a `cat` program"); - return (ret); + /* A concatenated archive, readable with --ignore-zeros. */ + + assertEqualInt((pa = slurpfile(&sa, "a.tar")) == NULL, 0); + assertEqualInt((pb = slurpfile(&sb, "b.tar")) == NULL, 0); + assertEqualInt((pc = malloc(sa + sb)) == NULL, 0); + + if (pa == NULL || pb == NULL || pc == NULL) { + skipping("failed to combine files"); + ret = 1; + goto done; } - return (0); + memcpy(pc, pa, sa); + memcpy(pc + sa, pb, sb); + assertMakeBinFile("ab-cat.tar", 0644, sa + sb, pc); + +done: + free(pa); + free(pb); + free(pc); + + return (ret); } DEFINE_TEST(test_option_ignore_zeros_mode_t) @@ -70,13 +86,6 @@ DEFINE_TEST(test_option_ignore_zeros_mode_x) DEFINE_TEST(test_option_ignore_zeros_mode_c) { -#if defined(_WIN32) && !defined(__CYGWIN__) - // The first command run by systemf below prints this to stderr: - // bsdtar.exe: a: Can't translate uname '(null)' to UTF-8 - // bsdtar.exe: b: Can't translate uname '(null)' to UTF-8 - skipping("TODO: figure out why this test fails on github workflows with MSVC"); - return; -#else if (make_files()) return; @@ -96,7 +105,6 @@ DEFINE_TEST(test_option_ignore_zeros_mode_c) assertEqualFile("out/a", "in/a"); assertEqualFile("out/b", "in/b"); assertEqualFile("out/c", "in/c"); -#endif } static void diff --git a/tar/test/test_option_mode.c b/tar/test/test_option_mode.c new file mode 100644 index 000000000000..285ddbc677b2 --- /dev/null +++ b/tar/test/test_option_mode.c @@ -0,0 +1,120 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Dan Nelson + * All rights reserved. + */ +#include "test.h" + +/* Compare the mode column of tar's list output with an array of expected + * values. + */ +static void check_modes(const char* testname, const char *buf, const char *lines[]) +{ + int line = 1; + while (*buf && *lines != NULL) + { + failure("On line %d of test %s", line, testname); + assertEqualMem(buf, *lines, strlen(*lines)); + while (*buf && !(*buf == '\r'|| *buf == '\n')) + buf++; + while (*buf == '\r' || *buf == '\n') + buf++; + lines++; + line++; + } + if (*lines && ! *buf) + { + failure("On line %d of test %s: unexpected end of file", line, testname); + assert(0); + } +} + +DEFINE_TEST(test_option_mode) +{ + int rv; + char *p; + + const char *test3mtree = + "#mtree\n" + "in/all mode=777 type=file\n" + "in/minimal mode=500 type=file\n"; + + const char *test3aExpected[] = { + "-rw-r--r--", + "-rw-r--r--", + NULL + }; + + const char *test3bExpected[] = { + "-rw-rwxr-x", + "-rw---x---", + NULL + }; + + assertMakeDir("in", 0755); + + /* Test invalid modes */ + rv = systemf("%s --mode 8 --format=pax -cf archive.tar in " + "> 1.out 2> 1.err", testprog); + assert(rv != 0); + + rv = systemf("%s --mode a+a --format=pax -cf archive.tar in " + "> 2.out 2> 2.err", testprog); + assert(rv != 0); + + /* Create some files with different modes */ + assertMakeFile("in/all", 0777, ""); + assertMakeFile("in/minimal", 0500, ""); + + /* Archive and override using an absolute mode */ + assertEqualInt(0, + systemf("%s --mode 644 --uid=0 --gid=0 --format=ustar " + "-cf archive1.tar in/all in/minimal", testprog)); + + /* Verify the modes */ + p = slurpfile(NULL, "archive1.tar"); + assertEqualString(p + 100,"000644 "); + assertEqualString(p + 612,"000644 "); + free(p); + +/* Skip relative symbolic mode checks on Windows; on-disk files always have + * mode 644. + */ +#if !defined(_WIN32) || defined(__CYGWIN__) + /* Archive and override using a symbolic mode */ + assertEqualInt(0, + systemf("%s --mode u+rw-x,g+X,o-w --uid=0 --gid=0 " + "--format=ustar -cf archive2.tar in/all in/minimal", + testprog)); + + /* Verify the modes */ + p = slurpfile(NULL, "archive2.tar"); + assertEqualString(p + 100,"000675 "); + assertEqualString(p + 612,"000610 "); + free(p); +#endif + + /* Create an archive file with custom modes. Using mtree here is + * simpler than building a tar file from files, and also works on + * Windows. + */ + assertMakeFile("archive3.mtree", 0644, test3mtree); + + /* list, overriding with absolute mode */ + assertEqualInt(0, + systemf("%s --mode 644 -tvf archive3.mtree > 3a.out", + testprog)); + p = slurpfile(NULL, "3a.out"); + check_modes("3a", p, test3aExpected); + free(p); + + /* list, overriding with relative mode */ + assertEqualInt(0, + systemf("%s --mode u+rw-x,g+X,o-w -tvf archive3.mtree > 3b.out", + testprog)); + p = slurpfile(NULL, "3b.out"); + check_modes("3b", p, test3bExpected); + free(p); + +} diff --git a/tar/test/test_option_owner.c b/tar/test/test_option_owner.c index ed212031058d..fb3f5e95a163 100644 --- a/tar/test/test_option_owner.c +++ b/tar/test/test_option_owner.c @@ -15,11 +15,11 @@ DEFINE_TEST(test_option_owner) assertUmask(0); assertMakeFile("file", 0644, "1234567890"); - /* Create archive with no special options. */ + /* Create archive with simple uid/gid options. */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", - testprog)); + systemf("%s cf archive1 --uid=0 --gid=0 --format=ustar file " + ">stdout1.txt 2>stderr1.txt", testprog)); assertEmptyFile("stdout1.txt"); assertEmptyFile("stderr1.txt"); reference = slurpfile(&s, "archive1"); @@ -27,8 +27,8 @@ DEFINE_TEST(test_option_owner) /* Create archive with --owner (numeric) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive2 --owner=65123 --format=ustar file >stdout2.txt 2>stderr2.txt", - testprog)); + systemf("%s cf archive2 --owner=65123 --gid=65123 --format=ustar " + "file >stdout2.txt 2>stderr2.txt", testprog)); assertEmptyFile("stdout2.txt"); assertEmptyFile("stderr2.txt"); data = slurpfile(&s, "archive2"); @@ -40,8 +40,8 @@ DEFINE_TEST(test_option_owner) /* Again with just --owner (name) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive3 --owner=foofoofoo --format=ustar file >stdout3.txt 2>stderr3.txt", - testprog)); + systemf("%s cf archive3 --uid=0 --owner=foofoofoo --gid=0 " + "--format=ustar file >stdout3.txt 2>stderr3.txt", testprog)); assertEmptyFile("stdout3.txt"); assertEmptyFile("stderr3.txt"); data = slurpfile(&s, "archive3"); @@ -53,8 +53,8 @@ DEFINE_TEST(test_option_owner) /* Again with just --owner (name:id) */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive4 --owner=foofoofoo:65123 --format=ustar file >stdout4.txt 2>stderr4.txt", - testprog)); + systemf("%s cf archive4 --owner=foofoofoo:65123 --gid=0 " + "--format=ustar file >stdout4.txt 2>stderr4.txt", testprog)); assertEmptyFile("stdout4.txt"); assertEmptyFile("stderr4.txt"); data = slurpfile(&s, "archive4"); diff --git a/tar/test/test_option_passphrase.c b/tar/test/test_option_passphrase.c index 968a33c00bdb..452ff0a74701 100644 --- a/tar/test/test_option_passphrase.c +++ b/tar/test/test_option_passphrase.c @@ -21,4 +21,23 @@ DEFINE_TEST(test_option_passphrase) assertTextFileContents("contents of file2.\n", "file2"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); + + /* The passphrase must also be applied to archives read via @archive. */ + failure("--passphrase option is broken with @archive"); + assertEqualInt(0, systemf( + "%s --passphrase pass1 -cf append.tar @%s " + ">append-create.out 2>append-create.err", + testprog, reffile)); + assertEmptyFile("append-create.out"); + assertEmptyFile("append-create.err"); + + assertMakeDir("append", 0755); + assertEqualInt(0, systemf( + "%s -xf append.tar -C append " + ">append-extract.out 2>append-extract.err", + testprog)); + assertTextFileContents("contents of file1.\n", "append/file1"); + assertTextFileContents("contents of file2.\n", "append/file2"); + assertEmptyFile("append-extract.out"); + assertEmptyFile("append-extract.err"); } diff --git a/tar/test/test_option_r.c b/tar/test/test_option_r.c index 622512c95f71..3def393e9444 100644 --- a/tar/test/test_option_r.c +++ b/tar/test/test_option_r.c @@ -14,7 +14,7 @@ DEFINE_TEST(test_option_r) char *buff; char *p0, *p1; size_t buff_size = 35000; - size_t s, buff_size_rounded; + size_t s, buff_size_rounded, filtered_size; int r, i; buff = NULL; @@ -23,7 +23,8 @@ DEFINE_TEST(test_option_r) /* Create an archive with one file. */ assertMakeFile("f1", 0644, "abc"); - r = systemf("%s cf archive.tar --format=ustar f1 >step1.out 2>step1.err", testprog); + r = systemf("%s cf archive.tar --uid=0 --gid=0 --format=ustar f1 " + ">step1.out 2>step1.err", testprog); failure("Error invoking %s cf archive.tar f1", testprog); assertEqualInt(r, 0); assertEmptyFile("step1.out"); @@ -50,7 +51,8 @@ DEFINE_TEST(test_option_r) buff[i] = "abcdefghijklmnopqrstuvwxyz"[rand() % 26]; buff[buff_size - 1] = '\0'; assertMakeFile("f1", 0644, buff); - r = systemf("%s rf archive.tar --format=ustar f1 >step2.out 2>step2.err", testprog); + r = systemf("%s rf archive.tar --uid=0 --gid=0 --format=ustar f1 " + ">step2.out 2>step2.err", testprog); failure("Error invoking %s rf archive.tar f1", testprog); assertEqualInt(r, 0); assertEmptyFile("step2.out"); @@ -76,7 +78,8 @@ DEFINE_TEST(test_option_r) /* Update the archive by adding a different file. */ assertMakeFile("f2", 0644, "f2"); - r = systemf("%s rf archive.tar --format=ustar f2 >step3.out 2>step3.err", testprog); + r = systemf("%s rf archive.tar --uid=0 --gid=0 --format=ustar f2 " + ">step3.out 2>step3.err", testprog); failure("Error invoking %s rf archive.tar f2", testprog); assertEqualInt(r, 0); assertEmptyFile("step3.out"); @@ -108,6 +111,31 @@ DEFINE_TEST(test_option_r) /* Verify that the second copy of f1 overwrote the first. */ assertFileContents(buff, (int)strlen(buff), "f1"); + + /* Appending to an empty filtered archive must fail. */ + free(p0); + p0 = NULL; + assertMakeFile("empty", 0644, ""); + r = systemf("%s cf empty.tar.uu --uid=0 --gid=0 --format=ustar " + "--uuencode -T empty >empty-create.out 2>empty-create.err", + testprog); + assertEqualInt(r, 0); + assertEmptyFile("empty-create.out"); + assertEmptyFile("empty-create.err"); + p0 = slurpfile(&filtered_size, "empty.tar.uu"); + if (!assert(p0 != NULL)) + goto done; + r = systemf("%s rf empty.tar.uu f2 " + ">empty-append.out 2>empty-append.err", testprog); + assert(r != 0); + assertEmptyFile("empty-append.out"); + assertNonEmptyFile("empty-append.err"); + p1 = slurpfile(&s, "empty.tar.uu"); + if (!assert(p1 != NULL)) + goto done; + assertEqualInt(filtered_size, s); + assertEqualMem(p0, p1, s); + free(p1); done: free(buff); free(p0); diff --git a/tar/test/test_option_s.c b/tar/test/test_option_s.c index 16e871087717..4e640945ff74 100644 --- a/tar/test/test_option_s.c +++ b/tar/test/test_option_s.c @@ -285,4 +285,16 @@ DEFINE_TEST(test_option_s) testprog); assertFileContents("foo", 3, "test14/in/d1/fzo"); assertFileContents("bar", 3, "test14/in/d1/baz"); + + /* + * Test 15: Directory substitution with escaped delimiter + */ + assertMakeDir("test15", 0755); + systemf("%s -cf test15.tar in/d1/foo", testprog); +#if defined(_WIN32) && !defined(__CYGWIN__) + systemf("%s -xf test15.tar -s /d1/d2\\/d3/ -C test15", testprog); +#else + systemf("%s -xf test15.tar -s /d1/d2\\\\/d3/ -C test15", testprog); +#endif + assertFileContents("foo", 3, "test15/in/d2/d3/foo"); } diff --git a/tar/test/test_option_uid_uname.c b/tar/test/test_option_uid_uname.c index a6f3c5bb979b..f9f7016e8cb2 100644 --- a/tar/test/test_option_uid_uname.c +++ b/tar/test/test_option_uid_uname.c @@ -14,11 +14,11 @@ DEFINE_TEST(test_option_uid_uname) assertUmask(0); assertMakeFile("file", 0644, "1234567890"); - /* Create archive with no special options. */ + /* Create archive with simple uid/gid options. */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive1 --format=ustar file >stdout1.txt 2>stderr1.txt", - testprog)); + systemf("%s cf archive1 --uid=0 --gid=0 --format=ustar file " + ">stdout1.txt 2>stderr1.txt", testprog)); assertEmptyFile("stdout1.txt"); assertEmptyFile("stderr1.txt"); reference = slurpfile(&s, "archive1"); @@ -26,7 +26,8 @@ DEFINE_TEST(test_option_uid_uname) /* Again with both --uid and --uname */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive2 --uid=65123 --uname=foofoofoo --format=ustar file >stdout2.txt 2>stderr2.txt", + systemf("%s cf archive2 --uid=65123 --uname=foofoofoo " + "--gid=65123 --format=ustar file >stdout2.txt 2>stderr2.txt", testprog)); assertEmptyFile("stdout2.txt"); assertEmptyFile("stderr2.txt"); @@ -39,8 +40,8 @@ DEFINE_TEST(test_option_uid_uname) /* Again with just --uid */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive3 --uid=65123 --format=ustar file >stdout3.txt 2>stderr3.txt", - testprog)); + systemf("%s cf archive3 --uid=65123 --gid=65123 --format=ustar " + "file >stdout3.txt 2>stderr3.txt", testprog)); assertEmptyFile("stdout3.txt"); assertEmptyFile("stderr3.txt"); data = slurpfile(&s, "archive3"); @@ -52,8 +53,8 @@ DEFINE_TEST(test_option_uid_uname) /* Again with just --uname */ failure("Error invoking %s c", testprog); assertEqualInt(0, - systemf("%s cf archive4 --uname=foofoofoo --format=ustar file >stdout4.txt 2>stderr4.txt", - testprog)); + systemf("%s cf archive4 --uid=0 --uname=foofoofoo --gid=0 " + "--format=ustar file >stdout4.txt 2>stderr4.txt", testprog)); assertEmptyFile("stdout4.txt"); assertEmptyFile("stderr4.txt"); data = slurpfile(&s, "archive4"); @@ -62,5 +63,19 @@ DEFINE_TEST(test_option_uid_uname) assertEqualMem(data + 265, "foofoofoo\0", 10); free(data); + /* Again with --uid and --uname, copying entries from archive1 */ + failure("Error invoking %s c", testprog); + assertEqualInt(0, + systemf("%s cf archive5 --uid=65123 --uname=foofoofoo " + "--gid=65123 --format=ustar @archive1 >stdout5.txt " + "2>stderr5.txt", testprog)); + assertEmptyFile("stdout5.txt"); + assertEmptyFile("stderr5.txt"); + data = slurpfile(&s, "archive5"); + /* Should force uid and uname fields in ustar header. */ + assertEqualMem(data + 108, "177143 \0", 8); + assertEqualMem(data + 265, "foofoofoo\0", 10); + free(data); + free(reference); } diff --git a/tar/util.c b/tar/util.c index 730a9adb2ea2..42b08a6e6da9 100644 --- a/tar/util.c +++ b/tar/util.c @@ -41,6 +41,7 @@ #endif #include "bsdtar.h" +#include "lafe_explicit_bzero.h" #include "lafe_err.h" #include "passphrase.h" @@ -59,92 +60,40 @@ static const char *strip_components(const char *path, int elements); * #endif */ -/* - * Print a string, taking care with any non-printable characters. - * - * Note that we use a stack-allocated buffer to receive the formatted - * string if we can. This is partly performance (avoiding a call to - * malloc()), partly out of expedience (we have to call vsnprintf() - * before malloc() anyway to find out how big a buffer we need; we may - * as well point that first call at a small local buffer in case it - * works). - */ +/* Print a string, taking care of any non-printable characters. */ void -safe_fprintf(FILE * restrict f, const char * restrict fmt, ...) +safe_fputs(const char * restrict s, FILE * restrict f) { - char fmtbuff_stack[256]; /* Place to format the printf() string. */ char outbuff[256]; /* Buffer for outgoing characters. */ - char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ - char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ - size_t fmtbuff_length; - int length, n; - va_list ap; + int n; const char *p; - size_t i; + size_t i, length; wchar_t wc; char try_wc; - /* Use a stack-allocated buffer if we can, for speed and safety. */ - memset(fmtbuff_stack, '\0', sizeof(fmtbuff_stack)); - fmtbuff_heap = NULL; - fmtbuff_length = sizeof(fmtbuff_stack); - fmtbuff = fmtbuff_stack; - - /* Try formatting into the stack buffer. */ - va_start(ap, fmt); - length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); - va_end(ap); - - /* If vsnprintf will always fail, stop early. */ - if (length < 0 && errno == EOVERFLOW) - return; - - /* If the result was too large, allocate a buffer on the heap. */ - while (length < 0 || (size_t)length >= fmtbuff_length) { - if (length >= 0 && (size_t)length >= fmtbuff_length) - fmtbuff_length = (size_t)length + 1; - else if (fmtbuff_length < 8192) - fmtbuff_length *= 2; - else if (fmtbuff_length < 1000000) - fmtbuff_length += fmtbuff_length / 4; - else { - fmtbuff[fmtbuff_length - 1] = '\0'; - length = (int)strlen(fmtbuff); - break; - } - free(fmtbuff_heap); - fmtbuff_heap = malloc(fmtbuff_length); - - /* Reformat the result into the heap buffer if we can. */ - if (fmtbuff_heap != NULL) { - fmtbuff = fmtbuff_heap; - va_start(ap, fmt); - length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap); - va_end(ap); - } else { - /* Leave fmtbuff pointing to the truncated - * string in fmtbuff_stack. */ - fmtbuff_stack[sizeof(fmtbuff_stack) - 1] = '\0'; - fmtbuff = fmtbuff_stack; - length = (int)strlen(fmtbuff); - break; - } - } - /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit * more portable, so we use that here instead. */ if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */ /* mbtowc() should never fail in practice, but * handle the theoretical error anyway. */ - free(fmtbuff_heap); return; } + p = s; + + /* Directly print as many ASCII characters as possible. */ + if (mbtowc(NULL, NULL, 0) == 0) { + while (*p != '\0' && isprint((unsigned char)*p) && + (unsigned char)*p < 128 && *p != '\\') + p++; + fwrite(s, 1, p - s, f); + } + /* Write data, expanding unprintable characters. */ - p = fmtbuff; i = 0; try_wc = 1; + length = strlen(p); while (*p != '\0') { /* Convert to wide char, test if the wide @@ -170,16 +119,11 @@ safe_fprintf(FILE * restrict f, const char * restrict fmt, ...) /* If our output buffer is full, dump it and keep going. */ if (i > (sizeof(outbuff) - 128)) { - outbuff[i] = '\0'; - fprintf(f, "%s", outbuff); + fwrite(outbuff, 1, i, f); i = 0; } } - outbuff[i] = '\0'; - fprintf(f, "%s", outbuff); - - /* If we allocated a heap-based formatting buffer, free it now. */ - free(fmtbuff_heap); + fwrite(outbuff, 1, i, f); } /* @@ -551,8 +495,13 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); - if (*hardlinkname == '\0') + if (*hardlinkname == '\0') { + lafe_warnc(0, "Skipping %s: hardlink target " + "becomes empty after removing leading '/'", + original_name); + bsdtar->return_value = 1; return (1); + } } } else { /* Strip redundant leading '/' characters. */ @@ -646,7 +595,7 @@ void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { - memset(ppbuff, 0, PPBUFF_SIZE); + explicit_bzero(ppbuff, PPBUFF_SIZE); free(ppbuff); } } @@ -667,6 +616,7 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) size_t sw; const char *p; const char *fmt; + double age; time_t tim; static time_t now; struct tm *ltime; @@ -737,13 +687,14 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); -#define HALF_YEAR (time_t)365 * 86400 / 2 + age = difftime(tim, now); +#define HALF_YEAR (365.0 * 86400 / 2) #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif - if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) + if (age < -HALF_YEAR || age > HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; @@ -761,12 +712,16 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) if (!ltime || !sw) p = "-- -- ----"; fprintf(out, " %s ", p); - safe_fprintf(out, "%s", archive_entry_pathname(entry)); + safe_fputs(archive_entry_pathname(entry), out); /* Extra information for links. */ - if (archive_entry_hardlink(entry)) /* Hard link */ - safe_fprintf(out, " link to %s", - archive_entry_hardlink(entry)); - else if (archive_entry_symlink(entry)) /* Symbolic link */ - safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); + if (archive_entry_hardlink(entry)) { + /* Hard link */ + fputs(" link to ", out); + safe_fputs(archive_entry_hardlink(entry), out); + } else if (archive_entry_symlink(entry)) { + /* Symbolic link */ + fputs(" -> ", out); + safe_fputs(archive_entry_symlink(entry), out); + } } diff --git a/tar/write.c b/tar/write.c index 13ff4eac766e..0f79dc9747c5 100644 --- a/tar/write.c +++ b/tar/write.c @@ -59,25 +59,16 @@ #include "bsdtar.h" #include "lafe_err.h" +#include "lafe_setmode.h" #include "line_reader.h" #ifndef O_BINARY #define O_BINARY 0 #endif -struct archive_dir_entry { - struct archive_dir_entry *next; - time_t mtime_sec; - int mtime_nsec; - char *name; -}; - -struct archive_dir { - struct archive_dir_entry *head, *tail; -}; - static int append_archive(struct bsdtar *, struct archive *, struct archive *ina); +static void edit_owner(struct bsdtar *, struct archive_entry *); static int append_archive_filename(struct bsdtar *, struct archive *, const char *fname); static void archive_names_from_file(struct bsdtar *bsdtar, @@ -296,13 +287,12 @@ tar_mode_r(struct bsdtar *bsdtar) lafe_errc(1, archive_errno(a), "Can't read archive %s: %s", bsdtar->filename, archive_error_string(a)); + if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { + archive_read_free(a); + close(bsdtar->fd); + lafe_errc(1, 0, "Cannot append to compressed archive"); + } while (0 == archive_read_next_header(a, &entry)) { - if (archive_filter_code(a, 0) != ARCHIVE_FILTER_NONE) { - archive_read_free(a); - close(bsdtar->fd); - lafe_errc(1, 0, - "Cannot append to compressed archive"); - } /* Keep going until we hit end-of-archive */ format = archive_format(a); } @@ -360,11 +350,6 @@ tar_mode_u(struct bsdtar *bsdtar) struct archive *a; struct archive_entry *entry; int format; - struct archive_dir_entry *p; - struct archive_dir archive_dir; - - bsdtar->archive_dir = &archive_dir; - memset(&archive_dir, 0, sizeof(archive_dir)); format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; @@ -428,14 +413,6 @@ tar_mode_u(struct bsdtar *bsdtar) close(bsdtar->fd); bsdtar->fd = -1; - - while (bsdtar->archive_dir->head != NULL) { - p = bsdtar->archive_dir->head->next; - free(bsdtar->archive_dir->head->name); - free(bsdtar->archive_dir->head); - bsdtar->archive_dir->head = p; - } - bsdtar->archive_dir->tail = NULL; } @@ -672,15 +649,16 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, set_reader_options(bsdtar, ina); archive_read_set_options(ina, "mtree:checkfs"); if (bsdtar->passphrase != NULL) - rc = archive_read_add_passphrase(a, bsdtar->passphrase); + rc = archive_read_add_passphrase(ina, bsdtar->passphrase); else rc = archive_read_set_passphrase_callback(ina, bsdtar, &passphrase_callback); if (rc != ARCHIVE_OK) - lafe_errc(1, 0, "%s", archive_error_string(a)); + lafe_errc(1, 0, "%s", archive_error_string(ina)); if (archive_read_open_filename(ina, filename, bsdtar->bytes_per_block)) { lafe_warnc(0, "%s", archive_error_string(ina)); + archive_read_free(ina); bsdtar->return_value = 1; return (0); } @@ -697,6 +675,32 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, return (rc); } +/* + * Apply --uid, --gid, --uname and --gname options. + */ +static void +edit_owner(struct bsdtar *bsdtar, struct archive_entry *entry) +{ + if (bsdtar->uid >= 0) { + archive_entry_set_uid(entry, bsdtar->uid); + if (!bsdtar->uname) + archive_entry_set_uname(entry, + archive_read_disk_uname(bsdtar->diskreader, + bsdtar->uid)); + } + if (bsdtar->gid >= 0) { + archive_entry_set_gid(entry, bsdtar->gid); + if (!bsdtar->gname) + archive_entry_set_gname(entry, + archive_read_disk_gname(bsdtar->diskreader, + bsdtar->gid)); + } + if (bsdtar->uname) + archive_entry_set_uname(entry, bsdtar->uname); + if (bsdtar->gname) + archive_entry_set_gname(entry, bsdtar->gname); +} + static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { @@ -722,13 +726,15 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if ((bsdtar->flags & OPTFLAG_INTERACTIVE) && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; + edit_owner(bsdtar, in_entry); edit_mtime(bsdtar, in_entry); if (bsdtar->verbose > 1) { - safe_fprintf(stderr, "a "); + fputs("a ", stderr); list_item_verbose(bsdtar, stderr, in_entry); - } else if (bsdtar->verbose > 0) - safe_fprintf(stderr, "a %s", - archive_entry_pathname(in_entry)); + } else if (bsdtar->verbose > 0) { + fputs("a ", stderr); + safe_fputs(archive_entry_pathname(in_entry), stderr); + } if (need_report()) report_write(bsdtar, a, in_entry, 0); @@ -828,7 +834,8 @@ copy_file_data_block(struct bsdtar *bsdtar, struct archive *a, progress += bytes_written; } if (r < ARCHIVE_WARN) { - lafe_warnc(archive_errno(a), "%s", archive_error_string(a)); + lafe_warnc(archive_errno(in_a), "%s", + archive_error_string(in_a)); return (-1); } return (0); @@ -903,7 +910,6 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) bsdtar->return_value = 1; return; } - bsdtar->first_fs = -1; for (;;) { archive_entry_free(entry); @@ -923,24 +929,13 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) continue; } - if (bsdtar->uid >= 0) { - archive_entry_set_uid(entry, bsdtar->uid); - if (!bsdtar->uname) - archive_entry_set_uname(entry, - archive_read_disk_uname(bsdtar->diskreader, - bsdtar->uid)); + edit_owner(bsdtar, entry); + + if (bsdtar->file_mode) { + mode_t m = archive_entry_mode(entry); + m = lafe_getmode(bsdtar->file_mode, m); + archive_entry_set_mode(entry, m); } - if (bsdtar->gid >= 0) { - archive_entry_set_gid(entry, bsdtar->gid); - if (!bsdtar->gname) - archive_entry_set_gname(entry, - archive_read_disk_gname(bsdtar->diskreader, - bsdtar->gid)); - } - if (bsdtar->uname) - archive_entry_set_uname(entry, bsdtar->uname); - if (bsdtar->gname) - archive_entry_set_gname(entry, bsdtar->gname); /* * Rewrite the pathname to be archived. If rewrite @@ -954,12 +949,12 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) /* Display entry as we process it. */ if (bsdtar->verbose > 1) { - safe_fprintf(stderr, "a "); + fputs("a ", stderr); list_item_verbose(bsdtar, stderr, entry); } else if (bsdtar->verbose > 0) { - /* This format is required by SUSv2. */ - safe_fprintf(stderr, "a %s", - archive_entry_pathname(entry)); + /* This format is required by SUSv2. */ + fputs("a ", stderr); + safe_fputs(archive_entry_pathname(entry), stderr); } /* Non-regular files get archived with zero size. */ @@ -1007,7 +1002,7 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (bsdtar->verbose > 1) { - safe_fprintf(stderr, "a "); + fputs("a ", stderr); list_item_verbose(bsdtar, stderr, entry); lafe_warnc(0, ": %s", archive_error_string(a)); } else { @@ -1050,12 +1045,11 @@ report_write(struct bsdtar *bsdtar, struct archive *a, else compression = (int)((uncomp - comp) * 100 / uncomp); fprintf(stderr, - " Out: %ju bytes, compression %d%%\n", + " Out: %ju bytes, compression %d%%\nCurrent: ", (uintmax_t)comp, compression); - safe_fprintf(stderr, "Current: %s (%jd", - archive_entry_pathname(entry), - (intmax_t)progress); - fprintf(stderr, "/%jd bytes)\n", + safe_fputs(archive_entry_pathname(entry), stderr); + fprintf(stderr, " (%jd/%jd bytes)\n", + (intmax_t)progress, (intmax_t)archive_entry_size(entry)); } diff --git a/test_utils/test_common.h b/test_utils/test_common.h index 2f19597dee6b..ed31fb1a7eaa 100644 --- a/test_utils/test_common.h +++ b/test_utils/test_common.h @@ -30,7 +30,10 @@ * The goal of this file (and the matching test.c) is to * simplify the very repetitive test-*.c test programs. */ -#if defined(HAVE_CONFIG_H) +#if defined(PLATFORM_CONFIG_H) +/* Use hand-built config.h in environments that need it. */ +#include PLATFORM_CONFIG_H +#elif defined(HAVE_CONFIG_H) /* Most POSIX platforms use the 'configure' script to build config.h */ #include "config.h" #elif defined(__FreeBSD__) @@ -386,8 +389,8 @@ void *getXattr(const char *, const char *, size_t *); /* Set extended attribute */ int setXattr(const char *, const char *, const void *, size_t); -/* Return true if the file has large i-node number(>0xffffffff). */ -int is_LargeInode(const char *); +/* Set locale and check its usability. */ +int setCheckedLocale(const char *, const char *, wchar_t); #if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ diff --git a/test_utils/test_main.c b/test_utils/test_main.c index 3238feed46c7..3ceb84e0b8a3 100644 --- a/test_utils/test_main.c +++ b/test_utils/test_main.c @@ -3313,28 +3313,6 @@ copy_reference_file(const char *name) fclose(in); } -int -is_LargeInode(const char *file) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - BY_HANDLE_FILE_INFORMATION bhfi; - int r; - - r = my_GetFileInformationByName(file, &bhfi); - if (r != 0) - return (0); - return (bhfi.nFileIndexHigh & 0x0000FFFFUL); -#else - struct stat st; - int64_t ino; - - if (stat(file, &st) < 0) - return (0); - ino = (int64_t)st.st_ino; - return (ino > 0xffffffff); -#endif -} - void extract_reference_files(const char **names) { @@ -3617,6 +3595,29 @@ set_environment(const char *key, const char *value) #endif } +int +setCheckedLocale(const char *locale, const char *mbc, wchar_t expected_wc) +{ +#ifdef HAVE_MBRTOWC + mbstate_t state; + size_t len; + wchar_t wc; +#endif + + if (setlocale(LC_ALL, locale) == NULL) + return (0); + +#ifdef HAVE_MBRTOWC + memset(&state, 0, sizeof(state)); + len = strlen(mbc); + if (mbrtowc(&wc, mbc, len, &state) != len || + wc != expected_wc) + return (0); +#endif + + return (1); +} + /* * Enforce C locale for (sub)processes. */ diff --git a/unzip/CMakeLists.txt b/unzip/CMakeLists.txt index 496b9b368810..3d0e8789845a 100644 --- a/unzip/CMakeLists.txt +++ b/unzip/CMakeLists.txt @@ -16,6 +16,8 @@ IF(ENABLE_UNZIP) la_queue.h ../libarchive_fe/lafe_err.c ../libarchive_fe/lafe_err.h + ../libarchive_fe/lafe_explicit_bzero.c + ../libarchive_fe/lafe_explicit_bzero.h ../libarchive_fe/lafe_fnmatch.c ../libarchive_fe/lafe_fnmatch.h ../libarchive_fe/lafe_getline.c diff --git a/unzip/bsdunzip.1 b/unzip/bsdunzip.1 index 932f947090f0..dd5a02fdcc52 100644 --- a/unzip/bsdunzip.1 +++ b/unzip/bsdunzip.1 @@ -148,7 +148,7 @@ Normally, the option should only affect files which are marked as text files in the zipfile's central directory. Since the -.Xr archive 3 +.Xr libarchive 3 library does not provide access to that information, it is not available to the .Nm @@ -181,7 +181,7 @@ The utility and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . It uses the -.Xr archive 3 +.Xr libarchive 3 library developed by .An Tim Kientzle Aq Mt kientzle@FreeBSD.org . .Sh CAVEATS diff --git a/unzip/bsdunzip.c b/unzip/bsdunzip.c index ba63e41ef0c3..e6426d5ee0b1 100644 --- a/unzip/bsdunzip.c +++ b/unzip/bsdunzip.c @@ -9,6 +9,7 @@ #include "bsdunzip_platform.h" #include "la_queue.h" +#include "lafe_explicit_bzero.h" #include "lafe_fnmatch.h" #include "lafe_getline.h" #ifdef HAVE_SYS_STAT_H @@ -45,6 +46,12 @@ #endif #ifdef HAVE_UNISTD_H #include +#elif defined(HAVE_IO_H) +/* + * Windows has no ; close(), isatty(), open() and write() are + * made available by including io.h instead. + */ +#include #endif #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) @@ -57,6 +64,13 @@ #include "passphrase.h" #include "lafe_err.h" +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + /* command-line options */ static int a_opt; /* convert EOL */ static int C_opt; /* match case-insensitively */ @@ -389,32 +403,32 @@ make_parent(char *path) { struct stat sb; char *sep; + size_t len; + /* Retrieve string length of parent directory. */ sep = strrchr(path, '/'); if (sep == NULL || sep == path) return; - *sep = '\0'; - if (lstat(path, &sb) == 0) { - if (S_ISDIR(sb.st_mode)) { - *sep = '/'; - return; - } - system_unlink(path); - } - make_parent(path); - system_mkdir(path, 0755); - *sep = '/'; + len = sep - path; -#if 0 - for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { - /* root in case of absolute d_arg */ - if (sep == path) - continue; + /* Find deepest already existing directory. */ + do { *sep = '\0'; - make_dir(path, 0755); - *sep = '/'; + if (lstat(path, &sb) == 0) { + if (S_ISDIR(sb.st_mode)) { + *sep = '/'; + break; + } + system_unlink(path); + } + sep = strrchr(path, '/'); + } while (sep != NULL && sep != path); + + /* Create missing directories and restore path. */ + while (strlen(path) <= len) { + system_mkdir(path, 0755); + path[strlen(path)] = '/'; } -#endif } /* @@ -733,7 +747,7 @@ recheck: return; } - if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) + if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, mode)) < 0) error("open('%s')", *path); info(" extracting: %s", *path); @@ -763,11 +777,31 @@ static int pathname_is_insecure(const char* pathname) { size_t len = strlen(pathname); - return (pathname[0] == '/' || +#if defined(_WIN32) && !defined(__CYGWIN__) + const char *p; +#endif + + if (pathname[0] == '/' || strcmp(pathname, "..") == 0 || strncmp(pathname, "../", 3) == 0 || strstr(pathname, "/../") != NULL || - (len >= 3 && strcmp(pathname + len - 3, "/..") == 0)); + (len >= 3 && strcmp(pathname + len - 3, "/..") == 0)) + return 1; + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (pathname[0] == '\\' || + (isalpha((unsigned char)pathname[0]) && pathname[1] == ':')) + return 1; + + /* Windows treats both slash characters as path separators. */ + for (p = pathname; *p != '\0'; ++p) { + if (p[0] == '.' && p[1] == '.' && + (p[2] == '\0' || p[2] == '/' || p[2] == '\\') && + (p == pathname || p[-1] == '/' || p[-1] == '\\')) + return 1; + } +#endif + return 0; } /* @@ -1013,7 +1047,8 @@ unzip(const char *fn) { struct archive *a; struct archive_entry *e; - int ret; + char *buf; + int fd, ret; uintmax_t total_size, file_count, error_count; if ((a = archive_read_new()) == NULL) @@ -1030,7 +1065,29 @@ unzip(const char *fn) archive_read_set_passphrase_callback(a, NULL, &passphrase_callback); - ac(archive_read_open_filename(a, fn, 8192)); + buf = NULL; + fd = open(fn, O_RDONLY | O_BINARY | O_CLOEXEC); + if (fd == -1) { + size_t s; + + s = strlen(fn) + 5; + buf = malloc(s); + if (buf == NULL) + error("Failed to construct filename"); + if (snprintf(buf, s, "%s.zip", fn) < 0) + errorx("Failed to construct filename"); + fd = open(buf, O_RDONLY | O_BINARY | O_CLOEXEC); + if (fd == -1) { + if (snprintf(buf, s, "%s.ZIP", fn) < 0) + errorx("Failed to construct filename"); + fd = open(buf, O_RDONLY | O_BINARY | O_CLOEXEC); + } + if (fd != -1) + fn = buf; + } + if (fd == -1) + errorx("Failed to open '%s'", fn); + ac(archive_read_open_fd(a, fd, 64 * 1024)); if (!zipinfo_mode) { if (!p_opt && !q_opt) @@ -1083,10 +1140,11 @@ unzip(const char *fn) } } - ac(archive_read_free(a)); + ac(archive_read_close(a)); + archive_read_free(a); if (passphrase_buf != NULL) { - memset(passphrase_buf, 0, PPBUFF_SIZE); + explicit_bzero(passphrase_buf, PPBUFF_SIZE); free(passphrase_buf); } @@ -1099,6 +1157,9 @@ unzip(const char *fn) fn); } } + + close(fd); + free(buf); } static void @@ -1200,7 +1261,8 @@ getopts(int argc, char *argv[]) case 'Z': zipinfo_mode = 1; if (bsdunzip->argument != NULL && - strcmp(bsdunzip->argument, "1") == 0) { + (strcmp(bsdunzip->argument, "1") == 0 || + strcmp(bsdunzip->argument, "-1") == 0)) { Z1_opt = 1; } break; @@ -1307,6 +1369,13 @@ main(int argc, char *argv[]) if (n_opt + o_opt + u_opt > 1) errorx("-n, -o and -u are contradictory"); +#if defined(_WIN32) && !defined(__CYGWIN__) + if (c_opt || p_opt) { + if (_setmode(STDOUT_FILENO, _O_BINARY) == -1) + errorx("unable to set binary output mode"); + } +#endif + unzip(zipfile); exit(EXIT_SUCCESS); diff --git a/unzip/test/CMakeLists.txt b/unzip/test/CMakeLists.txt index a90a1e16b29d..6ff244a4762a 100644 --- a/unzip/test/CMakeLists.txt +++ b/unzip/test/CMakeLists.txt @@ -30,6 +30,7 @@ IF(ENABLE_UNZIP AND ENABLE_TEST) test_t_bad.c test_u.c test_version.c + test_windows_paths.c test_x.c test_Z1.c test_P_encryption.c diff --git a/unzip/test/test_I.c b/unzip/test/test_I.c index e3721402f956..c2b5584c6f5d 100644 --- a/unzip/test/test_I.c +++ b/unzip/test/test_I.c @@ -6,10 +6,6 @@ */ #include "test.h" -#ifdef HAVE_LOCALE_H -#include -#endif - /* Test I arg - file name encoding */ DEFINE_TEST(test_I) { @@ -19,14 +15,10 @@ DEFINE_TEST(test_I) "LC_CTYPE=en_US.UTF-8"; int r; -#if HAVE_SETLOCALE - if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + if (!setCheckedLocale("en_US.UTF-8", "\xC3\xA4", L'\x00E4')) { skipping("en_US.UTF-8 locale not available on this system."); return; } -#else - skipping("setlocale() not available on this system."); -#endif extract_reference_file(reffile); r = systemf("%s %s -I UTF-8 %s >test.out 2>test.err", envstr, testprog, diff --git a/unzip/test/test_Z1.c b/unzip/test/test_Z1.c index 2a3012b5bf8f..6ae5fec030bf 100644 --- a/unzip/test/test_Z1.c +++ b/unzip/test/test_Z1.c @@ -19,3 +19,17 @@ DEFINE_TEST(test_Z1) assertTextFileContents("test_basic/\ntest_basic/a\ntest_basic/b\ntest_basic/c\ntest_basic/CAPS\n", "test.out"); assertEmptyFile("test.err"); } + +/* Test -Z -1 arg - List filenames */ +DEFINE_TEST(test_Z_1) +{ + const char *reffile = "test_basic.zip"; + int r; + + extract_reference_file(reffile); + r = systemf("%s -Z -1 test_basic >test.out 2>test.err", testprog); + assertEqualInt(0, r); + assertNonEmptyFile("test.out"); + assertTextFileContents("test_basic/\ntest_basic/a\ntest_basic/b\ntest_basic/c\ntest_basic/CAPS\n", "test.out"); + assertEmptyFile("test.err"); +} diff --git a/unzip/test/test_windows_paths.c b/unzip/test/test_windows_paths.c new file mode 100644 index 000000000000..8cd08f66c39e --- /dev/null +++ b/unzip/test/test_windows_paths.c @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Dwij Mehta + * All rights reserved. + */ +#include "test.h" + +/* Verify that native Windows path syntax cannot escape the destination. */ +DEFINE_TEST(test_windows_paths) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + const char *reffile = "test_windows_paths.zip"; + const char *expected_warnings[] = { + "unzip: skipping insecure entry 'safe/..\\..\\outside.txt'", + "unzip: skipping insecure entry 'C:/outside.txt'", + "unzip: skipping insecure entry '\\rooted/rooted.txt'", + "unzip: skipping insecure symlink 'links/escape' to " + "'..\\..\\outside-link.txt'", + NULL + }; + int r; + + extract_reference_file(reffile); + r = systemf("%s -d sandbox %s >test.out 2>test.err", testprog, + reffile); + assertEqualInt(0, r); + + assertFileNotExists("outside.txt"); + assertFileNotExists("sandbox/C:/outside.txt"); + assertFileNotExists("sandbox/\\rooted/rooted.txt"); + assertFileNotExists("sandbox/links/escape"); + assertFileContainsLinesAnyOrder("test.err", expected_warnings); + assertTextFileContents("ok\n", "sandbox/safe.txt"); +#else + skipping("Native Windows path semantics required"); +#endif +} diff --git a/unzip/test/test_windows_paths.zip.uu b/unzip/test/test_windows_paths.zip.uu new file mode 100644 index 000000000000..619a6fbb2557 --- /dev/null +++ b/unzip/test/test_windows_paths.zip.uu @@ -0,0 +1,16 @@ +begin 644 test_windows_paths.zip +M4$L#!!0`````````(0!]#A;:`P````,````6````'102P$"%``4`````````"$`?0X6V@,````#````#@`````` +M````````@`$W````0SHO;W5T'102P$"%``4`````````"$`?0X6 +MV@,````#````$@``````````````@`%F````7')O;W1E9"]R;V]T960N='AT +M4$L!`A0#%``````````A`'-O<`$6````%@````P``````````````/^AF0`` +M`&QI;FMS+V5S8V%P95!+`0(4`!0`````````(0!]#A;:`P````,````(```` +L``````````"``=D```!S869E+G1X=%!+!08`````!0`%`#`!```"`0`````` +` +end