diff options
128 files changed, 6882 insertions, 962 deletions
diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 00000000..ae72919a --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,108 @@ +env: + CIRRUS_CLONE_DEPTH: 1 + ARCH: amd64 + +FreeBSD_task: + matrix: + env: + BS: autotools + env: + BS: cmake + matrix: + freebsd_instance: + image: freebsd-12-0-release-amd64 + freebsd_instance: + image: freebsd-11-2-release-amd64 + prepare_script: + - ./build/ci/cirrus_ci/ci.sh prepare + configure_script: + - ./build/ci/build.sh -a autogen + - ./build/ci/build.sh -a configure + build_script: + - ./build/ci/build.sh -a build + test_script: + - ./build/ci/build.sh -a test + - ./build/ci/cirrus_ci/ci.sh test + +MacOS_task: + matrix: + env: + BS: autotools + env: + BS: cmake + matrix: + osx_instance: + image: mojave-xcode-10.2 + osx_instance: + image: high-sierra-xcode-10.0 + prepare_script: + - ./build/ci/cirrus_ci/ci.sh prepare + configure_script: + - ./build/ci/build.sh -a autogen + - ./build/ci/build.sh -a configure + build_script: + - ./build/ci/build.sh -a build + test_script: + - ./build/ci/build.sh -a test + - ./build/ci/cirrus_ci/ci.sh test + +Fedora_29_task: + container: + dockerfile: build/ci/cirrus_ci/Dockerfile.fedora29 + matrix: + env: + BS: autotools + env: + BS: cmake + configure_script: + - ./build/ci/build.sh -a autogen + - ./build/ci/build.sh -a configure + build_script: + - ./build/ci/build.sh -a build + test_script: + - ./build/ci/build.sh -a test + +Windows_MSVC_task: + windows_container: + dockerfile: build/ci/cirrus_ci/Dockerfile.msvc + os_version: 2019 + env: + BE: msvc + configure_script: + - build\ci\cirrus_ci\ci.cmd configure + build_script: + - build\ci\cirrus_ci\ci.cmd build + test_script: + - build\ci\cirrus_ci\ci.cmd test + +Windows_MinGW_task: + windows_container: + image: cirrusci/windowsservercore:2019 + os_version: 2019 + env: + BE: mingw-gcc + prepare_script: + - build\ci\cirrus_ci\ci.cmd prepare + deplibs_script: + - build\ci\cirrus_ci\ci.cmd deplibs + configure_script: + - build\ci\cirrus_ci\ci.cmd configure + build_script: + - build\ci\cirrus_ci\ci.cmd build + test_script: + - build\ci\cirrus_ci\ci.cmd test + +Windows_Cygwin_task: + windows_container: + image: cirrusci/windowsservercore:2019 + os_version: 2019 + env: + BE: cygwin-gcc + prepare_script: + - build\ci\cirrus_ci\ci.cmd prepare + configure_script: + - build\ci\cirrus_ci\ci.cmd configure + build_script: + - build\ci\cirrus_ci\ci.cmd build + test_script: + - build\ci\cirrus_ci\ci.cmd test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9f262b64..00000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: C -sudo: false -dist: xenial -addons: - apt: - packages: - - libacl1-dev - - libbz2-dev - - liblzma-dev - - libzip-dev - - lzop -os: - - linux - - osx -compiler: - - gcc - - clang -env: - - BUILD_SYSTEM=cmake - - BUILD_SYSTEM=autotools -matrix: - exclude: - - os: osx - compiler: gcc -before_install: - - if [ `uname` = "Darwin" ]; then brew update; fi -install: - - if [ `uname` = "Darwin" ]; then brew install xz lz4 zstd; fi -script: - - build/ci_build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 81aa1aeb..133a5ddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,8 @@ # CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR) +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) #3.12.0 `find_package()`` uses ``<PackageName>_ROOT`` variables. +endif() # PROJECT(libarchive C) # @@ -84,6 +87,11 @@ SET(CMAKE_REQUIRED_DEFINITIONS) SET(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_REQUIRED_LIBRARIES) SET(CMAKE_REQUIRED_FLAGS) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." ON) +else () + OPTION(ENABLE_WERROR "Treat warnings as errors - default is ON for Debug, OFF otherwise." OFF) +endif () # Especially for early development, we want to be a little # aggressive about diagnosing build problems; this can get @@ -93,10 +101,12 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^GNU$") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif () ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") @@ -108,11 +118,13 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^Clang$") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif () ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wextra") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wunused") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wshadow") @@ -125,15 +137,21 @@ IF (CMAKE_C_COMPILER_ID MATCHES "^XL$") ################################################################# # Set compile flags for all build types. SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qflag=e:e -qformat=sec") + if (ENABLE_WERROR) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qhalt=w") + endif () ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qhalt=w") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qflag=w:w") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -qinfo=pro:use") ENDIF(CMAKE_C_COMPILER_ID MATCHES "^XL$") IF (MSVC) + if (ENABLE_WERROR) + # /WX option is the same as gcc's -Werror option. + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") + endif () ################################################################# # Set compile flags for debug build. # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug" @@ -165,8 +183,6 @@ IF (MSVC) # Enable level 4 C4706: The test value in a conditional expression was the # result of an assignment. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4706") - # /WX option is the same as gcc's -Werror option. - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /WX") # /Oi option enables built-in functions. SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi") ################################################################# @@ -183,6 +199,7 @@ OPTION(ENABLE_LIBB2 "Enable the use of the system LIBB2 library if found" ON) OPTION(ENABLE_LZ4 "Enable the use of the system LZ4 library if found" ON) OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF) OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON) +OPTION(ENABLE_ZSTD "Enable the use of the system zstd library if found" ON) OPTION(ENABLE_ZLIB "Enable the use of the system ZLIB library if found" ON) OPTION(ENABLE_BZip2 "Enable the use of the system BZip2 library if found" ON) @@ -458,7 +475,7 @@ MARK_AS_ADVANCED(CLEAR BZIP2_LIBRARIES) IF(ENABLE_LZMA) FIND_PACKAGE(LibLZMA) ELSE() - SET(LIBZMA_FOUND FALSE) # Override cached value + SET(LIBLZMA_FOUND FALSE) # Override cached value ENDIF() IF(LIBLZMA_FOUND) @@ -480,6 +497,9 @@ IF(LIBLZMA_FOUND) ELSE(LIBLZMA_FOUND) # LZMA not found and will not be used. ENDIF(LIBLZMA_FOUND) +MARK_AS_ADVANCED(CLEAR LIBLZMA_INCLUDE_DIR) +MARK_AS_ADVANCED(CLEAR LIBLZMA_LIBRARY) + # # Find LZO2 # @@ -569,15 +589,19 @@ MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY) # # Find Zstd # -IF (ZSTD_INCLUDE_DIR) - # Already in cache, be silent - SET(ZSTD_FIND_QUIETLY TRUE) -ENDIF (ZSTD_INCLUDE_DIR) +IF(ENABLE_ZSTD) + IF (ZSTD_INCLUDE_DIR) + # Already in cache, be silent + SET(ZSTD_FIND_QUIETLY TRUE) + ENDIF (ZSTD_INCLUDE_DIR) -FIND_PATH(ZSTD_INCLUDE_DIR zstd.h) -FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd) -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) + FIND_PATH(ZSTD_INCLUDE_DIR zstd.h) + FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd) + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) +ELSE(ENABLE_ZSTD) + SET(ZSTD_FOUND FALSE) # Override cached value +ENDIF(ENABLE_ZSTD) IF(ZSTD_FOUND) SET(HAVE_ZSTD_H 1) INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR}) diff --git a/Makefile.am b/Makefile.am index ea6b2dc0..2a98c826 100644 --- a/Makefile.am +++ b/Makefile.am @@ -132,6 +132,8 @@ libarchive_la_SOURCES= \ libarchive/archive_ppmd_private.h \ libarchive/archive_ppmd7.c \ libarchive/archive_ppmd7_private.h \ + libarchive/archive_ppmd8.c \ + libarchive/archive_ppmd8_private.h \ libarchive/archive_private.h \ libarchive/archive_random.c \ libarchive/archive_random_private.h \ @@ -254,6 +256,8 @@ endif if INC_BLAKE2 libarchive_la_SOURCES+= \ + libarchive/archive_blake2.h \ + libarchive/archive_blake2_impl.h \ libarchive/archive_blake2s_ref.c \ libarchive/archive_blake2sp_ref.c endif @@ -284,6 +288,7 @@ libarchive_man_MANS= \ libarchive/archive_entry.3 \ libarchive/archive_entry_acl.3 \ libarchive/archive_entry_linkify.3 \ + libarchive/archive_entry_misc.3 \ libarchive/archive_entry_paths.3 \ libarchive/archive_entry_perms.3 \ libarchive/archive_entry_stat.3 \ @@ -400,7 +405,6 @@ libarchive_test_SOURCES= \ libarchive/test/test_compat_lzma.c \ libarchive/test/test_compat_lzop.c \ libarchive/test/test_compat_mac.c \ - libarchive/test/test_compat_pax_libarchive_2x.c \ libarchive/test/test_compat_perl_archive_tar.c \ libarchive/test/test_compat_plexus_archiver_tar.c \ libarchive/test/test_compat_solaris_tar_acl.c \ @@ -672,7 +676,6 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_compat_lzop_3.tar.lzo.uu \ libarchive/test/test_compat_mac-1.tar.Z.uu \ libarchive/test/test_compat_mac-2.tar.Z.uu \ - libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu \ libarchive/test/test_compat_perl_archive_tar.tar.uu \ libarchive/test/test_compat_plexus_archiver_tar.tar.uu \ libarchive/test/test_compat_solaris_pax_sparse_1.pax.Z.uu \ @@ -823,6 +826,30 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar_subblock.rar.uu \ libarchive/test/test_read_format_rar_unicode.rar.uu \ libarchive/test/test_read_format_rar_windows.rar.uu \ + libarchive/test/test_read_format_rar5_arm.rar.uu \ + libarchive/test/test_read_format_rar5_blake2.rar.uu \ + libarchive/test/test_read_format_rar5_compressed.rar.uu \ + libarchive/test/test_read_format_rar5_hardlink.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 \ + libarchive/test/test_read_format_rar5_multiarchive.part04.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part05.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part06.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part07.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive.part08.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part01.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part02.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part03.rar.uu \ + libarchive/test/test_read_format_rar5_multiarchive_solid.part04.rar.uu \ + libarchive/test/test_read_format_rar5_multiple_files.rar.uu \ + libarchive/test/test_read_format_rar5_multiple_files_solid.rar.uu \ + libarchive/test/test_read_format_rar5_owner.rar.uu \ + libarchive/test/test_read_format_rar5_solid.rar.uu \ + libarchive/test/test_read_format_rar5_stored.rar.uu \ + libarchive/test/test_read_format_rar5_stored_manyfiles.rar.uu \ + libarchive/test/test_read_format_rar5_symlink.rar.uu \ + libarchive/test/test_read_format_rar5_win32.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ libarchive/test/test_read_format_raw.data.uu \ @@ -835,6 +862,9 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_ustar_filename_koi8r.tar.Z.uu \ libarchive/test/test_read_format_warc.warc.uu \ libarchive/test/test_read_format_zip.zip.uu \ + libarchive/test/test_read_format_zip_bz2_hang.zip.uu \ + libarchive/test/test_read_format_zip_bzip2.zipx.uu \ + libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu \ libarchive/test/test_read_format_zip_comment_stored_1.zip.uu \ libarchive/test/test_read_format_zip_comment_stored_2.zip.uu \ libarchive/test/test_read_format_zip_encryption_data.zip.uu \ @@ -849,6 +879,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_filename_utf8_ru2.zip.uu \ 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_jar.jar.uu \ libarchive/test/test_read_format_zip_mac_metadata.zip.uu \ libarchive/test/test_read_format_zip_malformed1.zip.uu \ @@ -858,6 +889,10 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_padded1.zip.uu \ libarchive/test/test_read_format_zip_padded2.zip.uu \ libarchive/test/test_read_format_zip_padded3.zip.uu \ + libarchive/test/test_read_format_zip_ppmd8.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu \ + libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu \ libarchive/test/test_read_format_zip_sfx.uu \ libarchive/test/test_read_format_zip_symlink.zip.uu \ libarchive/test/test_read_format_zip_traditional_encryption_data.zip.uu \ @@ -867,6 +902,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_zip_winzip_aes256_large.zip.uu \ libarchive/test/test_read_format_zip_winzip_aes256_stored.zip.uu \ libarchive/test/test_read_format_zip_with_invalid_traditional_eocd.zip.uu \ + libarchive/test/test_read_format_zip_xz_multi.zipx.uu \ libarchive/test/test_read_format_zip_zip64a.zip.uu \ libarchive/test/test_read_format_zip_zip64b.zip.uu \ libarchive/test/test_read_large_splitted_rar_aa.uu \ @@ -995,6 +1031,7 @@ bsdtar_test_SOURCES= \ tar/test/test_option_b.c \ tar/test/test_option_b64encode.c \ tar/test/test_option_exclude.c \ + tar/test/test_option_exclude_vcs.c \ tar/test/test_option_fflags.c \ tar/test/test_option_gid_gname.c \ tar/test/test_option_grzip.c \ @@ -1,3 +1,13 @@ +Apr 16, 2019: Support for non-recursive list and extract + +Apr 14, 2019: New tar option: --exclude-vcs + +Mar 27, 2019: Support for file and directory symlinks on Windows + +Mar 12, 2019: Important fixes for storing file attributes and flags + +Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 compression in zip archives + Oct 06, 2018: RAR 5.0 reader Sep 03, 2018: libarchive 3.3.3 released diff --git a/build/ci_build.sh b/build/ci/build.sh index 65e5ceb5..d61336ea 100755 --- a/build/ci_build.sh +++ b/build/ci/build.sh @@ -3,15 +3,17 @@ # Automated build and test of libarchive on CI systems # # Variables that can be passed via environment: -# BUILD_SYSTEM= -# BUILDDIR= -# SRCDIR= -# CONFIGURE_ARGS= -# MAKE_ARGS= -# +# BS= # build system (autotools or cmake) +# BUILDDIR= # build directory +# SRCDIR= # source directory +# CONFIGURE_ARGS= # configure arguments +# MAKE_ARGS= # make arguments ACTIONS= -BUILD_SYSTEM="${BUILD_SYSTEM:-autotools}" +if [ -n "${BUILD_SYSTEM}" ]; then + BS="${BUILD_SYSTEM}" +fi +BS="${BS:-autotools}" MAKE="${MAKE:-make}" CMAKE="${CMAKE:-cmake}" CURDIR=`pwd` @@ -38,8 +40,8 @@ while getopts a:b:d:s: opt; do esac ACTIONS="${ACTIONS} ${OPTARG}" ;; - b) BUILD_SYSTEM="${OPTARG}" - case "${BUILD_SYSTEM}" in + b) BS="${OPTARG}" + case "${BS}" in autotools) ;; cmake) ;; *) inputerror "Invalid build system (-b)" ;; @@ -59,18 +61,18 @@ done if [ -z "${ACTIONS}" ]; then ACTIONS="autogen configure build test" fi -if [ -z "${BUILD_SYSTEM}" ]; then - inputerror "Missing type (-t) parameter" +if [ -z "${BS}" ]; then + inputerror "Missing build system (-b) parameter" fi if [ -z "${BUILDDIR}" ]; then - BUILDDIR="${CURDIR}/build_ci/${BUILD_SYSTEM}" + BUILDDIR="${CURDIR}/build_ci/${BS}" fi mkdir -p "${BUILDDIR}" for action in ${ACTIONS}; do cd "${BUILDDIR}" case "${action}" in autogen) - case "${BUILD_SYSTEM}" in + case "${BS}" in autotools) cd "${SRCDIR}" sh build/autogen.sh @@ -79,7 +81,7 @@ for action in ${ACTIONS}; do esac ;; configure) - case "${BUILD_SYSTEM}" in + case "${BS}" in autotools) "${SRCDIR}/configure" ${CONFIGURE_ARGS} ;; cmake) ${CMAKE} ${CONFIGURE_ARGS} "${SRCDIR}" ;; esac @@ -90,15 +92,16 @@ for action in ${ACTIONS}; do RET="$?" ;; test) - case "${BUILD_SYSTEM}" in + 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" ;; cmake) ${MAKE} ${MAKE_ARGS} test ;; esac RET="$?" + find ${TMPDIR:-/tmp} -path '*_test.*' -name '*.log' -print -exec cat {} \; ;; esac if [ "${RET}" != "0" ]; then diff --git a/build/ci/cirrus_ci/Dockerfile.cygwin b/build/ci/cirrus_ci/Dockerfile.cygwin new file mode 100644 index 00000000..453503e0 --- /dev/null +++ b/build/ci/cirrus_ci/Dockerfile.cygwin @@ -0,0 +1,4 @@ +FROM cirrusci/windowsservercore:2019 + +RUN choco install -y --no-progress cygwin +RUN C:\tools\cygwin\cygwinsetup.exe -q -P make,autoconf,automake,cmake,gcc-core,binutils,libtool,pkg-config,bison,sharutils,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel diff --git a/build/ci/cirrus_ci/Dockerfile.fedora29 b/build/ci/cirrus_ci/Dockerfile.fedora29 new file mode 100644 index 00000000..d88176b1 --- /dev/null +++ b/build/ci/cirrus_ci/Dockerfile.fedora29 @@ -0,0 +1,3 @@ +FROM fedora:29 + +RUN dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison sharutils pkgconf libacl-devel libasan librichacl-devel bzip2-devel libzip-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel diff --git a/build/ci/cirrus_ci/Dockerfile.mingw b/build/ci/cirrus_ci/Dockerfile.mingw new file mode 100644 index 00000000..f14bb0bc --- /dev/null +++ b/build/ci/cirrus_ci/Dockerfile.mingw @@ -0,0 +1,8 @@ +FROM cirrusci/windowsservercore:2019 + +RUN choco install -y --no-progress --installargs 'ADD_CMAKE_TO_PATH=User' cmake +RUN choco install -y --no-progress mingw +RUN curl -o zlib-1.2.11.tar.gz https://www.zlib.net/zlib-1.2.11.tar.gz +RUN tar -x -f zlib-1.2.11.tar.gz +RUN cd zlib-1.2.11 && cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE="Release" . && mingw32-make && mingw32-make install +RUN del /f /q /s zlib-1.2.11 zlib-1.2.11.tar.gz diff --git a/build/ci/cirrus_ci/Dockerfile.msvc b/build/ci/cirrus_ci/Dockerfile.msvc new file mode 100644 index 00000000..c9831828 --- /dev/null +++ b/build/ci/cirrus_ci/Dockerfile.msvc @@ -0,0 +1,9 @@ +FROM cirrusci/windowsservercore:2019 + +RUN choco install -y --no-progress --installargs 'ADD_CMAKE_TO_PATH=User' cmake +RUN choco install -y --no-progress visualstudio2017community +RUN choco install -y --no-progress visualstudio2017-workload-vctools +RUN curl -o zlib-1.2.11.tar.gz https://www.zlib.net/zlib-1.2.11.tar.gz +RUN tar -x -f zlib-1.2.11.tar.gz +RUN cd zlib-1.2.11 && cmake -G "Visual Studio 15 2017" . && cmake --build . --target ALL_BUILD --config Release && cmake --build . --target INSTALL --config Release +RUN del /f /q /s zlib-1.2.11 zlib-1.2.11.tar.gz diff --git a/build/ci/cirrus_ci/Dockerfile.windows b/build/ci/cirrus_ci/Dockerfile.windows new file mode 100644 index 00000000..34d6d32a --- /dev/null +++ b/build/ci/cirrus_ci/Dockerfile.windows @@ -0,0 +1,12 @@ +FROM cirrusci/windowsservercore:2019 + +RUN choco install -y --no-progress mingw +RUN choco install -y --no-progress --installargs 'ADD_CMAKE_TO_PATH=User' cmake +RUN choco install -y --no-progress visualstudio2017community +RUN choco install -y --no-progress visualstudio2017-workload-vctools +RUN curl -o zlib-1.2.11.tar.gz https://www.zlib.net/zlib-1.2.11.tar.gz +RUN tar -x -f zlib-1.2.11.tar.gz +RUN cd zlib-1.2.11 && cmake -G "Visual Studio 15 2017" . && cmake --build . --target ALL_BUILD --config Release && cmake --build . --target INSTALL --config Release +RUN del /f /q /s zlib-1.2.11 zlib-1.2.11.tar.gz +RUN choco install -y --no-progress cygwin +RUN C:\tools\cygwin\cygwinsetup.exe -q -P make,autoconf,automake,cmake,gcc-core,binutils,libtool,pkg-config,bison,sharutils,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel diff --git a/build/ci/cirrus_ci/ci.cmd b/build/ci/cirrus_ci/ci.cmd new file mode 100755 index 00000000..fd63f2a7 --- /dev/null +++ b/build/ci/cirrus_ci/ci.cmd @@ -0,0 +1,110 @@ +@ECHO OFF +SET ZLIB_VERSION=1.2.11 +IF NOT "%BE%"=="cygwin-gcc" ( + IF NOT "%BE%"=="mingw-gcc" ( + IF NOT "%BE%"=="msvc" ( + ECHO Environment variable BE must be cygwin-gcc, mingw-gcc or msvc + EXIT /b 1 + ) + ) +) + +IF "%1%"=="prepare" ( + IF "%BE%"=="cygwin-gcc" ( + @ECHO ON + choco install -y --no-progress cygwin || EXIT /b 1 + C:\tools\cygwin\cygwinsetup.exe -q -P make,autoconf,automake,cmake,gcc-core,binutils,libtool,pkg-config,bison,sharutils,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel || EXIT /b 1 + @EXIT /b 0 + ) ELSE IF "%BE%"=="mingw-gcc" ( + @ECHO ON + choco install -y --no-progress mingw || EXIT /b 1 + choco install -y --no-progress --installargs 'ADD_CMAKE_TO_PATH=System' cmake || EXIT /b 1 + @EXIT /b 0 + ) ELSE IF "%BE%"=="msvc" ( + @ECHO ON + choco install -y --no-progress visualstudio2017community || EXIT /b 1 + choco install -y --no-progress visualstudio2017-workload-vctools || EXIT /b 1 + choco install -y --no-progress --installargs 'ADD_CMAKE_TO_PATH=System' cmake || EXIT /b 1 + ) +) ELSE IF "%1"=="deplibs" ( + IF "%BE%"=="cygwin-gcc" ( + ECHO Skipping on this platform + EXIT /b 0 + ) + IF NOT EXIST build_ci\libs ( + MKDIR build_ci\libs + ) + CD build_ci\libs + IF NOT EXIST zlib-%ZLIB_VERSION%.tar.gz ( + curl -o zlib-%ZLIB_VERSION%.tar.gz https://www.zlib.net/zlib-%ZLIB_VERSION%.tar.gz + ) + IF NOT EXIST zlib-%ZLIB_VERSION% ( + tar -x -z -f zlib-%ZLIB_VERSION%.tar.gz + ) + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + CD zlib-%ZLIB_VERSION% + IF "%BE%"=="mingw-gcc" ( + cmake -G "MinGW Makefiles" -D CMAKE_BUILD_TYPE="Release" . || EXIT /b 1 + mingw32-make || EXIT /b 1 + mingw32-make test || EXIT /b 1 + mingw32-make install || EXIT /b 1 + ) ELSE IF "%BE%"=="msvc" ( + cmake -G "Visual Studio 15 2017" . || EXIT /b 1 + cmake --build . --target ALL_BUILD --config Release || EXIT /b 1 + cmake --build . --target RUN_TESTS --config Release || EXIT /b 1 + cmake --build . --target INSTALL --config Release || EXIT /b 1 + ) +) ELSE IF "%1%"=="configure" ( + IF "%BE%"=="cygwin-gcc" ( + SET BS=cmake + SET CONFIGURE_ARGS=-DENABLE_ACL=OFF + C:\tools\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a configure" || EXIT /b 1 + ) ELSE IF "%BE%"=="mingw-gcc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + MKDIR build_ci\cmake + CD build_ci\cmake + cmake -G "MinGW Makefiles" ..\.. || EXIT /b 1 + ) ELSE IF "%BE%"=="msvc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + MKDIR build_ci\cmake + CD build_ci\cmake + cmake -G "Visual Studio 15 2017" -D CMAKE_BUILD_TYPE="Release" ..\.. || EXIT /b 1 + ) +) ELSE IF "%1%"=="build" ( + IF "%BE%"=="cygwin-gcc" ( + SET BS=cmake + C:\tools\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a build" + ) ELSE IF "%BE%"=="mingw-gcc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + CD build_ci\cmake + mingw32-make || EXIT /b 1 + ) ELSE IF "%BE%"=="msvc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + CD build_ci\cmake + cmake --build . --target ALL_BUILD --config Release + ) +) ELSE IF "%1%"=="test" ( + IF "%BE%"=="cygwin-gcc" ( + ECHO "Skipping tests on this platform" + EXIT /b 0 + REM SET BS=cmake + REM SET SKIP_TEST_SPARSE=1 + REM C:\tools\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a test" + ) ELSE IF "%BE%"=="mingw-gcc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + COPY "C:\Program Files (x86)\zlib\bin\libzlib.dll" build_ci\cmake\bin\ + CD build_ci\cmake + SET SKIP_TEST_SPARSE=1 + mingw32-make test + ) ELSE IF "%BE%"=="msvc" ( + SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + ECHO "Skipping tests on this platform" + EXIT /b 0 + REM CD build_ci\cmake + REM cmake --build . --target RUN_TESTS --config Release + ) +) ELSE ( + ECHO "Usage: %0% prepare|deplibs|configure|build|test" + @EXIT /b 0 +) +@EXIT /b 0 diff --git a/build/ci/cirrus_ci/ci.sh b/build/ci/cirrus_ci/ci.sh new file mode 100755 index 00000000..c07ebfe9 --- /dev/null +++ b/build/ci/cirrus_ci/ci.sh @@ -0,0 +1,56 @@ +#!/bin/sh +UNAME=`uname` +if [ "$1" = "prepare" ] +then + if [ "${UNAME}" = "FreeBSD" ] + then + set -x -e + sed -i.bak -e 's,pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly,pkg+http://pkg.FreeBSD.org/\${ABI}/latest,' /etc/pkg/FreeBSD.conf + mount -u -o acls / + mkdir /tmp_acl_nfsv4 + MD=`mdconfig -a -t swap -s 128M` + newfs /dev/$MD + tunefs -N enable /dev/$MD + mount /dev/$MD /tmp_acl_nfsv4 + chmod 1777 /tmp_acl_nfsv4 + pkg install -y autoconf automake cmake libiconv libtool pkgconf expat libxml2 liblz4 zstd + elif [ "${UNAME}" = "Darwin" ] + then + set -x -e + brew update > /dev/null + for pkg in autoconf automake libtool pkg-config cmake xz lz4 zstd + do + brew list $pkg > /dev/null && brew upgrade $pkg || brew install $pkg + done + elif [ "${UNAME}" = "Linux" ] + then + if [ -f "/etc/debian_version" ] + then + apt-get -y update + apt-get -y install build-essential locales automake libtool bison sharutils pkgconf libacl1-dev libbz2-dev libzip-dev zlib1g-dev liblzma-dev liblz4-dev libzstd-dev libssl-dev lrzip cmake + elif [ -f "/etc/fedora-release" ] + then + dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison sharutils pkgconf libacl-devel librichacl-devel bzip2-devel libzip-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel + fi + fi +elif [ "$1" = "test" ] +then + if [ "${UNAME}" = "FreeBSD" ] + then + set -e + echo "Additional NFSv4 ACL tests" + CURDIR=`pwd` + if [ "${BS}" = "cmake" ] + then + BIN_SUBDIR="bin" + else + BIN_SUBDIR=. + 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 + fi +else + echo "Usage $0 prepare | test_nfsv4_acls" + exit 1 +fi diff --git a/build/ci_test_driver b/build/ci/test_driver index 69a5463c..69a5463c 100755 --- a/build/ci_test_driver +++ b/build/ci/test_driver diff --git a/build/ci/travis_ci.sh b/build/ci/travis_ci.sh new file mode 100755 index 00000000..8ed0543d --- /dev/null +++ b/build/ci/travis_ci.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -e +UNAME=`uname` +CURDIR=`pwd` +SRCDIR="${SRCDIR:-`pwd`}" +if [ -z "${BUILDDIR}" ]; then + BUILDDIR="${CURDIR}/build_ci/${BS}" +fi +mkdir -p "${BUILDDIR}" +cd "${BUILDDIR}" +case "$UNAME" in + MSYS*) + if [ "${BS}" = "msbuild" ]; then + set -x + cmake -G "Visual Studio 15 2017" -D CMAKE_BUILD_TYPE="Release" "${SRCDIR}" + cmake --build . --target ALL_BUILD + # Until fixed, we don't run tests on Windows (lots of fails + timeout) + #export SKIP_TEST_FUZZ=1 + #cmake --build . --target RUN_TESTS + set +x + elif [ "${BS}" = "mingw" ]; then + set -x + cmake -G "MSYS Makefiles" -D CMAKE_C_COMPILER="${CC}" -D CMAKE_MAKE_PROGRAM="mingw32-make" -D CMAKE_BUILD_TYPE="Release" "${SRCDIR}" + mingw32-make + #export SKIP_TEST_FUZZ=1 + #mingw32-make test + set +x + else + echo "Unknown or unspecified build type: ${BS}" + exit 1 + fi + ;; +esac diff --git a/contrib/shar/tree.c b/contrib/shar/tree.c index d5a04abf..a80d8366 100644 --- a/contrib/shar/tree.c +++ b/contrib/shar/tree.c @@ -530,8 +530,7 @@ tree_close(struct tree *t) /* Release anything remaining in the stack. */ while (t->stack != NULL) tree_pop(t); - if (t->buff) - free(t->buff); + free(t->buff); /* chdir() back to where we started. */ if (t->initialDirFd >= 0) { fchdir(t->initialDirFd); diff --git a/cpio/test/test_basic.c b/cpio/test/test_basic.c index 6e45d185..a8fedf89 100644 --- a/cpio/test/test_basic.c +++ b/cpio/test/test_basic.c @@ -46,7 +46,7 @@ verify_files(const char *msg) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* Another file with 1 link and different permissions. */ failure(msg); @@ -173,7 +173,7 @@ DEFINE_TEST(test_basic) /* Symlink to above file. */ if (canSymlink()) { - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); if (is_LargeInode("symlink")) { strncat(result, diff --git a/cpio/test/test_format_newc.c b/cpio/test/test_format_newc.c index 25864044..6c981f6a 100644 --- a/cpio/test/test_format_newc.c +++ b/cpio/test/test_format_newc.c @@ -114,7 +114,7 @@ DEFINE_TEST(test_format_newc) /* "symlink" */ if (canSymlink()) { - assertMakeSymlink("symlink", "file1"); + assertMakeSymlink("symlink", "file1", 0); fprintf(list, "symlink\n"); } @@ -233,7 +233,12 @@ DEFINE_TEST(test_format_newc) assert(is_hex(e, 110)); assertEqualMem(e + 0, "070701", 6); /* Magic */ assert(is_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); +#else assertEqualInt(0xa1ff, from_hex(e + 14, 8)); /* Mode */ +#endif assertEqualInt(from_hex(e + 22, 8), uid); /* uid */ assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */ assertEqualMem(e + 38, "00000001", 8); /* nlink */ diff --git a/cpio/test/test_gcpio_compat.c b/cpio/test/test_gcpio_compat.c index 461e427c..9bb98899 100644 --- a/cpio/test/test_gcpio_compat.c +++ b/cpio/test/test_gcpio_compat.c @@ -71,7 +71,7 @@ unpack_test(const char *from, const char *options, const char *se) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ assertIsDir("dir", 0775); diff --git a/cpio/test/test_option_L_upper.c b/cpio/test/test_option_L_upper.c index 1774343f..cab41b61 100644 --- a/cpio/test/test_option_L_upper.c +++ b/cpio/test/test_option_L_upper.c @@ -30,8 +30,10 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/test/test_option_L.c,v 1.2 2008/08/24 06:21 * tests won't run on Windows. */ #if defined(_WIN32) && !defined(__CYGWIN__) #define CAT "type" +#define SEP "\\" #else #define CAT "cat" +#define SEP "/" #endif DEFINE_TEST(test_option_L_upper) @@ -51,7 +53,7 @@ DEFINE_TEST(test_option_L_upper) fprintf(filelist, "file\n"); /* Symlink to above file. */ - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); fclose(filelist); @@ -61,7 +63,7 @@ DEFINE_TEST(test_option_L_upper) assertTextFileContents("1 block\n", "copy.err"); failure("Regular -p without -L should preserve symlinks."); - assertIsSymlink("copy/symlink", NULL); + assertIsSymlink("copy/symlink", NULL, 0); r = systemf(CAT " filelist | %s -pd -L copy-L >copy-L.out 2>copy-L.err", testprog); assertEqualInt(r, 0); @@ -77,13 +79,14 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("unpack", 0755); assertChdir("unpack"); - r = systemf(CAT " ../archive.out | %s -i >unpack.out 2>unpack.err", testprog); + r = systemf(CAT " .." SEP "archive.out | %s -i >unpack.out 2>unpack.err", testprog); + failure("Error invoking %s -i", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "unpack.err"); assertChdir(".."); - assertIsSymlink("unpack/symlink", NULL); + assertIsSymlink("unpack/symlink", NULL, 0); r = systemf(CAT " filelist | %s -oL >archive-L.out 2>archive-L.err", testprog); failure("Error invoking %s -oL", testprog); @@ -92,7 +95,8 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("unpack-L", 0755); assertChdir("unpack-L"); - r = systemf(CAT " ../archive-L.out | %s -i >unpack-L.out 2>unpack-L.err", testprog); + r = systemf(CAT " .." SEP "archive-L.out | %s -i >unpack-L.out 2>unpack-L.err", testprog); + failure("Error invoking %s -i < archive-L.out", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "unpack-L.err"); diff --git a/cpio/test/test_option_a.c b/cpio/test/test_option_a.c index 29638777..e96bdf3c 100644 --- a/cpio/test/test_option_a.c +++ b/cpio/test/test_option_a.c @@ -71,8 +71,13 @@ test_create(void) * #ifdef this section out. Most of the test below is * still valid. */ memset(×, 0, sizeof(times)); +#if defined(_WIN32) && !defined(CYGWIN) + times.actime = 86400; + times.modtime = 86400; +#else times.actime = 1; times.modtime = 3; +#endif assertEqualInt(0, utime(files[i].name, ×)); /* Record whatever atime the file ended up with. */ diff --git a/cpio/test/test_option_c.c b/cpio/test/test_option_c.c index fa47b7e2..013caed5 100644 --- a/cpio/test/test_option_c.c +++ b/cpio/test/test_option_c.c @@ -85,7 +85,7 @@ DEFINE_TEST(test_option_c) /* "symlink" */ if (canSymlink()) { - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); fprintf(filelist, "symlink\n"); } diff --git a/cpio/test/test_option_t.c b/cpio/test/test_option_t.c index 6bcaee3c..eaa73fa3 100644 --- a/cpio/test/test_option_t.c +++ b/cpio/test/test_option_t.c @@ -88,11 +88,11 @@ DEFINE_TEST(test_option_t) setlocale(LC_ALL, ""); #endif #if defined(_WIN32) && !defined(__CYGWIN__) - strftime(date2, sizeof(date), "%b %d %Y", localtime(&mtime)); - _snprintf(date, sizeof(date)-1, "%12s file", date2); + strftime(date2, sizeof(date2)-1, "%b %d %Y", localtime(&mtime)); + _snprintf(date, sizeof(date)-1, "%12.12s file", date2); #else - strftime(date2, sizeof(date), "%b %e %Y", localtime(&mtime)); - snprintf(date, sizeof(date)-1, "%12s file", date2); + strftime(date2, sizeof(date2)-1, "%b %e %Y", localtime(&mtime)); + snprintf(date, sizeof(date)-1, "%12.12s file", date2); #endif assertEqualMem(p + 42, date, strlen(date)); free(p); diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 79719773..ec775bb4 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -51,6 +51,8 @@ SET(libarchive_SOURCES archive_platform_acl.h archive_platform_xattr.h archive_ppmd_private.h + archive_ppmd8.c + archive_ppmd8_private.h archive_ppmd7.c archive_ppmd7_private.h archive_private.h @@ -168,6 +170,7 @@ SET(libarchive_MANS archive_entry.3 archive_entry_acl.3 archive_entry_linkify.3 + archive_entry_misc.3 archive_entry_paths.3 archive_entry_perms.3 archive_entry_stat.3 @@ -233,6 +236,7 @@ ENDIF() # Libarchive is a shared library ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS}) +TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .) TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS}) SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION}) diff --git a/libarchive/archive.h b/libarchive/archive.h index 438ce468..daaaf783 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -338,9 +338,9 @@ typedef const char *archive_passphrase_callback(struct archive *, #define ARCHIVE_FORMAT_LHA 0xB0000 #define ARCHIVE_FORMAT_CAB 0xC0000 #define ARCHIVE_FORMAT_RAR 0xD0000 -#define ARCHIVE_FORMAT_RAR_V5 (ARCHIVE_FORMAT_RAR | 1) #define ARCHIVE_FORMAT_7ZIP 0xE0000 #define ARCHIVE_FORMAT_WARC 0xF0000 +#define ARCHIVE_FORMAT_RAR_V5 0x100000 /* * Codes returned by archive_read_format_capabilities(). @@ -1095,6 +1095,8 @@ __LA_DECL int archive_match_excluded(struct archive *, */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); +/* Control recursive inclusion of directory content when directory is included. Default on. */ +__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c index 7beeee86..952e20df 100644 --- a/libarchive/archive_acl.c +++ b/libarchive/archive_acl.c @@ -138,14 +138,10 @@ archive_acl_clear(struct archive_acl *acl) free(acl->acl_head); acl->acl_head = ap; } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; acl->acl_p = NULL; acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ @@ -324,14 +320,10 @@ acl_new_entry(struct archive_acl *acl, return (NULL); } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; /* * If there's a matching entry already in the list, overwrite it. diff --git a/libarchive/archive_blake2sp_ref.c b/libarchive/archive_blake2sp_ref.c index 90a38577..aef10108 100644 --- a/libarchive/archive_blake2sp_ref.c +++ b/libarchive/archive_blake2sp_ref.c @@ -89,7 +89,7 @@ int blake2sp_init( blake2sp_state *S, size_t outlen ) return -1; for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; + if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1; S->R->last_node = 1; S->S[PARALLELISM_DEGREE - 1]->last_node = 1; @@ -112,7 +112,7 @@ int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t return -1; for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; + if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1; S->R->last_node = 1; S->S[PARALLELISM_DEGREE - 1]->last_node = 1; @@ -230,7 +230,7 @@ int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void if( keylen > BLAKE2S_KEYBYTES ) return -1; for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; + if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1; S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ diff --git a/libarchive/archive_disk_acl_sunos.c b/libarchive/archive_disk_acl_sunos.c index bc84fd67..b0f5dfad 100644 --- a/libarchive/archive_disk_acl_sunos.c +++ b/libarchive/archive_disk_acl_sunos.c @@ -145,10 +145,8 @@ sunacl_get(int cmd, int *aclcnt, int fd, const char *path) cnt = facl(fd, cmd, cnt, aclp); } } else { - if (aclp != NULL) { - free(aclp); - aclp = NULL; - } + free(aclp); + aclp = NULL; break; } } diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index f722bbe8..d9d54b87 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -168,6 +168,7 @@ archive_entry_clear(struct archive_entry *entry) archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; memset(entry, 0, sizeof(*entry)); return entry; } @@ -202,6 +203,9 @@ archive_entry_clone(struct archive_entry *entry) entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); + /* Copy symlink type */ + entry2->ae_symlink_type = entry->ae_symlink_type; + /* Copy encryption status */ entry2->encryption = entry->encryption; @@ -253,6 +257,7 @@ archive_entry_new2(struct archive *a) if (entry == NULL) return (NULL); entry->archive = a; + entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED; return (entry); } @@ -675,6 +680,12 @@ archive_entry_symlink(struct archive_entry *entry) return (NULL); } +int +archive_entry_symlink_type(struct archive_entry *entry) +{ + return (entry->ae_symlink_type); +} + const char * archive_entry_symlink_utf8(struct archive_entry *entry) { @@ -1246,6 +1257,12 @@ archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) } void +archive_entry_set_symlink_type(struct archive_entry *entry, int type) +{ + entry->ae_symlink_type = type; +} + +void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&entry->ae_symlink, linkname); @@ -1560,10 +1577,8 @@ archive_entry_acl_text_compat(int *flags) const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { - if (entry->acl.acl_text_w != NULL) { - free(entry->acl.acl_text_w); - entry->acl.acl_text_w = NULL; - } + free(entry->acl.acl_text_w); + entry->acl.acl_text_w = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl, NULL, flags, entry->archive); @@ -1574,10 +1589,8 @@ archive_entry_acl_text_w(struct archive_entry *entry, int flags) const char * archive_entry_acl_text(struct archive_entry *entry, int flags) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - entry->acl.acl_text = NULL; - } + free(entry->acl.acl_text); + entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL, flags, NULL); @@ -1590,10 +1603,8 @@ int _archive_entry_acl_text_l(struct archive_entry *entry, int flags, const char **acl_text, size_t *len, struct archive_string_conv *sc) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - entry->acl.acl_text = NULL; - } + free(entry->acl.acl_text); + entry->acl.acl_text = NULL; if (archive_entry_acl_text_compat(&flags) == 0) entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, @@ -1638,6 +1649,51 @@ _archive_entry_acl_text_l(struct archive_entry *entry, int flags, * SUCH DAMAGE. */ +/* + * Supported file flags on FreeBSD and Mac OS: + * sappnd,sappend SF_APPEND + * arch,archived SF_ARCHIVED + * schg,schange,simmutable SF_IMMUTABLE + * sunlnk,sunlink SF_NOUNLINK (FreeBSD only) + * uappnd,uappend UF_APPEND + * compressed UF_COMPRESSED (Mac OS only) + * hidden,uhidden UF_HIDDEN + * uchg,uchange,uimmutable UF_IMMUTABLE + * nodump UF_NODUMP + * uunlnk,uunlink UF_NOUNLINK (FreeBSD only) + * offline,uoffline UF_OFFLINE (FreeBSD only) + * opaque UF_OPAQUE + * rdonly,urdonly,readonly UF_READONLY (FreeBSD only) + * reparse,ureparse UF_REPARSE (FreeBSD only) + * sparse,usparse UF_SPARSE (FreeBSD only) + * system,usystem UF_SYSTEM (FreeBSD only) + * + * See chflags(2) for more information + * + * Supported file attributes on Linux: + * a append only FS_APPEND_FL sappnd + * A no atime updates FS_NOATIME_FL atime + * c compress FS_COMPR_FL compress + * C no copy on write FS_NOCOW_FL cow + * d no dump FS_NODUMP_FL dump + * D synchronous directory updates FS_DIRSYNC_FL dirsync + * i immutable FS_IMMUTABLE_FL schg + * j data journalling FS_JOURNAL_DATA_FL journal + * P project hierarchy FS_PROJINHERIT_FL projinherit + * s secure deletion FS_SECRM_FL securedeletion + * S synchronous updates FS_SYNC_FL sync + * t no tail-merging FS_NOTAIL_FL tail + * T top of directory hierarchy FS_TOPDIR_FL topdir + * u undeletable FS_UNRM_FL undel + * + * See ioctl_iflags(2) for more information + * + * Equivalent file flags supported on FreeBSD / Mac OS and Linux: + * SF_APPEND FS_APPEND_FL sappnd + * SF_IMMUTABLE FS_IMMUTABLE_FL schg + * UF_NODUMP FS_NODUMP_FL nodump + */ + static const struct flag { const char *name; const wchar_t *wname; @@ -1646,190 +1702,149 @@ static const struct flag { } flags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND - { "nosappnd", L"nosappnd", SF_APPEND, 0 }, - { "nosappend", L"nosappend", SF_APPEND, 0 }, + { "nosappnd", L"nosappnd", SF_APPEND, 0}, + { "nosappend", L"nosappend", SF_APPEND, 0}, #endif #if defined(FS_APPEND_FL) /* 'a' */ - { "nosappnd", L"nosappnd", FS_APPEND_FL, 0 }, - { "nosappend", L"nosappend", FS_APPEND_FL, 0 }, + { "nosappnd", L"nosappnd", FS_APPEND_FL, 0}, + { "nosappend", L"nosappend", FS_APPEND_FL, 0}, #elif defined(EXT2_APPEND_FL) /* 'a' */ - { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, - { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, + { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0}, + { "nosappend", L"nosappend", EXT2_APPEND_FL, 0}, #endif #ifdef SF_ARCHIVED - { "noarch", L"noarch", SF_ARCHIVED, 0 }, - { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, + { "noarch", L"noarch", SF_ARCHIVED, 0}, + { "noarchived", L"noarchived", SF_ARCHIVED, 0}, #endif #ifdef SF_IMMUTABLE - { "noschg", L"noschg", SF_IMMUTABLE, 0 }, - { "noschange", L"noschange", SF_IMMUTABLE, 0 }, - { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, + { "noschg", L"noschg", SF_IMMUTABLE, 0}, + { "noschange", L"noschange", SF_IMMUTABLE, 0}, + { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0}, #endif #if defined(FS_IMMUTABLE_FL) /* 'i' */ - { "noschg", L"noschg", FS_IMMUTABLE_FL, 0 }, - { "noschange", L"noschange", FS_IMMUTABLE_FL, 0 }, - { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 }, + { "noschg", L"noschg", FS_IMMUTABLE_FL, 0}, + { "noschange", L"noschange", FS_IMMUTABLE_FL, 0}, + { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0}, #elif defined(EXT2_IMMUTABLE_FL) /* 'i' */ - { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, - { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, - { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, + { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0}, + { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0}, + { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0}, #endif #ifdef SF_NOUNLINK - { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, - { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, -#endif -#ifdef SF_SNAPSHOT - { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, + { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0}, + { "nosunlink", L"nosunlink", SF_NOUNLINK, 0}, #endif #ifdef UF_APPEND - { "nouappnd", L"nouappnd", UF_APPEND, 0 }, - { "nouappend", L"nouappend", UF_APPEND, 0 }, + { "nouappnd", L"nouappnd", UF_APPEND, 0}, + { "nouappend", L"nouappend", UF_APPEND, 0}, #endif #ifdef UF_IMMUTABLE - { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, - { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, - { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, + { "nouchg", L"nouchg", UF_IMMUTABLE, 0}, + { "nouchange", L"nouchange", UF_IMMUTABLE, 0}, + { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0}, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #if defined(FS_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, FS_NODUMP_FL}, -#elif defined(EXT2_NODUMP_FL) /* 'd' */ +#elif defined(EXT2_NODUMP_FL) { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE - { "noopaque", L"noopaque", UF_OPAQUE, 0 }, + { "noopaque", L"noopaque", UF_OPAQUE, 0}, #endif #ifdef UF_NOUNLINK - { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, - { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, + { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0}, + { "nouunlink", L"nouunlink", UF_NOUNLINK, 0}, #endif #ifdef UF_COMPRESSED - { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, + /* Mac OS */ + { "nocompressed", L"nocompressed", UF_COMPRESSED, 0}, #endif #ifdef UF_HIDDEN - { "nohidden", L"nohidden", UF_HIDDEN, 0 }, + { "nohidden", L"nohidden", UF_HIDDEN, 0}, + { "nouhidden", L"nouhidden", UF_HIDDEN, 0}, #endif -#if defined(FS_UNRM_FL) - { "nouunlink", L"nouunlink", FS_UNRM_FL, 0}, -#elif defined(EXT2_UNRM_FL) - { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, +#ifdef UF_OFFLINE + { "nooffline", L"nooffline", UF_OFFLINE, 0}, + { "nouoffline", L"nouoffline", UF_OFFLINE, 0}, #endif - -#if defined(FS_BTREE_FL) - { "nobtree", L"nobtree", FS_BTREE_FL, 0 }, -#elif defined(EXT2_BTREE_FL) - { "nobtree", L"nobtree", EXT2_BTREE_FL, 0 }, +#ifdef UF_READONLY + { "nordonly", L"nordonly", UF_READONLY, 0}, + { "nourdonly", L"nourdonly", UF_READONLY, 0}, + { "noreadonly", L"noreadonly", UF_READONLY, 0}, #endif - -#if defined(FS_ECOMPR_FL) - { "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 }, -#elif defined(EXT2_ECOMPR_FL) - { "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 }, +#ifdef UF_SPARSE + { "nosparse", L"nosparse", UF_SPARSE, 0}, + { "nousparse", L"nousparse", UF_SPARSE, 0}, #endif - -#if defined(FS_COMPR_FL) /* 'c' */ - { "nocompress", L"nocompress", FS_COMPR_FL, 0 }, -#elif defined(EXT2_COMPR_FL) /* 'c' */ - { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, +#ifdef UF_REPARSE + { "noreparse", L"noreparse", UF_REPARSE, 0}, + { "noureparse", L"noureparse", UF_REPARSE, 0}, #endif - -#if defined(FS_NOATIME_FL) /* 'A' */ - { "noatime", L"noatime", 0, FS_NOATIME_FL}, -#elif defined(EXT2_NOATIME_FL) /* 'A' */ - { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, +#ifdef UF_SYSTEM + { "nosystem", L"nosystem", UF_SYSTEM, 0}, + { "nousystem", L"nousystem", UF_SYSTEM, 0}, #endif - -#if defined(FS_DIRTY_FL) - { "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0}, -#elif defined(EXT2_DIRTY_FL) - { "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0}, +#if defined(FS_UNRM_FL) /* 'u' */ + { "noundel", L"noundel", FS_UNRM_FL, 0}, +#elif defined(EXT2_UNRM_FL) + { "noundel", L"noundel", EXT2_UNRM_FL, 0}, #endif -#if defined(FS_COMPRBLK_FL) -#if defined(FS_NOCOMPR_FL) - { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL}, -#else - { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0}, -#endif -#elif defined(EXT2_COMPRBLK_FL) -#if defined(EXT2_NOCOMPR_FL) - { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL}, -#else - { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0}, +#if defined(FS_COMPR_FL) /* 'c' */ + { "nocompress", L"nocompress", FS_COMPR_FL, 0}, +#elif defined(EXT2_COMPR_FL) + { "nocompress", L"nocompress", EXT2_COMPR_FL, 0}, #endif + +#if defined(FS_NOATIME_FL) /* 'A' */ + { "noatime", L"noatime", 0, FS_NOATIME_FL}, +#elif defined(EXT2_NOATIME_FL) + { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif -#if defined(FS_DIRSYNC_FL) - { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, +#if defined(FS_DIRSYNC_FL) /* 'D' */ + { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, #elif defined(EXT2_DIRSYNC_FL) - { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, + { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif -#if defined(FS_INDEX_FL) - { "nohashidx", L"nohashidx", FS_INDEX_FL, 0}, -#elif defined(EXT2_INDEX_FL) - { "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0}, -#endif -#if defined(FS_IMAGIC_FL) - { "noimagic", L"noimagic", FS_IMAGIC_FL, 0}, -#elif defined(EXT2_IMAGIC_FL) - { "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0}, -#endif -#if defined(FS_JOURNAL_DATA_FL) - { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, +#if defined(FS_JOURNAL_DATA_FL) /* 'j' */ + { "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0}, + { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, #elif defined(EXT3_JOURNAL_DATA_FL) - { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, + { "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0}, + { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif -#if defined(FS_SECRM_FL) - { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, +#if defined(FS_SECRM_FL) /* 's' */ + { "nosecdel", L"nosecdel", FS_SECRM_FL, 0}, + { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, #elif defined(EXT2_SECRM_FL) - { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, + { "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0}, + { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif -#if defined(FS_SYNC_FL) - { "nosync", L"nosync", FS_SYNC_FL, 0}, +#if defined(FS_SYNC_FL) /* 'S' */ + { "nosync", L"nosync", FS_SYNC_FL, 0}, #elif defined(EXT2_SYNC_FL) - { "nosync", L"nosync", EXT2_SYNC_FL, 0}, + { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif -#if defined(FS_NOTAIL_FL) - { "notail", L"notail", 0, FS_NOTAIL_FL}, +#if defined(FS_NOTAIL_FL) /* 't' */ + { "notail", L"notail", 0, FS_NOTAIL_FL}, #elif defined(EXT2_NOTAIL_FL) - { "notail", L"notail", 0, EXT2_NOTAIL_FL}, + { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif -#if defined(FS_TOPDIR_FL) - { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, +#if defined(FS_TOPDIR_FL) /* 'T' */ + { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, #elif defined(EXT2_TOPDIR_FL) - { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, -#endif -#ifdef FS_ENCRYPT_FL - { "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0}, -#endif -#ifdef FS_HUGE_FILE_FL - { "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0}, -#endif -#ifdef FS_EXTENT_FL - { "noextent", L"noextent", FS_EXTENT_FL, 0}, -#endif -#ifdef FS_EA_INODE_FL - { "noeainode", L"noeainode", FS_EA_INODE_FL, 0}, -#endif -#ifdef FS_EOFBLOCKS_FL - { "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0}, -#endif -#ifdef FS_NOCOW_FL - { "nocow", L"nocow", FS_NOCOW_FL, 0}, -#endif -#ifdef FS_INLINE_DATA_FL - { "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0}, + { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif -#ifdef FS_PROJINHERIT_FL - { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, +#ifdef FS_NOCOW_FL /* 'C' */ + { "nocow", L"nocow", 0, FS_NOCOW_FL}, #endif -#if defined(FS_RESERVED_FL) - { "noreserved", L"noreserved", FS_RESERVED_FL, 0}, -#elif defined(EXT2_RESERVED_FL) - { "noreserved", L"noreserved", EXT2_RESERVED_FL, 0}, +#ifdef FS_PROJINHERIT_FL /* 'P' */ + { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, #endif - { NULL, NULL, 0, 0 } + { NULL, NULL, 0, 0} }; /* diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 47653f37..f8a7e532 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -191,6 +191,13 @@ struct archive_entry; #define AE_IFIFO ((__LA_MODE_T)0010000) /* + * Symlink types + */ +#define AE_SYMLINK_TYPE_UNDEFINED 0 +#define AE_SYMLINK_TYPE_FILE 1 +#define AE_SYMLINK_TYPE_DIRECTORY 2 + +/* * Basic object manipulation */ @@ -275,6 +282,7 @@ __LA_DECL int archive_entry_size_is_set(struct archive_entry *); __LA_DECL const char *archive_entry_strmode(struct archive_entry *); __LA_DECL const char *archive_entry_symlink(struct archive_entry *); __LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *); +__LA_DECL int archive_entry_symlink_type(struct archive_entry *); __LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *); __LA_DECL la_int64_t archive_entry_uid(struct archive_entry *); __LA_DECL const char *archive_entry_uname(struct archive_entry *); @@ -350,6 +358,7 @@ __LA_DECL void archive_entry_unset_size(struct archive_entry *); __LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *); __LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *); +__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int); __LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *); @@ -692,7 +701,6 @@ __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *, struct archive_entry **, struct archive_entry **); __LA_DECL struct archive_entry *archive_entry_partial_links( struct archive_entry_linkresolver *res, unsigned int *links); - #ifdef __cplusplus } #endif diff --git a/libarchive/archive_entry_misc.3 b/libarchive/archive_entry_misc.3 new file mode 100644 index 00000000..9b1e3ea2 --- /dev/null +++ b/libarchive/archive_entry_misc.3 @@ -0,0 +1,62 @@ +.\" Copyright (c) 2019 Martin Matuska +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 15, 2019 +.Dt ARCHIVE_ENTRY_MISC 3 +.Os +.Sh NAME +.Nm archive_entry_symlink_type , +.Nm archive_entry_set_symlink_type +.Nd miscellaneous functions for manipulating properties of archive_entry. +.Sh LIBRARY +Streaming Archive Library (libarchive, -larchive) +.Sh SYNOPSIS +.In archive_entry.h +.Ft int +.Fn archive_entry_symlink_type "struct archive_entry *a" +.Ft void +.Fn archive_entry_set_symlink_type "struct archive_entry *a" "int" +.Sh DESCRIPTION +The function +.Fn archive_entry_symlink_type +returns and the function +.Fn archive_entry_set_symlink_type +sets the type of the symbolic link stored in an archive entry. These functions +have special meaning on operating systems that support multiple symbolic link +types (e.g. Microsoft Windows). +.Pp +Supported values are: +.Bl -tag -width "AE_SYMLINK_TYPE_DIRECTORY" -compact +.It AE_SYMLINK_TYPE_UNDEFINED +Symbolic link target type is not defined (default on unix systems) +.It AE_SYMLINK_TYPE_FILE +Symbolic link points to a file +.It AE_SYMLINK_TYPE_DIRECTORY +Symbolic link points to a directory +.El +.Sh SEE ALSO +.Xr archive_entry 3 , +.Xr archive_entry_paths 3 , +.Xr archive_entry_stat 3 , +.Xr libarchive 3 diff --git a/libarchive/archive_entry_private.h b/libarchive/archive_entry_private.h index c69233e6..3d569bbf 100644 --- a/libarchive/archive_entry_private.h +++ b/libarchive/archive_entry_private.h @@ -176,6 +176,9 @@ struct archive_entry { /* Miscellaneous. */ char strmode[12]; + + /* Symlink type support */ + int ae_symlink_type; }; #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */ diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c index f2996557..392916de 100644 --- a/libarchive/archive_hmac.c +++ b/libarchive/archive_hmac.c @@ -83,6 +83,7 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { +#pragma GCC diagnostic ignored "-Wcast-qual" BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index f150e822..04747b1f 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -93,6 +93,9 @@ struct archive_match { /* exclusion/inclusion set flag. */ int setflag; + /* Recursively include directory content? */ + int recursive_include; + /* * Matching filename patterns. */ @@ -223,6 +226,7 @@ archive_match_new(void) return (NULL); a->archive.magic = ARCHIVE_MATCH_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; + a->recursive_include = 1; match_list_init(&(a->inclusions)); match_list_init(&(a->exclusions)); __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs); @@ -471,6 +475,28 @@ archive_match_path_excluded(struct archive *_a, } /* + * When recursive inclusion of directory content is enabled, + * an inclusion pattern that matches a directory will also + * include everything beneath that directory. Enabled by default. + * + * For compatibility with GNU tar, exclusion patterns always + * match if a subset of the full patch matches (i.e., they are + * are not rooted at the beginning of the path) and thus there + * is no corresponding non-recursive exclusion mode. + */ +int +archive_match_set_inclusion_recursion(struct archive *_a, int enabled) +{ + struct archive_match *a; + + archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, + ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion"); + a = (struct archive_match *)_a; + a->recursive_include = enabled; + return (ARCHIVE_OK); +} + +/* * Utility functions to get statistic information for inclusion patterns. */ int @@ -781,7 +807,10 @@ static int match_path_inclusion(struct archive_match *a, struct match *m, int mbs, const void *pn) { - int flag = PATHMATCH_NO_ANCHOR_END; + /* Recursive operation requires only a prefix match. */ + int flag = a->recursive_include ? + PATHMATCH_NO_ANCHOR_END : + 0; int r; if (mbs) { @@ -1232,7 +1261,7 @@ set_timefilter_pathname_mbs(struct archive_match *a, int timetype, archive_set_error(&(a->archive), EINVAL, "pathname is empty"); return (ARCHIVE_FAILED); } - if (stat(path, &st) != 0) { + if (la_stat(path, &st) != 0) { archive_set_error(&(a->archive), errno, "Failed to stat()"); return (ARCHIVE_FAILED); } diff --git a/libarchive/archive_pack_dev.c b/libarchive/archive_pack_dev.c index 53bddd79..a5e57ac2 100644 --- a/libarchive/archive_pack_dev.c +++ b/libarchive/archive_pack_dev.c @@ -60,6 +60,9 @@ __RCSID("$NetBSD$"); #ifdef HAVE_SYS_SYSMACROS_H #include <sys/sysmacros.h> #endif +#ifdef HAVE_SYS_MKDEV_H +#include <sys/mkdev.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif diff --git a/libarchive/archive_platform.h b/libarchive/archive_platform.h index 32b884c9..b8bcb52b 100644 --- a/libarchive/archive_platform.h +++ b/libarchive/archive_platform.h @@ -69,6 +69,8 @@ * either Windows or Posix APIs. */ #if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) #include "archive_windows.h" +#else +#define la_stat(path,stref) stat(path,stref) #endif /* diff --git a/libarchive/archive_ppmd8.c b/libarchive/archive_ppmd8.c new file mode 100644 index 00000000..d1779395 --- /dev/null +++ b/libarchive/archive_ppmd8.c @@ -0,0 +1,1287 @@ +/* Ppmd8.c -- PPMdI codec +2016-05-21 : Igor Pavlov : Public domain +This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */ + +#include "archive_platform.h" + +#include <string.h> + +#include "archive_ppmd8_private.h" + +const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + +#define MAX_FREQ 124 +#define UNIT_SIZE 12 + +#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) +#define U2I(nu) (p->Units2Indx[(nu) - 1]) +#define I2U(indx) (p->Indx2Units[indx]) + +#ifdef PPMD_32BIT + #define REF(ptr) (ptr) +#else + #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) +#endif + +#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) + +#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref)) +#define STATS(ctx) Ppmd8_GetStats(p, ctx) +#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx) +#define SUFFIX(ctx) CTX((ctx)->Suffix) + +#define kTop (1 << 24) +#define kBot (1 << 15) + +typedef CPpmd8_Context * CTX_PTR; + +struct CPpmd8_Node_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Node_ * + #else + UInt32 + #endif + CPpmd8_Node_Ref; + +typedef struct CPpmd8_Node_ +{ + UInt32 Stamp; + CPpmd8_Node_Ref Next; + UInt32 NU; +} CPpmd8_Node; + +#ifdef PPMD_32BIT + #define NODE(ptr) (ptr) +#else + #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs))) +#endif + +#define EMPTY_NODE 0xFFFFFFFF + +void Ppmd8_Construct(CPpmd8 *p) +{ + unsigned i, k, m; + + p->Base = 0; + + for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) + { + unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); + do { p->Units2Indx[k++] = (Byte)i; } while (--step); + p->Indx2Units[i] = (Byte)k; + } + + p->NS2BSIndx[0] = (0 << 1); + p->NS2BSIndx[1] = (1 << 1); + memset(p->NS2BSIndx + 2, (2 << 1), 9); + memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); + + for (i = 0; i < 5; i++) + p->NS2Indx[i] = (Byte)i; + for (m = i, k = 1; i < 260; i++) + { + p->NS2Indx[i] = (Byte)m; + if (--k == 0) + k = (++m) - 4; + } +} + +void Ppmd8_Free(CPpmd8 *p) +{ + free(p->Base); + p->Size = 0; + p->Base = 0; +} + +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size) +{ + if (p->Base == 0 || p->Size != size) + { + Ppmd8_Free(p); + p->AlignOffset = + #ifdef PPMD_32BIT + (4 - size) & 3; + #else + 4 - (size & 3); + #endif + if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0) + return False; + p->Size = size; + } + return True; +} + +static void InsertNode(CPpmd8 *p, void *node, unsigned indx) +{ + ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE; + ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx]; + ((CPpmd8_Node *)node)->NU = I2U(indx); + p->FreeList[indx] = REF(node); + p->Stamps[indx]++; +} + +static void *RemoveNode(CPpmd8 *p, unsigned indx) +{ + CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]); + p->FreeList[indx] = node->Next; + p->Stamps[indx]--; + return node; +} + +static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx) +{ + unsigned i, nu = I2U(oldIndx) - I2U(newIndx); + ptr = (Byte *)ptr + U2B(I2U(newIndx)); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); + } + InsertNode(p, ptr, i); +} + +static void GlueFreeBlocks(CPpmd8 *p) +{ + CPpmd8_Node_Ref head = 0; + CPpmd8_Node_Ref *prev = &head; + unsigned i; + + p->GlueCount = 1 << 13; + memset(p->Stamps, 0, sizeof(p->Stamps)); + + /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end. + All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */ + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + /* Glue free blocks */ + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i]; + p->FreeList[i] = 0; + while (next != 0) + { + CPpmd8_Node *node = NODE(next); + if (node->NU != 0) + { + CPpmd8_Node *node2; + *prev = next; + prev = &(node->Next); + while ((node2 = node + node->NU)->Stamp == EMPTY_NODE) + { + node->NU += node2->NU; + node2->NU = 0; + } + } + next = node->Next; + } + } + *prev = 0; + + /* Fill lists of free blocks */ + while (head != 0) + { + CPpmd8_Node *node = NODE(head); + unsigned nu; + head = node->Next; + nu = node->NU; + if (nu == 0) + continue; + for (; nu > 128; nu -= 128, node += 128) + InsertNode(p, node, PPMD_NUM_INDEXES - 1); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, node + k, nu - k - 1); + } + InsertNode(p, node, i); + } +} + +static void *AllocUnitsRare(CPpmd8 *p, unsigned indx) +{ + unsigned i; + void *retVal; + if (p->GlueCount == 0) + { + GlueFreeBlocks(p); + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + } + i = indx; + do + { + if (++i == PPMD_NUM_INDEXES) + { + UInt32 numBytes = U2B(I2U(indx)); + p->GlueCount--; + return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); + } + } + while (p->FreeList[i] == 0); + retVal = RemoveNode(p, i); + SplitBlock(p, retVal, i, indx); + return retVal; +} + +static void *AllocUnits(CPpmd8 *p, unsigned indx) +{ + UInt32 numBytes; + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + numBytes = U2B(I2U(indx)); + if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) + { + void *retVal = p->LoUnit; + p->LoUnit += numBytes; + return retVal; + } + return AllocUnitsRare(p, indx); +} + +#define MyMem12Cpy(dest, src, num) \ + { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \ + do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); } + +static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU) +{ + unsigned i0 = U2I(oldNU); + unsigned i1 = U2I(newNU); + if (i0 == i1) + return oldPtr; + if (p->FreeList[i1] != 0) + { + void *ptr = RemoveNode(p, i1); + MyMem12Cpy(ptr, oldPtr, newNU); + InsertNode(p, oldPtr, i0); + return ptr; + } + SplitBlock(p, oldPtr, i0, i1); + return oldPtr; +} + +static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu) +{ + InsertNode(p, ptr, U2I(nu)); +} + +static void SpecialFreeUnit(CPpmd8 *p, void *ptr) +{ + if ((Byte *)ptr != p->UnitsStart) + InsertNode(p, ptr, 0); + else + { + #ifdef PPMD8_FREEZE_SUPPORT + *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */ + #endif + p->UnitsStart += UNIT_SIZE; + } +} + +static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu) +{ + unsigned indx = U2I(nu); + void *ptr; + if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx]) + return oldPtr; + ptr = RemoveNode(p, indx); + MyMem12Cpy(ptr, oldPtr, nu); + if ((Byte*)oldPtr != p->UnitsStart) + InsertNode(p, oldPtr, indx); + else + p->UnitsStart += U2B(I2U(indx)); + return ptr; +} + +static void ExpandTextArea(CPpmd8 *p) +{ + UInt32 count[PPMD_NUM_INDEXES]; + unsigned i; + memset(count, 0, sizeof(count)); + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + { + CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart; + for (; node->Stamp == EMPTY_NODE; node += node->NU) + { + node->Stamp = 0; + count[U2I(node->NU)]++; + } + p->UnitsStart = (Byte *)node; + } + + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i]; + while (count[i] != 0) + { + CPpmd8_Node *node = NODE(*next); + while (node->Stamp == 0) + { + *next = node->Next; + node = NODE(*next); + p->Stamps[i]--; + if (--count[i] == 0) + break; + } + next = &node->Next; + } + } +} + +#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) + +static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) +{ + (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); + (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); +} + +#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); } + +static void RestartModel(CPpmd8 *p) +{ + unsigned i, k, m, r; + + memset(p->FreeList, 0, sizeof(p->FreeList)); + memset(p->Stamps, 0, sizeof(p->Stamps)); + RESET_TEXT(0); + p->HiUnit = p->Text + p->Size; + p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; + p->GlueCount = 0; + + p->OrderFall = p->MaxOrder; + p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; + p->PrevSuccess = 0; + + p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ + p->MinContext->Suffix = 0; + p->MinContext->NumStats = 255; + p->MinContext->Flags = 0; + p->MinContext->SummFreq = 256 + 1; + p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ + p->LoUnit += U2B(256 / 2); + p->MinContext->Stats = REF(p->FoundState); + for (i = 0; i < 256; i++) + { + CPpmd_State *s = &p->FoundState[i]; + s->Symbol = (Byte)i; + s->Freq = 1; + SetSuccessor(s, 0); + } + + for (i = m = 0; m < 25; m++) + { + while (p->NS2Indx[i] == m) + i++; + for (k = 0; k < 8; k++) + { + UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1)); + UInt16 *dest = p->BinSumm[m] + k; + for (r = 0; r < 64; r += 8) + dest[r] = val; + } + } + + for (i = m = 0; m < 24; m++) + { + while (p->NS2Indx[i + 3] == m + 3) + i++; + for (k = 0; k < 32; k++) + { + CPpmd_See *s = &p->See[m][k]; + s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4)); + s->Count = 7; + } + } +} + +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) +{ + p->MaxOrder = maxOrder; + p->RestoreMethod = restoreMethod; + RestartModel(p); + p->DummySee.Shift = PPMD_PERIOD_BITS; + p->DummySee.Summ = 0; /* unused */ + p->DummySee.Count = 64; /* unused */ +} + +static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale) +{ + unsigned i = ctx->NumStats, escFreq, sumFreq, flags; + CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1); + ctx->Stats = REF(s); + #ifdef PPMD8_FREEZE_SUPPORT + /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */ + scale |= (ctx->SummFreq >= ((UInt32)1 << 15)); + #endif + flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40); + escFreq = ctx->SummFreq - s->Freq; + sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale)); + do + { + escFreq -= (++s)->Freq; + sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale)); + flags |= 0x08 * (s->Symbol >= 0x40); + } + while (--i); + ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale)); + ctx->Flags = (Byte)flags; +} + +static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) +{ + CPpmd_State tmp = *t1; + *t1 = *t2; + *t2 = tmp; +} + +static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + int i; + unsigned tmp; + CPpmd_State *s; + + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart) + { + if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + if (SUCCESSOR(s) || order <= 9) /* O_BOUND */ + return REF(ctx); + } + SpecialFreeUnit(p, ctx); + return 0; + } + + ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1)); + + for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart) + { + CPpmd_State *s2 = STATS(ctx) + (i--); + SetSuccessor(s, 0); + SwapStates(s, s2); + } + else if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + if (i != ctx->NumStats && order) + { + ctx->NumStats = (Byte)i; + s = STATS(ctx); + if (i < 0) + { + FreeUnits(p, s, tmp); + SpecialFreeUnit(p, ctx); + return 0; + } + if (i == 0) + { + ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(ctx) = *s; + FreeUnits(p, s, tmp); + /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */ + ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3); + } + else + Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i); + } + return REF(ctx); +} + +#ifdef PPMD8_FREEZE_SUPPORT +static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + CPpmd_State *s; + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + /* Suffix context can be removed already, since different (high-order) + Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */ + if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF)) + { + FreeUnits(p, ctx, 1); + return 0; + } + else + return REF(ctx); + } + + for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + return REF(ctx); +} +#endif + +static UInt32 GetUsedMemory(const CPpmd8 *p) +{ + UInt32 v = 0; + unsigned i; + for (i = 0; i < PPMD_NUM_INDEXES; i++) + v += p->Stamps[i] * I2U(i); + return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v); +} + +#ifdef PPMD8_FREEZE_SUPPORT + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor) +#else + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1) +#endif + +static void RestoreModel(CPpmd8 *p, CTX_PTR c1 + #ifdef PPMD8_FREEZE_SUPPORT + , CTX_PTR fSuccessor + #endif + ) +{ + CTX_PTR c; + CPpmd_State *s; + RESET_TEXT(0); + for (c = p->MaxContext; c != c1; c = SUFFIX(c)) + if (--(c->NumStats) == 0) + { + s = STATS(c); + c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(c) = *s; + SpecialFreeUnit(p, s); + ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3); + } + else + Refresh(p, c, (c->NumStats+3) >> 1, 0); + + for (; c != p->MinContext; c = SUFFIX(c)) + if (!c->NumStats) + ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1)); + else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats) + Refresh(p, c, (c->NumStats + 2) >> 1, 1); + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + p->MaxContext = fSuccessor; + p->GlueCount += !(p->Stamps[1] & 1); + } + else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE) + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + RemoveBinContexts(p, p->MaxContext, 0); + p->RestoreMethod++; + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } + else + #endif + if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1)) + RestartModel(p); + else + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + do + { + CutOff(p, p->MaxContext, 0); + ExpandTextArea(p); + } + while (GetUsedMemory(p) > 3 * (p->Size >> 2)); + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } +} + +static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State upState; + Byte flags; + CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); + /* fixed over Shkarin's code. Maybe it could work without + 1 too. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + + if (!skip) + ps[numPs++] = p->FoundState; + + while (c->Suffix) + { + CPpmd_Void_Ref successor; + CPpmd_State *s; + c = SUFFIX(c); + if (s1) + { + s = s1; + s1 = NULL; + } + else if (c->NumStats != 0) + { + for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq++; + c->SummFreq++; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24))); + } + successor = SUCCESSOR(s); + if (successor != upBranch) + { + c = CTX(successor); + if (numPs == 0) + return c; + break; + } + ps[numPs++] = s; + } + + upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch); + SetSuccessor(&upState, upBranch + 1); + flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40)); + + if (c->NumStats == 0) + upState.Freq = ONE_STATE(c)->Freq; + else + { + UInt32 cf, s0; + CPpmd_State *s; + for (s = STATS(c); s->Symbol != upState.Symbol; s++); + cf = s->Freq - 1; + s0 = c->SummFreq - c->NumStats - cf; + upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0))); + } + + do + { + /* Create Child */ + CTX_PTR c1; /* = AllocContext(p); */ + if (p->HiUnit != p->LoUnit) + c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); + else if (p->FreeList[0] != 0) + c1 = (CTX_PTR)RemoveNode(p, 0); + else + { + c1 = (CTX_PTR)AllocUnitsRare(p, 0); + if (!c1) + return NULL; + } + c1->NumStats = 0; + c1->Flags = flags; + *ONE_STATE(c1) = upState; + c1->Suffix = REF(c); + SetSuccessor(ps[--numPs], REF(c1)); + c = c1; + } + while (numPs != 0); + + return c; +} + +static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State *s = NULL; + CTX_PTR c1 = c; + CPpmd_Void_Ref upBranch = REF(p->Text); + + #ifdef PPMD8_FREEZE_SUPPORT + /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + ps[numPs++] = p->FoundState; + #endif + + SetSuccessor(p->FoundState, upBranch); + p->OrderFall++; + + for (;;) + { + if (s1) + { + c = SUFFIX(c); + s = s1; + s1 = NULL; + } + else + { + if (!c->Suffix) + { + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + } + #endif + return c; + } + c = SUFFIX(c); + if (c->NumStats) + { + if ((s = STATS(c))->Symbol != p->FoundState->Symbol) + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (s->Freq < 32)); + } + } + if (SUCCESSOR(s)) + break; + #ifdef PPMD8_FREEZE_SUPPORT + ps[numPs++] = s; + #endif + SetSuccessor(s, upBranch); + p->OrderFall++; + } + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + c = CTX(SUCCESSOR(s)); + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + return c; + } + else + #endif + if (SUCCESSOR(s) <= upBranch) + { + CTX_PTR successor; + CPpmd_State *s2 = p->FoundState; + p->FoundState = s; + + successor = CreateSuccessors(p, False, NULL, c); + if (successor == NULL) + SetSuccessor(s, 0); + else + SetSuccessor(s, REF(successor)); + p->FoundState = s2; + } + + if (p->OrderFall == 1 && c1 == p->MaxContext) + { + SetSuccessor(p->FoundState, SUCCESSOR(s)); + p->Text--; + } + if (SUCCESSOR(s) == 0) + return NULL; + return CTX(SUCCESSOR(s)); +} + +static void UpdateModel(CPpmd8 *p) +{ + CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); + CTX_PTR c; + unsigned s0, ns, fFreq = p->FoundState->Freq; + Byte flag, fSymbol = p->FoundState->Symbol; + CPpmd_State *s = NULL; + + if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) + { + c = SUFFIX(p->MinContext); + + if (c->NumStats == 0) + { + s = ONE_STATE(c); + if (s->Freq < 32) + s->Freq++; + } + else + { + s = STATS(c); + if (s->Symbol != p->FoundState->Symbol) + { + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s[0].Freq >= s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + s--; + } + } + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + } + + c = p->MaxContext; + if (p->OrderFall == 0 && fSuccessor) + { + CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext); + if (cs == 0) + { + SetSuccessor(p->FoundState, 0); + RESTORE_MODEL(c, CTX(fSuccessor)); + } + else + { + SetSuccessor(p->FoundState, REF(cs)); + p->MaxContext = cs; + } + return; + } + + *p->Text++ = p->FoundState->Symbol; + successor = REF(p->Text); + if (p->Text >= p->UnitsStart) + { + RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */ + return; + } + + if (!fSuccessor) + { + CTX_PTR cs = ReduceOrder(p, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart) + { + CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + + if (--p->OrderFall == 0) + { + successor = fSuccessor; + p->Text -= (p->MaxContext != p->MinContext); + } + #ifdef PPMD8_FREEZE_SUPPORT + else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + successor = fSuccessor; + RESET_TEXT(0); + p->OrderFall = 0; + } + #endif + + s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq; + flag = (Byte)(0x08 * (fSymbol >= 0x40)); + + for (; c != p->MinContext; c = SUFFIX(c)) + { + unsigned ns1; + UInt32 cf, sf; + if ((ns1 = c->NumStats) != 0) + { + if ((ns1 & 1) != 0) + { + /* Expand for one UNIT */ + unsigned oldNU = (ns1 + 1) >> 1; + unsigned i = U2I(oldNU); + if (i != U2I(oldNU + 1)) + { + void *ptr = AllocUnits(p, i + 1); + void *oldPtr; + if (!ptr) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + oldPtr = STATS(c); + MyMem12Cpy(ptr, oldPtr, oldNU); + InsertNode(p, oldPtr, i); + c->Stats = STATS_REF(ptr); + } + } + c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns)); + } + else + { + CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0); + if (!s2) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + *s2 = *ONE_STATE(c); + c->Stats = REF(s2); + if (s2->Freq < MAX_FREQ / 4 - 1) + s2->Freq <<= 1; + else + s2->Freq = MAX_FREQ - 4; + c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2)); + } + cf = 2 * fFreq * (c->SummFreq + 6); + sf = (UInt32)s0 + c->SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf) + (cf >= 4 * sf); + c->SummFreq += 4; + } + else + { + cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf); + c->SummFreq = (UInt16)(c->SummFreq + cf); + } + { + CPpmd_State *s2 = STATS(c) + ns1 + 1; + SetSuccessor(s2, successor); + s2->Symbol = fSymbol; + s2->Freq = (Byte)cf; + c->Flags |= flag; + c->NumStats = (Byte)(ns1 + 1); + } + } + p->MaxContext = p->MinContext = CTX(fSuccessor); +} + +static void Rescale(CPpmd8 *p) +{ + unsigned i, adder, sumFreq, escFreq; + CPpmd_State *stats = STATS(p->MinContext); + CPpmd_State *s = p->FoundState; + { + CPpmd_State tmp = *s; + for (; s != stats; s--) + s[0] = s[-1]; + *s = tmp; + } + escFreq = p->MinContext->SummFreq - s->Freq; + s->Freq += 4; + adder = (p->OrderFall != 0 + #ifdef PPMD8_FREEZE_SUPPORT + || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE + #endif + ); + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq = s->Freq; + + i = p->MinContext->NumStats; + do + { + escFreq -= (++s)->Freq; + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq += s->Freq; + if (s[0].Freq > s[-1].Freq) + { + CPpmd_State *s1 = s; + CPpmd_State tmp = *s1; + do + s1[0] = s1[-1]; + while (--s1 != stats && tmp.Freq > s1[-1].Freq); + *s1 = tmp; + } + } + while (--i); + + if (s->Freq == 0) + { + unsigned numStats = p->MinContext->NumStats; + unsigned n0, n1; + do { i++; } while ((--s)->Freq == 0); + escFreq += i; + p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i); + if (p->MinContext->NumStats == 0) + { + CPpmd_State tmp = *stats; + tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq); + if (tmp.Freq > MAX_FREQ / 3) + tmp.Freq = MAX_FREQ / 3; + InsertNode(p, stats, U2I((numStats + 2) >> 1)); + p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40)); + *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; + return; + } + n0 = (numStats + 2) >> 1; + n1 = (p->MinContext->NumStats + 2) >> 1; + if (n0 != n1) + p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); + p->MinContext->Flags &= ~0x08; + p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40); + i = p->MinContext->NumStats; + do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i); + } + p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); + p->MinContext->Flags |= 0x4; + p->FoundState = STATS(p->MinContext); +} + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) +{ + CPpmd_See *see; + if (p->MinContext->NumStats != 0xFF) + { + see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] + + (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) + + 2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats < + ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) + + p->MinContext->Flags; + { + unsigned r = (see->Summ >> see->Shift); + see->Summ = (UInt16)(see->Summ - r); + *escFreq = r + (r == 0); + } + } + else + { + see = &p->DummySee; + *escFreq = 1; + } + return see; +} + +static void NextContext(CPpmd8 *p) +{ + CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); + if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart) + p->MinContext = p->MaxContext = c; + else + { + UpdateModel(p); + p->MinContext = p->MaxContext; + } +} + +void Ppmd8_Update1(CPpmd8 *p) +{ + CPpmd_State *s = p->FoundState; + s->Freq += 4; + p->MinContext->SummFreq += 4; + if (s[0].Freq > s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + p->FoundState = --s; + if (s->Freq > MAX_FREQ) + Rescale(p); + } + NextContext(p); +} + +void Ppmd8_Update1_0(CPpmd8 *p) +{ + p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq); + p->RunLength += p->PrevSuccess; + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + NextContext(p); +} + +void Ppmd8_UpdateBin(CPpmd8 *p) +{ + p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196)); + p->PrevSuccess = 1; + p->RunLength++; + NextContext(p); +} + +void Ppmd8_Update2(CPpmd8 *p) +{ + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + p->RunLength = p->InitRL; + UpdateModel(p); + p->MinContext = p->MaxContext; +} + +/* Ppmd8Dec.c -- PPMdI Decoder +2010-04-16 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p) +{ + unsigned i; + p->Low = 0; + p->Range = 0xFFFFFFFF; + p->Code = 0; + for (i = 0; i < 4; i++) + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + return (p->Code < 0xFFFFFFFF); +} + +static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total) +{ + return p->Code / (p->Range /= total); +} + +static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size) +{ + start *= p->Range; + p->Low += start; + p->Code -= start; + p->Range *= size; + + while ((p->Low ^ (p->Low + p->Range)) < kTop || + (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1))) + { + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + p->Range <<= 8; + p->Low <<= 8; + } +} + +#define MASK(sym) ((signed char *)charMask)[sym] + +int Ppmd8_DecodeSymbol(CPpmd8 *p) +{ + size_t charMask[256 / sizeof(size_t)]; + if (p->MinContext->NumStats != 0) + { + CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext); + unsigned i; + UInt32 count, hiCnt; + if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) + { + Byte symbol; + RangeDec_Decode(p, 0, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1_0(p); + return symbol; + } + p->PrevSuccess = 0; + i = p->MinContext->NumStats; + do + { + if ((hiCnt += (++s)->Freq) > count) + { + Byte symbol; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1(p); + return symbol; + } + } + while (--i); + if (count >= p->MinContext->SummFreq) + return -2; + RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt); + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(s->Symbol) = 0; + i = p->MinContext->NumStats; + do { MASK((--s)->Symbol) = 0; } while (--i); + } + else + { + UInt16 *prob = Ppmd8_GetBinSumm(p); + if (((p->Code / (p->Range >>= 14)) < *prob)) + { + Byte symbol; + RangeDec_Decode(p, 0, *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); + symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol; + Ppmd8_UpdateBin(p); + return symbol; + } + RangeDec_Decode(p, *prob, (1 << 14) - *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); + p->InitEsc = PPMD8_kExpEscape[*prob >> 10]; + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0; + p->PrevSuccess = 0; + } + for (;;) + { + CPpmd_State *ps[256], *s; + UInt32 freqSum, count, hiCnt; + CPpmd_See *see; + unsigned i, num, numMasked = p->MinContext->NumStats; + do + { + p->OrderFall++; + if (!p->MinContext->Suffix) + return -1; + p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix); + } + while (p->MinContext->NumStats == numMasked); + hiCnt = 0; + s = Ppmd8_GetStats(p, p->MinContext); + i = 0; + num = p->MinContext->NumStats - numMasked; + do + { + int k = (int)(MASK(s->Symbol)); + hiCnt += (s->Freq & k); + ps[i] = s++; + i -= k; + } + while (i != num); + + see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum); + freqSum += hiCnt; + count = RangeDec_GetThreshold(p, freqSum); + + if (count < hiCnt) + { + Byte symbol; + CPpmd_State **pps = ps; + for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); + s = *pps; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + Ppmd_See_Update(see); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update2(p); + return symbol; + } + if (count >= freqSum) + return -2; + RangeDec_Decode(p, hiCnt, freqSum - hiCnt); + see->Summ = (UInt16)(see->Summ + freqSum); + do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); + } +} + +/* H->I changes: + NS2Indx + GlewCount, and Glue method + BinSum + See / EscFreq + CreateSuccessors updates more suffix contexts + UpdateModel consts. + PrevSuccess Update +*/ + +const IPpmd8 __archive_ppmd8_functions = +{ + &Ppmd8_Construct, + &Ppmd8_Alloc, + &Ppmd8_Free, + &Ppmd8_Init, + &Ppmd8_RangeDec_Init, + &Ppmd8_DecodeSymbol, +}; diff --git a/libarchive/archive_ppmd8_private.h b/libarchive/archive_ppmd8_private.h new file mode 100644 index 00000000..53492786 --- /dev/null +++ b/libarchive/archive_ppmd8_private.h @@ -0,0 +1,148 @@ +/* Ppmd8.h -- PPMdI codec +2011-01-27 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +#ifndef __PPMD8_H +#define __PPMD8_H + +#include "archive_ppmd_private.h" + +#define PPMD8_MIN_ORDER 2 +#define PPMD8_MAX_ORDER 16 + +struct CPpmd8_Context_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Context_ * + #else + UInt32 + #endif + CPpmd8_Context_Ref; + +#pragma pack(push, 1) + +typedef struct CPpmd8_Context_ +{ + Byte NumStats; + Byte Flags; + UInt16 SummFreq; + CPpmd_State_Ref Stats; + CPpmd8_Context_Ref Suffix; +} CPpmd8_Context; + +#pragma pack(pop) + +#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) + +/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed + code is not compatible with original code for some files compressed + in FREEZE mode. So we disable FREEZE mode support. */ + +enum +{ + PPMD8_RESTORE_METHOD_RESTART, + PPMD8_RESTORE_METHOD_CUT_OFF + #ifdef PPMD8_FREEZE_SUPPORT + , PPMD8_RESTORE_METHOD_FREEZE + #endif +}; + +typedef struct +{ + CPpmd8_Context *MinContext, *MaxContext; + CPpmd_State *FoundState; + unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder; + Int32 RunLength, InitRL; /* must be 32-bit at least */ + + UInt32 Size; + UInt32 GlueCount; + Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; + UInt32 AlignOffset; + unsigned RestoreMethod; + + /* Range Coder */ + UInt32 Range; + UInt32 Code; + UInt32 Low; + union + { + IByteIn *In; + IByteOut *Out; + } Stream; + + Byte Indx2Units[PPMD_NUM_INDEXES]; + Byte Units2Indx[128]; + CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; + UInt32 Stamps[PPMD_NUM_INDEXES]; + + Byte NS2BSIndx[256], NS2Indx[260]; + CPpmd_See DummySee, See[24][32]; + UInt16 BinSumm[25][64]; +} CPpmd8; + +void Ppmd8_Construct(CPpmd8 *p); +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size); +void Ppmd8_Free(CPpmd8 *p); +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod); +#define Ppmd8_WasAllocated(p) ((p)->Base != NULL) + + +/* ---------- Internal Functions ---------- */ + +extern const Byte PPMD8_kExpEscape[16]; + +#ifdef PPMD_32BIT + #define Ppmd8_GetPtr(p, ptr) (ptr) + #define Ppmd8_GetContext(p, ptr) (ptr) + #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats) +#else + #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs))) + #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs))) + #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats))) +#endif + +void Ppmd8_Update1(CPpmd8 *p); +void Ppmd8_Update1_0(CPpmd8 *p); +void Ppmd8_Update2(CPpmd8 *p); +void Ppmd8_UpdateBin(CPpmd8 *p); + +#define Ppmd8_GetBinSumm(p) \ + &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \ + p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \ + p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)] + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale); + + +/* ---------- Decode ---------- */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p); +#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0) +int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */ + +/* ---------- Encode ---------- */ + +#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; } +void Ppmd8_RangeEnc_FlushData(CPpmd8 *p); +void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */ + +typedef struct +{ + /* Base Functions */ + void (*Ppmd8_Construct)(CPpmd8 *p); + Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size); + void (*Ppmd8_Free)(CPpmd8 *p); + void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method); + #define Ppmd7_WasAllocated(p) ((p)->Base != NULL) + + /* Decode Functions */ + int (*Ppmd8_RangeDec_Init)(CPpmd8 *p); + int (*Ppmd8_DecodeSymbol)(CPpmd8 *p); +} IPpmd8; + +extern const IPpmd8 __archive_ppmd8_functions; + +#endif diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index 0e56e76e..de964f25 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -611,6 +611,15 @@ choose_filters(struct archive_read *a) return (ARCHIVE_FATAL); } +int +__archive_read_header(struct archive_read *a, struct archive_entry *entry) +{ + if (a->filter->read_header) + return a->filter->read_header(a->filter, entry); + else + return (ARCHIVE_OK); +} + /* * Read header of next entry. */ diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 1786cff3..45417e9a 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -163,6 +163,9 @@ archive_read_disk_entry_from_file(struct archive *_a, int initial_fd = fd; int r, r1; + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, + "archive_read_disk_entry_from_file"); + archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) @@ -188,7 +191,7 @@ archive_read_disk_entry_from_file(struct archive *_a, } } else #endif - if (stat(path, &s) != 0) { + if (la_stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index cdf75412..c4df6c94 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -856,7 +856,12 @@ next_entry(struct archive_read_disk *a, struct tree *t, const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; - int descend, r; + int delayed, delayed_errno, descend, r; + struct archive_string delayed_str; + + delayed = ARCHIVE_OK; + delayed_errno = 0; + archive_string_init(&delayed_str); st = NULL; lst = NULL; @@ -885,14 +890,26 @@ next_entry(struct archive_read_disk *a, struct tree *t, case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { + if (errno == ENOENT && t->depth > 0) { + delayed = ARCHIVE_WARN; + delayed_errno = errno; + if (delayed_str.length == 0) { + archive_string_sprintf(&delayed_str, + "%s", tree_current_path(t)); + } else { + archive_string_sprintf(&delayed_str, + " %s", tree_current_path(t)); + } + } else { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); + } } break; - } + } } while (lst == NULL); #ifdef __APPLE__ @@ -1083,6 +1100,18 @@ next_entry(struct archive_read_disk *a, struct tree *t, r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); + if (r == ARCHIVE_OK) { + r = delayed; + if (r != ARCHIVE_OK) { + archive_string_sprintf(&delayed_str, ": %s", + "File removed before we read it"); + archive_set_error(&(a->archive), delayed_errno, + "%s", delayed_str.s); + } + } + if (!archive_string_empty(&delayed_str)) + archive_string_free(&delayed_str); + return (r); } @@ -1266,10 +1295,23 @@ archive_read_disk_descend(struct archive *_a) if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); + /* + * We must not treat the initial specified path as a physical dir, + * because if we do then we will try and ascend out of it by opening + * ".." which is (a) wrong and (b) causes spurious permissions errors + * if ".." is not readable by us. Instead, treat it as if it were a + * symlink. (This uses an extra fd, but it can only happen once at the + * top level of a traverse.) But we can't necessarily assume t->st is + * valid here (though t->lst is), which complicates the logic a + * little. + */ if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); - t->stack->flags |= isDir; + if (t->stack->parent->parent != NULL) + t->stack->flags |= isDir; + else + t->stack->flags |= isDirLink; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); @@ -2122,6 +2164,17 @@ tree_open(const char *path, int symlink_mode, int restore_time) static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { +#if defined(O_PATH) + /* Linux */ + const int o_flag = O_PATH; +#elif defined(O_SEARCH) + /* SunOS */ + const int o_flag = O_SEARCH; +#elif defined(O_EXEC) + /* FreeBSD */ + const int o_flag = O_EXEC; +#endif + t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; @@ -2143,6 +2196,15 @@ tree_reopen(struct tree *t, const char *path, int restore_time) t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); +#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC) + /* + * Most likely reason to fail opening "." is that it's not readable, + * so try again for execute. The consequences of not opening this are + * unhelpful and unnecessary errors later. + */ + if (t->initial_dir_fd < 0) + t->initial_dir_fd = open(".", o_flag | O_CLOEXEC); +#endif __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); @@ -2450,7 +2512,7 @@ tree_current_stat(struct tree *t) #else if (tree_enter_working_dir(t) != 0) return NULL; - if (stat(tree_current_access_path(t), &t->st) != 0) + if (la_stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index d82048de..ff79cc05 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -299,8 +299,155 @@ static int close_and_restore_time(HANDLE, struct tree *, struct restore_time *); static int setup_sparse_from_disk(struct archive_read_disk *, struct archive_entry *, HANDLE); +static int la_linkname_from_handle(HANDLE, wchar_t **, int *); +static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *); +static void entry_symlink_from_pathw(struct archive_entry *, + const wchar_t *path); + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +/* + * Reads the target of a symbolic link + * + * Returns 0 on success and -1 on failure + * outbuf is allocated in the function + */ +static int +la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype) +{ + DWORD inbytes; + REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; + size_t len; + BOOL ret; + BYTE *indata; + wchar_t *tbuf; + + ret = GetFileInformationByHandle(h, &st); + if (ret == 0 || + (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + return (-1); + } + + indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, + 1024, &inbytes, NULL); + if (ret == 0) { + la_dosmaperr(GetLastError()); + free(indata); + return (-1); + } + + buf = (REPARSE_DATA_BUFFER *) indata; + if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + free(indata); + /* File is not a symbolic link */ + errno = EINVAL; + return (-1); + } + len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength; + if (len <= 0) { + free(indata); + return (-1); + } + + tbuf = malloc(len + 1 * sizeof(wchar_t)); + if (tbuf == NULL) { + free(indata); + return (-1); + } + + memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer) + [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len); + free(indata); + + tbuf[len / sizeof(wchar_t)] = L'\0'; + + *linkname = tbuf; + + /* + * Translate backslashes to slashes for libarchive internal use + */ + while(*tbuf != L'\0') { + if (*tbuf == L'\\') + *tbuf = L'/'; + tbuf++; + } + + if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + *linktype = AE_SYMLINK_TYPE_FILE; + else + *linktype = AE_SYMLINK_TYPE_DIRECTORY; + + return (0); +} + +/* + * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error + */ +static int +la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype) +{ + HANDLE h; + const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT; + int ret; + + h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, + NULL); + if (h == INVALID_HANDLE_VALUE) { + la_dosmaperr(GetLastError()); + return (-1); + } + + ret = la_linkname_from_handle(h, outbuf, linktype); + CloseHandle(h); + + return (ret); +} + +static void +entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path) +{ + wchar_t *linkname = NULL; + int ret, linktype; + + ret = la_linkname_from_pathw(path, &linkname, &linktype); + if (ret != 0) + return; + if (linktype >= 0) { + archive_entry_copy_symlink_w(entry, linkname); + archive_entry_set_symlink_type(entry, linktype); + } + free(linkname); + + return; +} static struct archive_vtable * archive_read_disk_vtable(void) @@ -1838,9 +1985,10 @@ entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path, mode |= S_IWUSR | S_IWGRP | S_IWOTH; if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && findData != NULL && - findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) + findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) { mode |= S_IFLNK; - else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + entry_symlink_from_pathw(entry, path); + } else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; else { const wchar_t *p; @@ -2139,6 +2287,8 @@ archive_read_disk_entry_from_file(struct archive *_a, fileAttributes = bhfi.dwFileAttributes; } else { archive_entry_copy_stat(entry, st); + if (st->st_mode & S_IFLNK) + entry_symlink_from_pathw(entry, path); h = INVALID_HANDLE_VALUE; } diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index bfe933bf..101dae6c 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -174,8 +174,7 @@ file_close(struct archive *a, void *client_data) struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ - if (mine->buffer != NULL) - free(mine->buffer); + free(mine->buffer); free(mine); return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 78546dca..bf04f641 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -98,6 +98,8 @@ struct archive_read_filter { int (*close)(struct archive_read_filter *self); /* Function that handles switching from reading one block to the next/prev */ int (*sswitch)(struct archive_read_filter *self, unsigned int iindex); + /* Read any header metadata if available. */ + int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry); /* My private data. */ void *data; @@ -250,6 +252,7 @@ int64_t __archive_read_seek(struct archive_read*, int64_t, int); int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int); int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); +int __archive_read_header(struct archive_read *, struct archive_entry *); int __archive_read_program(struct archive_read_filter *, const char *); void __archive_read_free_filters(struct archive_read *); struct archive_read_extract *__archive_read_get_extract(struct archive_read *); diff --git a/libarchive/archive_read_set_format.c b/libarchive/archive_read_set_format.c index 190f4369..1d3e49d1 100644 --- a/libarchive/archive_read_set_format.c +++ b/libarchive/archive_read_set_format.c @@ -73,6 +73,9 @@ archive_read_set_format(struct archive *_a, int code) case ARCHIVE_FORMAT_RAR: strcpy(str, "rar"); break; + case ARCHIVE_FORMAT_RAR_V5: + strcpy(str, "rar5"); + break; case ARCHIVE_FORMAT_TAR: strcpy(str, "tar"); break; diff --git a/libarchive/archive_read_support_filter_gzip.c b/libarchive/archive_read_support_filter_gzip.c index fa8c675d..cbc1039a 100644 --- a/libarchive/archive_read_support_filter_gzip.c +++ b/libarchive/archive_read_support_filter_gzip.c @@ -37,6 +37,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_STRING_H #include <string.h> #endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -45,6 +48,8 @@ __FBSDID("$FreeBSD$"); #endif #include "archive.h" +#include "archive_entry.h" +#include "archive_endian.h" #include "archive_private.h" #include "archive_read_private.h" @@ -56,6 +61,8 @@ struct private_data { size_t out_block_size; int64_t total_out; unsigned long crc; + uint32_t mtime; + char *name; char eof; /* True = found end of compressed data. */ }; @@ -123,7 +130,8 @@ archive_read_support_filter_gzip(struct archive *_a) * count of bits verified, suitable for use by bidder. */ static ssize_t -peek_at_header(struct archive_read_filter *filter, int *pbits) +peek_at_header(struct archive_read_filter *filter, int *pbits, + struct private_data *state) { const unsigned char *p; ssize_t avail, len; @@ -144,7 +152,9 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) return (0); bits += 3; header_flags = p[3]; - /* Bytes 4-7 are mod time. */ + /* Bytes 4-7 are mod time in little endian. */ + if (state) + state->mtime = archive_le32dec(p + 4); /* Byte 8 is deflate flags. */ /* XXXX TODO: return deflate flags back to consume_header for use in initializing the decompressor. */ @@ -161,6 +171,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) /* Null-terminated optional filename. */ if (header_flags & 8) { + ssize_t file_start = len; do { ++len; if (avail < len) @@ -169,6 +180,12 @@ peek_at_header(struct archive_read_filter *filter, int *pbits) if (p == NULL) return (0); } while (p[len - 1] != 0); + + if (state) { + /* Reset the name in case of repeat header reads. */ + free(state->name); + state->name = strdup((const char *)&p[file_start]); + } } /* Null-terminated optional comment. */ @@ -214,11 +231,28 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self, (void)self; /* UNUSED */ - if (peek_at_header(filter, &bits_checked)) + if (peek_at_header(filter, &bits_checked, NULL)) return (bits_checked); return (0); } +static int +gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry) +{ + struct private_data *state; + + state = (struct private_data *)self->data; + + /* A mtime of 0 is considered invalid/missing. */ + if (state->mtime != 0) + archive_entry_set_mtime(entry, state->mtime, 0); + + /* If the name is available, extract it. */ + if (state->name) + archive_entry_set_pathname(entry, state->name); + + return (ARCHIVE_OK); +} #ifndef HAVE_ZLIB_H @@ -272,6 +306,7 @@ gzip_bidder_init(struct archive_read_filter *self) self->read = gzip_filter_read; self->skip = NULL; /* not supported */ self->close = gzip_filter_close; + self->read_header = gzip_read_header; state->in_stream = 0; /* We're not actually within a stream yet. */ @@ -289,7 +324,7 @@ consume_header(struct archive_read_filter *self) state = (struct private_data *)self->data; /* If this is a real header, consume it. */ - len = peek_at_header(self->upstream, NULL); + len = peek_at_header(self->upstream, NULL, state); if (len == 0) return (ARCHIVE_EOF); __archive_read_filter_consume(self->upstream, len); @@ -408,6 +443,8 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) "truncated gzip input"); return (ARCHIVE_FATAL); } + if (avail_in > UINT_MAX) + avail_in = UINT_MAX; state->stream.avail_in = (uInt)avail_in; /* Decompress and consume some of that data. */ @@ -469,6 +506,7 @@ gzip_filter_close(struct archive_read_filter *self) } } + free(state->name); free(state->out_block); free(state); return (ret); diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index bccbf896..8ca422ec 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -2964,13 +2964,7 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { /* Copy mode. */ - /* - * Note: '1' here is a performance optimization. - * Recall that the decompression layer returns a count of - * available bytes; asking for more than that forces the - * decompressor to combine reads by copying data. - */ - *buff = __archive_read_ahead(a, 1, &bytes_avail); + *buff = __archive_read_ahead(a, minimum, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -3323,8 +3317,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, * Release the memory which the previous folder used for BCJ2. */ for (i = 0; i < 3; i++) { - if (zip->sub_stream_buff[i] != NULL) - free(zip->sub_stream_buff[i]); + free(zip->sub_stream_buff[i]); zip->sub_stream_buff[i] = NULL; } diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 1b0205cc..296b7db0 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -138,8 +138,7 @@ archive_read_format_ar_cleanup(struct archive_read *a) struct ar *ar; ar = (struct ar *)(a->format->data); - if (ar->strtab) - free(ar->strtab); + free(ar->strtab); free(ar); (a->format->data) = NULL; return (ARCHIVE_OK); @@ -388,9 +387,10 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry, /* * "/" is the SVR4/GNU archive symbol table. + * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table. */ - if (strcmp(filename, "/") == 0) { - archive_entry_copy_pathname(entry, "/"); + if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) { + archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ r = ar_parse_common_header(ar, entry, h); /* Force the file type to a regular file. */ diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index 67d5b21e..1c96e6ac 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -955,8 +955,7 @@ archive_read_format_cpio_cleanup(struct archive_read *a) while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; - if (cpio->links_head->name) - free(cpio->links_head->name); + free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index 28acfefb..db14d41d 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -1724,8 +1724,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a) free(iso9660->read_ce_req.reqs); archive_string_free(&iso9660->pathname); archive_string_free(&iso9660->previous_pathname); - if (iso9660->pending_files.files) - free(iso9660->pending_files.files); + free(iso9660->pending_files.files); #ifdef HAVE_ZLIB_H free(iso9660->entry_zisofs.uncompressed_buffer); free(iso9660->entry_zisofs.block_pointers); @@ -2102,6 +2101,7 @@ parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *p, const unsigned char *end) { struct iso9660 *iso9660; + int entry_seen = 0; iso9660 = (struct iso9660 *)(a->format->data); @@ -2257,8 +2257,16 @@ parse_rockridge(struct archive_read *a, struct file_info *file, } p += p[2]; + entry_seen = 1; + } + + if (entry_seen) + return (ARCHIVE_OK); + else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Tried to parse Rockridge extensions, but none found"); + return (ARCHIVE_WARN); } - return (ARCHIVE_OK); } static int @@ -3029,8 +3037,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap, if (heap->allocated) memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index 9314f7a9..a2e5b68e 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -33,6 +33,9 @@ #ifdef HAVE_ZLIB_H #include <zlib.h> /* crc32 */ #endif +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif #include "archive.h" #ifndef HAVE_ZLIB_H @@ -78,9 +81,13 @@ static unsigned char rar5_signature[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; static const ssize_t rar5_signature_size = sizeof(rar5_signature); -/* static const size_t g_unpack_buf_chunk_size = 1024; */ static const size_t g_unpack_window_size = 0x20000; +/* These could have been static const's, but they aren't, because of + * Visual Studio. */ +#define MAX_NAME_IN_CHARS 2048 +#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS) + struct file_header { ssize_t bytes_remaining; ssize_t unpacked_size; @@ -103,8 +110,39 @@ struct file_header { uint8_t blake2sp[32]; blake2sp_state b2state; char has_blake2; + + /* Optional redir fields */ + uint64_t redir_type; + uint64_t redir_flags; +}; + +enum EXTRA { + EX_CRYPT = 0x01, + EX_HASH = 0x02, + EX_HTIME = 0x03, + EX_VERSION = 0x04, + EX_REDIR = 0x05, + EX_UOWNER = 0x06, + EX_SUBDATA = 0x07 }; +#define REDIR_SYMLINK_IS_DIR 1 + +enum REDIR_TYPE { + REDIR_TYPE_NONE = 0, + REDIR_TYPE_UNIXSYMLINK = 1, + REDIR_TYPE_WINSYMLINK = 2, + REDIR_TYPE_JUNCTION = 3, + REDIR_TYPE_HARDLINK = 4, + REDIR_TYPE_FILECOPY = 5, +}; + +#define OWNER_USER_NAME 0x01 +#define OWNER_GROUP_NAME 0x02 +#define OWNER_USER_UID 0x04 +#define OWNER_GROUP_GID 0x08 +#define OWNER_MAXNAMELEN 256 + enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ @@ -250,7 +288,7 @@ struct main_header { uint8_t endarc : 1; uint8_t notused : 5; - int vol_no; + unsigned int vol_no; }; struct generic_header { @@ -262,7 +300,7 @@ struct generic_header { }; struct multivolume { - int expected_vol_no; + unsigned int expected_vol_no; uint8_t* push_buf; }; @@ -279,6 +317,13 @@ struct rar5 { * extraction mode. This is used during checksum calculation functions. */ int skip_mode; + /* Set to not zero if we're in block merging mode (i.e. when switching + * to another file in multivolume archive, last block from 1st archive + * needs to be merged with 1st block from 2nd archive). This flag guards + * against recursive use of the merging function, which doesn't support + * recursive calls. */ + int merge_mode; + /* An offset to QuickOpen list. This is not supported by this unpacker, * because we're focusing on streaming interface. QuickOpen is designed * to make things quicker for non-stream interfaces, so it's not our @@ -449,18 +494,7 @@ static inline struct rar5* get_context(struct archive_read* a) { } /* Convenience functions used by filter implementations. */ - -static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { - return archive_le32dec(&rar->cstate.window_buf[offset]); -} - -static void write_filter_data(struct rar5* rar, uint32_t offset, - uint32_t value) -{ - archive_le32enc(&rar->cstate.filtered_buf[offset], value); -} - -static void circular_memcpy(uint8_t* dst, uint8_t* window, const int mask, +static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask, int64_t start, int64_t end) { if((start & mask) > (end & mask)) { @@ -474,6 +508,19 @@ static void circular_memcpy(uint8_t* dst, uint8_t* window, const int mask, } } +static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { + uint8_t linear_buf[4]; + circular_memcpy(linear_buf, rar->cstate.window_buf, rar->cstate.window_mask, + offset, offset + 4); + return archive_le32dec(linear_buf); +} + +static void write_filter_data(struct rar5* rar, uint32_t offset, + uint32_t value) +{ + archive_le32enc(&rar->cstate.filtered_buf[offset], value); +} + /* Allocates a new filter descriptor and adds it to the filter array. */ static struct filter_info* add_new_filter(struct rar5* rar) { struct filter_info* f = @@ -534,17 +581,17 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, uint32_t addr; uint32_t offset = (i + flt->block_start) % file_size; - addr = read_filter_data(rar, (rar->cstate.solid_offset + + addr = read_filter_data(rar, (uint32_t)(rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask); if(addr & 0x80000000) { if(((addr + offset) & 0x80000000) == 0) { - write_filter_data(rar, i, addr + file_size); + write_filter_data(rar, (uint32_t)i, addr + file_size); } } else { if((addr - file_size) & 0x80000000) { uint32_t naddr = addr - offset; - write_filter_data(rar, i, naddr); + write_filter_data(rar, (uint32_t)i, naddr); } } @@ -558,7 +605,6 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { ssize_t i = 0; uint32_t offset; - const int mask = rar->cstate.window_mask; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, @@ -568,16 +614,16 @@ static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { for(i = 0; i < flt->block_length - 3; i += 4) { uint8_t* b = &rar->cstate.window_buf[(rar->cstate.solid_offset + - flt->block_start + i) & mask]; + flt->block_start + i) & rar->cstate.window_mask]; if(b[3] == 0xEB) { /* 0xEB = ARM's BL (branch + link) instruction. */ offset = read_filter_data(rar, (rar->cstate.solid_offset + - flt->block_start + i) & mask) & 0x00ffffff; + flt->block_start + i) & rar->cstate.window_mask) & 0x00ffffff; offset -= (uint32_t) ((i + flt->block_start) / 4); offset = (offset & 0x00ffffff) | 0xeb000000; - write_filter_data(rar, i, offset); + write_filter_data(rar, (uint32_t)i, offset); } } @@ -588,8 +634,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { int ret; struct rar5* rar = get_context(a); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); rar->cstate.filtered_buf = malloc(flt->block_length); if(!rar->cstate.filtered_buf) { @@ -615,7 +660,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported filter type: 0x%02x", flt->type); + "Unsupported filter type: 0x%x", flt->type); return ARCHIVE_FATAL; } @@ -644,7 +689,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) { static void push_data(struct archive_read* a, struct rar5* rar, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { - const int wmask = rar->cstate.window_mask; + const uint64_t wmask = rar->cstate.window_mask; const ssize_t solid_write_ptr = (rar->cstate.solid_offset + rar->cstate.last_write_ptr) & wmask; @@ -772,7 +817,7 @@ static void free_filters(struct rar5* rar) { struct filter_info* f = NULL; /* Pop_front will also decrease the collection's size. */ - if(CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)) && f != NULL) + if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) free(f); } @@ -797,6 +842,9 @@ static void reset_file_context(struct rar5* rar) { rar->cstate.last_write_ptr = 0; rar->cstate.last_unstore_ptr = 0; + rar->file.redir_type = REDIR_TYPE_NONE; + rar->file.redir_flags = 0; + free_filters(rar); } @@ -818,7 +866,6 @@ static int read_ahead(struct archive_read* a, size_t how_many, ssize_t avail = -1; *ptr = __archive_read_ahead(a, how_many, &avail); - if(*ptr == NULL) { return 0; } @@ -873,7 +920,7 @@ static int read_var(struct archive_read* a, uint64_t* pvalue, /* Strip the MSB from the input byte and add the resulting number * to the `result`. */ - result += (b & 0x7F) << shift; + result += (b & (uint64_t)0x7F) << shift; /* MSB set to 1 means we need to continue decoding process. MSB set * to 0 means we're done. @@ -947,7 +994,7 @@ static int read_var_sized(struct archive_read* a, size_t* pvalue, } static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { - uint32_t bits = p[rar->bits.in_addr] << 24; + uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; bits |= p[rar->bits.in_addr + 1] << 16; bits |= p[rar->bits.in_addr + 2] << 8; bits |= p[rar->bits.in_addr + 3]; @@ -958,7 +1005,7 @@ static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { } static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { - int bits = (int) p[rar->bits.in_addr] << 16; + int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; bits |= (int) p[rar->bits.in_addr + 1] << 8; bits |= (int) p[rar->bits.in_addr + 2]; bits >>= (8 - rar->bits.bit_addr); @@ -1135,7 +1182,7 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, *extra_data_size -= hash_size; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported hash type (0x%02x)", (int) hash_type); + "Unsupported hash type (0x%x)", (int) hash_type); return ARCHIVE_FATAL; } @@ -1170,6 +1217,59 @@ static int parse_htime_item(struct archive_read* a, char unix_time, return ARCHIVE_OK; } +static int parse_file_extra_version(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + size_t flags = 0; + size_t version = 0; + size_t value_len = 0; + struct archive_string version_string; + struct archive_string name_utf8_string; + + /* Flags are ignored. */ + if(!read_var_sized(a, &flags, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + if(!read_var_sized(a, &version, &value_len)) + return ARCHIVE_EOF; + + *extra_data_size -= value_len; + if(ARCHIVE_OK != consume(a, value_len)) + return ARCHIVE_EOF; + + /* extra_data_size should be zero here. */ + + const char* cur_filename = archive_entry_pathname_utf8(e); + if(cur_filename == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Version entry without file name"); + return ARCHIVE_FATAL; + } + + archive_string_init(&version_string); + archive_string_init(&name_utf8_string); + + /* Prepare a ;123 suffix for the filename, where '123' is the version + * value of this file. */ + archive_string_sprintf(&version_string, ";%ld", version); + + /* Build the new filename. */ + archive_strcat(&name_utf8_string, cur_filename); + archive_strcat(&name_utf8_string, version_string.s); + + /* Apply the new filename into this file's context. */ + archive_entry_update_pathname_utf8(e, name_utf8_string.s); + + /* Free buffers. */ + archive_string_free(&version_string); + archive_string_free(&name_utf8_string); + return ARCHIVE_OK; +} + static int parse_file_extra_htime(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) @@ -1221,6 +1321,147 @@ static int parse_file_extra_htime(struct archive_read* a, return ARCHIVE_OK; } +static int parse_file_extra_redir(struct archive_read* a, + struct archive_entry* e, struct rar5* rar, + ssize_t* extra_data_size) +{ + uint64_t value_size = 0; + size_t target_size = 0; + char target_utf8_buf[MAX_NAME_IN_BYTES]; + const uint8_t* p; + + if(!read_var(a, &rar->file.redir_type, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var(a, &rar->file.redir_flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if(!read_var_sized(a, &target_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= target_size + 1; + if(!read_ahead(a, target_size, &p)) + return ARCHIVE_EOF; + + if(target_size > (MAX_NAME_IN_CHARS - 1)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Link target is too long"); + return ARCHIVE_FATAL; + } + if(target_size == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "No link target specified"); + return ARCHIVE_FATAL; + } + memcpy(target_utf8_buf, p, target_size); + target_utf8_buf[target_size] = 0; + + if(ARCHIVE_OK != consume(a, (int64_t)target_size)) + return ARCHIVE_EOF; + + switch(rar->file.redir_type) { + case REDIR_TYPE_UNIXSYMLINK: + case REDIR_TYPE_WINSYMLINK: + archive_entry_set_filetype(e, AE_IFLNK); + archive_entry_update_symlink_utf8(e, target_utf8_buf); + if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_DIRECTORY); + } else { + archive_entry_set_symlink_type(e, + AE_SYMLINK_TYPE_FILE); + } + break; + + case REDIR_TYPE_HARDLINK: + archive_entry_set_filetype(e, AE_IFREG); + archive_entry_update_hardlink_utf8(e, target_utf8_buf); + break; + + default: + /* Unknown redir type, skip it. */ + break; + } + return ARCHIVE_OK; +} + +static int parse_file_extra_owner(struct archive_read* a, + struct archive_entry* e, ssize_t* extra_data_size) +{ + uint64_t flags = 0; + uint64_t value_size = 0; + uint64_t id = 0; + size_t name_len = 0; + size_t name_size = 0; + char namebuf[OWNER_MAXNAMELEN]; + const uint8_t* p; + + if(!read_var(a, &flags, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + if ((flags & OWNER_USER_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + if (name_size >= OWNER_MAXNAMELEN) + name_len = OWNER_MAXNAMELEN - 1; + else + name_len = name_size; + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_uname(e, namebuf); + } + if ((flags & OWNER_GROUP_NAME) != 0) { + if(!read_var_sized(a, &name_size, NULL)) + return ARCHIVE_EOF; + *extra_data_size -= name_size + 1; + if(!read_ahead(a, name_size, &p)) + return ARCHIVE_EOF; + if (name_size >= OWNER_MAXNAMELEN) + name_len = OWNER_MAXNAMELEN - 1; + else + name_len = name_size; + memcpy(namebuf, p, name_len); + namebuf[name_len] = 0; + if(ARCHIVE_OK != consume(a, (int64_t)name_size)) + return ARCHIVE_EOF; + + archive_entry_set_gname(e, namebuf); + } + if ((flags & OWNER_USER_UID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_uid(e, (la_int64_t)id); + } + if ((flags & OWNER_GROUP_GID) != 0) { + if(!read_var(a, &id, &value_size)) + return ARCHIVE_EOF; + if(ARCHIVE_OK != consume(a, (int64_t)value_size)) + return ARCHIVE_EOF; + *extra_data_size -= value_size; + + archive_entry_set_gid(e, (la_int64_t)id); + } + return ARCHIVE_OK; +} + static int process_head_file_extra(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) @@ -1230,11 +1471,6 @@ static int process_head_file_extra(struct archive_read* a, int ret = ARCHIVE_FATAL; size_t var_size; - enum EXTRA { - CRYPT = 0x01, HASH = 0x02, HTIME = 0x03, VERSION_ = 0x04, - REDIR = 0x05, UOWNER = 0x06, SUBDATA = 0x07 - }; - while(extra_data_size > 0) { if(!read_var_sized(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; @@ -1253,27 +1489,28 @@ static int process_head_file_extra(struct archive_read* a, } switch(extra_field_id) { - case HASH: + case EX_HASH: ret = parse_file_extra_hash(a, rar, &extra_data_size); break; - case HTIME: + case EX_HTIME: ret = parse_file_extra_htime(a, e, rar, &extra_data_size); break; - case CRYPT: - /* fallthrough */ - case VERSION_: - /* fallthrough */ - case REDIR: - /* fallthrough */ - case UOWNER: + case EX_REDIR: + ret = parse_file_extra_redir(a, e, rar, &extra_data_size); + break; + case EX_UOWNER: + ret = parse_file_extra_owner(a, e, &extra_data_size); + break; + case EX_VERSION: + ret = parse_file_extra_version(a, e, &extra_data_size); + break; + case EX_CRYPT: /* fallthrough */ - case SUBDATA: + case EX_SUBDATA: /* fallthrough */ default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unknown extra field in file/service block: 0x%02x", - (int) extra_field_id); - return ARCHIVE_FATAL; + /* Skip unsupported entry. */ + return consume(a, extra_data_size); } } @@ -1298,10 +1535,10 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, uint64_t unpacked_size; uint32_t mtime = 0, crc = 0; int c_method = 0, c_version = 0, is_dir; - char name_utf8_buf[2048 * 4]; + char name_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; - memset(entry, 0, sizeof(struct archive_entry)); + archive_entry_clear(entry); /* Do not reset file context if we're switching archives. */ if(!rar->cstate.switch_multivolume) { @@ -1335,6 +1572,11 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, UNKNOWN_UNPACKED_SIZE = 0x0008, }; + enum FILE_ATTRS { + ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4, + ATTR_DIRECTORY = 0x10, + }; + enum COMP_INFO_FLAGS { SOLID = 0x0040, }; @@ -1392,21 +1634,30 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(host_os == HOST_WINDOWS) { /* Host OS is Windows */ - unsigned short mode = 0660; + __LA_MODE_T mode; - if(is_dir) - mode |= AE_IFDIR; - else - mode |= AE_IFREG; + if(file_attr & ATTR_DIRECTORY) { + mode = 0755 | AE_IFDIR; + } else { + if (file_attr & ATTR_READONLY) + mode = 0444 | AE_IFREG; + else + mode = 0644 | AE_IFREG; + } archive_entry_set_mode(entry, mode); + + /* + * TODO: implement attribute support (READONLY, HIDDEN, SYSTEM) + * This requires a platform-independent extended attribute handling + */ } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ - archive_entry_set_mode(entry, (unsigned short) file_attr); + archive_entry_set_mode(entry, (__LA_MODE_T) file_attr); } else { /* Unknown host OS */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported Host OS: 0x%02x", (int) host_os); + "Unsupported Host OS: 0x%x", (int) host_os); return ARCHIVE_FATAL; } @@ -1417,7 +1668,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; - if(name_size > 2047) { + if(name_size > (MAX_NAME_IN_CHARS - 1)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Filename is too long"); @@ -1437,6 +1688,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, return ARCHIVE_EOF; } + archive_entry_update_pathname_utf8(entry, name_utf8_buf); + if(extra_data_size > 0) { int ret = process_head_file_extra(a, entry, rar, extra_data_size); @@ -1453,7 +1706,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { rar->file.unpacked_size = (ssize_t) unpacked_size; - archive_entry_set_size(entry, unpacked_size); + if(rar->file.redir_type == REDIR_TYPE_NONE) + archive_entry_set_size(entry, unpacked_size); } if(file_flags & UTIME) { @@ -1464,8 +1718,6 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, rar->file.stored_crc32 = crc; } - archive_entry_update_pathname_utf8(entry, name_utf8_buf); - if(!rar->cstate.switch_multivolume) { /* Do not reinitialize unpacking state if we're switching archives. */ rar->cstate.block_parsing_finished = 1; @@ -1544,8 +1796,12 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, if(!read_var_sized(a, &v, NULL)) { return ARCHIVE_EOF; } - - rar->main.vol_no = (int) v; + if (v > UINT_MAX) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid volume number"); + return ARCHIVE_FATAL; + } + rar->main.vol_no = (unsigned int) v; } else { rar->main.vol_no = 0; } @@ -1596,13 +1852,45 @@ static int process_head_main(struct archive_read* a, struct rar5* rar, break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported extra type (0x%02x)", (int) extra_field_id); + "Unsupported extra type (0x%x)", (int) extra_field_id); return ARCHIVE_FATAL; } return ARCHIVE_OK; } +static int skip_unprocessed_bytes(struct archive_read* a) { + struct rar5* rar = get_context(a); + int ret; + + if(rar->file.bytes_remaining) { + /* Use different skipping method in block merging mode than in + * normal mode. If merge mode is active, rar5_read_data_skip can't + * be used, because it could allow recursive use of merge_block() + * function, and this function doesn't support recursive use. */ + if(rar->merge_mode) { + /* Discard whole merged block. This is valid in solid mode as + * well, because the code will discard blocks only if those + * blocks are safe to discard (i.e. they're not FILE blocks). */ + ret = consume(a, rar->file.bytes_remaining); + if(ret != ARCHIVE_OK) { + return ret; + } + + rar->file.bytes_remaining = 0; + } else { + /* If we're not in merge mode, use safe skipping code. This + * will ensure we'll handle solid archives properly. */ + ret = rar5_read_data_skip(a); + if(ret != ARCHIVE_OK) { + return ret; + } + } + } + + return ARCHIVE_OK; +} + static int scan_for_signature(struct archive_read* a); /* Base block processing function. A 'base block' is a RARv5 header block @@ -1662,12 +1950,9 @@ static int process_base_block(struct archive_read* a, int ret; /* Skip any unprocessed data for this file. */ - if(rar->file.bytes_remaining) { - ret = rar5_read_data_skip(a); - if(ret != ARCHIVE_OK) { - return ret; - } - } + ret = skip_unprocessed_bytes(a); + if(ret != ARCHIVE_OK) + return ret; /* Read the expected CRC32 checksum. */ if(!read_u32(a, &hdr_crc)) { @@ -1717,8 +2002,8 @@ static int process_base_block(struct archive_read* a, rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; - rar->generic.size = hdr_size; - rar->generic.last_header_id = header_id; + rar->generic.size = (int)hdr_size; + rar->generic.last_header_id = (int)header_id; rar->main.endarc = 0; /* Those are possible header ids in RARv5. */ @@ -1763,6 +2048,12 @@ static int process_base_block(struct archive_read* a, if(ret == ARCHIVE_FATAL) { return ARCHIVE_EOF; } else { + if(rar->vol.expected_vol_no == UINT_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Header error"); + return ARCHIVE_FATAL; + } + rar->vol.expected_vol_no = rar->main.vol_no + 1; return ARCHIVE_OK; } @@ -1795,8 +2086,16 @@ static int skip_base_block(struct archive_read* a) { int ret; struct rar5* rar = get_context(a); - struct archive_entry entry; - ret = process_base_block(a, &entry); + /* Create a new local archive_entry structure that will be operated on + * by header reader; operations on this archive_entry will be discarded. + */ + struct archive_entry* entry = archive_entry_new(); + ret = process_base_block(a, entry); + + /* Discard operations on this archive_entry structure. */ + archive_entry_free(entry); + if(ret == ARCHIVE_FATAL) + return ret; if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) return ARCHIVE_OK; @@ -1836,13 +2135,14 @@ static int rar5_read_header(struct archive_read *a, static void init_unpack(struct rar5* rar) { rar->file.calculated_crc32 = 0; - rar->cstate.window_mask = rar->cstate.window_size - 1; + if (rar->cstate.window_size) + rar->cstate.window_mask = rar->cstate.window_size - 1; + else + rar->cstate.window_mask = 0; - if(rar->cstate.window_buf) - free(rar->cstate.window_buf); + free(rar->cstate.window_buf); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); rar->cstate.window_buf = calloc(1, rar->cstate.window_size); rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); @@ -1927,7 +2227,7 @@ static int create_decode_tables(uint8_t* bit_length, } } - quick_data_size = 1 << table->quick_bits; + quick_data_size = (int64_t)1 << table->quick_bits; cur_len = 1; for(code = 0; code < quick_data_size; code++) { int bit_field = code << (16 - table->quick_bits); @@ -2012,6 +2312,13 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, /* The data for table generation is compressed using a simple RLE-like * algorithm when storing zeroes, so we need to unpack it first. */ for(w = 0, i = 0; w < HUFF_BC;) { + if(i >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables"); + return ARCHIVE_FATAL; + } + value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) @@ -2037,7 +2344,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, int k; /* Fill zeroes. */ - for(k = 0; k < value + 2; k++) { + for(k = 0; (k < value + 2) && (w < HUFF_BC); k++) { bit_length[w++] = 0; } } @@ -2059,6 +2366,13 @@ static int parse_tables(struct archive_read* a, struct rar5* rar, for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; + if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { + /* Truncated data, can't continue. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated data in huffman tables (#2)"); + return ARCHIVE_FATAL; + } + ret = decode_number(a, &rar->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -2210,7 +2524,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p, if(calculated_cksum != hdr->block_cksum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Block checksum error: got 0x%02x, expected 0x%02x", + "Block checksum error: got 0x%x, expected 0x%x", hdr->block_cksum, calculated_cksum); return ARCHIVE_FATAL; @@ -2238,7 +2552,9 @@ static int parse_filter_data(struct rar5* rar, const uint8_t* p, return ARCHIVE_EOF; } - data += (byte >> 8) << (i * 8); + /* Cast to uint32_t will ensure the shift operation will not produce + * undefined result. */ + data += ((uint32_t) byte >> 8) << (i * 8); skip_bits(rar, 8); } @@ -2358,8 +2674,8 @@ static int decode_code_length(struct rar5* rar, const uint8_t* p, static int copy_string(struct archive_read* a, int len, int dist) { struct rar5* rar = get_context(a); - const int cmask = rar->cstate.window_mask; - const int64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; + const uint64_t cmask = rar->cstate.window_mask; + const uint64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; int i; /* The unpacker spends most of the time in this function. It would be @@ -2384,7 +2700,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { uint16_t num; int ret; - const int cmask = rar->cstate.window_mask; + const uint64_t cmask = rar->cstate.window_mask; const struct compressed_block_header* hdr = &rar->last_block_hdr; const uint8_t bit_size = 1 + bf_bit_size(hdr); @@ -2462,7 +2778,11 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { dist += dist_slot; } else { dbits = dist_slot / 2 - 1; - dist += (2 | (dist_slot & 1)) << dbits; + + /* Cast to uint32_t will make sure the shift left operation + * won't produce undefined result. Then, the uint32_t type will + * be implicitly casted to int. */ + dist += (uint32_t) (2 | (dist_slot & 1)) << dbits; } if(dbits > 0) { @@ -2563,7 +2883,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { /* The program counter shouldn't reach here. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported block code: 0x%02x", num); + "Unsupported block code: 0x%x", num); return ARCHIVE_FATAL; } @@ -2628,14 +2948,34 @@ static int advance_multivolume(struct archive_read* a) { while(1) { if(rar->main.endarc == 1) { + int looping = 1; + rar->main.endarc = 0; - while(ARCHIVE_RETRY == skip_base_block(a)); + + while(looping) { + lret = skip_base_block(a); + switch(lret) { + case ARCHIVE_RETRY: + /* Continue looping. */ + break; + case ARCHIVE_OK: + /* Break loop. */ + looping = 0; + break; + default: + /* Forward any errors to the caller. */ + return lret; + } + } + break; } else { /* Skip current base block. In order to properly skip it, * we really need to simply parse it and discard the results. */ lret = skip_base_block(a); + if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED) + return lret; /* The `skip_base_block` function tells us if we should continue * with skipping, or we should stop skipping. We're trying to skip @@ -2669,6 +3009,13 @@ static int merge_block(struct archive_read* a, ssize_t block_size, const uint8_t* lp; int ret; + if(rar->merge_mode) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Recursive merge is not allowed"); + + return ARCHIVE_FATAL; + } + /* Set a flag that we're in the switching mode. */ rar->cstate.switch_multivolume = 1; @@ -2676,13 +3023,21 @@ static int merge_block(struct archive_read* a, ssize_t block_size, if(rar->vol.push_buf) free((void*) rar->vol.push_buf); - rar->vol.push_buf = malloc(block_size); + /* Increasing the allocation block by 8 is due to bit reading functions, + * which are using additional 2 or 4 bytes. Allocating the block size + * by exact value would make bit reader perform reads from invalid memory + * block when reading the last byte from the buffer. */ + rar->vol.push_buf = malloc(block_size + 8); if(!rar->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a " "merge block buffer."); return ARCHIVE_FATAL; } + /* Valgrind complains if the extension block for bit reader is not + * initialized, so initialize it. */ + memset(&rar->vol.push_buf[block_size], 0, 8); + /* A single block can span across multiple multivolume archive files, * so we use a loop here. This loop will consume enough multivolume * archive files until the whole block is read. */ @@ -2732,9 +3087,12 @@ static int merge_block(struct archive_read* a, ssize_t block_size, /* If we don't have any bytes to read, this means we should switch * to another multivolume archive file. */ if(rar->file.bytes_remaining == 0) { + rar->merge_mode++; ret = advance_multivolume(a); - if(ret != ARCHIVE_OK) + rar->merge_mode--; + if(ret != ARCHIVE_OK) { return ret; + } } } @@ -2762,8 +3120,6 @@ static int process_block(struct archive_read* a) { if(rar->cstate.block_parsing_finished) { ssize_t block_size; - rar->cstate.block_parsing_finished = 0; - /* The header size won't be bigger than 6 bytes. */ if(!read_ahead(a, 6, &p)) { /* Failed to prefetch data block header. */ @@ -2779,8 +3135,9 @@ static int process_block(struct archive_read* a) { * argument. */ ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr); - if(ret != ARCHIVE_OK) + if(ret != ARCHIVE_OK) { return ret; + } /* Skip block header. Next data is huffman tables, if present. */ ssize_t to_skip = sizeof(struct compressed_block_header) + @@ -2836,6 +3193,7 @@ static int process_block(struct archive_read* a) { rar->cstate.block_buf = p; rar->cstate.cur_block_size = cur_block_size; + rar->cstate.block_parsing_finished = 0; rar->bits.in_addr = 0; rar->bits.bit_addr = 0; @@ -2849,6 +3207,7 @@ static int process_block(struct archive_read* a) { } } } else { + /* Block parsing not finished, reuse previous memory buffer. */ p = rar->cstate.block_buf; } @@ -3180,7 +3539,7 @@ static int do_unpack(struct archive_read* a, struct rar5* rar, return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Compression method not supported: 0x%08x", + "Compression method not supported: 0x%x", rar->cstate.method); return ARCHIVE_FATAL; @@ -3394,14 +3753,11 @@ static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, static int rar5_cleanup(struct archive_read *a) { struct rar5* rar = get_context(a); - if(rar->cstate.window_buf) - free(rar->cstate.window_buf); + free(rar->cstate.window_buf); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); - if(rar->vol.push_buf) - free(rar->vol.push_buf); + free(rar->vol.push_buf); free_filters(rar); cdeque_free(&rar->cstate.filters); diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c index efa2c6a3..ec0520b6 100644 --- a/libarchive/archive_read_support_format_raw.c +++ b/libarchive/archive_read_support_format_raw.c @@ -120,7 +120,9 @@ archive_read_format_raw_read_header(struct archive_read *a, archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_perm(entry, 0644); /* I'm deliberately leaving most fields unset here. */ - return (ARCHIVE_OK); + + /* Let the filter fill out any fields it might have. */ + return __archive_read_header(a, entry); } static int diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 60800bb8..81c03c88 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -1942,6 +1942,15 @@ pax_attribute(struct archive_read *a, struct tar *tar, pax_time(value, &s, &n); archive_entry_set_birthtime(entry, s, n); } + if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) { + if (strcmp(value, "file") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_FILE); + } else if (strcmp(value, "dir") == 0) { + archive_entry_set_symlink_type(entry, + AE_SYMLINK_TYPE_DIRECTORY); + } + } if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0) pax_attribute_xattr(entry, key, value); break; diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c index e8fc8428..c1c54450 100644 --- a/libarchive/archive_read_support_format_warc.c +++ b/libarchive/archive_read_support_format_warc.c @@ -744,8 +744,9 @@ _warc_rdlen(const char *buf, size_t bsz) /* there must be at least one digit */ if (!isdigit((unsigned char)*val)) return -1; + errno = 0; len = strtol(val, &on, 10); - if (on != eol) { + if (errno != 0 || on != eol) { /* line must end here */ return -1; } diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c index c4dd915a..16ccfd2a 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -798,7 +798,8 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry) xattr = file->xattr_list; while (xattr != NULL) { const void *d; - size_t outbytes, used; + size_t outbytes = 0; + size_t used = 0; r = move_reading_point(a, xattr->offset); if (r != ARCHIVE_OK) @@ -847,7 +848,7 @@ xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct xar *xar; - size_t used; + size_t used = 0; int r; xar = (struct xar *)(a->format->data); @@ -1229,8 +1230,7 @@ heap_add_entry(struct archive_read *a, } memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 55752084..b1d7d15d 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -52,6 +52,12 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #ifdef HAVE_ZLIB_H #include <zlib.h> #endif +#ifdef HAVE_BZLIB_H +#include <bzlib.h> +#endif +#ifdef HAVE_LZMA_H +#include <lzma.h> +#endif #include "archive.h" #include "archive_digest_private.h" @@ -63,6 +69,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" +#include "archive_ppmd8_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" @@ -165,13 +172,30 @@ struct zip { char decompress_init; char end_of_entry; -#ifdef HAVE_ZLIB_H unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; + +#ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + lzma_stream zipx_lzma_stream; + char zipx_lzma_valid; +#endif + +#ifdef HAVE_BZLIB_H + bz_stream bzstream; + char bzstream_valid; +#endif + + IByteIn zipx_ppmd_stream; + ssize_t zipx_ppmd_read_compressed; + CPpmd8 ppmd8; + char ppmd8_valid; + char ppmd8_stream_failed; + struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; @@ -222,6 +246,33 @@ struct zip { /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) +/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 + * streams inside ZIP files. It has 2 purposes: one is to fetch the next + * compressed byte from the stream, second one is to increase the counter how + * many compressed bytes were read. */ +static Byte +ppmd_read(void* p) { + /* Get the handle to current decompression context. */ + struct archive_read *a = ((IByteIn*)p)->a; + struct zip *zip = (struct zip*) a->format->data; + ssize_t bytes_avail = 0; + + /* Fetch next byte. */ + const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 1) { + zip->ppmd8_stream_failed = 1; + return 0; + } + + __archive_read_consume(a, 1); + + /* Increment the counter. */ + ++zip->zipx_ppmd_read_compressed; + + /* Return the next compressed byte. */ + return data[0]; +} + /* ------------------------------------------------------------------------ */ /* @@ -372,6 +423,8 @@ static const struct { {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ + {95, "xz"}, /* XZ compressed data */ + {96, "jpeg"}, /* JPEG compressed data */ {97, "wav-pack"}, /* WavPack compressed data */ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ {99, "aes"} /* WinZip AES encryption */ @@ -1290,6 +1343,718 @@ zip_read_data_none(struct archive_read *a, const void **_buff, return (ARCHIVE_OK); } +static int +consume_optional_marker(struct archive_read *a, struct zip *zip) +{ + if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + const char *p; + + if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP end-of-file record"); + return (ARCHIVE_FATAL); + } + /* Consume the optional PK\007\010 marker. */ + if (p[0] == 'P' && p[1] == 'K' && + p[2] == '\007' && p[3] == '\010') { + p += 4; + zip->unconsumed = 4; + } + if (zip->entry->flags & LA_USED_ZIP64) { + uint64_t compressed, uncompressed; + zip->entry->crc32 = archive_le32dec(p); + compressed = archive_le64dec(p + 4); + uncompressed = archive_le64dec(p + 12); + if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Overflow of 64-bit file sizes"); + return ARCHIVE_FAILED; + } + zip->entry->compressed_size = compressed; + zip->entry->uncompressed_size = uncompressed; + zip->unconsumed += 20; + } else { + zip->entry->crc32 = archive_le32dec(p); + zip->entry->compressed_size = archive_le32dec(p + 4); + zip->entry->uncompressed_size = archive_le32dec(p + 8); + zip->unconsumed += 12; + } + } + + return (ARCHIVE_OK); +} + +#if HAVE_LZMA_H && HAVE_LIBLZMA +static int +zipx_xz_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "xz initialization failed(%d)", + r); + + return (ARCHIVE_FAILED); + } + + zip->zipx_lzma_valid = 1; + + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for xz decompression"); + return (ARCHIVE_FATAL); + } + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + const uint8_t* p; + +#pragma pack(push) +#pragma pack(1) + struct _alone_header { + uint8_t bytes[5]; + uint64_t uncompressed_size; + } alone_header; +#pragma pack(pop) + + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma that + * is a part of XZ Utils. The stream format stored inside ZIPX file is a + * modified "lzma alone" file format, that was used by the `lzma` utility + * which was later deprecated in favour of `xz` utility. Since those + * formats are nearly the same, we can use a standard "lzma alone" decoder + * from XZ Utils. */ + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "lzma initialization failed(%d)", r); + + return (ARCHIVE_FAILED); + } + + /* Flag the cleanup function that we want our lzma-related structures + * to be freed later. */ + zip->zipx_lzma_valid = 1; + + /* The "lzma alone" file format and the stream format inside ZIPx are + * almost the same. Here's an example of a structure of "lzma alone" + * format: + * + * $ cat /bin/ls | lzma | xxd | head -n 1 + * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814 + * + * 5 bytes 8 bytes n bytes + * <lzma_params><uncompressed_size><data...> + * + * lzma_params is a 5-byte blob that has to be decoded to extract + * parameters of this LZMA stream. The uncompressed_size field is an + * uint64_t value that contains information about the size of the + * uncompressed file, or UINT64_MAX if this value is unknown. The <data...> + * part is the actual lzma-compressed data stream. + * + * Now here's the structure of the stream inside the ZIPX file: + * + * $ cat stream_inside_zipx | xxd | head -n 1 + * 00000000: 0914 0500 5d00 8000 0000 2814 .... .... + * + * 2byte 2byte 5 bytes n bytes + * <magic1><magic2><lzma_params><data...> + * + * This means that the ZIPX file contains an additional magic1 and magic2 + * headers, the lzma_params field contains the same parameter set as in the + * "lzma alone" format, and the <data...> field is the same as in the "lzma + * alone" format as well. Note that also the zipx format is missing the + * uncompressed_size field. + * + * So, in order to use the "lzma alone" decoder for the zipx lzma stream, + * we simply need to shuffle around some fields, prepare a new lzma alone + * header, feed it into lzma alone decoder so it will initialize itself + * properly, and then we can start feeding normal zipx lzma stream into the + * decoder. + */ + + /* Read magic1,magic2,lzma_params from the ZIPX stream. */ + if((p = __archive_read_ahead(a, 9, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma data"); + return (ARCHIVE_FATAL); + } + + if(p[2] != 0x05 || p[3] != 0x00) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid lzma data"); + return (ARCHIVE_FATAL); + } + + /* Prepare an lzma alone header: copy the lzma_params blob into a proper + * place into the lzma alone header. */ + memcpy(&alone_header.bytes[0], p + 4, 5); + + /* Initialize the 'uncompressed size' field to unknown; we'll manually + * monitor how many bytes there are still to be uncompressed. */ + alone_header.uncompressed_size = UINT64_MAX; + + if(!zip->uncompressed_buffer) { + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for lzma decompression"); + return (ARCHIVE_FATAL); + } + } + + zip->zipx_lzma_stream.next_in = (void*) &alone_header; + zip->zipx_lzma_stream.avail_in = sizeof(alone_header); + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Feed only the header into the lzma alone decoder. This will effectively + * initialize the decoder, and will not produce any output bytes yet. */ + r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + if (r != LZMA_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "lzma stream initialization error"); + return ARCHIVE_FATAL; + } + + /* We've already consumed some bytes, so take this into account. */ + __archive_read_consume(a, 9); + zip->entry_bytes_remaining -= 9; + zip->entry_compressed_bytes_read += 9; + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_xz(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume = 0; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_xz_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated xz file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_NO_CHECK: + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_STREAM_END: + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + + if((int64_t) zip->zipx_lzma_stream.total_in != + zip->entry_bytes_remaining) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz premature end of stream"); + return (ARCHIVE_FATAL); + } + + zip->end_of_entry = 1; + break; + } + + to_consume = zip->zipx_lzma_stream.total_in; + + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_lzma_alone_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + /* Fetch more compressed data. The same note as in deflate handler applies + * here as well: + * + * Note: '1' here is a performance optimization. Recall that the + * decompression layer returns a count of available bytes; asking for more + * than that forces the decompressor to combine reads by copying data. + */ + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma file body"); + return (ARCHIVE_FATAL); + } + + /* Set decompressor parameters. */ + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = + /* These lzma_alone streams lack end of stream marker, so let's make + * sure the unpacker won't try to unpack more than it's supposed to. */ + zipmin((int64_t) zip->uncompressed_buffer_size, + zip->entry->uncompressed_size - + zip->entry_uncompressed_bytes_read); + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + } + + to_consume = zip->zipx_lzma_stream.total_in; + + /* Update pointers. */ + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + if(zip->entry_bytes_remaining == 0) { + zip->end_of_entry = 1; + } + + /* Return values. */ + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + /* Behave the same way as during deflate decompression. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + /* Free lzma decoder handle because we'll no longer need it. */ + if(zip->end_of_entry) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* If we're here, then we're good! */ + return (ARCHIVE_OK); +} +#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */ + +static int +zipx_ppmd8_init(struct archive_read *a, struct zip *zip) +{ + const void* p; + uint32_t val; + uint32_t order; + uint32_t mem; + uint32_t restore_method; + + /* Remove previous decompression context if it exists. */ + if(zip->ppmd8_valid) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Create a new decompression context. */ + __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8); + zip->ppmd8_stream_failed = 0; + + /* Setup function pointers required by Ppmd8 decompressor. The + * 'ppmd_read' function will feed new bytes to the decompressor, + * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */ + zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream; + zip->zipx_ppmd_stream.a = a; + zip->zipx_ppmd_stream.Read = &ppmd_read; + + /* Reset number of read bytes to 0. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Read Ppmd8 header (2 bytes). */ + p = __archive_read_ahead(a, 2, NULL); + if(!p) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated file data in PPMd8 stream"); + return (ARCHIVE_FATAL); + } + __archive_read_consume(a, 2); + + /* Decode the stream's compression parameters. */ + val = archive_le16dec(p); + order = (val & 15) + 1; + mem = ((val >> 4) & 0xff) + 1; + restore_method = (val >> 12); + + if(order < 2 || restore_method > 2) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid parameter set in PPMd8 stream (order=%d, " + "restore=%d)", order, restore_method); + return (ARCHIVE_FAILED); + } + + /* Allocate the memory needed to properly decompress the file. */ + if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) { + archive_set_error(&a->archive, ENOMEM, + "Unable to allocate memory for PPMd8 stream: %d bytes", + mem << 20); + return (ARCHIVE_FATAL); + } + + /* Signal the cleanup function to release Ppmd8 context in the + * cleanup phase. */ + zip->ppmd8_valid = 1; + + /* Perform further Ppmd8 initialization. */ + if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "PPMd8 stream range decoder initialization error"); + return (ARCHIVE_FATAL); + } + + __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, restore_method); + + /* Allocate the buffer that will hold uncompressed data. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if(zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for PPMd8 decompression"); + return ARCHIVE_FATAL; + } + + /* Ppmd8 initialization is done. */ + zip->decompress_init = 1; + + /* We've already read 2 bytes in the output stream. Additionally, + * Ppmd8 initialization code could read some data as well. So we + * are advancing the stream by 2 bytes plus whatever number of + * bytes Ppmd8 init function used. */ + zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed; + + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + size_t consumed_bytes = 0; + ssize_t bytes_avail = 0; + + (void) offset; /* UNUSED */ + + /* If we're here for the first time, initialize Ppmd8 decompression + * context first. */ + if(!zip->decompress_init) { + ret = zipx_ppmd8_init(a, zip); + if(ret != ARCHIVE_OK) + return ret; + } + + /* Fetch for more data. We're reading 1 byte here, but libarchive should + * prefetch more bytes. */ + (void) __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); + return (ARCHIVE_FATAL); + } + + /* This counter will be updated inside ppmd_read(), which at one + * point will be called by Ppmd8_DecodeSymbol. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Decompression loop. */ + do { + int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(&zip->ppmd8); + if(sym < 0) { + zip->end_of_entry = 1; + break; + } + + /* This field is set by ppmd_read() when there was no more data + * to be read. */ + if(zip->ppmd8_stream_failed) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); + return (ARCHIVE_FATAL); + } + + zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym; + ++consumed_bytes; + } while(consumed_bytes < zip->uncompressed_buffer_size); + + /* Update pointers for libarchive. */ + *buff = zip->uncompressed_buffer; + *size = consumed_bytes; + + /* Update pointers so we can continue decompression in another call. */ + zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed; + zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed; + zip->entry_uncompressed_bytes_read += consumed_bytes; + + /* If we're at the end of stream, deinitialize Ppmd8 context. */ + if(zip->end_of_entry) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Seek for optional marker, same way as in each zip entry. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return ret; + + return ARCHIVE_OK; +} + +#ifdef HAVE_BZLIB_H +static int +zipx_bzip2_init(struct archive_read *a, struct zip *zip) +{ + int r; + + /* Deallocate already existing BZ2 decompression context if it + * exists. */ + if(zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + zip->bzstream_valid = 0; + } + + /* Allocate a new BZ2 decompression context. */ + memset(&zip->bzstream, 0, sizeof(bz_stream)); + r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1); + if(r != BZ_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "bzip2 initialization failed(%d)", + r); + + return ARCHIVE_FAILED; + } + + /* Mark the bzstream field to be released in cleanup phase. */ + zip->bzstream_valid = 1; + + /* (Re)allocate the buffer that will contain decompressed bytes. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for bzip2 decompression"); + return ARCHIVE_FATAL; + } + + /* Initialization done. */ + zip->decompress_init = 1; + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip *zip = (struct zip *)(a->format->data); + ssize_t bytes_avail = 0, in_bytes, to_consume; + const void *compressed_buff; + int r; + uint64_t total_out; + + (void) offset; /* UNUSED */ + + /* Initialize decompression context if we're here for the first time. */ + if(!zip->decompress_init) { + r = zipx_bzip2_init(a, zip); + if(r != ARCHIVE_OK) + return r; + } + + /* Fetch more compressed bytes. */ + compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated bzip2 file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + if(in_bytes < 1) { + /* libbz2 doesn't complain when caller feeds avail_in == 0. It will + * actually return success in this case, which is undesirable. This is + * why we need to make this check manually. */ + + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated bzip2 file body"); + return (ARCHIVE_FATAL); + } + + /* Setup buffer boundaries. */ + zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; + zip->bzstream.avail_in = in_bytes; + zip->bzstream.total_in_hi32 = 0; + zip->bzstream.total_in_lo32 = 0; + zip->bzstream.next_out = (char*) zip->uncompressed_buffer; + zip->bzstream.avail_out = zip->uncompressed_buffer_size; + zip->bzstream.total_out_hi32 = 0; + zip->bzstream.total_out_lo32 = 0; + + /* Perform the decompression. */ + r = BZ2_bzDecompress(&zip->bzstream); + switch(r) { + case BZ_STREAM_END: + /* If we're at the end of the stream, deinitialize the + * decompression context now. */ + switch(BZ2_bzDecompressEnd(&zip->bzstream)) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up bzip2 decompressor"); + return ARCHIVE_FATAL; + } + + zip->end_of_entry = 1; + break; + case BZ_OK: + /* The decompressor has successfully decoded this chunk of + * data, but more data is still in queue. */ + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "bzip2 decompression failed"); + return ARCHIVE_FATAL; + } + + /* Update the pointers so decompressor can continue decoding. */ + to_consume = zip->bzstream.total_in_lo32; + __archive_read_consume(a, to_consume); + + total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + + zip->bzstream.total_out_lo32; + + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += total_out; + + /* Give libarchive its due. */ + *size = total_out; + *buff = zip->uncompressed_buffer; + + /* Seek for optional marker, like in other entries. */ + r = consume_optional_marker(a, zip); + if(r != ARCHIVE_OK) + return r; + + return ARCHIVE_OK; +} + +#endif + #ifdef HAVE_ZLIB_H static int zip_deflate_init(struct archive_read *a, struct zip *zip) @@ -1464,42 +2229,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (r); } - if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { - const char *p; - - if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP end-of-file record"); - return (ARCHIVE_FATAL); - } - /* Consume the optional PK\007\010 marker. */ - if (p[0] == 'P' && p[1] == 'K' && - p[2] == '\007' && p[3] == '\010') { - p += 4; - zip->unconsumed = 4; - } - if (zip->entry->flags & LA_USED_ZIP64) { - uint64_t compressed, uncompressed; - zip->entry->crc32 = archive_le32dec(p); - compressed = archive_le64dec(p + 4); - uncompressed = archive_le64dec(p + 12); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Overflow of 64-bit file sizes"); - return ARCHIVE_FAILED; - } - zip->entry->compressed_size = compressed; - zip->entry->uncompressed_size = uncompressed; - zip->unconsumed += 20; - } else { - zip->entry->crc32 = archive_le32dec(p); - zip->entry->compressed_size = archive_le32dec(p + 4); - zip->entry->uncompressed_size = archive_le32dec(p + 8); - zip->unconsumed += 12; - } - } + r = consume_optional_marker(a, zip); + if (r != ARCHIVE_OK) + return (r); return (ARCHIVE_OK); } @@ -1927,6 +2659,24 @@ archive_read_format_zip_read_data(struct archive_read *a, case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; +#ifdef HAVE_BZLIB_H + case 12: /* ZIPx bzip2 compression. */ + r = zip_read_data_zipx_bzip2(a, buff, size, offset); + break; +#endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + case 14: /* ZIPx LZMA compression. */ + r = zip_read_data_zipx_lzma_alone(a, buff, size, offset); + break; + case 95: /* ZIPx XZ compression. */ + r = zip_read_data_zipx_xz(a, buff, size, offset); + break; +#endif + /* PPMd support is built-in, so we don't need any #if guards. */ + case 98: /* ZIPx PPMd compression. */ + r = zip_read_data_zipx_ppmd(a, buff, size, offset); + break; + #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); @@ -1935,8 +2685,8 @@ archive_read_format_zip_read_data(struct archive_read *a, default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported ZIP compression method (%s)", - compression_name(zip->entry->compression)); + "Unsupported ZIP compression method (%d: %s)", + zip->entry->compression, compression_name(zip->entry->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_FAILED); @@ -1994,11 +2744,29 @@ archive_read_format_zip_cleanup(struct archive_read *a) struct zip_entry *zip_entry, *next_zip_entry; zip = (struct zip *)(a->format->data); + #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); - free(zip->uncompressed_buffer); #endif + +#if HAVE_LZMA_H && HAVE_LIBLZMA + if (zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + } +#endif + +#ifdef HAVE_BZLIB_H + if (zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + } +#endif + + free(zip->uncompressed_buffer); + + if (zip->ppmd8_valid) + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + if (zip->zip_entries) { zip_entry = zip->zip_entries; while (zip_entry != NULL) { @@ -3140,3 +3908,5 @@ archive_read_support_format_zip_seekable(struct archive *_a) free(zip); return (ARCHIVE_OK); } + +/*# vim:set noet:*/ diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 554533ec..76a16240 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -1512,8 +1512,10 @@ get_current_codepage(void) p = strrchr(locale, '.'); if (p == NULL) return (GetACP()); + if (strcmp(p+1, "utf8") == 0) + return CP_UTF8; cp = my_atoi(p+1); - if (cp <= 0) + if ((int)cp <= 0) return (GetACP()); return (cp); } @@ -4050,6 +4052,7 @@ archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; + return (0); } aes->aes_set = AES_SET_UTF8; archive_string_empty(&(aes->aes_mbs)); @@ -4064,6 +4067,7 @@ archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, { if (wcs == NULL) { aes->aes_set = 0; + return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 96d61456..3399c0b5 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -449,7 +449,7 @@ __archive_mktemp(const char *tmpdir) temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } - if (stat(temp_name.s, &st) < 0) + if (la_stat(temp_name.s, &st) < 0) goto exit_tmpfile; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index e77cd08f..87d8c891 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -112,10 +112,7 @@ #if !defined(__BORLANDC__) && !defined(__WATCOMC__) #define setmode _setmode #endif -#ifdef stat -#undef stat -#endif -#define stat(path,stref) __la_stat(path,stref) +#define la_stat(path,stref) __la_stat(path,stref) #if !defined(__WATCOMC__) #if !defined(__BORLANDC__) #define strdup _strdup diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c index b0f25a6e..0f7c8cfc 100644 --- a/libarchive/archive_write_add_filter_xz.c +++ b/libarchive/archive_write_add_filter_xz.c @@ -390,10 +390,13 @@ archive_compressor_xz_options(struct archive_write_filter *f, data->compression_level = 6; return (ARCHIVE_OK); } else if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_WARN); - data->threads = (int)strtoul(value, NULL, 10); - if (data->threads == 0 && errno != 0) { + errno = 0; + data->threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { data->threads = 1; return (ARCHIVE_WARN); } diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 003e17d7..84e8f80e 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -1791,10 +1791,8 @@ finish_metadata: a->fd = -1; } /* If there's an entry, we can release it now. */ - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } @@ -2034,7 +2032,7 @@ restore_entry(struct archive_write_disk *a) * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) - r = stat(a->name, &a->st); + r = la_stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. @@ -2200,7 +2198,7 @@ create_filesystem_object(struct archive_write_disk *a) #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else - r = stat(a->name, &st); + r = la_stat(a->name, &st); #endif if (r != 0) r = errno; @@ -2398,8 +2396,7 @@ _archive_write_disk_free(struct archive *_a) ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); @@ -2591,8 +2588,11 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, */ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(restore_pwd); - if (restore_pwd < 0) + if (restore_pwd < 0) { + fsobj_error(a_eno, a_estr, errno, + "Could not open ", path); return (ARCHIVE_FATAL); + } head = path; tail = path; last = 0; @@ -2712,7 +2712,7 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, * This is needed to extract hardlinks over * symlinks. */ - r = stat(head, &st); + r = la_stat(head, &st); if (r != 0) { tail[0] = c; if (errno == ENOENT) { @@ -3052,7 +3052,7 @@ create_dir(struct archive_write_disk *a, char *path) * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ - if (stat(path, &st) == 0) { + if (la_stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { @@ -3110,7 +3110,7 @@ create_dir(struct archive_write_disk *a, char *path) * don't add it to the fixup list here, as it's already been * added. */ - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", @@ -3131,12 +3131,14 @@ create_dir(struct archive_write_disk *a, char *path) static int set_ownership(struct archive_write_disk *a) { -#ifndef __CYGWIN__ -/* unfortunately, on win32 there is no 'root' user with uid 0, - so we just have to try the chown and see if it works */ - - /* If we know we can't change it, don't bother trying. */ - if (a->user_uid != 0 && a->user_uid != a->uid) { +#if !defined(__CYGWIN__) && !defined(__linux__) +/* + * On Linux, a process may have the CAP_CHOWN capability. + * On Windows there is no 'root' user with uid 0. + * Elsewhere we can skip calling chown if we are not root and the desired + * user id does not match the current user. + */ + if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); @@ -3503,9 +3505,7 @@ set_fflags(struct archive_write_disk *a) struct fixup_entry *le; unsigned long set, clear; int r; - int critical_flags; mode_t mode = archive_entry_mode(a->entry); - /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, @@ -3521,33 +3521,33 @@ set_fflags(struct archive_write_disk *a) * other programs that might try to muck with files as they're * being restored. */ - /* Hopefully, the compiler will optimize this mess into a constant. */ - critical_flags = 0; + const int critical_flags = 0 #ifdef SF_IMMUTABLE - critical_flags |= SF_IMMUTABLE; + | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE - critical_flags |= UF_IMMUTABLE; + | UF_IMMUTABLE #endif #ifdef SF_APPEND - critical_flags |= SF_APPEND; + | SF_APPEND #endif #ifdef UF_APPEND - critical_flags |= UF_APPEND; + | UF_APPEND #endif #if defined(FS_APPEND_FL) - critical_flags |= FS_APPEND_FL; + | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) - critical_flags |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #if defined(FS_IMMUTABLE_FL) - critical_flags |= FS_IMMUTABLE_FL; + | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) - critical_flags |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif #ifdef FS_JOURNAL_DATA_FL - critical_flags |= FS_JOURNAL_DATA_FL; + | FS_JOURNAL_DATA_FL #endif + ; if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); @@ -3578,29 +3578,27 @@ set_fflags(struct archive_write_disk *a) static int clear_nochange_fflags(struct archive_write_disk *a) { - int nochange_flags; mode_t mode = archive_entry_mode(a->entry); - - /* Hopefully, the compiler will optimize this mess into a constant. */ - nochange_flags = 0; + const int nochange_flags = 0 #ifdef SF_IMMUTABLE - nochange_flags |= SF_IMMUTABLE; + | SF_IMMUTABLE #endif #ifdef UF_IMMUTABLE - nochange_flags |= UF_IMMUTABLE; + | UF_IMMUTABLE #endif #ifdef SF_APPEND - nochange_flags |= SF_APPEND; + | SF_APPEND #endif #ifdef UF_APPEND - nochange_flags |= UF_APPEND; + | UF_APPEND #endif #ifdef EXT2_APPEND_FL - nochange_flags |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #ifdef EXT2_IMMUTABLE_FL - nochange_flags |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif + ; return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); @@ -3616,8 +3614,22 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; - + const int sf_mask = 0 +#ifdef SF_APPEND + | SF_APPEND +#endif +#ifdef SF_ARCHIVED + | SF_ARCHIVED +#endif +#ifdef SF_IMMUTABLE + | SF_IMMUTABLE +#endif +#ifdef SF_NOUNLINK + | SF_NOUNLINK +#endif + ; (void)mode; /* UNUSED */ + if (set == 0 && clear == 0) return (ARCHIVE_OK); @@ -3632,6 +3644,12 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, a->st.st_flags &= ~clear; a->st.st_flags |= set; + + /* Only super-user may change SF_* flags */ + + if (a->user_uid != 0) + a->st.st_flags &= ~sf_mask; + #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) @@ -3673,22 +3691,6 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, int ret; int myfd = fd; int newflags, oldflags; - int sf_mask = 0; - - if (set == 0 && clear == 0) - return (ARCHIVE_OK); - /* Only regular files and dirs can have flags. */ - if (!S_ISREG(mode) && !S_ISDIR(mode)) - return (ARCHIVE_OK); - - /* If we weren't given an fd, open it ourselves. */ - if (myfd < 0) { - myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); - __archive_ensure_cloexec_flag(myfd); - } - if (myfd < 0) - return (ARCHIVE_OK); - /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but @@ -3696,19 +3698,36 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ + const int sf_mask = 0 #if defined(FS_IMMUTABLE_FL) - sf_mask |= FS_IMMUTABLE_FL; + | FS_IMMUTABLE_FL #elif defined(EXT2_IMMUTABLE_FL) - sf_mask |= EXT2_IMMUTABLE_FL; + | EXT2_IMMUTABLE_FL #endif #if defined(FS_APPEND_FL) - sf_mask |= FS_APPEND_FL; + | FS_APPEND_FL #elif defined(EXT2_APPEND_FL) - sf_mask |= EXT2_APPEND_FL; + | EXT2_APPEND_FL #endif #if defined(FS_JOURNAL_DATA_FL) - sf_mask |= FS_JOURNAL_DATA_FL; + | FS_JOURNAL_DATA_FL #endif + ; + + if (set == 0 && clear == 0) + return (ARCHIVE_OK); + /* Only regular files and dirs can have flags. */ + if (!S_ISREG(mode) && !S_ISDIR(mode)) + return (ARCHIVE_OK); + + /* If we weren't given an fd, open it ourselves. */ + if (myfd < 0) { + myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(myfd); + } + if (myfd < 0) + return (ARCHIVE_OK); + /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX diff --git a/libarchive/archive_write_disk_set_standard_lookup.c b/libarchive/archive_write_disk_set_standard_lookup.c index 5c766d75..5fccdb9d 100644 --- a/libarchive/archive_write_disk_set_standard_lookup.c +++ b/libarchive/archive_write_disk_set_standard_lookup.c @@ -114,8 +114,7 @@ lookup_gid(void *private_data, const char *gname, int64_t gid) return ((gid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(gname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; @@ -184,8 +183,7 @@ lookup_uid(void *private_data, const char *uname, int64_t uid) return ((uid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(uname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index 78eda4ab..e3e65ba1 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -205,6 +205,8 @@ struct archive_write_disk { #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 +static int disk_unlink(const wchar_t *); +static int disk_rmdir(const wchar_t *); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, @@ -474,6 +476,11 @@ permissive_name_w(struct archive_write_disk *a) { archive_wstrncpy(&(a->_name_data), wsp, l); } + else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\') + { + archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8); + archive_wstrncat(&(a->_name_data), wsp+2, l-2); + } else { archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4); @@ -551,8 +558,10 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } - if (!f) + if (!f) { + errno = ENOTSUP; return (0); + } ret = (*f)(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove @@ -577,6 +586,103 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) return (ret); } +/* + * Create file or directory symolic link + * + * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from + * the link target + */ +static int +la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target, + int linktype) { + static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); + static int set; + wchar_t *ttarget, *p; + int len; + DWORD attrs = 0; + DWORD flags = 0; + DWORD newflags = 0; + BOOL ret = 0; + + if (!set) { + set = 1; + f = la_GetFunctionKernel32("CreateSymbolicLinkW"); + } + if (!f) + return (0); + + len = wcslen(target); + if (len == 0) { + errno = EINVAL; + return(0); + } + /* + * When writing path targets, we need to translate slashes + * to backslashes + */ + ttarget = malloc((len + 1) * sizeof(wchar_t)); + if (ttarget == NULL) + return(0); + + p = ttarget; + + while(*target != L'\0') { + if (*target == L'/') + *p = L'\\'; + else + *p = *target; + target++; + p++; + } + *p = L'\0'; + + /* + * In case of undefined symlink type we guess it from the target. + * If the target equals ".", "..", ends with a backslash or a + * backslash followed by "." or ".." we assume it is a directory + * symlink. In all other cases we assume a file symlink. + */ + if (linktype != AE_SYMLINK_TYPE_FILE && ( + linktype == AE_SYMLINK_TYPE_DIRECTORY || + *(p - 1) == L'\\' || (*(p - 1) == L'.' && ( + len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && ( + len == 2 || *(p - 3) == L'\\')))))) { +#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; +#else + flags |= 0x1; +#endif + } + +#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) + newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +#else + newflags = flags | 0x2; +#endif + + /* + * Windows won't overwrite existing links + */ + attrs = GetFileAttributesW(linkname); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + disk_rmdir(linkname); + else + disk_unlink(linkname); + } + + ret = (*f)(linkname, ttarget, newflags); + /* + * Prior to Windows 10 calling CreateSymbolicLinkW() will fail + * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set + */ + if (!ret) { + ret = (*f)(linkname, ttarget, flags); + } + free(ttarget); + return (ret); +} + static int la_ftruncate(HANDLE handle, int64_t length) { @@ -696,10 +802,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->entry = archive_entry_clone(entry); a->fh = INVALID_HANDLE_VALUE; a->fd_offset = 0; @@ -1145,10 +1249,8 @@ _archive_write_disk_finish_entry(struct archive *_a) a->fh = INVALID_HANDLE_VALUE; } /* If there's an entry, we can release it now. */ - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } @@ -1239,7 +1341,7 @@ archive_write_disk_new(void) } static int -disk_unlink(wchar_t *path) +disk_unlink(const wchar_t *path) { wchar_t *fullname; int r; @@ -1254,7 +1356,7 @@ disk_unlink(wchar_t *path) } static int -disk_rmdir(wchar_t *path) +disk_rmdir(const wchar_t *path) { wchar_t *fullname; int r; @@ -1359,28 +1461,45 @@ restore_entry(struct archive_write_disk *a) en = create_filesystem_object(a); } else if (en == EEXIST) { mode_t st_mode; + mode_t lst_mode; + BY_HANDLE_FILE_INFORMATION lst; /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; + int dirlnk = 0; + /* * The SECURE_SYMLINK logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. - */ - if (S_ISDIR(a->mode)) - r = file_information(a, a->name, &a->st, &st_mode, 0); - /* * If it's not a dir (or it's a broken symlink), * then don't follow it. + * + * Windows distinguishes file and directory symlinks. + * A file symlink may erroneously point to a directory + * and a directory symlink to a file. Windows does not follow + * such symlinks. We always need both source and target + * information. */ - if (r != 0 || !S_ISDIR(a->mode)) - r = file_information(a, a->name, &a->st, &st_mode, 1); + r = file_information(a, a->name, &lst, &lst_mode, 1); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); + } else if (S_ISLNK(lst_mode)) { + if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + dirlnk = 1; + /* In case of a symlink we need target information */ + r = file_information(a, a->name, &a->st, &st_mode, 0); + if (r != 0) { + a->st = lst; + st_mode = lst_mode; + } + } else { + a->st = lst; + st_mode = lst_mode; } /* @@ -1404,8 +1523,15 @@ restore_entry(struct archive_write_disk *a) } if (!S_ISDIR(st_mode)) { - /* A non-dir is in the way, unlink it. */ - if (disk_unlink(a->name) != 0) { + /* Edge case: a directory symlink pointing to a file */ + if (dirlnk) { + if (disk_rmdir(a->name) != 0) { + archive_set_error(&a->archive, errno, + "Can't unlink directory symlink"); + return (ARCHIVE_FAILED); + } + } else if (disk_unlink(a->name) != 0) { + /* A non-dir is in the way, unlink it. */ archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); @@ -1514,7 +1640,16 @@ create_filesystem_object(struct archive_write_disk *a) #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else - return (EPERM); + errno = 0; + r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname, + archive_entry_symlink_type(a->entry)); + if (r == 0) { + if (errno == 0) + la_dosmaperr(GetLastError()); + r = errno; + } else + r = 0; + return (r); #endif } @@ -1690,8 +1825,7 @@ _archive_write_disk_free(struct archive *_a) ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); archive_wstring_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_wstring_free(&a->path_safe); @@ -1827,6 +1961,9 @@ check_symlinks(struct archive_write_disk *a) p = a->path_safe.s; while ((*pn != '\0') && (*p == *pn)) ++p, ++pn; + /* Skip leading backslashes */ + while (*pn == '\\') + ++pn; c = pn[0]; /* Keep going until we've checked the entire name. */ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) { @@ -1844,11 +1981,17 @@ check_symlinks(struct archive_write_disk *a) } else if (S_ISLNK(st_mode)) { if (c == '\0') { /* - * Last element is symlink; remove it - * so we can overwrite it with the + * Last element is a file or directory symlink. + * Remove it so we can overwrite it with the * item being extracted. */ - if (disk_unlink(a->name)) { + if (st.dwFileAttributes & + FILE_ATTRIBUTE_DIRECTORY) { + r = disk_rmdir(a->name); + } else { + r = disk_unlink(a->name); + } + if (r) { archive_set_error(&a->archive, errno, "Could not remove symlink %ls", a->name); @@ -1872,7 +2015,13 @@ check_symlinks(struct archive_write_disk *a) return (0); } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (disk_unlink(a->name) != 0) { + if (st.dwFileAttributes & + FILE_ATTRIBUTE_DIRECTORY) { + r = disk_rmdir(a->name); + } else { + r = disk_unlink(a->name); + } + if (r != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening " "symlink %ls", a->name); @@ -1888,6 +2037,8 @@ check_symlinks(struct archive_write_disk *a) return (ARCHIVE_FAILED); } } + pn[0] = c; + pn++; } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c index f63a2266..92a87f74 100644 --- a/libarchive/archive_write_set_format_7zip.c +++ b/libarchive/archive_write_set_format_7zip.c @@ -439,7 +439,8 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) r = file_new(a, entry, &file); if (r < ARCHIVE_WARN) { - file_free(file); + if (file != NULL) + file_free(file); return (r); } if (file->size == 0 && file->dir) { diff --git a/libarchive/archive_write_set_format_ar.c b/libarchive/archive_write_set_format_ar.c index 50305ccb..253cac82 100644 --- a/libarchive/archive_write_set_format_ar.c +++ b/libarchive/archive_write_set_format_ar.c @@ -187,6 +187,11 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) buff[AR_name_offset] = '/'; goto stat; } + if (strcmp(pathname, "/SYM64/") == 0) { + /* Entry is archive symbol table in GNU 64-bit format */ + memcpy(buff + AR_name_offset, "/SYM64/", 7); + goto stat; + } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ memcpy(buff + AR_name_offset, "__.SYMDEF", 9); diff --git a/libarchive/archive_write_set_format_cpio.c b/libarchive/archive_write_set_format_cpio.c index a4c9d1ed..16cefad7 100644 --- a/libarchive/archive_write_set_format_cpio.c +++ b/libarchive/archive_write_set_format_cpio.c @@ -408,8 +408,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } diff --git a/libarchive/archive_write_set_format_cpio_newc.c b/libarchive/archive_write_set_format_cpio_newc.c index 957f1a33..2d923cc3 100644 --- a/libarchive/archive_write_set_format_cpio_newc.c +++ b/libarchive/archive_write_set_format_cpio_newc.c @@ -366,8 +366,7 @@ write_header(struct archive_write *a, struct archive_entry *entry) } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c index 1966c53f..e7757c22 100644 --- a/libarchive/archive_write_set_format_gnutar.c +++ b/libarchive/archive_write_set_format_gnutar.c @@ -565,8 +565,7 @@ archive_write_gnutar_header(struct archive_write *a, gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index 5a4c45a1..cf2a1f95 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -1114,6 +1114,10 @@ archive_write_pax_header(struct archive_write *a, if (!need_extension && acl_types != 0) need_extension = 1; + /* If the symlink type is defined, we need an extension */ + if (!need_extension && archive_entry_symlink_type(entry_main) > 0) + need_extension = 1; + /* * Libarchive used to include these in extended headers for * restricted pax format, but that confused people who @@ -1247,6 +1251,17 @@ archive_write_pax_header(struct archive_write *a, archive_string_free(&entry_name); return (ARCHIVE_FATAL); } + + /* Store extended symlink information */ + if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_FILE) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "file"); + } else if (archive_entry_symlink_type(entry_main) == + AE_SYMLINK_TYPE_DIRECTORY) { + add_pax_attr(&(pax->pax_header), + "LIBARCHIVE.symlinktype", "dir"); + } } /* Only regular files have data. */ diff --git a/libarchive/archive_write_set_format_shar.c b/libarchive/archive_write_set_format_shar.c index 5be310a0..600c8825 100644 --- a/libarchive/archive_write_set_format_shar.c +++ b/libarchive/archive_write_set_format_shar.c @@ -169,8 +169,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) } /* Save the entry for the closing. */ - if (shar->entry) - archive_entry_free(shar->entry); + archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); name = archive_entry_pathname(entry); @@ -289,8 +288,7 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) "mkdir -p %s > /dev/null 2>&1\n", shar->quoted_name.s); /* Record that we just created this directory. */ - if (shar->last_dir != NULL) - free(shar->last_dir); + free(shar->last_dir); shar->last_dir = strdup(name); /* Trim a trailing '/'. */ diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c index c54aeabd..ad4ccb77 100644 --- a/libarchive/archive_write_set_format_ustar.c +++ b/libarchive/archive_write_set_format_ustar.c @@ -352,14 +352,12 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) #endif ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) @@ -367,8 +365,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } diff --git a/libarchive/archive_write_set_format_v7tar.c b/libarchive/archive_write_set_format_v7tar.c index 53c0db0e..1fdaafd2 100644 --- a/libarchive/archive_write_set_format_v7tar.c +++ b/libarchive/archive_write_set_format_v7tar.c @@ -330,14 +330,12 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) #endif ret = format_header_v7tar(a, buff, entry, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) @@ -345,8 +343,7 @@ archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) v7tar->entry_bytes_remaining = archive_entry_size(entry); v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } diff --git a/libarchive/archive_write_set_format_xar.c b/libarchive/archive_write_set_format_xar.c index 36d4a615..5e4b90e0 100644 --- a/libarchive/archive_write_set_format_xar.c +++ b/libarchive/archive_write_set_format_xar.c @@ -496,10 +496,13 @@ xar_options(struct archive_write *a, const char *key, const char *value) return (ARCHIVE_OK); } if (strcmp(key, "threads") == 0) { + char *endptr; + if (value == NULL) return (ARCHIVE_FAILED); - xar->opt_threads = (int)strtoul(value, NULL, 10); - if (xar->opt_threads == 0 && errno != 0) { + errno = 0; + xar->opt_threads = (int)strtoul(value, &endptr, 10); + if (errno != 0 || *endptr != '\0') { xar->opt_threads = 1; archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index f69b8467..7fcd1a07 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -564,10 +564,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) zip->entry_uses_zip64 = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); zip->entry_encryption = 0; - if (zip->entry != NULL) { - archive_entry_free(zip->entry); - zip->entry = NULL; - } + archive_entry_free(zip->entry); + zip->entry = NULL; if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); @@ -1430,6 +1428,9 @@ write_path(struct archive_entry *entry, struct archive_write *archive) type = archive_entry_filetype(entry); written_bytes = 0; + if (path == NULL) + return (ARCHIVE_FATAL); + ret = __archive_write_output(archive, path, strlen(path)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index b18fc8a8..cb1c1e35 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -58,7 +58,6 @@ IF(ENABLE_TEST) test_compat_lzma.c test_compat_lzop.c test_compat_mac.c - test_compat_pax_libarchive_2x.c test_compat_perl_archive_tar.c test_compat_plexus_archiver_tar.c test_compat_solaris_pax_sparse.c diff --git a/libarchive/test/test_compat_pax_libarchive_2x.c b/libarchive/test/test_compat_pax_libarchive_2x.c deleted file mode 100644 index 4830d9e4..00000000 --- a/libarchive/test/test_compat_pax_libarchive_2x.c +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * Copyright (c) 2011 Michihiro NAKAJIMA - * 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" -__FBSDID("$FreeBSD"); - -#include <locale.h> - -/* - * Test "tar:compat-2x" option that enables the string conversion of - * libarchive 2.x, which made incorrect UTF-8 form filenames for the - * pax format on some platform the wchar_t of which was not Unicode form. - * The option is unneeded if people have been using UTF-8 locale during - * making tar files(in pax format). - * - * NOTE: The sample tar file was made with bsdtar 2.x in LANG=KOI8-R on - * FreeBSD. - */ - -DEFINE_TEST(test_compat_pax_libarchive_2x) -{ -#if (defined(_WIN32) && !defined(__CYGWIN__)) \ - || defined(__STDC_ISO_10646__) || defined(__APPLE__) - skipping("This test only for the platform the WCS of which is " - "not Unicode."); -#else - struct archive *a; - struct archive_entry *ae; - char c; - wchar_t wc; - const char *refname = "test_compat_pax_libarchive_2x.tar.Z"; - - /* - * Read incorrect format UTF-8 filename in ru_RU.KOI8-R with - * "tar:compat-2x" option. We should correctly - * read two filenames. - */ - if (NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { - skipping("ru_RU.KOI8-R locale not available on this system."); - return; - } - - /* - * Test if wchar_t format is the same as FreeBSD wchar_t. - */ - assert(-1 != wctomb(NULL, L'\0')); - wc = (wchar_t)0xd0; - c = 0; - if (wctomb(&c, wc) != 1 || (unsigned char)c != 0xd0) { - skipping("wchar_t format is different on this platform."); - return; - } - - 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_set_options(a, "tar:compat-2x")); - assertEqualIntA(a, ARCHIVE_OK, - archive_read_open_filename(a, refname, 10240)); - - /* Verify regular first file. */ - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assertEqualString("\xd0\xd2\xc9\xd7\xc5\xd4", - archive_entry_pathname(ae)); - assertEqualInt(6, archive_entry_size(ae)); - - /* Verify regular second file. */ - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assertEqualString("\xf0\xf2\xe9\xf7\xe5\xf4", - archive_entry_pathname(ae)); - assertEqualInt(6, archive_entry_size(ae)); - - - /* End of archive. */ - assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); - - /* Verify archive format. */ - assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); - assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, - archive_format(a)); - - /* Close the archive. */ - assertEqualInt(ARCHIVE_OK, archive_read_close(a)); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); - - /* - * Without "tar:compat-2x" option. - * Neither first file name nor second file name can be translated - * to KOI8-R. - */ - 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)); - - /* We cannot correctly read the filename. */ - // This test used to look for WARN here coming from a - // character-conversion failure. But: Newer iconv tables are - // more tolerant so we can't always detect the conversion - // failures. - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assert(strcmp("\xd0\xd2\xc9\xd7\xc5\xd4", - archive_entry_pathname(ae)) != 0); - assertEqualInt(6, archive_entry_size(ae)); - - /* We cannot correctly read the filename. */ - // Same here: The test is still valid (it sill verifies that - // the converted pathname is different), but we can no longer - // rely on WARN here. - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assert(strcmp("\xf0\xf2\xe9\xf7\xe5\xf4", - archive_entry_pathname(ae)) != 0); - assertEqualInt(6, archive_entry_size(ae)); - - - /* End of archive. */ - assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); - - /* Verify archive format. */ - assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); - assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, - archive_format(a)); - - /* Close the archive. */ - assertEqualInt(ARCHIVE_OK, archive_read_close(a)); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); -#endif -} diff --git a/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu b/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu deleted file mode 100644 index f4405411..00000000 --- a/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu +++ /dev/null @@ -1,15 +0,0 @@ -begin 644 test_compat_pax_libarchive_2x.tar.Z -M'YV04,+@05(F#)DR<EY`DY;L6C%J`")*G$BQHL6+&#-JW%@1AL<;-6J``.`Q -M!L@8(TN>3.FQ94D;-D#$B%&#QHT9,CS.B`DCADT;-P"`P,.QJ-&C2)-:K#.' -M3A@Y)&&,J5-&:<:I5:U>=.F1I<N16L.*'4NV;%D9*.&$H8.FQS!(PR0-2S3L -MTK!"PR@IR`EB#)TT;<KTB#$#!EJ0.&+@V`L#Q%K`@@D;QG$C!PX9.128!#%E -M")(D3+*X.&BG1XX:>V5P]@Q:=!HW;P87IE'C!@W-.%9_#NW"#9O7:P8K,$N\ -MN/'CR),K7\Z\N?.)"QL^?$[=:$N0(J.:K(%2^\JH7%O&G%GS9DX8.T'T+`PC -MJ/KJ\)$R=0K5(];B]XF']]H2;/S_`"H'CSSIW%,./0$FJ.""##;HX(,01@A` -M0`,5=%!"`Q9XH(3Z?1022]MUIQ)W_+D4`TPRT6033CKQ1),,0`E%%(?PS?=4 -M5/F9E6-9^X'7'XU`-H@6"&JQY18LP\@R3"K#W#),*</0PEA??P4FFV';);88 -M7X]9*9D,E%F&F68WZ-;::&64=EIJ9O+V6FR2T082;FV*YAMPP@6IYYY\]NFG -M4AD:B."?9EWWH7<D(BIB>.*E6!Z+Z+DX0V'NP4!H<C;6)Q55^'':X5<^=G7I ->J,<%NB&IJ*:JZJJLMNKJJ[#&*NNLM-9JZZVXYDH< -` -end diff --git a/libarchive/test/test_entry.c b/libarchive/test/test_entry.c index 0ccc9e8f..a0a2607a 100644 --- a/libarchive/test/test_entry.c +++ b/libarchive/test/test_entry.c @@ -336,7 +336,7 @@ DEFINE_TEST(test_entry) /* Converting fflags bitmap to string is currently system-dependent. */ /* TODO: Make this system-independent. */ assertEqualString(archive_entry_fflags_text(e), - "uappnd,nouchg,nodump,noopaque,uunlnk"); + "uappnd,nouchg,nodump,noopaque,uunlnk,nosystem"); /* Test archive_entry_copy_fflags_text_w() */ archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,uunlnk"); archive_entry_fflags(e, &set, &clear); diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c index 2025834c..d02fd993 100644 --- a/libarchive/test/test_fuzz.c +++ b/libarchive/test/test_fuzz.c @@ -58,6 +58,14 @@ test_fuzz(const struct files *filesets) size_t blk_size; int64_t blk_offset; int n; + const char *skip_fuzz_tests; + + skip_fuzz_tests = getenv("SKIP_TEST_FUZZ"); + if (skip_fuzz_tests != NULL) { + skipping("Skipping fuzz tests due to SKIP_TEST_FUZZ " + "environment variable"); + return; + } for (n = 0; filesets[n].names != NULL; ++n) { const size_t buffsize = 30000000; diff --git a/libarchive/test/test_read_disk_directory_traversals.c b/libarchive/test/test_read_disk_directory_traversals.c index 705b3d98..7dd19157 100644 --- a/libarchive/test/test_read_disk_directory_traversals.c +++ b/libarchive/test/test_read_disk_directory_traversals.c @@ -40,7 +40,30 @@ atimeIsUpdated(void) { const char *fn = "fs_noatime"; struct stat st; - +#if defined(_WIN32) && !defined(CYGWIN) + char *buff = NULL; + char *ptr; + int r; + + r = systemf("fsutil behavior query disableLastAccess > query_atime"); + if (r == 0) { + buff = slurpfile(NULL, "query_atime"); + if (buff != NULL) { + ptr = buff; + while(*ptr != '\0' && !isdigit(*ptr)) { + ptr++; + } + if (*ptr == '0') { + free(buff); + return(1); + } else if (*ptr == '1' || *ptr == '2') { + free(buff); + return(0); + } + free(buff); + } + } +#endif if (!assertMakeFile(fn, 0666, "a")) return (0); if (!assertUtimes(fn, 1, 0, 1, 0)) @@ -570,13 +593,13 @@ test_symlink_hybrid(void) assertMakeDir("h", 0755); assertChdir("h"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); @@ -727,13 +750,13 @@ test_symlink_logical(void) assertMakeDir("l", 0755); assertChdir("l"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Note: this test uses archive_read_next_header() @@ -961,8 +984,8 @@ test_symlink_logical_loop(void) assertMakeDir("d1/d2/d3", 0755); assertMakeDir("d2", 0755); assertMakeFile("d2/file1", 0644, "d2/file1"); - assertMakeSymlink("d1/d2/ld1", "../../d1"); - assertMakeSymlink("d1/d2/ld2", "../../d2"); + assertMakeSymlink("d1/d2/ld1", "../../d1", 1); + assertMakeSymlink("d1/d2/ld2", "../../d2", 1); assertChdir(".."); assert((ae = archive_entry_new()) != NULL); @@ -1567,6 +1590,254 @@ test_nodump(void) archive_entry_free(ae); } +static void +test_parent(void) +{ + struct archive *a; + struct archive_entry *ae; + const void *p; + size_t size; + int64_t offset; + int file_count; + int match_count; + int r; + + assertMakeDir("lock", 0311); + assertMakeDir("lock/dir1", 0755); + assertMakeFile("lock/dir1/f1", 0644, "0123456789"); + assertMakeDir("lock/lock2", 0311); + assertMakeDir("lock/lock2/dir1", 0755); + assertMakeFile("lock/lock2/dir1/f1", 0644, "0123456789"); + + assert((ae = archive_entry_new()) != NULL); + assert((a = archive_read_disk_new()) != NULL); + + /* + * Test1: Traverse lock/dir1 as . + */ + assertChdir("lock/dir1"); + + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), ".") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + assertChdir("../.."); + + /* + * Test2: Traverse lock/dir1 directly + */ + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), "lock/dir1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + /* + * Test3: Traverse lock/dir1/. + */ + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock/dir1/.")); + + file_count = 2; + match_count = 0; + while (file_count--) { + archive_entry_clear(ae); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), "lock/dir1/.") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), "lock/dir1/./f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory traverse + * tries to ascend past the initial directory, since it lacks permission + * to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + + /* + * Test4: Traverse lock/lock2/dir1 from inside lock. + * + * This test is expected to fail on platforms with no O_EXEC or + * equivalent (e.g. O_PATH on Linux or O_SEARCH on SunOS), because + * the current traversal code can't handle the case where it can't + * obtain an open fd for the initial current directory. We need to + * check that condition here, because if O_EXEC _does_ exist, we don't + * want to overlook any failure. + */ + assertChdir("lock"); + + failure("Directory traversals should work as well"); + assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, "lock2/dir1")); + + archive_entry_clear(ae); + r = archive_read_next_header2(a, ae); + if (r == ARCHIVE_FAILED) { +#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC) + assertEqualIntA(a, ARCHIVE_OK, r); +#endif + /* Close the disk object. */ + archive_read_close(a); + } else { + file_count = 2; + match_count = 0; + while (file_count--) { + if (file_count == 0) + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header2(a, ae)); + if (strcmp(archive_entry_pathname(ae), + "lock2/dir1") == 0) { + assertEqualInt(archive_entry_filetype(ae), + AE_IFDIR); + ++match_count; + } else if (strcmp(archive_entry_pathname(ae), + "lock2/dir1/f1") == 0) { + assertEqualInt(archive_entry_filetype(ae), + AE_IFREG); + assertEqualInt(archive_entry_size(ae), 10); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_data_block(a, &p, &size, + &offset)); + assertEqualInt((int)size, 10); + assertEqualInt((int)offset, 0); + assertEqualMem(p, "0123456789", 10); + assertEqualInt(ARCHIVE_EOF, + archive_read_data_block(a, &p, &size, + &offset)); + assertEqualInt((int)size, 0); + assertEqualInt((int)offset, 10); + ++match_count; + } + if (archive_read_disk_can_descend(a)) { + /* Descend into the current object */ + assertEqualIntA(a, ARCHIVE_OK, + archive_read_disk_descend(a)); + } + } + failure("Did not match expected filenames"); + assertEqualInt(match_count, 2); + /* + * There is no entry. This will however fail if the directory + * traverse tries to ascend past the initial directory, since + * it lacks permission to do so. + */ + failure("There should be no entry and no error"); + assertEqualIntA(a, ARCHIVE_EOF, + archive_read_next_header2(a, ae)); + + /* Close the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + } + + assertChdir(".."); + + /* Destroy the disk object. */ + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + archive_entry_free(ae); +} + DEFINE_TEST(test_read_disk_directory_traversals) { /* Basic test. */ @@ -1583,4 +1854,6 @@ DEFINE_TEST(test_read_disk_directory_traversals) test_callbacks(); /* Test nodump. */ test_nodump(); + /* Test parent overshoot. */ + test_parent(); } diff --git a/libarchive/test/test_read_extract.c b/libarchive/test/test_read_extract.c index c537e4f9..cd06096e 100644 --- a/libarchive/test/test_read_extract.c +++ b/libarchive/test/test_read_extract.c @@ -161,7 +161,7 @@ DEFINE_TEST(test_read_extract) assertIsDir("dir4/b", 0755); assertIsDir("dir4/c", 0711); if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); free(buff); free(file_buff); diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c index 6392d8f5..9b9d6bda 100644 --- a/libarchive/test/test_read_format_rar.c +++ b/libarchive/test/test_read_format_rar.c @@ -28,6 +28,22 @@ #include <locale.h> +DEFINE_TEST(test_read_format_rar_set_format) +{ + struct archive *a; + struct archive_entry *ae; + const char reffile[] = "test_read_format_rar.rar"; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR)); + assertA(0 == archive_read_open_filename(a, reffile, 10240)); + assertA(0 == 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_rar_basic) { char buff[64]; diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c index 7f2f32df..5248172b 100644 --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -96,7 +96,7 @@ int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) { int ret = 1; uint32_t computed_crc; - fsize = archive_entry_size(ae); + fsize = (la_ssize_t) archive_entry_size(ae); buf = malloc(fsize); if(buf == NULL) return 1; @@ -110,13 +110,28 @@ int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) { computed_crc = crc32(0, buf, fsize); assertEqualInt(computed_crc, crc); ret = 0; - + fn_exit: free(buf); return ret; } -DEFINE_TEST(test_read_format_rar5_stored) +DEFINE_TEST(test_read_format_rar5_set_format) +{ + struct archive *a; + struct archive_entry *ae; + const char reffile[] = "test_read_format_rar5_stored.rar"; + + extract_reference_file(reffile); + assert((a = archive_read_new()) != NULL); + assertA(0 == archive_read_support_filter_all(a)); + assertA(0 == archive_read_set_format(a, ARCHIVE_FORMAT_RAR_V5)); + assertA(0 == archive_read_open_filename(a, reffile, 10240)); + assertA(0 == archive_read_next_header(a, &ae)); + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_stored) { const char helloworld_txt[] = "hello libarchive test suite!\n"; la_ssize_t file_size = sizeof(helloworld_txt) - 1; @@ -143,7 +158,7 @@ DEFINE_TEST(test_read_format_rar5_stored) DEFINE_TEST(test_read_format_rar5_compressed) { const int DATA_SIZE = 1200; - uint8_t buff[DATA_SIZE]; + uint8_t buff[1200]; PROLOGUE("test_read_format_rar5_compressed.rar"); @@ -161,7 +176,7 @@ DEFINE_TEST(test_read_format_rar5_compressed) DEFINE_TEST(test_read_format_rar5_multiple_files) { const int DATA_SIZE = 4096; - uint8_t buff[DATA_SIZE]; + uint8_t buff[4096]; PROLOGUE("test_read_format_rar5_multiple_files.rar"); @@ -173,7 +188,7 @@ DEFINE_TEST(test_read_format_rar5_multiple_files) assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(verify_data(buff, 1, DATA_SIZE)); - + assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); @@ -207,7 +222,7 @@ DEFINE_TEST(test_read_format_rar5_multiple_files) DEFINE_TEST(test_read_format_rar5_multiple_files_solid) { const int DATA_SIZE = 4096; - uint8_t buff[DATA_SIZE]; + uint8_t buff[4096]; PROLOGUE("test_read_format_rar5_multiple_files_solid.rar"); @@ -216,7 +231,7 @@ DEFINE_TEST(test_read_format_rar5_multiple_files_solid) assertEqualInt(DATA_SIZE, archive_entry_size(ae)); assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); assertA(verify_data(buff, 1, DATA_SIZE)); - + assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test2.bin", archive_entry_pathname(ae)); assertEqualInt(DATA_SIZE, archive_entry_size(ae)); @@ -309,7 +324,7 @@ DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_second) DEFINE_TEST(test_read_format_rar5_blake2) { const la_ssize_t proper_size = 814; - uint8_t buf[proper_size]; + uint8_t buf[814]; PROLOGUE("test_read_format_rar5_blake2.rar"); assertA(0 == archive_read_next_header(a, &ae)); @@ -334,7 +349,7 @@ DEFINE_TEST(test_read_format_rar5_arm_filter) * test. */ const la_ssize_t proper_size = 90808; - uint8_t buf[proper_size]; + uint8_t buf[90808]; PROLOGUE("test_read_format_rar5_arm.rar"); assertA(0 == archive_read_next_header(a, &ae)); @@ -426,6 +441,57 @@ DEFINE_TEST(test_read_format_rar5_stored_skip_all_in_part) EPILOGUE(); } +DEFINE_TEST(test_read_format_rar5_multiarchive_solid_extr_all) +{ + const char* reffiles[] = { + "test_read_format_rar5_multiarchive_solid.part01.rar", + "test_read_format_rar5_multiarchive_solid.part02.rar", + "test_read_format_rar5_multiarchive_solid.part03.rar", + "test_read_format_rar5_multiarchive_solid.part04.rar", + NULL + }; + + PROLOGUE_MULTI(reffiles); + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("cebula.txt", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7E5EC49E)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7cca70cd)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x7e13b2c6)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xf166afcb)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x9fb123d9)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x10c43ed4)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xb9d155f2)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x36a448ff)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0x886F91EB)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + EPILOGUE(); +} + DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all) { const char* reffiles[] = { @@ -598,7 +664,7 @@ DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_last) EPILOGUE(); } -DEFINE_TEST(test_read_format_rar5_solid_skip_all) +DEFINE_TEST(test_read_format_rar5_solid_skip_all) { const char* reffile = "test_read_format_rar5_solid.rar"; @@ -623,7 +689,7 @@ DEFINE_TEST(test_read_format_rar5_solid_skip_all) EPILOGUE(); } -DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first) +DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first) { const char* reffile = "test_read_format_rar5_solid.rar"; @@ -649,7 +715,7 @@ DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first) EPILOGUE(); } -DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_second) +DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_second) { const char* reffile = "test_read_format_rar5_solid.rar"; @@ -705,25 +771,37 @@ DEFINE_TEST(test_read_format_rar5_extract_win32) { PROLOGUE("test_read_format_rar5_win32.rar"); assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("testdir", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755); + assertA(0 == extract_one(a, ae, 0)); + assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x7CCA70CD)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test1.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x7E13B2C6)); assertA(0 == archive_read_next_header(a, &ae)); + /* Read only file */ assertEqualString("test2.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0444); assertA(0 == extract_one(a, ae, 0xF166AFCB)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test3.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x9FB123D9)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test4.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x10C43ED4)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test5.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0xB9D155F2)); assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test6.bin", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644); assertA(0 == extract_one(a, ae, 0x36A448FF)); EPILOGUE(); } @@ -768,3 +846,194 @@ DEFINE_TEST(test_read_format_rar5_block_by_block) assertEqualInt(computed_crc, 0x7CCA70CD); EPILOGUE(); } + +DEFINE_TEST(test_read_format_rar5_owner) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_owner.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("root.txt", archive_entry_pathname(ae)); + assertEqualString("root", archive_entry_uname(ae)); + assertEqualString("wheel", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("nobody.txt", archive_entry_pathname(ae)); + assertEqualString("nobody", archive_entry_uname(ae)); + assertEqualString("nogroup", archive_entry_gname(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("numeric.txt", archive_entry_pathname(ae)); + assertEqualInt(9999, archive_entry_uid(ae)); + assertEqualInt(8888, archive_entry_gid(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_symlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_symlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("symlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_FILE, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dirlink", archive_entry_pathname(ae)); + assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); + assertEqualString("dir", archive_entry_symlink(ae)); + assertEqualInt(AE_SYMLINK_TYPE_DIRECTORY, archive_entry_symlink_type(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("dir", archive_entry_pathname(ae)); + assertEqualInt(AE_IFDIR, archive_entry_filetype(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_hardlink) +{ + const int DATA_SIZE = 5; + uint8_t buff[5]; + + PROLOGUE("test_read_format_rar5_hardlink.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("file.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertA((int) archive_entry_mtime(ae) > 0); + assertEqualInt(DATA_SIZE, archive_entry_size(ae)); + assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("hardlink.txt", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); + assertEqualString("file.txt", archive_entry_hardlink(ae)); + assertA(0 == archive_read_data(a, NULL, archive_entry_size(ae))); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_extra_field_version) +{ + PROLOGUE("test_read_format_rar5_extra_field_version.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("bin/2to3;1", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xF24181B7)); + + assertA(0 == archive_read_next_header(a, &ae)); + assertEqualString("bin/2to3", archive_entry_pathname(ae)); + assertA(0 == extract_one(a, ae, 0xF24181B7)); + + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_readtables_overflow) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_readtables_overflow.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * buffer overflow errors during reading rar5 tables. */ + assertA(0 == archive_read_data(a, buf, sizeof(buf))); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_leftshift1) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_leftshift1.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(ARCHIVE_FATAL == archive_read_data(a, buf, sizeof(buf))); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_leftshift2) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_leftshift2.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(ARCHIVE_FATAL == archive_read_data(a, buf, sizeof(buf))); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_truncated_huff) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_truncated_huff.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to undefined operations when using -fsanitize. */ + assertA(ARCHIVE_FATAL == archive_read_data(a, buf, sizeof(buf))); + assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae)); + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_invalid_dict_reference) +{ + uint8_t buf[16]; + + PROLOGUE("test_read_format_rar5_invalid_dict_reference.rar"); + + assertA(0 == archive_read_next_header(a, &ae)); + /* This archive is invalid. However, processing it shouldn't cause any + * errors related to buffer underflow when using -fsanitize. */ + assertA(ARCHIVE_FATAL == archive_read_data(a, buf, sizeof(buf))); + assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae)); + + EPILOGUE(); +}
\ No newline at end of file diff --git a/libarchive/test/test_read_format_rar5_extra_field_version.rar.uu b/libarchive/test/test_read_format_rar5_extra_field_version.rar.uu new file mode 100644 index 00000000..f9a5ce69 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_extra_field_version.rar.uu @@ -0,0 +1,10 @@ +begin 644 test_read_format_rar5_extra_field_version.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`"BNGU4(0(#!%D&7^V#`I?:-URW@4'R +M@`,!"&)I;B\R=&\S`P0``<7)5B550C]U!#WY13&:5TJ$=$IZ(*3`C\#F0%O\ +M)$)*@]X[RK.Z.G*HUT;V5FO&;:X/FDW,W`95I8T%@@C:DD="_8Z.9+CQH^IG +M8!ZF0N)JSY2?R(W@25K`U&W)Q1X"`MD`!M\`[8,"E]HW7+>!0?*``P$(8FEN +M+S)T;S/%R58E54(_=00]^44QFE=*A'1*>B"DP(_`YD!;_"1"2H/>.\JSNCIR +MJ-=&]E9KQFVN#YI-S-P&5:6-!8((VI)'0OV.CF2X\:/J9V`>ID+B:L^4G\B- +,X$E:P!UW5E$#!00` +` +end diff --git a/libarchive/test/test_read_format_rar5_hardlink.rar.uu b/libarchive/test/test_read_format_rar5_hardlink.rar.uu new file mode 100644 index 00000000..0ec085d4 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_hardlink.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_hardlink.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!KI,1X'@("A0`&A0"D@P(XC;=<(1>3 +M?8```0AF:6QE+G1X=#$R,S0*[8@"T2X"`PT`!@6D@P(XC;=<`````(```0QH +@87)D;&EN:RYT>'0,!00`"&9I;&4N='AT'7=640,%!``` +` +end diff --git a/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu b/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu new file mode 100644 index 00000000..9b78c9b3 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_invalid_dict_reference.rar +M4F%R(1H'`0"-[P+2``+#!QR`!P`F^P#_^_O[^_O[^R4``B$<`0(`#@```0`` +M`"#2````_____QH(`/__^P#_W5)04(#_`(:&;;%DS+?,L0```````````+%D +MS+*RLK*R/@``____Y`"R````XP```````!4``````.X````````````````` +M%5<M;&@W;3$W"2!S;'$2C5L`_____@D0````$"('``"8F)@+````/__?```` +M@```2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$A(2$@S2(``2$A(2$A( +>2$A(2$A(2$A(2$A(2$A(2$Q(2$A(2$A(2$A(2)]( +` +end diff --git a/libarchive/test/test_read_format_rar5_leftshift1.rar.uu b/libarchive/test/test_read_format_rar5_leftshift1.rar.uu new file mode 100644 index 00000000..694a27f6 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_leftshift1.rar.uu @@ -0,0 +1,9 @@ +begin 644 test_read_format_rar5_leftshift1.rar +M4F%R(1H'`0"-[P+2``(''(`'`"``_R4``B$<`0(`#@```0```"#2````____ +M_P`(`/__^P#_W0`"(8#_`(:&;;%DS+?,L=:NL0#(3`$````````````````` +M``"``````````+!DS+*RL[*RL@```-P``````````````````(`````````` +ML&3,LK*RLK*R````W`````#X____````````````````````````%5H>;&@T +M+3HW"2!SB^)_<Z3_`````?40'Q\?'Q\?'Q\?'Q\?'Q\?'Q\?'Q\?'Q\````` +5`````````````````/H`>@`````` +` +end diff --git a/libarchive/test/test_read_format_rar5_leftshift2.rar.uu b/libarchive/test/test_read_format_rar5_leftshift2.rar.uu new file mode 100644 index 00000000..57ffad72 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_leftshift2.rar.uu @@ -0,0 +1,6 @@ +begin 644 test_read_format_rar5_leftshift2.rar +M4F%R(1H'`0"-[P+2``(''(`'`2``_RL``B'+`0(`,O__````-WJ\KR<<)0`" +M(;<*`BY*`!```&;%T#%24%"`_R4`[@K+(2Y*`&$``'__`/\E``(N2@`0`0(` +0(?__`%N&?Q2UH.CHZ.CHZ``` +` +end diff --git a/libarchive/test/test_read_format_rar5_owner.rar.uu b/libarchive/test/test_read_format_rar5_owner.rar.uu new file mode 100644 index 00000000..285bdb91 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_owner.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_owner.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`!W:E-^+0(##H4`!H4`I(,"_8VW7"$7 +MDWV```$(<F]O="YT>'0-!@,$<F]O=`5W:&5E;#$R,S0*2J"P0S,"`Q*%``:% +M`*2#`FZ-MUQFP<VL@``!"FYO8F]D>2YT>'01!@,&;F]B;V1Y!VYO9W)O=7`U +M-C<X"L=81/8I`@,'A0`&A0"D@P)LD[=<>B#;(H```0MN=6UE<FEC+G1X=`8& +2#(].N$4Y.3DY"AUW5E$#!00` +` +end diff --git a/libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu b/libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu new file mode 100644 index 00000000..611c2af2 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu @@ -0,0 +1,15 @@ +begin 644 test_read_format_rar5_readtables_overflow.rar +M4F%R(1H'`0"-[P+2`)3+'_4`C>\"T@`"T@"4RQ_5]0#O0````,L?Q_T``(`" +MT@"4RQ_=V-C8`)3+']W=]0"-\`+2`)3+']WU`(WO`M(``M(`E,L?U?4`[P+2 +M`)3+'\?]``"``M(`E,L?W=C8V`"4RQ_=]0#V`(WO`M'UV,?8V-C8$=C8V-C8 +MV(W8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8!]C8V-C8V-C8V-C8V-C8V-C8 +MV-C8V-C8V-C(V-C8V-C2`)3+']W8V-C8V-C8V-C8V-C8V-C8@-C8V-C8V-C8 +MV/+8V-C8V-C8V-C8`038V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8!-C8 +MV-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8V-C8`(`"V`7V`(WO`M'U`]L? +MW?4`C>\"T@`"T@"4'__U`(WO`N``E,L?W84`C0`0T@"4RQ_=V-C8V-C8V`"4 +MR_\R]0#V`(W8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-C8V-C8 +MV-C8V-C8V-C8V-C8R-C8V-C8T@"4RQ_=V-C8V-C8V-C8V-C8V-C8V(#8V-C8 +MV-C8````9-C8V-C8V!'8V-C8V-C8]]C8V-C8V-C8V-C8V/+8V-C8V-C8V-C8 +=`038V-C8V-C8V-C8V-C8V-C8V`?8V-C8V-C8V-@` +` +end diff --git a/libarchive/test/test_read_format_rar5_symlink.rar.uu b/libarchive/test/test_read_format_rar5_symlink.rar.uu new file mode 100644 index 00000000..f603e3c0 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_symlink.rar.uu @@ -0,0 +1,8 @@ +begin 644 test_read_format_rar5_symlink.rar +M4F%R(1H'`0`SDK7E"@$%!@`%`0&`@`"$O8RN'@("A0`&A0"D@P(XC;=<(1>3 +M?8!``0AF:6QE+G1X=#$R,S0*8QI3.2T"`PT`!@CMPP)7C;=<`````(!``0MS +M>6UL:6YK+G1X=`P%`0`(9FEL92YT>'2.NOQU)`(#"``&`^W#`DF6MUP````` +M@$`!!V1I<FQI;FL'!0$!`V1I<J/_?\87`@(`!P#M@P%&EK=<`````(```0-D +*:7(==U91`P4$```` +` +end diff --git a/libarchive/test/test_read_format_rar5_truncated_huff.rar.uu b/libarchive/test/test_read_format_rar5_truncated_huff.rar.uu new file mode 100644 index 00000000..12d9e255 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_truncated_huff.rar.uu @@ -0,0 +1,7 @@ +begin 644 test_read_format_rar5_truncated_huff.rar +M4F%R(1H'`0"-[P+2``'#]#P\7P$'`0"-[P+2``+2`!;#M#Q::7!)2?__'`!I +M?_O_0B\*0RX-,'%O.\(#!-'^T#4````0`P1_``!#(3`P,./H`P```*^OKZ^O +MKZ^OKZ^OKZ^OKZ^OKZ^OKZ^OKZ^OKZ^OKZ^OKZ\0`*^OKZ^A``KZ``$`2^\# +9T>WMNP$+-5H*^@`!`$OOB]$````0"S5:*@`` +` +end diff --git a/libarchive/test/test_read_format_rar5_win32.rar.uu b/libarchive/test/test_read_format_rar5_win32.rar.uu index 59920490..16d9fce1 100644 --- a/libarchive/test/test_read_format_rar5_win32.rar.uu +++ b/libarchive/test/test_read_format_rar5_win32.rar.uu @@ -1,68 +1,69 @@ begin 644 test_read_format_rar5_win32.rar -M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``/^"F5B0"`POI`@2P"2#-<,I\@`4` -M"'1E<W0N8FEN"@,"/.U\@0U:U`'*]&4!)V5@5!]5=EV_E))QR<]EEED,<-EE -MDQC%DA.&0DDDX9"0DDDD)..222$(220A"220DDA"$A+-M6][?3,.@'T=`ZX_ -MGKJ?]T'^]]KWW7FO.E_H`$`!`,@@GY^/;U]/+Q[^[LZ^KIZ.?EY./BX>#?W= -MS:V=?6U=33TM#/S<S*R<C'QL7$P\&_O;R[N;BWMK6TL[*QKZZMJZJIIZ6DHZ -M*AH)^=G)N9EY:4DTLE(R$>A0'XZ,BHF'.G#9J&A(*`?GQ[>GAV='-R<6]M:V -MEG96-A7UU;65=54R$W]?R/T"4^B4Y_A$ET6*,&>6%VHX*:8IE!?F0M%VG48R -MPSC`J$?_H)3&:0I?95"L5E8+BP&1:#<N!W7A!L`/,02,@GF85C0+IJ&6V#>; -MAX.`8'(/KF$3H*9V&`\#;>A[O8=7T(W\6$"-&#'G"AY-"8-B^.#F.APB!*GA -MA18[8T/Y\5"`;"$-"(32,9R0+R4`8B?AZB4"`POG`@2`("#&LA-^@`4`"71E -M<W0Q+F)I;@H#`A*5H8`-6M0!R/!C`1!D168O5P5_4DDDDDDDDDDDDDDDDDDD -MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD^_/D;F=O/:[?;Z2Z_N -M[6[OF=&;N]._TF,S#.GS#C<WS/.'/XKG;\@`@`@!L,/'3AM\>_MZ^GGY>/AW -M]W;V=?5T]'/S<O)Q\7#P;^]N[FWM;.QKZVKJ:>EHZ&?G9N9EY63D8^-BXF'A -M8.!?WU[>7=U<W%O;6MI9V5C85]=6UE755-13TU+24=%0T$_/3LY-S4S,2\M* -MRDG)2,A'QT;&1<5$Q$/#0L)!P4#`/[\^^]['VA2X4M7TE0H#.Q@!;-"-XC0$ -M.A"U@6`X1/;M+3@AZ=F@%F`%LT(W2-`0Z$+6!8#A$]N4M."'IR:`68`6S0C< -M(T!#H0M8%@.$3VW2TX(>FYH!9@!;-"-LC0$.A"U@6`X1-8__0K^QS*_>TU/6 -MRJ_(8IKGE-.XIH6GK_HGV@`)6BG`)0(#"^8"!(`@(,NO9O&`!0`)=&5S=#(N -M8FEN"@,"A/;"@`U:U`'/]F(!$&1%9B]7!7]2222222222222222222222222 -M2222222222222222222222222222222222223[\^1N9V\]KM]OI+K^[M;N^9 -MT9N[T[_28S,,Z?,.-S?,\X<_BN=OR`"`"`&IAXZ<-OCW]O7T\_+Q\._N[>SK -MZNGHY^;EY./BX>#?WMW<V]K9V-?6U=33TM'0S\[-S,O*R<C'QL7$P\+!P+^^ -MO;R[NKFXM[:UM+.RL;"OKJVLJZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64DY*1 -MD(^.C8R+BHF(AX:%A(."@8!_?GWWO8^T*7"EJ^DJ%`9V,`+9H1O$:`AT(6L" -MP'")[=I:<$/3LT`LP`MFA&Z1H"'0A:P+`<(GMREIP0].30"S`"V:$;A&@(=" -M%K`L!PB>VZ6G!#TW-`+,`+9H1MD:`AT(6L"P'")K'_Z%?V.97[VFIZV57Y#% -M-<\IIW%-"T]?]$^TVMWAVR4"`POT`@2`("#9([&?@`4`"71E<W0S+F)I;@H# -M`H1PWX`-6M0!RN%P`1!D554O5P5^)))))))))))))))))))))))))))))))) -M))))))))))))))))))))))))))))))SO.\[S<:UGCPYS=>O+LEY]]M9^&\\, -MWO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#848765^/?V]?3S\O'P[^[M[.OJ -MZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z] -MO+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923DI&0 -MCXZ-C(N*B8B'AH6$@X*!@']^??=]3[8AOPALOH4&#J;"`FDX$V"95@TP3;@P -MF6DRPI=O")0\&G@!T,0$TG`FN3*KFER;<&$RTF6%+MV1*'@T[`.AB`FDX$UB -M958T/&FX`#:3+"EVZ(E#P:=`'0Q`32<":I,JJ:'C3<`!M)EA2^4__8C^QK(_ -M>\:'K=J/R*(<ZY#CV(<&IZ_]"_:`$R,0$"4"`POT`@2`("#4/L00@`4`"71E -M<W0T+F)I;@H#`@*,&8$-6M0!R^!P`1!D554O5P5^I))))))))))))))))))) -M))))))))))))))))))))))))))))))))))))))))))SO.\YS<:UGCPYS=>O+ -MLEY]]M9^&\\,WO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#7=,+*J?'O[>OIY -M^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV;F9>5DY&/C -M8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3<U, -MS$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_/ON^Z^V(;\(;+Z%!@ZFP@)I. -M!-@F58-,$VX,)EI,L*7:Y$H>#2X!T,0$TG`FL3*K&EB;<&$RTF6%+MX1*'@T -M\`.AB`FDX$U2954T/&FX`#:3+"EV[(E#P:=@'0Q`32<":A,JH:'C3<`!M)EA -M2^=?_L1_8UD?O>-#UNU'Y%$.=<AQ[$.#4]?^A?M`Y!DD9R4"`PO\`@2`("#R -M5=&Y@`4`"71E<W0U+F)I;@H#`BI\2($-6M0!S.]X`1!D554O=6ZDDDDDDDDD -MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG.\[SO-Q -MFM>/..;N7KV[)>??;>LS[/'-[UX[_28S,,\?L,TUS/OM&_Q7/7Y`!`!`#6<+ -MK*J?'O[>OIY^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGY -MV;F9>5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14 -M-!/ST[.3<U,S$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_/ON^X^W$)PA)M -MD@@5^H.&2UDI2.+AC"BYI<-/"!MIL`Z/;L$0M;)V8*FSADM9*4CBP8PHL:6# -M3P@;:;`.CVZ!$+6R=&"ILX9+62E(XJ&,**FE0T\(&VFP#H]N01"ULG)@J;.& -M2UDI2.*!C"BAI0-/"!MIL`Z/G'_[B/[&LC][QH>MVH_(HAYUR'CV(>#4]?^@ -M_:`_"MBZ)0(#"_P"!(`@(/](I#:`!0`)=&5S=#8N8FEN"@,"%79G@0U:U`'- -M[G@!$&1552]U;J2222222222222222222222222222222222222222222222 -M222222222222222<[SO.\W&:UX\XYNY>O;LEY]]MZS/L\<WO7CO])C,PSQ^P -MS37,^^T;_%<]?D`$`$`-TX7654^/?V]?3S\O'P[^[M[.OJZ>CGYN7DX^+AX- -M_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z]O+NZN;BWMK6TL[ -M*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923DI&0CXZ-C(N*B8B'AH -M6$@X*!@']^??=]Q]N(3A"3;)!`K]0<,EK)2D<7#&%%S2X:>$#;38!T>W8(A: -MV3LP5-G#):R4I'%@QA18TL&GA`VTV`='MT"(6MDZ,%39PR6LE*1Q4,845-*A -MIX0-M-@'1[<@B%K9.3!4V<,EK)2D<4#&%%#2@:>$#;38!T?./_W$?V-9'[WC -;0];M1^11#SKD/'L0\&IZ_]!^T!UW5E$#!00` +M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``EV&"+"$"`PL`!0`0`````(````=T +M97-T9&ER"@,"5N<QTHH!U0&_NE<D)`(#"^D"!+`)(,UPRGR``P`(=&5S="YB +M:6X*`P(\[7R!#5K4`<KT90$G96!4'U5V7;^4DG')SV6660QPV663&,62$X9" +M223AD)"2220DXY))(0A))"$)))"22$(2$LVU;WM],PZ`?1T#KC^>NI_W0?[W +MVO?=>:\Z7^@`0`$`R""?GX]O7T\O'O[NSKZNGHY^7DX^+AX-_=W-K9U];5U- +M/2T,_-S,K)R,?&Q<3#P;^]O+NYN+>VM;2SLK&OKJVKJJFGI:2CHJ&@GYV<FY +MF7EI232R4C(1Z%`?CHR*B8<Z<-FH:$@H!^?'MZ>'9T<W)Q;VUK:6=E8V%?75 +MM95U53(3?U_(_0)3Z)3G^$2718HP9Y87:C@IIBF4%^9"T7:=1C+#.,"H1_^@ +ME,9I"E]E4*Q65@N+`9%H-RX'=>$&P`\Q!(R">9A6-`NFH9;8-YN'@X!@<@^N +M81.@IG88#P-MZ'N]AU?0C?Q80(T8,><*'DT)@V+XX.8Z'"($J>&%%CMC0_GQ +M4(!L(0T(A-(QG)`O)0`QGD%J)0(#"^L"!(`@(,:R$WZ``P`)=&5S=#$N8FEN +M"@,"$I6A@`U:U`'-\6<!$&1&92]7!7^22222222222222222222222222222 +M222222222222222222222222222222223GY^RW,[??J[><7#G7SNUN[YG1F[ +MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&PPZ<-FGU\_'O[>OIY^7CX=_=V +M]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV;F9>5DY&/C8N)AX6#@ +M7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3<U,S$O+2LI) +MR4C(1\=&QD7%1,1#PT+"0<%`P#_[WL?R%+A2U?J5"@,[&`%LT(W2-`0Z$+6! +M8#A$]N4M."'IR:`68`6S0C<(T!#H0M8%@.$3VW2TX(>FYH!9@!;-"-LC0$.A +M"U@6`X1/;5+3@AZ:F@%F`%LT(VB-`0Z$+6!8#A$UA]Z%,?GS*>SVE?X8*>=C +M4\-BG=.J=HXIZ?]R?M!(:PP')0(#"^L"!(`@(<NO9O&``P`)=&5S=#(N8FEN +M"@,"A/;"@`U:U`',\&<!$&1&92]7!7^22222222222222222222222222222 +M222222222222222222222222222222223GY^RW,[??J[><7#G7SNUN[YG1F[ +MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&IATX;-/KY^/?V]?3S\O'P[^[M +M[.OJZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'` +MO[Z]O+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923 +MDI&0CXZ-C(N*B8B'AH6$@X*!@'_WO8_D*7"EJ_4J%`9V,`+9H1ND:`AT(6L" +MP'")[<I:<$/3DT`LP`MFA&X1H"'0A:P+`<(GMNEIP0]-S0"S`"V:$;9&@(=" +M%K`L!PB>VJ6G!#TU-`+,`+9H1M$:`AT(6L"P'")K#[T*8_/F4]GM*_PP4\[& +MIX;%.Z=4[1Q3T_[D_:#Z7*U;)0(#"_<"!(`@(-DCL9^``P`)=&5S=#,N8FEN +M"@,"A'#?@`U:U`'.YG,!$&1552]7!7XDDDDDDDDDDDDDDDDDDDDDDDDDDDDD +MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG>\[SO-RUK/'AS>Z]>]72\[VUGX; +MSPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!LK,L+N/CW]O7T\_+Q\._N +M[>SKZNGHY^;EY./BX>#?WMW<V]K9V-?6U=33TM'0S\[-S,O*R<C'QL7$P\+! +MP+^^O;R[NKFXM[:UM+.RL;"OKJVLJZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64 +MDY*1D(^.C8R+BHF(AX:%A(."@8!_?O_[[UOQB$^$)+[%!@ZJP@)I6!-DF59- +M,DTP85+::84MW9$H?1AV`=#$!-*P)L$RK!I@FF#"I;33"ENZ(E#Z,.@#H8@) +MI6!-<F57-#AI,`!MIIA2W<D2A]&'(!T,0$TK`FX)E7!H<-)@`-M-,*7RW][$ +M+?7[(?3[2/T-$/>UH>G1#RK4/$L0^/_<7F@SHER0)0(#"_<"!(`@(-0^Q!"` +M`P`)=&5S=#0N8FEN"@,"`HP9@0U:U`'/YW,!$&1552]7!7XDDDDDDDDDDDDD +MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG.\[SG-RUK/' +MASFZ]>]72\[VUGX;SPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!L*,KK +M*_'O[>OIY^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV; +MF9>5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-! +M/ST[.3<U,S$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_?_WWJ?C$.>$.%]B +M@P=380$TG`FR3*LFF2;D&$RVFF%+=@B4/HPP`=#$!-)P)KDRJYI<FY!A,MII +MA2W=D2A]&'8!T,0$TG`FL3*K&APTY``;::84MW1$H?1AT`=#$!-)P)JDRJIH +M<-.0`&VFF%+Y3^]B%/K]D/I]I'Z&B'O:T/3HAY5J'B6(?'_N+S0M?'0I)0(# +M"X`#!(`@(/)5T;F``P`)=&5S=#4N8FEN"@,"*GQ(@0U:U`'([WP!$&1552]U +M;J2222222222222222222222222222222222222222222222222222222222 +M222<[SO.\W+-:\><<W<O7O5TO.]MYF?9XZUO?CO]%F,,\?C,,TUW/OM&_7OX +M'GK\@`@`@!K.%UE5/CW]O7T\_+Q\._N[>SKZNGHY^;EY./BX>#?WMW<V]K9V +M-?6U=33TM'0S\[-S,O*R<C'QL7$P\+!P+^^O;R[NKFXM[:UM+.RL;"OKJVLJ +MZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64DY*1D(^.C8R+BHF(AX:%A(."@8!_? +MO_[[W'XXA.D)1LT$"OW!PR6LE*QQ<,847-+AIH@;:C`.CV[!$+6S=F"ILX9+ +M62E8XL&,*+&E@TT0-M1@'1[=`B%K9NC!4V<,EK)2L<5#&%%32H::(&VHP#H] +MN01"ULW)@J;.&2UDI6.*!C"BAI0--$#;48!T?G'][B''U_9#Z?:1^AHA[VM# +MT\$/*RH>)D0^/_</F@#V;XCT)0(#"X`#!(`@(/](I#:``P`)=&5S=#8N8FEN +M"@,"%79G@0U:U`')[GP!$&1552]U;J222222222222222222222222222222 +M2222222222222222222222222222222<[SO.\W+-:\><<W<O7O5TO.]MYF?9 +MXZUO?CO]%F,,\?C,,TUW/OM&_7OX'GK\@`@`@!NG"ZRJGQ[^WKZ>?EX^'?W= +MO9U]73T<_-R\G'Q</!O[V[N;>UL[&OK:NIIZ6CH9^=FYF7E9.1CXV+B8>%@X +M%_?7MY=W5S<6]M:VEG96-A7UU;65=54U%/34M)1T5#03\].SDW-3,Q+RTK*2 +M<E(R$?'1L9%Q43$0\-"PD'!0,`_OW_]][C\<0G2$HV:"!7[@X9+62E8XN&,* +M+FEPTT0-M1@'1[=@B%K9NS!4V<,EK)2L<6#&%%C2P::(&VHP#H]N@1"ULW1@ +MJ;.&2UDI6.*AC"BII4--$#;48!T>W((A:V;DP5-G#):R4K'%`QA10TH&FB!M +KJ,`Z/SC^]Q#CZ_LA]/M(_0T0][6AZ>"'E94/$R(?'_N'S0`==U91`P4$```` ` end diff --git a/libarchive/test/test_read_format_raw.c b/libarchive/test/test_read_format_raw.c index 831bcec1..ccd9d0ac 100644 --- a/libarchive/test/test_read_format_raw.c +++ b/libarchive/test/test_read_format_raw.c @@ -36,6 +36,7 @@ DEFINE_TEST(test_read_format_raw) const char *reffile1 = "test_read_format_raw.data"; const char *reffile2 = "test_read_format_raw.data.Z"; const char *reffile3 = "test_read_format_raw.bufr"; + const char *reffile4 = "test_read_format_raw.data.gz"; /* First, try pulling data out of an uninterpretable file. */ extract_reference_file(reffile1); @@ -113,6 +114,27 @@ DEFINE_TEST(test_read_format_raw) assert(!archive_entry_ctime_is_set(ae)); assert(!archive_entry_mtime_is_set(ae)); + /* Fourth, try with gzip which has metadata. */ + extract_reference_file(reffile4); + 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_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, reffile4, 1)); + + /* First (and only!) Entry */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("test-file-name.data", archive_entry_pathname(ae)); + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); + assert(archive_entry_mtime_is_set(ae)); + assertEqualIntA(a, archive_entry_mtime(ae), 0x5cbafd25); + /* Most fields should be unset (unknown) */ + assert(!archive_entry_size_is_set(ae)); + assert(!archive_entry_atime_is_set(ae)); + assert(!archive_entry_ctime_is_set(ae)); + /* Test EOF */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); diff --git a/libarchive/test/test_read_format_raw.data.gz.uu b/libarchive/test/test_read_format_raw.data.gz.uu new file mode 100644 index 00000000..cf1f7b30 --- /dev/null +++ b/libarchive/test/test_read_format_raw.data.gz.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_raw.data.gz +L'XL(""7]NEP``W1E<W0M9FEL92UN86UE+F1A=&$`2\O/YP(`J&4R?@0````` +` +end diff --git a/libarchive/test/test_read_format_xar.c b/libarchive/test/test_read_format_xar.c index 4654f838..1c852452 100644 --- a/libarchive/test/test_read_format_xar.c +++ b/libarchive/test/test_read_format_xar.c @@ -799,7 +799,7 @@ static void verify(unsigned char *d, size_t s, static void verifyB(unsigned char *d, size_t s) { struct archive* a; struct archive_entry *entry = NULL; - la_int64_t buf_size; + size_t buf_size; unsigned char *buf; assert((a = archive_read_new()) != NULL); @@ -826,20 +826,20 @@ static void verifyB(unsigned char *d, size_t s) { // f1, content "onetwothree\n", size 12 bytes assertA(0 == archive_read_next_header(a, &entry)); - buf_size = archive_entry_size(entry); + buf_size = (size_t) archive_entry_size(entry); assertA(buf_size == 12); buf = (unsigned char*) malloc(buf_size); assertA(NULL != buf); - assertA(buf_size == archive_read_data(a, buf, buf_size)); + assertA(buf_size == (size_t) archive_read_data(a, buf, buf_size)); free(buf); // f2, content "fourfivesix\n", size 12 bytes assertA(0 == archive_read_next_header(a, &entry)); - buf_size = archive_entry_size(entry); + buf_size = (size_t) archive_entry_size(entry); assertA(buf_size == 12); buf = (unsigned char*) malloc(buf_size); assertA(NULL != buf); - assertA(buf_size == archive_read_data(a, buf, buf_size)); + assertA(buf_size == (size_t) archive_read_data(a, buf, buf_size)); free(buf); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); diff --git a/libarchive/test/test_read_format_zip.c b/libarchive/test/test_read_format_zip.c index 6cc25a66..e47d17cd 100644 --- a/libarchive/test/test_read_format_zip.c +++ b/libarchive/test/test_read_format_zip.c @@ -26,6 +26,76 @@ #include "test.h" __FBSDID("$FreeBSD: head/lib/libarchive/test/test_read_format_zip.c 189482 2009-03-07 03:30:35Z kientzle $"); +#define __LIBARCHIVE_BUILD +#include <archive_crc32.h> + +static +int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) +{ + la_ssize_t fsize, bytes_read; + uint8_t* buf; + int ret = 1; + uint32_t computed_crc; + + fsize = (la_ssize_t) archive_entry_size(ae); + buf = malloc(fsize); + if(buf == NULL) + return 1; + + bytes_read = archive_read_data(a, buf, fsize); + if(bytes_read != fsize) { + assertEqualInt(bytes_read, fsize); + goto fn_exit; + } + + computed_crc = crc32(0, buf, fsize); + assertEqualInt(computed_crc, crc); + ret = 0; + +fn_exit: + free(buf); + return ret; +} + +static +int extract_one_using_blocks(struct archive* a, int block_size, uint32_t crc) +{ + uint8_t* buf; + int ret = 1; + uint32_t computed_crc = 0; + la_ssize_t bytes_read; + + buf = malloc(block_size); + if(buf == NULL) + return 1; + + while(1) { + bytes_read = archive_read_data(a, buf, block_size); + if(bytes_read == ARCHIVE_RETRY) + continue; + else if(bytes_read == 0) + break; + else if(bytes_read < 0) { + /* If we're here, it means the decompressor has failed + * to properly decode test file. */ + assertA(0); + ret = 1; + goto fn_exit; + } else { + /* ok */ + } + + computed_crc = crc32(computed_crc, buf, bytes_read); + } + + assertEqualInt(computed_crc, crc); + ret = 0; + +fn_exit: + free(buf); + return ret; +} + /* * The reference file for this has been manually tweaked so that: * * file2 has length-at-end but file1 does not @@ -312,3 +382,537 @@ DEFINE_TEST(test_read_format_zip) test_extract_length_at_end(); test_symlink(); } + +DEFINE_TEST(test_read_format_zip_ppmd_one_file) +{ + const char *refname = "test_read_format_zip_ppmd8.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_one_file_blockread) +{ + const char *refname = "test_read_format_zip_ppmd8.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_multi) +{ + const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_multi_blockread) +{ + const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_one_file) +{ + const char *refname = "test_read_format_zip_lzma.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_one_file_blockread) +{ + const char *refname = "test_read_format_zip_lzma.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_multi) +{ + const char *refname = "test_read_format_zip_lzma_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_multi_blockread) +{ + const char *refname = "test_read_format_zip_lzma_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + + +DEFINE_TEST(test_read_format_zip_bzip2_one_file) +{ + const char *refname = "test_read_format_zip_bzip2.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + skipping("bzip2 is not fully supported on this platform"); + archive_read_close(a); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_one_file_blockread) +{ + const char *refname = "test_read_format_zip_bzip2.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + skipping("bzip2 is not fully supported on this platform"); + archive_read_close(a); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_multi) +{ + const char *refname = "test_read_format_zip_bzip2_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + skipping("bzip2 is not fully supported on this platform"); + archive_read_close(a); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_multi_blockread) +{ + const char *refname = "test_read_format_zip_bzip2_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + skipping("bzip2 is not fully supported on this platform"); + archive_read_close(a); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_xz_multi) +{ + const char *refname = "test_read_format_zip_xz_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("bash.bashrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xF751B8C9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("pacman.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xB20B7F88)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("profile", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x2329F054)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_xz_multi_blockread) +{ + const char *refname = "test_read_format_zip_xz_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading not fully supported on this platform"); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("bash.bashrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0xF751B8C9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("pacman.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xB20B7F88)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("profile", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0x2329F054)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd8_crash_1) +{ + const char *refname = "test_read_format_zip_ppmd8_crash_2.zipx"; + struct archive *a; + struct archive_entry *ae; + char buf[64]; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 100)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* This file shouldn't be properly decompressed, because it's invalid. + * However, unpacker should return an error during unpacking. Without the + * proper fix, the unpacker was entering an unlimited loop. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 1)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bz2_hang_on_invalid) +{ + const char *refname = "test_read_format_zip_bz2_hang.zip"; + struct archive *a; + struct archive_entry *ae; + char buf[8]; + + assert((a = archive_read_new()) != NULL); + if (ARCHIVE_OK != archive_read_support_filter_bzip2(a)) { + skipping("bzip2 is not fully supported on this platform"); + archive_read_close(a); + return; + } + extract_reference_file(refname); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* The file `refname` is invalid in this case, so this call should fail. + * But it shouldn't crash. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 64)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd8_crash_2) +{ + const char *refname = "test_read_format_zip_ppmd8_crash_2.zipx"; + struct archive *a; + struct archive_entry *ae; + char buf[64]; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* The file `refname` is invalid in this case, so this call should fail. + * But it shouldn't crash. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 64)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_alone_leak) +{ + const char *refname = "test_read_format_zip_lzma_alone_leak.zipx"; + struct archive *a; + struct archive_entry *ae; + char buf[64]; + + /* OSSFuzz #14470 sample file. */ + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + if(ARCHIVE_OK != archive_read_support_filter_lzma(a)) { + skipping("lzma reading is not fully supported on this platform"); + archive_read_close(a); + return; + } + + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* Extraction of this file should fail, because the sample file is invalid. + * But it shouldn't crash. */ + assertEqualIntA(a, ARCHIVE_FAILED, archive_read_data(a, buf, sizeof(buf))); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + + /* Extraction of this file should fail, because the sample file is invalid. + * But it shouldn't crash. */ + assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, sizeof(buf))); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); + + /* This testcase shouldn't produce any memory leaks. When running test + * suite under Valgrind or ASan, the test runner won't return with + * exit code 0 in case if a memory leak. */ +} diff --git a/libarchive/test/test_read_format_zip_bz2_hang.zip.uu b/libarchive/test/test_read_format_zip_bz2_hang.zip.uu new file mode 100644 index 00000000..5193a77a --- /dev/null +++ b/libarchive/test/test_read_format_zip_bz2_hang.zip.uu @@ -0,0 +1,5 @@ +begin 644 test_read_format_zip_bz2_hang.zip +M4$L#!)LP,#`,,#`P,#`P,#`P,#`P,#`P,#`$`!P`,#`P,#`P"0`P,#`P,#`P +1,#!U>`L`8(0P,#`P,#`P,#`` +` +end diff --git a/libarchive/test/test_read_format_zip_bzip2.zipx.uu b/libarchive/test/test_read_format_zip_bzip2.zipx.uu new file mode 100644 index 00000000..6d9884ae --- /dev/null +++ b/libarchive/test/test_read_format_zip_bzip2.zipx.uu @@ -0,0 +1,19 @@ +begin 644 bzip2.zipx +M4$L#!"X#```,`#TQD4VJ.XZZ-`(``)`#```%````=FEM<F-"6F@Y,4%9)E-9 +MYPC!D@``3%^``!!TY^!2(B.7`+__W^%``C$[0!AJ>E,ID:&U-'E`T&C1IIIZ +M@8&J>$9":`IZ@``'J:/2&#```````!@`2FB$T:31D9"F@8FC0`\H^:+Y;81F +M6OH?UN<?%+%N:%31B%P(0RVO1<SQJ<1.]SV*&^IH14$2>^\U&IFQOP9(.Z29 +MPY<T(I:;+;+%&$B@2M1Y72W?=(*2WDS,\HT0@,EKOQMPK@2EG,=`4I\L2`([ +M5,]S''3%_7<)2">-_:/D\-$@RAM/2I>(FNV$2K!!&6I"Q+LXG0-YLG/4-JQE +M=%^\,6JL1H*""1!1W]<O1I!W61YH<W#N(;L:?>4\/.E))/*(3J)$L1_D*SPD +MAZY)I5-#7@)=-5H06@2PP-)YSB5D!.3J63\EJ*\X%RMDE]>>,6XG5O@<^@:T +MT671Y,.:/=ICJ(=@\88>!>M^W)$;,QY(*`F*%Q2YZ^%TF$V^/85:V;L\W53/ +M?9[-IX&M.O38C\;C@Q';$E9[,C'?<#304$$9O&4U<2"$^;O(<O4$WDF@5")3 +M:<V%Q;W"+5#B4$L8:VN_J<@<6I7.ME"(6?"""#O+0A5/?<$^+"0D5-NSRK// +M"%<_?]^4-QYYJ,(+(C;@Y3')J>0X:)KR2UT?=.E*K!>,09(4\>BYUO&*8;D4 +MY=51BK5L/;$+WB"S8Z-?)M+GT/]^;,7$S'N0)))KF]$Z(GL[.L=F*G#!4NC) +M4)I$P%W)%.%"0YPC!DA02P$"/P,N`P``#``],9%-JCN.NC0"``"0`P``!0`D +M`````````""`I($`````=FEM<F,*`"````````$`&```EU'!QI74`8#"I,S& +AE=0!`)=1P<:5U`%02P4&``````$``0!7````5P(````` +` +end diff --git a/libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu b/libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu new file mode 100644 index 00000000..1802ac0f --- /dev/null +++ b/libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu @@ -0,0 +1,96 @@ +begin 644 test_read_format_zip_bzip2_multi.zipx +M4$L#!"X#```,`)$XD4V>-]>-Q`H``"L:```+````<VUA<G1D+F-O;F9"6F@Y +M,4%9)E-9)%%XJ@`"I5^`4!!,Y__W?^??]+_O__1@"^U]9V%K:9UFZ$O2A7K' +M77-*UK(BDFF*DG<-30(`:9(V@@-2?JFGB)Z(TVIH9J>H'J-##(C0U&1)Y*>F +MF4:'E-&0:!H``-``)30B!0]3TFDVD])ZF(::`!Z@`T9#30,)"0@FBGD*9-IZ +MIZGE,F@9``-``&0'`````#````````)$00FC1,(TE/T:,HTVJ?J3/5-#,4]3 +MU#TADVAYI!A\^>RT<GE6YYS5N0CDH-W\T^/EI(<6MF;^`>Q(BD3$3I$[^,/; +MLS`?JD6R.G5Z9H!*U7PV]92S^P:OR\HL0A0_3>$BI8)(/R)[BOOLC2%8YJD( +MA.HIB.`H&#CG`1Q7XZ?+[_/^7_[H%_86(A9>\@A,<LAFVO+>_,XZ=,XL5W'7 +MHP4M+H9TD.>'-*H@_0+C17D%)P0)RW]IX%[$"C`LP1H=G0_&E!1":MS%AMXQ +M.;)R8XI%.5\07+<T%D;E`.H3-$BPXHC9O9][@4+5@?#H]U\C:>:U'.MSO0WA +M_'JHZ5GH`I2IS+,0&673'.$+E#O<4'!QIOJ(=A]MS$!DF&L:[UWD`N@'J&ON +MK"9F:A.4,3W/HWR,FFXKDW<H8/K$-R-J7+-EF/I^Y'"+NG5CCEZ+0:?KD<+3 +M`"I<U;86QQF!%.4@B\T!6@0*$:O*&.M*[F3XHCB!PDNC=;?B?(XH83B<VJ3, +M@\!-,#RI8$6_#3WTX6'H[6MHG!,&=ACHLI1OX975N=BR)Q(P3"R]T*(N@FVS +MEY4+OL3<O1,KCHTPOX=VJF:N9O,42K4RCC=]+6O5$9[3/FKPO@0(HFMMCG(M +MS(TAPC;*E<FMNE0SN-MRQGSE<D$G2%7;F3AT+J@5+!!70<<",M>_PF?19FN: +M8JZ13*&(2TCX35$",$])GPS2BLBY&_H+ZVL;>UD^#7W[$P11S-:X+;I&4P_2 +MY2FF=^Q>)*S'RAW&B*<0LART09,I)(!+E@9";HI&D+G>(HXB:0(%/23%!+S@ +M:NR/5*I%\9CK3:PVI<JJC%D=$J4%/&554*(4_,7#440X'":4,8L63JN=>^LQ +MG?A&-RQT6VZ[1QPG2Y9UVU<7W0N_@W]>`:ZV*-IMC1AC'"5PL)0>9PPF\/OD +MXXHMUZ\<Z25(;=^+RI'$VR3AK-]]ZYZ5:F:A:K!+N/1,2[.%G;@BE(-[@.1` +M22/B@`/?%#.CZ)`,.^U9$,*\\.[JTU#1MZ7P>%[^LVOK%3L]),1F)*MOQ5)H +MBOICX8!01L:?$[E<$R]4`!$&F!"N^Z]2A2JH&\;#/"R\MG<`!XM)L2.#%FT@ +MI$"[T#%/63SY#K=LX#RSQ*%:^J%L1'HN2F*:.2LQ2X?"^Y^<9@U@PV(.4E%C +MNS^(@N"/+X8L:9,0T^E]7FZXBP#;&@K7#\.W[MOUG8-`-,<'O._DJ$2@7*?N +M#ZO8W/#Q7.=4KXGTY)7:1_NJ7-3FF\<6*$<^N]+-!C)W4+U60F$@>:,&%)NE +M6U9J0*F"F4!.R,Y`.G*"*P_*(P>-<7WV6=I7*E]98+HG":L0P4Y,?%RY^X[* +MDSY./:P;H^X)PU&L=#"2/IS$=1]X>MB:/!;68(&.EX`-X^"ZW_F;B,/8(F45 +M`_?U2XOBQ%&$HQ1YCTEC[_A42U:75Z>4-MI3.*/X>72/`C"+5Z>0B4C49O3A +M'N8'1IOJC95M$"-@1RC6(E"P.0[ZZ:;J?E0+#0?LP-6OA,=$^`?$]R<D,R:6 +M&AO6\BZF>)HW%K-',>W@ZBCI&CX:1\Q,YOY&7QLLV_J,<-^S+:AAO&'.A;*V +M3AQROQ<,BPB$?,M'!#1QR@/4P#T<S7`8"66LBP\H218.SMGB,,\1%E*\/0*# +MQ_'#_%KC&V`62ZV)_`]OL^PI[Y+3Y'D:D>M5"<!K'\`NAZ/[F;AK;1&O\DA= +M0]#/L9O[-T_:F38NS8'QH!;O!*9%KW46U%$P7^K@8H&S*H\KD-/C1E92^52F +M)F1C$@7G-V(O%9#LFI%D%E!$P!\'1B(0-?))-42@&?](DR&2ZE`T8XW\%:FB +MF*Z1QI590R+_@5,W2:2C-`LE@8*Q&:H8NM`['><*D.$6U5>6NB0:@2#RD1AD +M(26E6M&U7;I&M?><,-FD48N@:,:2[U9AD#8DO`F8G`NH2VG:1>/.?`E4V(J^ +M#5)1$;.G/E?/U7S,RZJL\[5*#)Y+2^&5-1.L.G9B!:4)5`06C6IFT)6E9!%+ +M2NC400RE=1Y`*MU7P;V<R<"<-'X?;X97UARL<KW%NB7,<_XCW;#Y=&J42S?< +MCYV65#`1E'MK>=>_94Q:13C>/8$;NI99OUE6QCKTH&-VKEJ+?SICS8UQ>&55 +MS!AN47T#J*?QEQ6,M2+`@9;Z#.*GM;18!J.<)J@Q;$1#.I*.KS4[,-,#$>-\ +M)PO&"I/*D7`.K'08;Q%TA8:$`G^HT8=;C3N5_4;IF)[F0N$3TDH*Q0-P<[42 +M,33G53,R)8(253>'`I(/(`C4Y$P=<ZI3<Q9F-%6NDX[@@<-.#$:"ZHK=Y"2W +ML':Y1S;)()A8F:NQI,Z6<4EA=#ZP7K"X9XEI=Y1^>LZC+Z#,LC!<`8.=$0&& +MHL)>D\@AT(1A;6.-(URT`6J-@0P%Y020G3VP:QRCZPM[@QB;&[&]I'1L3HG6 +MHMV_<`9H[#0AJH/K\46RSPQC,;0Z:L!U8.MI-<Z53:%EM8O;@;NC/2H=';XV +M-Z@X,TK*TU++@]A*YJ;NAQ6:N!AY.&J8HED08VR&!22SM(6OK:A%ET7;U&!4 +MVK6\B1<8S:RZ$2&P0S%>>!:8E!XF+SMT&6HO@%[!H\1%0L[N;T&#(0;8].D% +M:TK%I(F/ON*[L1CE)4QE?&7UU65^]]5"5--B;I#AF:-,&WEQ%G1QM<7=I+I- +M#%HED2A!O:62V29]+QED;,>@Q1/-)PPU+A@W!SW0+W/DU,0V*IU>![3,1S&> +MJH>)MQ]&0><M19/ROSLZEN3#B_9J<R3UR(K`N`N21DVKG"X%,+[7TX+(=*P1 +MNJH*&-M^.1NI8I=_4LF'\I+3TCS]0COXP9&VQ[OL=S+9D5III],[VS7W')'H +MV^<R#4%Y)U(&T?;3\\!'6[>D=NTHM!=_;/K:@AM+BTH-:,C49$_(R)IC&^1Y +M[EI=1!L8O8:A+.F,:91'!%LGX.Y*[9W'8Q>2DN<Q$3J253N=[B+$M5`,`ZAP +MX#7D2G.9Q9&85E=C::&FQI#=#:77?K,:7Y2%%""A5E:3S95'D*ZI%['/%R@N +MPC:9L35R#[8BW=,#-3+S!A7!WNSP."]&D&T:/,C4#0L]K,!G8G9),'9JIE". +MT&JZK3L";)&ZF\:#Z+0V:/`NPWLAN#;)1&)7&:):CTRMF6MYRNVWEM,W"3=O +M9OBR&5B:>$BIBQ<M:6NS/6M-<.;%:!:CM-Q:7P+F[9<^U)ITR0:D$:JK$S%: +M9V8;[^0=S9:"MAG>(Y()FJNVR"%T-;E6?@6^'&XMHR)AAWR$C9972+M#HSN- +M);+!>$HVAW9>(FP:U,1BUS180R6S:<$UD99O<NY=G[''Z\C9AG)U+.%99@9C +M;.=-Q(V\S5@<WPQZ(`71O7+I2%C0S36*BC[[+MW<$P5LFC!]"[=Y5+L:1H65 +MAB'!(B:A*;VK2KG,`:&Z<:9\L7<@'RG1`XHDFZ*SB=DQ(`&LRTFHZ?^+N2*< +M*$@2*+Q5`%!+`P0N`P``#`"(.)%-,9OE>L@!``!4`P``!P```'1S+F-O;F9" +M6F@Y,4%9)E-9[2!3B0``35^``!1(A>=")0$"`+__W^!``=V;:TML-3U*>D]I +M3`F]4]-(T9`T:&@T8I`F(U'ZIZ@!H`>IZ&`````P``"4T4R9-3U`$T]$`,0: +M9KMEA9T)[OUF#<]_H_:R!%VW(Z5<8GI,92Q$\@_99'.V;0!9M_JQW(Q83@(> +MU(&RK]97O.4,(?<D&2+:<8T+C,9*N8XBL0!($BWA2>9Q\O->C6^K@,2&/*Q^ +MQ&&;:95MR[%C&6KSOTJXV':CWZ0#H?FO+]L:K$L-@\J??66K"[C''`Q(05A5 +MAA`H<]68]Z8WD3,92%/*1-[+TH^&FHBX0;F(=TXT`&\A85\I0".^@K=W'6BV +M6W7I=FG(:-GP/>J!T.)K9&E&`70E\*O7<@B"XC8N],ESUC>J<#0$&LF`$Q"I +MPFSXM(AMXP]!B.B$A)+*A@FQI7N,888C3<<1)$V8APW#69B,^)R&)J6R?!9U +MVQ?F1G9TTEH&P"PV"U$F)!()!JGQ4/1670D)VLF!P-;I.\BHY"F((+!C$AZJ +MZ4L(AR$T$'4N]0NK3DSCOKGQ@9(O?$J:V8>5Q'*QHI5Q[SIJ>$D/XNY(IPH2 +M':0*<2!02P,$+@,```P`/3&13:H[CKHT`@``D`,```4```!V:6UR8T)::#DQ +M05DF4UGG",&2``!,7X``$'3GX%(B(Y<`O__?X4`",3M`&&IZ4RF1H;4T>4#0 +M:-&FFGJ!@:IX1D)H"GJ```>IH](8,```````&`!*:(31I-&1D*:!B:-`#RCY +MHOEMA&9:^A_6YQ\4L6YH5-&(7`A#+:]%S/&IQ$[W/8H;ZFA%01)[[S4:F;&_ +M!D@[I)G#ES0BEILMLL482*!*U'E=+=]T@I+>3,SRC1"`R6N_&W"N!*6<QT!2 +MGRQ(`CM4SW,<=,7]=PE()XW]H^3PT2#*&T]*EXB:[81*L$$9:D+$NSB=`WFR +M<]0VK&5T7[PQ:JQ&@H()$%'?UR]&D'=9'FAS<.XANQI]Y3P\Z4DD\HA.HD2Q +M'^0K/"2'KDFE4T->`ETU6A!:!+#`TGG.)60$Y.I9/R6HKS@7*V27UYXQ;B=6 +M^!SZ!K319='DPYH]VF.HAV#QAAX%ZW[<D1LS'D@H"8H7%+GKX7283;X]A5K9 +MNSS=5,]]GLVG@:TZ]-B/QN.#$=L25GLR,=]P--!001F\935Q((3YN\AR]03> +M2:!4(E-IS87%O<(M4.)02QAK:[^IR!Q:E<ZV4(A9\(((.\M"%4]]P3XL)"14 +MV[/*L\\(5S]_WY0W'GFHP@LB-N#E,<FIY#AHFO)+71]TZ4JL%XQ!DA3QZ+G6 +M\8IAN13EU5&*M6P]L0O>(+-CHU\FTN?0_WYLQ<3,>Y`DDFN;T3HB>SLZQV8J +M<,%2Z,E0FD3`7<D4X4)#G",&2%!+`0(_`RX#```,`)$XD4V>-]>-Q`H``"L: +M```+`"0`````````(("D@0````!S;6%R=&0N8V]N9@H`(````````0`8`(#N +M7F'.E=0!`,YV<<Z5U`&`[EYASI74`5!+`0(_`RX#```,`(@XD4TQF^5ZR`$` +M`%0#```'`"0`````````(("D@>T*``!T<RYC;VYF"@`@```````!`!@``/`\ +M5\Z5U`$`SG9QSI74`0#P/%?.E=0!4$L!`C\#+@,```P`/3&13:H[CKHT`@`` +MD`,```4`)``````````@@*2!V@P``'9I;7)C"@`@```````!`!@``)=1P<:5 +HU`&`0`0-QY74`0`TN_O&E=0!4$L%!@`````#``,`#0$``#$/```````` +` +end diff --git a/libarchive/test/test_read_format_zip_lzma.zipx.uu b/libarchive/test/test_read_format_zip_lzma.zipx.uu new file mode 100644 index 00000000..24fdc8e7 --- /dev/null +++ b/libarchive/test/test_read_format_zip_lzma.zipx.uu @@ -0,0 +1,19 @@ +begin 644 lzma.zipx +M4$L#!#\#```.`#TQD4VJ.XZZ.0(``)`#```%````=FEM<F,0`@4`70`0```` +M$0@$J,)\D;(#4L%<^$P5TO^CM0KI0HWG08B&_].4<,CJ")TW/L>)82Q1PWAL +M+U`,N0L_$]^&650C/X$D6#4QFD$\A/"_![4!O/5O/!KH`WCQ*4?T2*]4P#/D +M0'9I?EZG=N69Z0V;H0I=C<!C<J6O^834W097PY1$%=-++.YUA'!>P*$?".I\ +MGMG/80.A'^W>R4J'S/CZ%P`8`>F=R>R&R$2T@EM#X)"OQH1?A7,`:4IU9WV! +M#2W*DXT',;.4YIN4A:-X)O=IREL201ZSOC=YSAU[C4-::/YV8\)%"L17+>VC +M%/'B]ZCQN$2(Q*9*\KJZ`Y131`]5C&G';@1S-QES_RZF!2OX45@58+??ES%( +MUJ<(\`11M$NO)HK#/MK-9RT"15.2I:IZN8<TJR>VTM1_?$G\L#BH67]$S%[4 +M%C-$\Q<+./&HV](4,7)OL-@C^M0F"2O!0N$OHOW54H87^QLBQVH*D%A<#SI% +M/#+-5U(W';:KC)RE>0Y^5YI!RECQNR"R4.UW9IR!@:B!UB8?_D5$FT8YCJHJ +M2[2"-&-_D2BJ6#XK[6G=%K"%;'^-+0]FHCY4ER#`^<I-M<!"D:-0H@);U"P" +MPYX+4#8!&$7\M.+%%MZ:KQ2GX0<]$"P7F^HT)J5JM<$VO9/D[#7KZ\'FITL/ +MYIF"=GO+-L?F[8QS4KC7+=A)1`")V<.8DX629Q;;Y4XA\M-%O&MWC)^)`NO< +M.J6(5V2UY9"I(C*QKA[Z#)-#XU!+`0(_`S\#```.`#TQD4VJ.XZZ.0(``)`# +M```%`"0`````````(("D@0````!V:6UR8PH`(````````0`8``"74<'&E=0! +F`)=1P<:5U`$`EU'!QI74`5!+!08``````0`!`%<```!<`@`````` +` +end diff --git a/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu b/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu new file mode 100644 index 00000000..5fce2135 --- /dev/null +++ b/libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu @@ -0,0 +1,5 @@ +begin 644 test_read_format_zip_lzma_alone_leak.zipx +M4$L#!"`@6B`.("`@("`@("`@%0```"`@("````4``0`!`"`@(`4``"`````` +J(/\@("`@("`@("!02P,$("`@(`X@(/________\@("`@("`@(``````@ +` +end diff --git a/libarchive/test/test_read_format_zip_lzma_multi.zipx.uu b/libarchive/test/test_read_format_zip_lzma_multi.zipx.uu new file mode 100644 index 00000000..22c4d5e8 --- /dev/null +++ b/libarchive/test/test_read_format_zip_lzma_multi.zipx.uu @@ -0,0 +1,95 @@ +begin 644 test_read_format_zip_lzma_multi.zipx +M4$L#!#\#```.`)$XD4V>-]>-A`H``"L:```+````<VUA<G1D+F-O;F80`@4` +M70`@````$8@&YWH`1\"K>?C+)VT(T,`U9;I1/'N)!6C.3M-O9LO]H<^=OZF\ +MXD\QEZIMP!JM4)W+(F]%N;U#&+F]WZ%S9>>]&^75:X)0Q-5>)IRT/%INC887 +ML0UF,PS8J@3;WGH95EW4\KBH,6ZK9,'![3$-(WL/C<S3&K[A1C?*`W/KQ4&Q +M`/BGQ$#SJ^?;Z_7,NJ_(<YJKYEW5LH4:QPH.Z]G1VW_M3E2QS5S'\6S@-/7M +M9+&F/N+H[TX:GRGI</0(-B-?I7II0M#&)*LQO[%N?0,S48\!,6C%"FGSL(59 +MPVH.\D`%['MI;`8-TPQ<'NX'K:"0+`MQCQ)B*FT/.17CT@8%^,I/-[K?`GGD +M\2YAO1K`&COVK><[0.6%L9@]0;]_>&[A'#Q^##`6+*PM@YT+]E[B9<QN2EY> +M;QK38:)CO+S]4X2KF'=)BVO-GM+_#]PB0'*J89+$Q11IG\\+84M](58^RW.E +MGM3S6RH]#73P.KV0V,2=,/A!;G>2Y4R!!.4(0U+D<.'5V*%./ZZYD")=W4*4 +M\SL"IUJLN])WM`-&4L&!9&U_-1%%SPU0N4*.^L5&33"A5MZ;@8F+I^D7JB'K +ME&,P>"UI]NT;ZX`<E]/3;Z;:;KU;K(9,W7@41O,3:LQJH%H%RXZ.<A,Y\=Q; +M`]D>UU->]V$?<H!@ZNFP0M6BYMPO95IJLC48"DOTCXD/#?VW%LVZD2SSZ7FN +M'KLU!HVB+H7FJ)Z@GPA7L"R'[&^62*7E6S:TJ[XYM+U?YR1K21?P]5TN],0/ +MV05M[H@C9&OY+CA3(2G0HZD@0M2`"O;9&ORU2_2D#8T`3=,>KZV(DX&-6L>? +ME,(J<;DNA6/2.<9$>?-P]_!S8L3/2P@[C'H$FOA$WUOB>;X$)E7;K+7#X9M( +MM+H^AK#CML0LX<B:.L9A!G3PTXL5UK?O<K5?S00^"%V;EC>TO#P#I)J_5"@O +MMGH9+OXD^P7AG@<%QGH<>FIWE]/>L)6Q2Q\CC=[?TCG262"1EO)'_W_C_K4< +M8.7YNR"3\9WTQ"0-!>^,M>WT.Y0A&3AK.4/YN5EW]"&HQ1=8Q-NHQABVH7$% +M24Q:J/<8%F1EI)-!HND&FP_;]E,JI$*AGE&]Y#FJ5R6<(UM;;>U?KU@OQ,M_ +MI/+:;0^0'8JA"==H5/;]Y10P&PS,1_M+D1I>`%>_Y\&F[_R/##784#0AQ1!O +M'8>IBY?;&;(VAOI$+Z[VMI>]WC!['W$&Z5/D+"6["[[.<DH&[&K^*"`/UB[Z +M2Q?D`J)9WKDL?`4\\V0;#,28BU'V+#=0IGN6YS1Y9,V,<-2!(-L@Z20XL8I: +M5#"QC1CFF*"!].U\TX1;$IL?#Y<\`1C*%*`XW?^@>[P!/?9C/;CD66^J/OC) +MO0JXP'Y6]PGW8N@X(#-XXWMP"A%E$A8L+X>QIC[0P,UT0A!IYQ/#+>E60KX/ +M2JC'F-Z@_,-/$7+F5F$1\#^"CTSY;H2EAE,/8>F$^'5H!M2@D?2O@+NJ2&-O +M6IV-[<6F-JG0(,W-YJ%PJ<7T.F")'/+F_`F6B-<MX=E68$!XW'W(RG>HG436 +MJM-VK0?>9*R?K#]OHC90!]BCG'^D&%!JZ2T_&[RWV?0Z?T:#_\B`?:MRC$D5 +M1Z.)!/;3/=GT*T;DC/XLR;7S%VEP+U-M)-*V,1?M<377S9-`J]S4[OI4SR>V +M_?_H2WWH>\@*$V@`LZO?-<BM\X$'4(L'MF_HV4RT1V'^V>'Z[4_4"T2R@=%! +M!\1]:4M((1B=ATAJ)23HE7>]=,7I#SK5'2OUZLX0LX*_67!+6S:*0UL)^GS6 +MM;$2!(-TY%4ME1>*'M)S'[0JP^&J%<A'CQ?7FI>"-&A[C1YE;1:X_4Y&K9*2 +M'14NC*]O(JXNLX#M_(LDE6$G<0,+V/;44]-*^S%7G*.\$W&>2=9,EYB)/@XK +MX+E4?>NIP=*X]Z>3T,V?!"5:/M@<Y?5&/FI$@I@$N)0*3];_\;[7)(:*+#2, +M$JTT&;V1H(;O<>X-H%DZEV,*9WIK;T_C18UC5$%^Z5PY8;USNB?S]`D-K2V2 +M-C#0-45C'YFF>#*$9JZ!$>%0;=VQ@LGY"J<.C.10"1'</GIK=$9`=+K0L,5< +M/])*:+OWS(:V1=R"?*)9&DJ;KZ/0ZI0L8,^</;B?N`=1UB<"P1!T-'$3:D-% +M8\K1LH+E![6M]42AXTZ]FR@_6W0Y*@?[L(<\D-<C[(//PKW-#"'].EG9-=!9 +M'B9)"486TDIB/J5RT`,JO'_SX1LJI@H^47%.'QJ/KU`P5K<C!A3],-2995TS +M-5B#!(:B;,]CXTQ_ER3(3>JL^B(VXPNK^]Q97E\`HX0MG2)F=>38R,IFGK\R +MVH!.V3_)_D,II8A=SL;X1!2D9E67=.$P+_D6&'X^1;4%:`ERV>!,''7<#=NE +MP')[\,A89G35\;ZN:IIY5DDSB$(8D$[G>7-L!(+(-7=LQ0Q3=S`L-)R9YT;S +M;%ZYVD%6)M=I]*QS>!3='"SB'E/4>J#EQ.&]0*U<?2AYA#J0V*IJ#AC@C./2 +M542_X#'@J3M.%(D.\<Q/^[S\^[K8P\I@1(RM[%G$O='.Q#'@6.:]E-P6$<9` +M6A_HT;>0H/Y1%DM].)E/UFD0?Q)B;_;;)-K_]6Z<)D_>^[=?49%4D4B@REC` +M,_KU,KX-N,44CL/:*^D>7X(;6_1&C;<;&`K3+4NY[=4%K2K:U"Q%T9)',7," +M+PC``PBBDVNY9V-LN'V')B4@4;OH(\/M%UBX$S."4_!;&=9W\L`!2O\!ANM5 +M8<J4RY>+""#5J&/M[2;M?47WN=0[4C*$<!KL:]JI#:F(GID#"X3/_+OW4^9$ +MH(D!M_S%*9ME+?QP:[JJ)<;$_/.0R`B9\^)8##D)RWG"SP>7D#QB&`!*@5_0 +M9(5SZ7,Q"N?0W!\0WGQF,0JKH[G-(<6^PMJZV&VHQIT?VKJ^7^!EW?)`EC`$ +M0%,=?X0ME@T_&5UXJIJ9)5(<[=W]M#Y.$.96:_T8WM/H+"T_Q;QZN"`5DC&' +M.+(^")0XO=#&#@KVL2R9+.#F)P02=`6PT[21&>T*Q3Y2WVL><:PHB)R?*'+3 +M+,OK(7PNXHXMVFT<GB/X]4Z(@V!G@I*W2+?/E8B$.YQWJ::$&5/A"L1:8E%K +MB=/,3Y3G]Y=64JWA"@J:$P16J+IAUZFU7R^QK;H&;^D;!``-$E-)!6#@YQ#" +M[4,OT;*U7IE$\KI[&:W.&KS7ZEO@1/GQZ\3O-'FX<<!+Z2>"!)??8!H^I,&V +M8BOE(Z/VYY(5WLSG$.+8$;.A>]+PVB`WV,9M@YL='Q_<%2STU?_IG.I17G^L +M>#-=')K6T<!)<XY!]\5&A$"^\9^X:W^2@`!L:56ZV^TU.)Y$<1%OM2BUNZ./ +M&YD1MAR0^RP*&R%@"21'W-^R\=D,\1JM63J<>?C[^&,7Z)0!B[2-)2:&?*5, +M,;&-HI_/O(/V"4-:_E>0Z)=C=1/9HQJO$R:(&H@4RP"3%T[BOBZ`3_2>)K49 +M\ZL-26)MQ\%(>T0S5A4>EUCU[),JI"_/9<5)*6X\97`N:8YSR0%)@*#_'9'? +M/E=RFK"L&(#RS$S_0[?/!DW.`\FH$M\#"JSQ@*&C*W\`4$L#!#\#```.`(@X +MD4TQF^5ZO0$``%0#```'````=',N8V]N9A`"!0!=`"`````1B`2H,L'S88)4 +M*WW.8!PX^&C9J^P?<4.RSJ^WF7L-W#+OB4@(A-83?&.PVF@$.:VM<4\8K(_6 +M`CRI/@MR:,)C8$&U*MZKA\W'KCI%E2GDYYE/SB'*\S@5]NO,ZBF!;HY@ +M3Q=F"]@P02:6^LC>JT$T7BB(.:*G`I.?%/53CC#4I0N_ARFXFG`DZSI*$)UM +M1^X:IZ^^)T*5E,C6L<,L%=ASF-1:0CP6*FX_;?<K)^Q*:<'E!WV3Y^)QW7=" +M/.)?WNY-<("[VKFR$BQ*_6D4][NEB\IZ<[_,,`1C%(([BG)4G[]C,^8$W5:S +M!X8MX+YUOE1,?OV<7IV(TJY<0RPDRI%4`^%8]HL^H?>R;<M-IR(=C:[/JFI\ +M@1WIP=G)@F5A::X3B@1A;CYD=PUCB*'VM^R:X2:XWH8&"Y"ZB-A`SBC*D4SN +M3DAH,0A!PJ\ZHL?#PB)4OI"FOD^Y$@I+T0N$05U+3K3,)DW%,#3SCA:7=D#^ +M&^`PD\!WF#VEY5I;2G@*ZN8XPLL!)AC#&5GR&:MW:!`3"^JARM%?H&>#]C:/ +M)C@@M]\FLU4*%H^>8VW4@@%D0U!02P,$/P,```X`/3&13:H[CKHY`@``D`,` +M``4```!V:6UR8Q`"!0!=`"`````1"`2HPGR1L@-2P5SX3!72_Z.U"NE"C>=! +MB(;_TY1PR.H(G3<^QXEA+%'#>&PO4`RY"S\3WX995",_@218-3&:03R$\+\' +MM0&\]6\\&N@#>/$I1_1(KU3`,^1`=FE^7J=VY9GI#9NA"EV-P&-RI:_YA-3= +M!E?#E$05TTLL[G6$<%[`H1\(ZGR>V<]A`Z$?[=[)2H?,^/H7`!@!Z9W)[(;( +M1+2"6T/@D*_&A%^%<P!I2G5G?8$-+<J3C0<QLY3FFY2%HW@F]VG*6Q)!'K.^ +M-WG.'7N-0UIH_G9CPD4*Q%<M[:,4\>+WJ/&X1(C$IDKRNKH#E%-$#U6,:<=N +M!',W&7/_+J8%*_A16!5@M]^7,4C6IPCP!%&T2Z\FBL,^VLUG+0)%4Y*EJGJY +MAS2K)[;2U']\2?RP.*A9?T3,7M06,T3S%PLX\:C;TA0Q<F^PV"/ZU"8)*\%" +MX2^B_=52AA?[&R+':@J06%P/.D4\,LU74C<=MJN,G*5Y#GY7FD'*6/&[(+)0 +M[7=FG(&!J('6)A_^142;1CF.JBI+M((T8W^1**I8/BOM:=T6L(5L?XTM#V:B +M/E27(,#YRDVUP$*1HU"B`EO4+`+#G@M0-@$81?RTXL46WIJO%*?A!ST0+!>; +MZC0FI6JUP3:]D^3L->OKP>:G2P_FF8)V>\LVQ^;MC'-2N-<MV$E$`(G9PYB3 +MA9)G%MOE3B'RTT6\:W>,GXD"Z]PZI8A79+7ED*DB,K&N'OH,DT/C4$L!`C\# +M/P,```X`D3B139XWUXV$"@``*QH```L`)``````````@@*2!`````'-M87)T +M9"YC;VYF"@`@```````!`!@`@.Y>8<Z5U`&`[EYASI74`8#N7F'.E=0!4$L! +M`C\#/P,```X`B#B133&;Y7J]`0``5`,```<`)``````````@@*2!K0H``'1S +M+F-O;F8*`"````````$`&```\#Q7SI74`0#P/%?.E=0!`/`\5\Z5U`%02P$" +M/P,_`P``#@`],9%-JCN.NCD"``"0`P``!0`D`````````""`I(&/#```=FEM +M<F,*`"````````$`&```EU'!QI74`8!`!`W'E=0!`#2[^\:5U`%02P4&```` +/``,``P`-`0``ZPX````` +` +end diff --git a/libarchive/test/test_read_format_zip_ppmd8.zipx.uu b/libarchive/test/test_read_format_zip_ppmd8.zipx.uu new file mode 100644 index 00000000..4440e95b --- /dev/null +++ b/libarchive/test/test_read_format_zip_ppmd8.zipx.uu @@ -0,0 +1,17 @@ +begin 644 ppmd8.zipx +M4$L#!#\#``!B`#TQD4VJ.XZZV`$``)`#```%````=FEM<F,'`"']JO"&\1[R +M;;G)@`(8>EJ>3<8@F_*<(\B>K]4_(WC8#)`_QSG+`7`B&_11VIJ)@#(<W.9L +M>K)_I8R^=`VUO2_S,1C=1CAU>-*`]CC+&6Q;EE'#CG-W=^[,F,+UR-TE(9,G +M1_&$NYD`,5WY5QX7@%5XX-*7+7&#W'1#XF+"?6*U!H=B063.]NTA`1+&\J(K +M&U]D<Z8#:-^E0@IRO9J$W&\^A>E8$ZYUU'UT\$!88I,6+(M/"=_+3@V5H+24 +MBA0>^0?[W07K"B%SQ&OA-R)S9W>DH,<4##E@?5YF:%K<JGI_YMJ^QT4/IK3' +MME]>$?5U@W[IVG+2#8FC4'GT3-$L%_N,B$^)UY1GAD=.Z(HI#3[T"979&<`+ +M`S]9G+PI';5"==`18UE,>N"?>"_C"MSHX<[!&8$+A6U_7][TK*6.^\-O-UK! +M`)>[*.D1:1H!I,?PEIPW,NQ5CCX2NCY+%UH"T5X!O$&=6+X#"()33:]FPF:F +ME-O)\652#KD$^ZFBJ7K`55]R/A"OA"T7\R6K%B(2&6*/H0>*-:@<P:\X15#O +M7`.(BCE?O\0!1%K[A;:`MWNF3\/K9SJ28+D-Z2I92^$J-BWY6";ANPDO&M>< +MJ2V9^KJSL+P4A&$`4$L!`C\#/P,``&(`/3&13:H[CKK8`0``D`,```4`)``` +M```````@@*2!`````'9I;7)C"@`@```````!`!@``)=1P<:5U`&`PJ3,QI74 +?`0"74<'&E=0!4$L%!@`````!``$`5P```/L!```````` +` +end diff --git a/libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu b/libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu new file mode 100644 index 00000000..fb6050fc --- /dev/null +++ b/libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_zip_ppmd8_crash_1.zipx +K4$L'"(=02P,$\+N.O&*A>*\+."U``$H`````````@``````#````6(0````` +` +end diff --git a/libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu b/libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu new file mode 100644 index 00000000..58de4125 --- /dev/null +++ b/libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu @@ -0,0 +1,4 @@ +begin 644 test_read_format_zip_ppmd8_crash_2.zipx +L4$L'"(=02P,$\+N.O&*A>*\+.2U`@$H`````````@``````#```````````` +` +end diff --git a/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu b/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu new file mode 100644 index 00000000..6fadcea6 --- /dev/null +++ b/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu @@ -0,0 +1,84 @@ +begin 644 test_read_format_zip_ppmd8_multi.zipx +M4$L#!#\#``!B`)$XD4V>-]>-.0D``"L:```+````<VUA<G1D+F-O;F8'`"+\ +MO.+63+[=I+F0!)GF1VJ,J;4U"`+-]H\Q#[5$#:5W]29`:.D\EI:7BM#X2\84 +MDE)J.5>X%[[99#N5_N2Y@T-?"T%*8O@,-K>QSOR>-).,0-Q:=7)+:*Z&:B9' +M-YXDKOWK"T?"(.;F$3'A>TW+.W#W%Y0`:_5XI+35&4_L<0>95Q!XP,\T=VCM +MZB%S4G,4ED*-4T#5Y'BUCMS!0Z)0*50U=EQWM_F]2)<8#VF(432?^.!'O`I/ +MLP!A8AN1>8;MQ:S?/4-$K07X@["K?T]0<=84:OJ]Y$.D)3MNMK`U.(ORV3M& +MV`2EDVHT&D'F-=\,_"^;UGWW!=,KJI4^NZAF72$S=N.*PW!#ZJ?KQ0;$7E!] +MFC,:ST/]Y(,:K3SK7,%&A_%1`($9H@/\G.RN$J7BA?C\P"[U&[8MITK*`U5E +MVH'&CJ8?%1+H-H0=B*8]_(F;;/P`371(@?QN2T<4C52L;%S-XV),E4-V05&I +MA.V?U+HM63;JH=1]LJ_E"@JM?QH\6#NQ7I\I*%-!)LWB?U):O^--2R=1C):^ +M^@GFC0GETH%>LXD8_D*J'KS5[/M5+89M;6@3?]I.`M=;(S7*1?(?.9G!`S26 +M([JDK/(RCM%WYC`>G*L7:NJ!5TS[7<CU3:8J00'UVNP^80ML:5-$)@YGVB]V +MRCR-N\+'6U?T;,];2KLQJK05&CW>MI^E[4/\RU9$&1XJ5#V;<'F^AX^R@P7< +M/2?UB;-2+I11]^X;,J\:,'^R=&8"@[EI0$J:":*LQ-F(IOSXC?1W>/;?4$P* +M;=V"OEY@<N7-M&<4#[>S\%3'&F-^K0FME.]%1DTO*OP8.:M('3W)\S"1.@8# +M6Y1&@L+#!YVL]8.,[UP/\HMAR1"O!M/2?DY-8[N'P[T&2J7-V5%=XWMRQ,EM +M7TL^99!FXY0+B1^7K36"#!85"WXE,WFEU=B!+GX2$D<)MW>&A,8A9RPL]CR( +M!=1JX99-:5D;PM@S4(>?<OGI*B\"Y0\$Z?+-^5A2D>BKTFAQ>"%9\Q^Y\6T\ +MTS"*[M.TW7AE:#\(:M_JLJ#*-9-O,UW\T:PVW8G01OR!B59P=[:XNH0<C21F +M^#W^ZH9HB=LH)[+R]HC:HC(7GUO2YL]P^!VT?*^WF0VT2:4W-@W[3&)7]3B= +M(S`169(MJ"A4\AUA:?Z22".6">Y=OC9`KJ_%.>^)RW-,G^%%(U9HC957G/\@ +MW1\J*,JLX*CF*,-!'^O*E7=V#S8_#G`9YQ)#%,AJ8"-4B,OR8RW4@L)8F$JB +M.3QD;;M_L<:*T3B>8\OECY@*'6?[2VYXB3B!`<:65*HB/.^@;]\`IE<HRA&2 +MY[*2&*\%-#I).D+(^GQEE#E-F`8`X0#[ADT>)<D?R$&7^?/$"/0,Q_%FSX== +M"9!Q9!-0>HTIG=_X:*3A'#M\P0L*]^ZFWC\UL`O;WL@#?"!(_]OZ4=B<\QL/ +MWD%/TL5<Q77`U;^:R@9J2]QL&U3HQ7B4]3:>[P\,^_3G\]WD-Q`?XU]K5LFW +MI6J$G\=W(Q+2_U&THGA'_6)`"WK=X74-OD_B%WJU+JH/A"K#JQ@)A<`>A6[H +MC2A>S7]ZV/H?/1KV&OR8B]IS\>Y:\5;B-:+E^V`:5_O[+(!,6LW0&[5(F4UH +MKM;R]_V\?PI6I+"7M1H%E':WL<1#J"O<P+?ZX&A,*";+5(?J&-:@61*&H;B. +M9B4X%W]`K]$KZPB]0A*A;G\=7?CYO>`>]S*UKS02ME,S7]X66@[B`_#?H$^) +MT&07IY[)18RK+`@#?UXT=8B=XC$J'SSJ>XP2<K$26GGWWM9=P5`,`X4UVP!@ +M#MX&,/?QD)D![9=.L@S>RJ]>02[1J:R;0"M&L>KY(3*9QLH1N@K?LL%3J%O? +MR!>+JHS'L3_<*?;*81D1'/A-A"%%P..0%#Q32<!G</*&E4&O8A8B1KP*KTAB +MNX.M*01N][XQ_K3:G.`JQ5+G46/\J&RY*Q1_FP43*F"8.3"88\"4Z`\_P,%: +M7MCC4B.N40+?-*3IN6^F*9"3L(W1A1.&JJ7`)XZ/<L'9.UW>?L88\C'/'W?D +MG766\,!14W_TY,.;G,\C.+L1LR__7UR*_H!IE,1LZO,_+#?C[OW4+[4I`[7] +MZ;U%`5#@;6A5#JU]H+\E;^^LQBHQ$-T.P5.V/J<JU?4=KX>S69'PI06.%#D4 +M%B.K!<G&.&@9I0RD;=/A_8R>F(.G*6@/>[P-FU3-#%N.EY%W*]P_]<>?07:7 +MK&JYBO\0HXN^[/OA#_MP/QQG-+1?Z)7*WRL14*DMWL2795*UHMN/'POIN@Z= +M&&>Z/LT#HX00EWLZ%/G7)X<":!ST&P+Q0R)=V6!#D]TY1RYO!C&5&''Y<9UK +MK=LY46W;=`;(^L<>RMMG%(;??BF+AY,7=+H)6XSZ:2#^`W=RR7KIE>XQ/[_^ +M^$F4*967)DE3/^&\(7W?:EKRJ,PN<L4,/5Q6W;5[+GEI+O/I3U!D$79IUE3M +M4HYO[&D19#(*'$P_OM/=UI,F,Z>@HOT:U_.!1-"7A-=LX\Y.=-37_355)/2H +M5V[LT(83$V*>?^YW7'6.SD=MYSM<`[M'IU<FJVD8>]?H2ZU8\SLD+R-Q__8G +M2A&2#H5-J+*NQHQ&ZX$YO_(.OC\W[3B6WB#"MKV?*/<=(JBTKB`D_X'X+7"Q +MJ$B]N)L:OTXC':GK/!QY<P!&UE.=,;$.PU*8==0B'Y6=#_?KY]GA9CQ^TW5O +MMU(;@UU')@:TC.W#]H(1XK7]P4"#7>SR.,%%T3XI6(C(3&XZV`.ZLS=\O7>0 +M#N;$J[W)(Q.5<_I5G1K^FB10(.'.#Z!,B8Y'WX_=XOM8\+X/;Z\!"0HTWZG# +M[:0<%(2)`J>6>9=.B5&CWX.,@3RF>FN>'-0))N"!IY,\_?'B`DNYJ4'+!JK] +M\NX-@,,Y%)>#36Z7>CBBL=0S<@W(QRXPD3:VF-W)CS52HO!/T%NV[$0^B01. +MZH\`/9ZTF=$,",`0J\ANSPF&X.0O!?$8\^M%XSM$`D)W".C60(#6;('?*'.` +MTN9>^YI5M0P:-7MCSU2HCCU'`[!+^"9P9BH%1<=K_B#UZ3CDJWX+V\C^S$GD +M]M3M2VJY4''"]RN@>]D^K6B7ZGQG!<4'LZCK,BU]QH,1G(P$AB-W/G%7[.`! +M"P[DH--W#`"^#];7=H09``!02P,$/P,``&(`B#B133&;Y7IM`0``5`,```<` +M``!T<RYC;VYF!P`B_*OG:F'0^6V7Z#AEO-S)0GKA:ECH_T+"P;T[NJ]?$C0[ +M,MX^7V9KZ9MU"@0<<.!D&=?F$.JS%K,#M#-;"1%O"UC,M?$.VW%?.TDHIS;* +M.J0W;IC-$^7I2N^-=M9TT780=OGC4U;LJ_"$@IGWJ.1$V$5P17*TMP&?G20! +MVWS"WCT0*.*S4`W48FN2YUA0]97ZY^`G/3LWH#9%8!"U:M];QEL0(FWB&J:H +MAJ<_\(Q\4;Q=KL-N!U/1BA-)>CE(/-F<.B*/H_C$3G..AVGS/6(LHZG/Y[UI +MO1E2,[]ZU6`C\+WOIWU\>8)Z?4>]`A<H/#+0&.W1ND$$W53&5&._B;GH@R=R +M7"5C8>@"D*I3B52YGN&M&ME*-/]I!1,P.R@8#3<S=7@F64SM_WL)G3,."XL\ +MPIJ,X+\=FIQR7`R81]?13"]JIRC9U"6V[4X"?PAYT&+U;I:&./TNYQ.%T0OI +M2_`Y9V^P]/8%ZO&0MP!02P,$/P,``&(`/3&13:H[CKK8`0``D`,```4```!V +M:6UR8P<`(?VJ\(;Q'O)MN<F``AAZ6IY-QB";\IPCR)ZOU3\C>-@,D#_'.<L! +M<"(;]%':FHF`,AS<YFQZLG^EC+YT#;6]+_,Q&-U&.'5XTH#V.,L9;%N64<.. +M<W=W[LR8PO7(W24ADR='\82[F0`Q7?E7'A>`57C@TI<M<8/<=$/B8L)]8K4& +MAV)!9,[V[2$!$L;RHBL;7V1SI@-HWZ5""G*]FH3<;SZ%Z5@3KG74?73P0%AB +MDQ8LBT\)W\M.#96@M)2*%![Y!_O=!>L*(7/$:^$W(G-G=Z2@QQ0,.6!]7F9H +M6MRJ>G_FVK['10^FM,>V7UX1]76#?NG:<M(-B:-0>?1,T2P7^XR(3XG7E&>& +M1T[HBBD-/O0)E=D9P`L#/UF<O"D=M4)UT!%C64QZX)]X+^,*W.CASL$9@0N% +M;7]?WO2LI8[[PV\W6L$`E[LHZ1%I&@&DQ_"6G#<R[%6./A*Z/DL76@+17@&\ +M09U8O@,(@E--KV;"9J:4V\GQ95(.N03[J:*I>L!57W(^$*^$+1?S):L6(A(9 +M8H^A!XHUJ!S!KSA%4.]<`XB*.5^_Q`%$6ON%MH"W>Z9/P^MG.I)@N0WI*EE+ +MX2HV+?E8)N&["2\:UYRI+9GZNK.PO!2$80!02P$"/P,_`P``8@"1.)%-GC?7 +MC3D)```K&@``"P`D`````````""`I($`````<VUA<G1D+F-O;F8*`"`````` +M``$`&`"`[EYASI74`0#.=G'.E=0!@.Y>8<Z5U`%02P$"/P,_`P``8@"(.)%- +M,9OE>FT!``!4`P``!P`D`````````""`I(%B"0``=',N8V]N9@H`(``````` +M`0`8``#P/%?.E=0!`,YV<<Z5U`$`\#Q7SI74`5!+`0(_`S\#``!B`#TQD4VJ +M.XZZV`$``)`#```%`"0`````````(("D@?0*``!V:6UR8PH`(````````0`8 +M``"74<'&E=0!@$`$#<>5U`$`-+O[QI74`5!+!08``````P`#``T!``#O#``` +"```` +` +end diff --git a/libarchive/test/test_read_format_zip_xz_multi.zipx.uu b/libarchive/test/test_read_format_zip_xz_multi.zipx.uu new file mode 100644 index 00000000..cbb04346 --- /dev/null +++ b/libarchive/test/test_read_format_zip_xz_multi.zipx.uu @@ -0,0 +1,125 @@ +begin 644 test_read_format_zip_xz_multi.zipx +M4$L#!!0```!?`$JCP4K)N%'WP`0``,4(```+````8F%S:"YB87-H<F/]-WI8 +M6@```/\2V4$"`"$!`````#<GE];@",0$A5X`$8@'"$]-R=[8/`@?.;[S=,Y& +M[VH\(ZKW[WW6#J*YR&@J,$#)$2NK8K]E/*C-X`:RK.Z[I<]8QM>%H<2H0"6A +MIYB7=GGPH!_8+Q:$I09*"\)&%HUP2'X^1_;,\T5=!'^A#5>U*#)2^Z>L/*E5 +M%Y=SZRY&`Q_\(3_!//$I\E.SDZXL)WCSE7*0<#ZW/W*WM?^3CV,,)YEK01]T +MT,<7Y)@5O0':C!S_0^WG/9:,_0!\NG>*V2&:&Y6HU["3TUJ[%2F5'QVDJ49T +MXGT,!*J&0,&"B11L_MOP3I7+N6GR9P(1N,#/>TI'$'P9K@^7G77Y&\%&7G#U +M.(1KPE@G:PEDVEKT='HZIP\2CZXN#7@$BKO\_/`A&S$A2;^3%0VJ0B<>6Z7* +M'(W4;<E\MUPG7GLM/8N!$W4<+44KK,Q1"\NY'(>J5SB-RW4&#Z`/N2.@8_6$ +M@.Y1A%M"F_0Y9)K*3PGD$TRK,@"9C'8J9SEZHM`Y$LI/?PG%_42+$3TP@+I` +M+:FR6X1[JU^A\'RX,7__;W>6RC#^PQ8?M\"W+\9N+5D6E<,I6YVTKS#\$JVA +MZ;K0DY^(!*H9_',HH[YJO:)!+$6'J^.N'A`#`W;24DH<EDV^*_J.:G@$T.&\ +M:/WYUS#@;3V>V[N/$M_I"<*S-?4"H#MX!C`%KOF),."`K?#U%$R3O$^-3L$B +M4!;(W2EW.X`!YA8N;NJ=9MQ+0L:V/,NE<$C'^^VK:'5:ISTE3397ZF]A-RNV +M_0['T/F3^RM,C)6LKU.-W*KX4K%_0E/L1N*_EGPJ.$1^?N[5CQ%1QVWW7=PQ +MRBO*[C$);8<A:S3\W0T.G*[&4!<63#3W'!WA4W>)1VXE3/D#0,<6UN<]Z2^" +MDK"/$_T4Y*0'L%^0ZU9V*83*/A7Q58HO'/N:&4-[4C;Z<I(#5/G/"$&SNMJ_ +M0QMHDX0AT[MY.CC5#/L_:`(I^V7\T9+UG-@C0!@;[DR,RI\,5R;LK5NJHJ\F +M$%LKFA;*KT/DTQ`!UH.UMH>"VG<LW`[2MG@-6Q;5^B0ZM6[SJ<T'0T#HA.V? +M11DT&2%F13N)M&"$V<?FW)M6)8%0YDI*9%M4O50%^6'LRE&.WT5A&N/LR_;M +M@ONUEVPG\YS589ME`KQ88?QHFH>4F*YB&&-DUJDN>P.WW_W/(C8EWN-?5&H` +ME(KXMK!7NU+"*$'9`APUBFPA)L:VI),V)&P9=E5XX@LEL0`6F[B]V<#9XN[& +MRCL*9L(.?C[(@XE%VY>?B<'!&6!:!_F-/_H10MNL_<GN1<NC>AK01%_7!^2_ +MK-;P"DF0A;4'A4:'>AL55U%9:8*0`4^?W8+9DT"V0RD8LG/39(VE@V_M&V+K +MAMS@CU$V!O(Z>CG.!!.(;UXV'W%\:.5'^#G(S+5'+V!>C07US+?DIUL/62Z: +M&B4VHH.FA1S9(^$W)M3*NTHC:"FCSF;3$8P4Z@>L4)!K1Y*\<:F0-]$9L=HB +MY)E#TDD5?I!S4M5OOCCC'R/!D.`9%'K&$F[4E5VH"YP$-,%9!F'3#+:9192$ +M+UB^3S-M%M@;228EH`````````&9"<41``!4)ENHJ``*_`(``````%E:4$L# +M!!0```!?`$DZ/4N(?PNR0`4```8*```+````<&%C;6%N+F-O;F;]-WI86@`` +M`/\2V4$"`"$!`````#<GE];@"@4%"%X`$8*`DLR_*=QE1JN>[C.DH'JHS8W0 +M#?$V8!+]F!+4KI<$@5HR#OF&_!+?L]N#167^N/Z\RR?GT6KQ]?CNM8>==$!O +M=?@QVU.*S@O@?_!ZYES=OXHQYJ$K_DR%MMLC^2[U):T3=<QHSS@T"PON#O/] +M]_CO:>"8KH6E\^#TM[(#:A4]+,_]N-'V@&OOT`,J)*`1$6H.A\AQ<:=18H%@ +ML;39WZ/[&[?/?SG0:`)Z@@QK2DRGY04F&/;-K*Z2%^"N_BP_F6O6MO(I/U.T +M,L^0$-SQ!\_E:AA7X(VUV<S]4"ELYPD=P;@@.66.X-<G]R1I*I/^H6P6I/X( +M\!Z9KL/8S2H];49$#P%D'PAO'@@/K=`KRJLA>.Y`_]<]=`A6(D?^?S$N7OB; +MT(0U)^NL7%.Y4Y=[UNW8JWFUU2Z%N5G/Z:2V?%%%6/';]$%?%<3`>;=\3"TZ +MOQ<VD7BHNFKI30O`J1[3_^Q\.(#1S/ZI%92ID\(@2?UY0FH?NYZ!;VBDH'7A +M`TAC743^:7WL#7Q6XL:MM+>H>DR`T^1`8>\&>-+PCS8;)$M]WJ2O3&-M5NR) +M$WG>P*D3/:ZM,!QAAXTJ^?^DMH4:%0+):![`U`''+%K.;9,DM-OWB^)49!83 +MO-XVJ-N?)]_]87HXQ6(V0%4!R+DSR&(F=2*,KFTT2V$[`UZVVMLSI3T^B.%" +M3D->'YYCN2Z9EC%"/S^=W%139%7:"`%-3":\73B.TK]C)8:O)4=W6=.WU7J2 +M@Y#"B90[N&9_-Z@-5-#!_EI#N]D^NX!(OD[^I4G/L<]:Y&KD/P[5G?Q)6SR. +MDWT*.9G^KK"3^&1]@*+P6$#\Z4SCR,)E-_G'AVC:U4C%35O[,9B\$>QS9A// +M4-6<B.A;K708P1-X]Y+WF!XNW\DHC;<;*ID'M.O<W:(`W['F%65H7TLT[_,N +MGB29I+E'S=/2>)6T<XMX]'KC6:]DK/A)L_\X=I(8=,O:HM<0B@3"UZK1#++1 +M.V9\WDZ@I[@!5Y*V[TF@"&_"[7Y[I-LO3\1_<Q*K*9_B>L?R;D":$KYVE+4" +M&J><3=ZH:XM&"9ZG5-4HEZVM<B9PK;(__N<"WY44Y`S>9YO'8X@P#G1HSFI) +M2W.U-!_N1K4*.N#AAUV?:`+5)2Q\ST04<\TLD-92B`V7$2.54`>G]G_GE!`] +M_IBNF"]3MS#G]1S*N!MZE$UQX#1ICZMB[T'>_#A&8:3CP,B^UU\D$[3QNZ\P +M=]=4'BLWD`?H@Z@Z1;^A03I/VJ2$9]*>-&-,(=@TI22.6-'S#KS*<STIRC.# +M@RD`\A%W+)QX'$-/M&SF`8&!792A8R`J0,SW"C3RZ]S.C&L4E78WA%*[\`D[ +M;X![6?=Y*OWQ465EE5S/F`Z:NAM>+AT&AY64ZN7$Q'B,&TN<$OCO2W@';2/5 +MEKGSV4@OF10@NP4EBO7\Z_Z/ET#+&R::*OH_-<8/2\VF!^Y["EA#=>3&>K=? +M3^N#$@0V@=Y#K\T!ZU-B?E02#N6U>="G!A6&D?[?!.55MDR7@#`#^[T2I%,A +M+@+0O`/8^4,FN!XRSE;S@C93NICEP.5CEB_787X1B`&3L2FW@E,@?%HW,)36 +M&;CMA--B'7O,LRD\RJ3&]S+/5/3#/?F.Y/5_]51?5DU<Y%5OQ>T3'R5UY.+M +M3HA.NI":EB`FI,"L@%9/?/J.E'FE+AIF8,A&N_G:M,4%DNJM5&/OK\3N="=5 +MYWM9VORAD0```9P*AA0```_X7#"H``K\`@``````65I02P,$%````%\`2J/! +M2E3P*2/$"0``R1D```<```!P<F]F:6QE_3=Z6%H```#_$ME!`@`A`0(```"\ +M[YY\X!G("8E>`!&(!PA/3<G>V#P('SF^\W3.1N]J/".J]^]]U@ZBN<AH*C!` +MR1$KJV*_93RHS>`&LJSNNZ7/6,;7A:'$J$`EH:>8EW9Y\*`?V"\6A*4&2@O" +M1A:-<$A^/D?VS/-%701_H0U7M2@R4ONGK#RI51>7<^LN1@,?_"$_P3SQ*?)3 +MLY.N+"=X\Y5RD'`^MS]RM[7_DX]C#">9:T$?=-#'%^28%;T!VHP<_T/MYSV6 +MC/T`?+IWBMDAFAN5J->PD]-:NQ4IE1\=I*E&=.)]#`2JAD#!@HD4;/[;\$Z5 +MR[EI\F<"$;C`SWM*1Q!\&:X/EYUU^1O!1EYP]3B$:\)8)VL)9-I:]'1Z.J</ +M$H^N+@UX!(J[_/SP(1LJ?/$*R"5G\>-8JFWEH.CT0J&T-]!<(2-ZX3@)RN\J +MD0J4H/6N:=KFT$;\&+`]@#Q%F@SF+-%(65*C<K?8?5\U^M#E=M)1G;SV)&^# +MNC>B7!$HAZK&W*TS\4$TS&_-!XUFUI$7*A-;',>)+^5/P,ZD`[HWR.A9G^#C +M-6KTNK(S\@+HL$6:2FA5[-R2LQ[3WK4@G[G^^#%XET%6G82[QS;7(S,^%>P. +MYKA-5S)0G[)N+EKS^1K@1W;SU#4':SY-Q*(HA+6WR\"M,7!$$QQVKNVE\P[0 +MBBWM8'\!):GY@^BZZ/>2OU-EQ9/907=8<Z!9`4MFZ3U">"CMA,Y1-O?0VM$[ +M,QLUL^VK@(FD@-<=6)L`J(Q]$R2&TIS@=/S2CA0:)DA&H\H`$K>#+BZ#L5!& +M]8O1Y2#,@L6`*Z_D^1@3[G%I48$DK(:8@ZH;WXDD0/L?(I^Z'Y4Y+\D8XG`( +MSAG!!38.H:+L%5M[]Y^`-CTFJ#CX<7/IC$#M`L)E=Z68`.Z15.!\)150B=YZ +M<&XZF>5(BYL_(:&P;,V^D';WU2KR?A0+4-E:0%O1F9`\";RR09,+G:>77.C7 +M]KV`K:4D9*B)Q7-\>:1&]N#JUDJ(<;NGJG2;_\!+&29%3S'E+/YN\/.CP_`+ +M2C&6BOI"R]9V%EB^`F/J&*.2?`KS5D\7+CM*1],_W<7&0.MI\<*%EQY7Q@SQ +M]J35[E'P\:C"YF9--<<]IB4/#I9@9S3^CAVB3]UU"M=C#6^!]'`R!N<"BA3D +MN!$ISF;-DTGCY>18H,[#4UN),"U;=7J/F]7_C]<O]5R`K28E[0F*>K`/LRZ> +ML--U<[]+X!IV+&GCQ]@_E!=D2Y1SD_+@R,2:B,26E+GCGX,S56]3OA<@OBVL +MP`N1,^ZJXI1U"XKCE:I(CGLU!K\^_VU7+TJ2/;81PC?GHW(SPC^[!L2+DA`2 +MY\B!@>P$N?<D]:T,Z)A[7-/MIQ1JBUD;K_]PB8>Z%:.G%!3DJDM;N*"DN*/0 +M.&`?D?G/MZ-/H^LA*3<3>4'L">#EY&"!H9TB4M/1;]:<*%/-*%*ZZ2,FF"T` +M1-VLY<VH$W76[Q(`1P,4DDU"S\-I!#2(AA@-X@^_L"KL&[M33N&`;J-X`J\5 +MX?[HHMI1Y<E.XR`(:_,&MIIK?[<`@.92!S"=O79P_S],\4.Y]!H1MU]2>D+N +M@QH1*4'INB@E6X<9,1=\T'`K<_PIF!F*CV&59B&:2$-.E\%OM2!\T"\X#?W) +M_\E.2DS4TN&FVR:GZQ#7+A4-!3L*KI`(NX#-0JA_OH3'J@OJ/G'C\>/BB@8\ +MO9AR=;IRPX06AZX1'^R/RE>AE,'6#4](`V?FM0=BN]-3P7I*Y1N1VF>PL"IJ +MGF]0@PM>JH[PYGC*F^0"*N9OI-E(9C^LSD&NHWHEQ=_$X4]3I1L][E,)<J@6 +M5]NI3VG%<\N<2#P[%02Y!OHRBB,L7/:@64Z"A]%5&[./@&=#.G@J;P9.N0_2 +M6D==8ED)-C14(-%]Q+J9?SRI`"S01!VKK!Z;LF_&&-"YRBQ!_U[I,0',FCAY +M[1*J;":(1#V5`O:M>5C%OKU.]ZXIDSD]%)$8-'2>$@08,C&F")$0X!^:[H:2 +M77[$CGQ]J0)I&=%/9U>ZZ!2Y93+,3^<38JQEC-]%YBW9?`S,U]*_N)5*MF1; +M6\2$>DXDY!G'+D3!)U47?ELRTEI9>)&;2]..PL(AM-=O/69L[N:Y:FYU$4OV +M^,N@H"C'ZH#0&TGK?'HWAY=!0Y!^[6!S_P:>A)^@YZ;N82NGABF]X@0;_,-? +M>;?A^0=`'34QXH8VW08:$;&O]ML<VNJ9Q47!GI)^+RX,T."9_"Z)J!-)MS<C +MD6;<."3*RZJI(V>ZDV-KC!/$@J-K$9[N0&GOS+2GI;?LN)L!M0MJD)X0D:^^ +M)C-S@Y6IMQ)#AE,[..:E&_PK.Y=^1&=;H[0VN#H>-LQAW3+GYKV6I=X6B3=8 +M.@Q5EULK;>80C>Q2*R&+D7DQ0E3,-2X+C]$9NN9R62RYTV[PC,4H>F[\5?A# +ML..XN!<+<NA[EF^-.*;3H5I\`B]HS(!-W=/#"$JF+\O,1'N;&F`1\5E\TZ?) +M9:F)!<(?>40H+7WGS&4<3H)3#?4%;XFXM&K'OQ*Z'L*"N<<IS07M\G?%U2W$ +M?8S<#I)AB851@=^772M_X4IH:`C8/\8&L9Z7018'AAE0$[K1F6Y\C^2<&4IQ +M7>J@/Y%[H1GLJ4SH:YM^-V'+5CKLSP:+]J--=`-B,9Z+53^H2Q]->KE@3ZYY +M\OZ`-KB^'V-<@\MC.J++G-TJ>RS-M>)-3OU&NT5CLUM2@\\A+I;`GDR'T11U +MAW1H\ZECZZD*=!@DMO3,60<UI6SB2@!8'95NX14S<)A])Z`.MOU'3'_]#QNO +M(>R-<SR`[BR*@SZ=C3';<(`R#8WL'KSGQY$!I]-)OQL\-Z@06A_F7OCXKD5: +M<MDXBV4G@R`FZ\Y/FVOVHZM;/A#<VC1AHH5(8H(W6U>`\W7_X6C,8FB3C-2V +M(@S*@2LK1T%!';G7&%\&D>=1/;=64G$92-FPMB(<+37:DLED52:_VFEY47*O +M3ZKVJ;/.\O<_2Z?IB'U9W2:6S6_,X$F=(#VZO.@V-4CY<68\B@A`UKG0$AR1 +MC3-VV!4W/,85QD39F&!67\SGM$KS:=TRN]$V"EC=+O7\PN8.A__Z)$@0>8,] +M!NHPA*?U=-A#U>OEK!#NG,RB2Y@@`5JNHJR0;9A'3FP'V/.UYMG+D*L*">.% +MKY&BW>NHE92\QT;V$D8_X^)[*WA-4GBAS.]:Q0"SGL>J*KNDX)76,LF["/13 +M[SM72F%\\//WRT1^2GJA553G>V*&$Z9ZK.*:"0K;06@IC_N`Z$&Z<O$;4=9; +M)F$MK2SKZUO<>4?!2=`CN071_Z=#```````!G1/),P``5\:%:*@`"OP"```` +M``!96E!+`0(4`!0```!?`$JCP4K)N%'WP`0``,4(```+`"0```````$`(``` +M``````!B87-H+F)A<VAR8PH`(````````0`8`(#_S/$,V](!H&U5$).6U`$0 +M=OM:DZ'3`5!+`0(4`!0```!?`$DZ/4N(?PNR0`4```8*```+`"0```````$` +M(````.D$``!P86-M86XN8V]N9@H`(````````0`8``#QLKWJ.-,!T%`4$9.6 +MU`'0*<"<\7?4`5!+`0(4`!0```!?`$JCP4I4\"DCQ`D``,D9```'`"0````` +M``$`(````%(*``!P<F]F:6QE"@`@```````!`!@`@/_,\0S;T@&0.\4/DY;4 +?`?#K!%N3H=,!4$L%!@`````#``,`$P$``#L4```````` +` +end diff --git a/libarchive/test/test_sparse_basic.c b/libarchive/test/test_sparse_basic.c index 0df0f1d3..f12b6af4 100644 --- a/libarchive/test/test_sparse_basic.c +++ b/libarchive/test/test_sparse_basic.c @@ -68,6 +68,14 @@ struct sparse { static void create_sparse_file(const char *, const struct sparse *); +#if defined(__APPLE__) +/* On APFS holes need to be at least 4096x4097 bytes */ +#define MIN_HOLE 16781312 +#else +/* Elsewhere we work with 4096*10 bytes */ +#define MIN_HOLE 409600 +#endif + #if defined(_WIN32) && !defined(__CYGWIN__) #include <winioctl.h> /* @@ -491,6 +499,7 @@ DEFINE_TEST(test_sparse_basic) { char *cwd; struct archive *a; + const char *skip_sparse_tests; /* * The alignment of the hole of sparse files deeply depends * on filesystem. In my experience, sparse_file2 test with @@ -501,42 +510,42 @@ DEFINE_TEST(test_sparse_basic) */ const struct sparse sparse_file0[] = { // 0 // 1024 - { DATA, 1024 }, { HOLE, 2048000 }, + { DATA, 1024 }, { HOLE, MIN_HOLE + 1638400 }, // 2049024 // 2051072 - { DATA, 2048 }, { HOLE, 2048000 }, + { DATA, 2048 }, { HOLE, MIN_HOLE + 1638400 }, // 4099072 // 4103168 - { DATA, 4096 }, { HOLE, 20480000 }, + { DATA, 4096 }, { HOLE, MIN_HOLE + 20070400 }, // 24583168 // 24591360 - { DATA, 8192 }, { HOLE, 204800000 }, + { DATA, 8192 }, { HOLE, MIN_HOLE + 204390400 }, // 229391360 // 229391361 { DATA, 1 }, { END, 0 } }; const struct sparse sparse_file1[] = { - { HOLE, 409600 }, { DATA, 1 }, - { HOLE, 409600 }, { DATA, 1 }, - { HOLE, 409600 }, { END, 0 } + { HOLE, MIN_HOLE }, { DATA, 1 }, + { HOLE, MIN_HOLE }, { DATA, 1 }, + { HOLE, MIN_HOLE }, { END, 0 } }; const struct sparse sparse_file2[] = { - { HOLE, 409600 * 1 }, { DATA, 1024 }, - { HOLE, 409600 * 2 }, { DATA, 1024 }, - { HOLE, 409600 * 3 }, { DATA, 1024 }, - { HOLE, 409600 * 4 }, { DATA, 1024 }, - { HOLE, 409600 * 5 }, { DATA, 1024 }, - { HOLE, 409600 * 6 }, { DATA, 1024 }, - { HOLE, 409600 * 7 }, { DATA, 1024 }, - { HOLE, 409600 * 8 }, { DATA, 1024 }, - { HOLE, 409600 * 9 }, { DATA, 1024 }, - { HOLE, 409600 * 10}, { DATA, 1024 },/* 10 */ - { HOLE, 409600 * 1 }, { DATA, 1024 * 1 }, - { HOLE, 409600 * 2 }, { DATA, 1024 * 2 }, - { HOLE, 409600 * 3 }, { DATA, 1024 * 3 }, - { HOLE, 409600 * 4 }, { DATA, 1024 * 4 }, - { HOLE, 409600 * 5 }, { DATA, 1024 * 5 }, - { HOLE, 409600 * 6 }, { DATA, 1024 * 6 }, - { HOLE, 409600 * 7 }, { DATA, 1024 * 7 }, - { HOLE, 409600 * 8 }, { DATA, 1024 * 8 }, - { HOLE, 409600 * 9 }, { DATA, 1024 * 9 }, - { HOLE, 409600 * 10}, { DATA, 1024 * 10},/* 20 */ + { HOLE, MIN_HOLE }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 }, + { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 },/* 10 */ + { HOLE, MIN_HOLE }, { DATA, 1024 * 1 }, + { HOLE, MIN_HOLE + 409600 * 1 }, { DATA, 1024 * 2 }, + { HOLE, MIN_HOLE + 409600 * 2 }, { DATA, 1024 * 3 }, + { HOLE, MIN_HOLE + 409600 * 3 }, { DATA, 1024 * 4 }, + { HOLE, MIN_HOLE + 409600 * 4 }, { DATA, 1024 * 5 }, + { HOLE, MIN_HOLE + 409600 * 5 }, { DATA, 1024 * 6 }, + { HOLE, MIN_HOLE + 409600 * 6 }, { DATA, 1024 * 7 }, + { HOLE, MIN_HOLE + 409600 * 7 }, { DATA, 1024 * 8 }, + { HOLE, MIN_HOLE + 409600 * 8 }, { DATA, 1024 * 9 }, + { HOLE, MIN_HOLE + 409600 * 9}, { DATA, 1024 * 10},/* 20 */ { END, 0 } }; const struct sparse sparse_file3[] = { @@ -553,6 +562,13 @@ DEFINE_TEST(test_sparse_basic) */ test_sparse_whole_file_data(); + skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); + if (skip_sparse_tests != NULL) { + skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " + "environment variable"); + return; + } + /* Check if the filesystem where CWD on can * report the number of the holes of a sparse file. */ #ifdef PATH_MAX @@ -599,10 +615,19 @@ DEFINE_TEST(test_fully_sparse_files) { char *cwd; struct archive *a; + const char *skip_sparse_tests; const struct sparse sparse_file[] = { - { HOLE, 409600 }, { END, 0 } + { HOLE, MIN_HOLE }, { END, 0 } }; + + skip_sparse_tests = getenv("SKIP_TEST_SPARSE"); + if (skip_sparse_tests != NULL) { + skipping("Skipping sparse tests due to SKIP_TEST_SPARSE " + "environment variable"); + return; + } + /* Check if the filesystem where CWD on can * report the number of the holes of a sparse file. */ #ifdef PATH_MAX diff --git a/libarchive/test/test_write_disk_symlink.c b/libarchive/test/test_write_disk_symlink.c index 13089c78..aeadfee8 100644 --- a/libarchive/test/test_write_disk_symlink.c +++ b/libarchive/test/test_write_disk_symlink.c @@ -99,6 +99,139 @@ DEFINE_TEST(test_write_disk_symlink) assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); + /* Symbolic link: dot -> . */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: dotdot -> .. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, ".."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: slash -> / */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldot -> /. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldotdot -> /.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Dir: d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1"); + archive_entry_set_mode(ae, AE_IFDIR | 0777); + archive_entry_unset_size(ae); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1nosl -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1nosl"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slash -> d1/ */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1sldot -> d1/. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slddot -> d1/.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slddot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1dir -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1dir"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1file -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1file"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_FILE); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); /* Test the entries on disk. */ @@ -107,11 +240,30 @@ DEFINE_TEST(test_write_disk_symlink) assertIsReg("link1a", -1); assertFileSize("link1a", sizeof(data)); assertFileNLinks("link1a", 1); - assertIsSymlink("link1b", "link1a"); + assertIsSymlink("link1b", "link1a", 0); /* Test #2: Should produce identical results to test #1 */ assertIsReg("link2a", -1); assertFileSize("link2a", sizeof(data)); assertFileNLinks("link2a", 1); - assertIsSymlink("link2b", "link2a"); + assertIsSymlink("link2b", "link2a", 0); + + /* Test #3: Special symlinks */ + assertIsSymlink("dot", ".", 1); + assertIsSymlink("dotdot", "..", 1); + assertIsSymlink("slash", "/", 1); + assertIsSymlink("sldot", "/.", 1); + assertIsSymlink("sldotdot", "/..", 1); + + /* Test #4: Directory symlink mixed with . and .. */ + assertIsDir("d1", -1); + /* On Windows, d1nosl should be a file symlink */ + assertIsSymlink("d1nosl", "d1", 0); + assertIsSymlink("d1slash", "d1/", 1); + assertIsSymlink("d1sldot", "d1/.", 1); + assertIsSymlink("d1slddot", "d1/..", 1); + + /* Test #5: symlink_type is set */ + assertIsSymlink("d1dir", "d1", 1); + assertIsSymlink("d1file", "d1", 0); } diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index 132e1145..82840547 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -204,6 +204,18 @@ Do not process files or directories that match the specified pattern. Note that exclusions take precedence over patterns or filenames specified on the command line. +.It Fl Fl exclude-vcs +Do not process files or directories internally used by the +version control systems +.Sq CVS , +.Sq RCS , +.Sq SCCS , +.Sq SVN , +.Sq Arch , +.Sq Bazaar , +.Sq Mercurial +and +.Sq Darcs . .It Fl Fl fflags (c, r, u, x modes only) Archive or extract file flags. This is the reverse of @@ -386,8 +398,7 @@ and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl n , Fl Fl norecurse , Fl Fl no-recursion -(c, r, u modes only) -Do not recursively archive the contents of directories. +Do not operate recursively on the content of directories. .It Fl Fl newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. diff --git a/tar/bsdtar.c b/tar/bsdtar.c index e70b3929..b59963d0 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -129,6 +129,28 @@ static void version(void) __LA_DEAD; (ARCHIVE_EXTRACT_SECURE_SYMLINKS \ | ARCHIVE_EXTRACT_SECURE_NODOTDOT) +static char const * const vcs_files[] = { + /* CVS */ + "CVS", ".cvsignore", + /* RCS */ + "RCS", + /* SCCS */ + "SCCS", + /* SVN */ + ".svn", + /* git */ + ".git", ".gitignore", ".gitattributes", ".gitmodules", + /* Arch */ + ".arch-ids", "{arch}", "=RELEASE-ID", "=meta-update", "=update", + /* Bazaar */ + ".bzr", ".bzrignore", ".bzrtags", + /* Mercurial */ + ".hg", ".hgignore", ".hgtags", + /* darcs */ + "_darcs", + NULL +}; + int main(int argc, char **argv) { @@ -318,6 +340,15 @@ main(int argc, char **argv) lafe_errc(1, 0, "Couldn't exclude %s\n", bsdtar->argument); break; + case OPTION_EXCLUDE_VCS: /* GNU tar */ + for(t=0; vcs_files[t]; t++) { + if (archive_match_exclude_pattern( + bsdtar->matching, + vcs_files[t]) != ARCHIVE_OK) + lafe_errc(1, 0, "Couldn't " + "exclude %s\n", vcs_files[t]); + } + break; case OPTION_FFLAGS: bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; bsdtar->readdisk_flags &= ~ARCHIVE_READDISK_NO_FFLAGS; @@ -808,8 +839,6 @@ main(int argc, char **argv) break; } } - if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) - only_mode(bsdtar, "-n", "cru"); if (bsdtar->flags & OPTFLAG_STDOUT) only_mode(bsdtar, "-O", "xt"); if (bsdtar->flags & OPTFLAG_UNLINK_FIRST) @@ -859,6 +888,16 @@ main(int argc, char **argv) only_mode(bsdtar, buff, "cru"); } + /* + * When creating an archive from a directory tree, the directory + * walking code will already avoid entering directories when + * recursive inclusion of directory content is disabled, therefore + * changing the matching behavior has no effect for creation modes. + * It is relevant for extraction or listing. + */ + archive_match_set_inclusion_recursion(bsdtar->matching, + !(bsdtar->flags & OPTFLAG_NO_SUBDIRS)); + /* Filename "-" implies stdio. */ if (strcmp(bsdtar->filename, "-") == 0) bsdtar->filename = NULL; diff --git a/tar/bsdtar.h b/tar/bsdtar.h index 543a228c..c61d568f 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -135,6 +135,7 @@ enum { OPTION_CHROOT, OPTION_CLEAR_NOCHANGE_FFLAGS, OPTION_EXCLUDE, + OPTION_EXCLUDE_VCS, OPTION_FFLAGS, OPTION_FORMAT, OPTION_GID, diff --git a/tar/cmdline.c b/tar/cmdline.c index 66cf4c2d..21558e12 100644 --- a/tar/cmdline.c +++ b/tar/cmdline.c @@ -85,6 +85,7 @@ static const struct bsdtar_option { { "disable-copyfile", 0, OPTION_NO_MAC_METADATA }, { "exclude", 1, OPTION_EXCLUDE }, { "exclude-from", 1, 'X' }, + { "exclude-vcs", 0, OPTION_EXCLUDE_VCS }, { "extract", 0, 'x' }, { "fast-read", 0, 'q' }, { "fflags", 0, OPTION_FFLAGS }, diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt index d7de42d6..459d9dcb 100644 --- a/tar/test/CMakeLists.txt +++ b/tar/test/CMakeLists.txt @@ -40,6 +40,7 @@ IF(ENABLE_TAR AND ENABLE_TEST) test_option_b.c test_option_b64encode.c test_option_exclude.c + test_option_exclude_vcs.c test_option_fflags.c test_option_gid_gname.c test_option_grzip.c diff --git a/tar/test/test_basic.c b/tar/test/test_basic.c index 0008e1cf..9bb966a0 100644 --- a/tar/test/test_basic.c +++ b/tar/test/test_basic.c @@ -42,7 +42,7 @@ make_files(void) /* Symlink to above file. */ if (canSymlink()) - assertMakeSymlink("symlink", "file"); + assertMakeSymlink("symlink", "file", 0); /* Directory. */ assertMakeDir("dir", 0775); @@ -78,7 +78,7 @@ verify_files(const char *target) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ failure("%s", target); diff --git a/tar/test/test_copy.c b/tar/test/test_copy.c index e6e31f45..b828666b 100644 --- a/tar/test/test_copy.c +++ b/tar/test/test_copy.c @@ -176,7 +176,7 @@ create_tree(void) sprintf(buff, "s/%s", filenames[i]); sprintf(buff2, "../f/%s", filenames[i]); failure("buff=\"%s\" buff2=\"%s\"", buff, buff2); - assertMakeSymlink(buff, buff2); + assertMakeSymlink(buff, buff2, 0); } /* Create a dir named "d/abcdef...". */ buff[0] = 'd'; @@ -222,7 +222,7 @@ verify_tree(size_t limit) sprintf(name1, "s/%s", filenames[i]); sprintf(name2, "../f/%s", filenames[i]); if (strlen(name2) <= limit) - assertIsSymlink(name1, name2); + assertIsSymlink(name1, name2, 0); } /* Verify dir "d/abcdef...". */ diff --git a/tar/test/test_option_C_mtree.c b/tar/test/test_option_C_mtree.c index caf8044b..f0903066 100644 --- a/tar/test/test_option_C_mtree.c +++ b/tar/test/test_option_C_mtree.c @@ -36,6 +36,9 @@ DEFINE_TEST(test_option_C_mtree) p0 = NULL; char *content = "./foo type=file uname=root gname=root mode=0755\n"; char *filename = "output.tar"; +#if defined(_WIN32) && !defined(CYGWIN) + char *p; +#endif /* an absolute path to mtree file */ char *mtree_file = "/METALOG.mtree"; @@ -48,9 +51,21 @@ DEFINE_TEST(test_option_C_mtree) assertMakeDir("bar", 0775); assertMakeFile("bar/foo", 0777, "abc"); - r = systemf("%s -cf %s -C bar \"@%s\" >step1.out 2>step1.err", testprog, filename, absolute_path); +#if defined(_WIN32) && !defined(CYGWIN) + p = absolute_path; + while(*p != '\0') { + if (*p == '/') + *p = '\\'; + p++; + } + r = systemf("%s -cf %s -C bar @%s >step1.out 2>step1.err", testprog, filename, absolute_path); failure("Error invoking %s -cf %s -C bar @%s", testprog, filename, absolute_path); +#else + r = systemf("%s -cf %s -C bar \"@%s\" >step1.out 2>step1.err", testprog, filename, absolute_path); + failure("Error invoking %s -cf %s -C bar \"@%s\"", testprog, filename, absolute_path); +#endif + assertEqualInt(r, 0); assertEmptyFile("step1.out"); assertEmptyFile("step1.err"); diff --git a/tar/test/test_option_H_upper.c b/tar/test/test_option_H_upper.c index adc294b5..2c2ad33c 100644 --- a/tar/test/test_option_H_upper.c +++ b/tar/test/test_option_H_upper.c @@ -39,13 +39,13 @@ DEFINE_TEST(test_option_H_upper) assertMakeDir("in", 0755); assertChdir("in"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 1: Without -H */ @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -H, no symlink on command line. */ @@ -69,11 +69,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test2"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -H, some symlinks on command line. */ @@ -84,9 +84,9 @@ DEFINE_TEST(test_option_H_upper) assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("d1/link1", "file1"); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("d1/link1", "file1", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/tar/test/test_option_L_upper.c b/tar/test/test_option_L_upper.c index f5a3c5ab..5697b0f2 100644 --- a/tar/test/test_option_L_upper.c +++ b/tar/test/test_option_L_upper.c @@ -39,13 +39,13 @@ DEFINE_TEST(test_option_L_upper) assertMakeDir("in", 0755); assertChdir("in"); assertMakeDir("d1", 0755); - assertMakeSymlink("ld1", "d1"); + assertMakeSymlink("ld1", "d1", 1); assertMakeFile("d1/file1", 0644, "d1/file1"); assertMakeFile("d1/file2", 0644, "d1/file2"); - assertMakeSymlink("d1/link1", "file1"); - assertMakeSymlink("d1/linkX", "fileX"); - assertMakeSymlink("link2", "d1/file2"); - assertMakeSymlink("linkY", "d1/fileY"); + assertMakeSymlink("d1/link1", "file1", 0); + assertMakeSymlink("d1/linkX", "fileX", 0); + assertMakeSymlink("link2", "d1/file2", 0); + assertMakeSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 1: Without -L */ @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_L_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -L, no symlink on command line. */ @@ -71,9 +71,9 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -L, some symlinks on command line. */ @@ -85,8 +85,8 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/tar/test/test_option_U_upper.c b/tar/test/test_option_U_upper.c index 2c43e002..d864e13c 100644 --- a/tar/test/test_option_U_upper.c +++ b/tar/test/test_option_U_upper.c @@ -79,10 +79,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test3", 0755); assertChdir("test3"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); r = systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog); assert(r != 0); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileNotExists("d1/file1"); assertEmptyFile("test.out"); assertNonEmptyFile("test.err"); @@ -92,7 +92,7 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test4", 0755); assertChdir("test4"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xUf ../archive.tar >test.out 2>test.err", testprog)); assertIsDir("d1", -1); @@ -105,10 +105,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test5", 0755); assertChdir("test5"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); @@ -118,10 +118,10 @@ DEFINE_TEST(test_option_U_upper) assertMakeDir("test6", 0755); assertChdir("test6"); assertMakeDir("realDir", 0755); - assertMakeSymlink("d1", "realDir"); + assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); @@ -132,7 +132,7 @@ DEFINE_TEST(test_option_U_upper) assertChdir("test7"); assertMakeDir("d1", 0755); assertMakeFile("d1/realfile1", 0644, "realfile1"); - assertMakeSymlink("d1/file1", "d1/realfile1"); + assertMakeSymlink("d1/file1", "d1/realfile1", 0); assertEqualInt(0, systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); assertIsReg("d1/file1", umasked(0644)); @@ -147,7 +147,7 @@ DEFINE_TEST(test_option_U_upper) assertChdir("test8"); assertMakeDir("d1", 0755); assertMakeFile("d1/realfile1", 0644, "realfile1"); - assertMakeSymlink("d1/file1", "d1/realfile1"); + assertMakeSymlink("d1/file1", "d1/realfile1", 0); assertEqualInt(0, systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); assertIsReg("d1/file1", umasked(0644)); diff --git a/tar/test/test_option_exclude_vcs.c b/tar/test/test_option_exclude_vcs.c new file mode 100644 index 00000000..20215113 --- /dev/null +++ b/tar/test/test_option_exclude_vcs.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2019 Martin Matuska + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(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" +__FBSDID("$FreeBSD$"); + +DEFINE_TEST(test_option_exclude_vcs) +{ + assertMakeDir("in", 0755); + assertChdir("in"); + assertMakeFile("file", 0644, ""); + assertMakeDir("dir", 0755); + assertMakeDir("CVS", 0755); + assertMakeFile("CVS/fileattr", 0644, ""); + assertMakeFile(".cvsignore", 0644, ""); + assertMakeDir("RCS", 0755); + assertMakeFile("RCS/somefile", 0655, ""); + assertMakeDir("SCCS", 0755); + assertMakeFile("SCCS/somefile", 0655, ""); + assertMakeDir(".svn", 0755); + assertMakeFile(".svn/format", 0655, ""); + assertMakeDir(".git", 0755); + assertMakeFile(".git/config", 0655, ""); + assertMakeFile(".gitignore", 0644, ""); + assertMakeFile(".gitattributes", 0644, ""); + assertMakeFile(".gitmodules", 0644, ""); + assertMakeDir(".arch-ids", 0755); + assertMakeFile(".arch-ids/somefile", 0644, ""); + assertMakeDir("{arch}", 0755); + assertMakeFile("{arch}/somefile", 0644, ""); + assertMakeFile("=RELEASE-ID", 0644, ""); + assertMakeFile("=meta-update", 0644, ""); + assertMakeFile("=update", 0644, ""); + assertMakeDir(".bzr", 0755); + assertMakeDir(".bzr/checkout", 0755); + assertMakeFile(".bzrignore", 0644, ""); + assertMakeFile(".bzrtags", 0644, ""); + assertMakeDir(".hg", 0755); + assertMakeFile(".hg/dirstate", 0644, ""); + assertMakeFile(".hgignore", 0644, ""); + assertMakeFile(".hgtags", 0644, ""); + assertMakeDir("_darcs", 0755); + assertMakeFile("_darcs/format", 0644, ""); + assertChdir(".."); + + assertEqualInt(0, systemf("%s -c -C in -f included.tar .", testprog)); + assertEqualInt(0, + systemf("%s -c --exclude-vcs -C in -f excluded.tar .", testprog)); + + /* No flags, archive with vcs files */ + assertMakeDir("vcs-noexclude", 0755); + assertEqualInt(0, systemf("%s -x -C vcs-noexclude -f included.tar", + testprog)); + assertChdir("vcs-noexclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertIsDir("CVS", 0755); + assertFileExists("CVS/fileattr"); + assertFileExists(".cvsignore"); + assertIsDir("RCS", 0755); + assertFileExists("RCS/somefile"); + assertIsDir("SCCS", 0755); + assertFileExists("SCCS/somefile"); + assertIsDir(".svn", 0755); + assertFileExists(".svn/format"); + assertIsDir(".git", 0755); + assertFileExists(".git/config"); + assertFileExists(".gitignore"); + assertFileExists(".gitattributes"); + assertFileExists(".gitmodules"); + assertIsDir(".arch-ids", 0755); + assertFileExists(".arch-ids/somefile"); + assertIsDir("{arch}", 0755); + assertFileExists("{arch}/somefile"); + assertFileExists("=RELEASE-ID"); + assertFileExists("=meta-update"); + assertFileExists("=update"); + assertIsDir(".bzr", 0755); + assertIsDir(".bzr/checkout", 0755); + assertFileExists(".bzrignore"); + assertFileExists(".bzrtags"); + assertIsDir(".hg", 0755); + assertFileExists(".hg/dirstate"); + assertFileExists(".hgignore"); + assertFileExists(".hgtags"); + assertIsDir("_darcs", 0755); + assertFileExists("_darcs/format"); + assertChdir(".."); + + /* --exclude-vcs, archive with vcs files */ + assertMakeDir("vcs-exclude", 0755); + assertEqualInt(0, + systemf("%s -x --exclude-vcs -C vcs-exclude -f included.tar", testprog)); + assertChdir("vcs-exclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); + assertChdir(".."); + + /* --exclude-vcs, archive without vcs files */ + assertMakeDir("novcs-exclude", 0755); + assertEqualInt(0, + systemf("%s -x --exclude-vcs -C novcs-exclude -f excluded.tar", + testprog)); + assertChdir("novcs-exclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); + assertChdir(".."); + + /* No flags, archive without vcs files */ + assertMakeDir("novcs-noexclude", 0755); + assertEqualInt(0, + systemf("%s -x -C novcs-noexclude -f excluded.tar", testprog)); + assertChdir("novcs-noexclude"); + assertFileExists("file"); + assertIsDir("dir", 0755); + assertFileNotExists("CVS"); + assertFileNotExists("CVS/fileattr"); + assertFileNotExists(".cvsignore"); + assertFileNotExists("RCS"); + assertFileNotExists("RCS/somefile"); + assertFileNotExists("SCCS"); + assertFileNotExists("SCCS/somefile"); + assertFileNotExists(".svn"); + assertFileNotExists(".svn/format"); + assertFileNotExists(".git"); + assertFileNotExists(".git/config"); + assertFileNotExists(".gitignore"); + assertFileNotExists(".gitattributes"); + assertFileNotExists(".gitmodules"); + assertFileNotExists(".arch-ids"); + assertFileNotExists(".arch-ids/somefile"); + assertFileNotExists("{arch}"); + assertFileNotExists("{arch}/somefile"); + assertFileNotExists("=RELEASE-ID"); + assertFileNotExists("=meta-update"); + assertFileNotExists("=update"); + assertFileNotExists(".bzr"); + assertFileNotExists(".bzr/checkout"); + assertFileNotExists(".bzrignore"); + assertFileNotExists(".bzrtags"); + assertFileNotExists(".hg"); + assertFileNotExists(".hg/dirstate"); + assertFileNotExists(".hgignore"); + assertFileNotExists(".hgtags"); + assertFileNotExists("_darcs"); + assertFileNotExists("_darcs/format"); +} diff --git a/tar/test/test_option_n.c b/tar/test/test_option_n.c index 18ab6142..e474ac1d 100644 --- a/tar/test/test_option_n.c +++ b/tar/test/test_option_n.c @@ -25,8 +25,14 @@ #include "test.h" __FBSDID("$FreeBSD$"); +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + DEFINE_TEST(test_option_n) { + int status; + assertMakeDir("d1", 0755); assertMakeFile("d1/file1", 0644, "d1/file1"); @@ -58,4 +64,79 @@ DEFINE_TEST(test_option_n) assertIsDir("d1", umasked(0755)); assertFileNotExists("d1/file1"); assertChdir(".."); + + /* + * Create a test archive with the following content: + * d1/ + * d1/file1 + * d1/file2 + * file3 + * d2/file4 + * + * Extracting uses the same code as listing and thus does not + * get tested separately. This also covers the + * archive_match_set_inclusion_recursion() + * API. + */ + assertMakeFile("d1/file2", 0644, "d1/file2"); + assertMakeFile("file3", 0644, "file3"); + assertMakeDir("d2", 0755); + assertMakeFile("d2/file4", 0644, "d2/file4"); + assertEqualInt(0, + systemf("%s -cnf partial-archive.tar d1 d1/file1 d1/file2 file3 " + "d2/file4 >c.out 2>c.err", testprog)); + + /* Test 3: -t without other options */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar >test3.out 2>test3.err", + testprog)); + assertEmptyFile("test3.err"); + assertTextFileContents("d1/\n" + "d1/file1\n" + "d1/file2\n" + "file3\n" + "d2/file4\n", + "test3.out"); + + /* Test 4: -t without -n and some entries selected */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar d1 file3 d2/file4 " + ">test4.out 2>test4.err", testprog)); + assertEmptyFile("test4.err"); + assertTextFileContents("d1/\n" + "d1/file1\n" + "d1/file2\n" + "file3\n" + "d2/file4\n", + "test4.out"); + + /* Test 5: -t with -n and some entries selected */ + assertEqualInt(0, + systemf("%s -tnf partial-archive.tar d1 file3 d2/file4 " + ">test5.out 2>test5.err", testprog)); + assertEmptyFile("test5.err"); + assertTextFileContents("d1/\n" + "file3\n" + "d2/file4\n", + "test5.out"); + + /* Test 6: -t without -n and non-existant directory selected */ + assertEqualInt(0, + systemf("%s -tf partial-archive.tar d2 >test6.out 2>test6.err", + testprog)); + assertEmptyFile("test6.err"); + assertTextFileContents("d2/file4\n", + "test6.out"); + + /* Test 7: -t with -n and non-existant directory selected */ + status = systemf("%s -tnf partial-archive.tar d2 " + ">test7.out 2>test7.err", testprog); + assert(status); + assert(status != -1); +#if !defined(_WIN32) || defined(__CYGWIN__) + assert(WIFEXITED(status)); + assertEqualInt(1, WEXITSTATUS(status)); +#endif + assertNonEmptyFile("test7.err"); + assertEmptyFile("test7.out"); } diff --git a/tar/test/test_option_s.c b/tar/test/test_option_s.c index ee8332f3..09c72ee7 100644 --- a/tar/test/test_option_s.c +++ b/tar/test/test_option_s.c @@ -36,7 +36,7 @@ DEFINE_TEST(test_option_s) assertMakeFile("in/d1/bar", 0644, "bar"); if (canSymlink()) { assertMakeFile("in/d1/realfile", 0644, "realfile"); - assertMakeSymlink("in/d1/symlink", "realfile"); + assertMakeSymlink("in/d1/symlink", "realfile", 0); } assertMakeFile("in/d1/hardlink1", 0644, "hardlinkedfile"); assertMakeHardlink("in/d1/hardlink2", "in/d1/hardlink1"); @@ -109,14 +109,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test6a/in/d2/realfile"); assertFileContents("realfile", 8, "test6a/in/d2/symlink"); - assertIsSymlink("test6a/in/d2/symlink", "realfile"); + assertIsSymlink("test6a/in/d2/symlink", "realfile", 0); /* At creation time. */ assertMakeDir("test6b", 0755); systemf("%s -cf - -s /d1/d2/ in/d1 | %s -xf - -C test6b", testprog, testprog); assertFileContents("realfile", 8, "test6b/in/d2/realfile"); assertFileContents("realfile", 8, "test6b/in/d2/symlink"); - assertIsSymlink("test6b/in/d2/symlink", "realfile"); + assertIsSymlink("test6b/in/d2/symlink", "realfile", 0); } /* @@ -129,14 +129,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test7a/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7a/in/d1/symlink"); - assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed", 0); /* At creation. */ assertMakeDir("test7b", 0755); systemf("%s -cf - -s /realfile/realfile-renamed/ in/d1 | %s -xf - -C test7b", testprog, testprog); assertFileContents("realfile", 8, "test7b/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7b/in/d1/symlink"); - assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed", 0); } /* @@ -192,7 +192,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10a/in/d1/foo"); assertFileContents("foo", 3, "test10a/in/d1/realfile"); assertFileContents("foo", 3, "test10a/in/d1/symlink"); - assertIsSymlink("test10a/in/d1/symlink", "realfile"); + assertIsSymlink("test10a/in/d1/symlink", "realfile", 0); /* At creation. */ assertMakeDir("test10b", 0755); systemf("%s -cf - -s /realfile/foo/S -s /foo/realfile/ in/d1 | %s -xf - -C test10b", @@ -200,7 +200,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10b/in/d1/foo"); assertFileContents("foo", 3, "test10b/in/d1/realfile"); assertFileContents("foo", 3, "test10b/in/d1/symlink"); - assertIsSymlink("test10b/in/d1/symlink", "realfile"); + assertIsSymlink("test10b/in/d1/symlink", "realfile", 0); } /* @@ -214,7 +214,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11a/in/d1/foo"); assertFileContents("realfile", 8, "test11a/in/d1/realfile"); assertFileContents("foo", 3, "test11a/in/d1/symlink"); - assertIsSymlink("test11a/in/d1/symlink", "foo"); + assertIsSymlink("test11a/in/d1/symlink", "foo", 0); /* At creation. */ assertMakeDir("test11b", 0755); systemf("%s -cf - -s /realfile/foo/R in/d1 | %s -xf - -C test11b", @@ -222,7 +222,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11b/in/d1/foo"); assertFileContents("realfile", 8, "test11b/in/d1/realfile"); assertFileContents("foo", 3, "test11b/in/d1/symlink"); - assertIsSymlink("test11b/in/d1/symlink", "foo"); + assertIsSymlink("test11b/in/d1/symlink", "foo", 0); } /* diff --git a/tar/test/test_strip_components.c b/tar/test/test_strip_components.c index d195af1b..090fb0db 100644 --- a/tar/test/test_strip_components.c +++ b/tar/test/test_strip_components.c @@ -36,8 +36,8 @@ DEFINE_TEST(test_strip_components) assertMakeHardlink("l1", "d1/d2/f1"); assertMakeHardlink("d1/l2", "d1/d2/f1"); if (canSymlink()) { - assertMakeSymlink("s1", "d1/d2/f1"); - assertMakeSymlink("d1/s2", "d2/f1"); + assertMakeSymlink("s1", "d1/d2/f1", 0); + assertMakeSymlink("d1/s2", "d2/f1", 0); } assertChdir(".."); @@ -64,9 +64,10 @@ DEFINE_TEST(test_strip_components) failure("d0/d1/s2 is a symlink to something that won't be extracted"); /* If platform supports symlinks, target/s2 is a broken symlink. */ /* If platform does not support symlink, target/s2 doesn't exist. */ - assertFileNotExists("target/s2"); if (canSymlink()) - assertIsSymlink("target/s2", "d2/f1"); + assertIsSymlink("target/s2", "d2/f1", 0); + else + assertFileNotExists("target/s2"); failure("d0/d1/d2 should be extracted"); assertIsDir("target/d2", -1); @@ -122,7 +123,7 @@ DEFINE_TEST(test_strip_components) /* If platform supports symlinks, target/s2 is included. */ if (canSymlink()) { failure("d0/d1/s2 is a symlink to something included in archive"); - assertIsSymlink("target2/s2", "d2/f1"); + assertIsSymlink("target2/s2", "d2/f1", 0); } failure("d0/d1/d2 should be archived"); assertIsDir("target2/d2", -1); diff --git a/tar/test/test_symlink_dir.c b/tar/test/test_symlink_dir.c index 852e00b3..5836647c 100644 --- a/tar/test/test_symlink_dir.c +++ b/tar/test/test_symlink_dir.c @@ -66,22 +66,22 @@ DEFINE_TEST(test_symlink_dir) /* "dir" is a symlink to an existing "dest1/real_dir" */ assertMakeDir("dest1/real_dir", 0755); if (canSymlink()) { - assertMakeSymlink("dest1/dir", "real_dir"); + assertMakeSymlink("dest1/dir", "real_dir", 1); /* "dir2" is a symlink to a non-existing "real_dir2" */ - assertMakeSymlink("dest1/dir2", "real_dir2"); + assertMakeSymlink("dest1/dir2", "real_dir2", 1); } else { skipping("Symlinks are not supported on this platform"); } /* "dir3" is a symlink to an existing "non_dir3" */ assertMakeFile("dest1/non_dir3", 0755, "abcdef"); if (canSymlink()) - assertMakeSymlink("dest1/dir3", "non_dir3"); + assertMakeSymlink("dest1/dir3", "non_dir3", 1); /* "file" is a symlink to existing "real_file" */ assertMakeFile("dest1/real_file", 0755, "abcdefg"); if (canSymlink()) { - assertMakeSymlink("dest1/file", "real_file"); + assertMakeSymlink("dest1/file", "real_file", 0); /* "file2" is a symlink to non-existing "real_file2" */ - assertMakeSymlink("dest1/file2", "real_file2"); + assertMakeSymlink("dest1/file2", "real_file2", 0); } assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog)); @@ -108,32 +108,32 @@ DEFINE_TEST(test_symlink_dir) /* "dir" is a symlink to existing "real_dir" */ assertMakeDir("dest2/real_dir", 0755); if (canSymlink()) - assertMakeSymlink("dest2/dir", "real_dir"); + assertMakeSymlink("dest2/dir", "real_dir", 1); /* "dir2" is a symlink to a non-existing "real_dir2" */ if (canSymlink()) - assertMakeSymlink("dest2/dir2", "real_dir2"); + assertMakeSymlink("dest2/dir2", "real_dir2", 1); /* "dir3" is a symlink to an existing "non_dir3" */ assertMakeFile("dest2/non_dir3", 0755, "abcdefgh"); if (canSymlink()) - assertMakeSymlink("dest2/dir3", "non_dir3"); + assertMakeSymlink("dest2/dir3", "non_dir3", 1); /* "file" is a symlink to existing "real_file" */ assertMakeFile("dest2/real_file", 0755, "abcdefghi"); if (canSymlink()) - assertMakeSymlink("dest2/file", "real_file"); + assertMakeSymlink("dest2/file", "real_file", 0); /* "file2" is a symlink to non-existing "real_file2" */ if (canSymlink()) - assertMakeSymlink("dest2/file2", "real_file2"); + assertMakeSymlink("dest2/file2", "real_file2", 0); assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog)); /* "dir4" is a symlink to existing "real_dir" */ if (canSymlink()) - assertMakeSymlink("dest2/dir4", "real_dir"); + assertMakeSymlink("dest2/dir4", "real_dir", 1); assertEqualInt(0, systemf("%s -xPf test2.tar -C dest2", testprog)); /* dest2/dir and dest2/dir4 symlinks should be followed */ if (canSymlink()) { - assertIsSymlink("dest2/dir", "real_dir"); - assertIsSymlink("dest2/dir4", "real_dir"); + assertIsSymlink("dest2/dir", "real_dir", 1); + assertIsSymlink("dest2/dir4", "real_dir", 1); assertIsDir("dest2/real_dir", -1); } diff --git a/test_utils/test_common.h b/test_utils/test_common.h index dd7e4101..7538d8cb 100644 --- a/test_utils/test_common.h +++ b/test_utils/test_common.h @@ -83,7 +83,9 @@ #include <sys/richacl.h> #endif #ifdef HAVE_WINDOWS_H +#define NOCRYPT #include <windows.h> +#include <winioctl.h> #endif /* @@ -218,8 +220,8 @@ assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsReg(pathname, mode) \ assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) +#define assertIsSymlink(pathname, contents, isdir) \ + assertion_is_symlink(__FILE__, __LINE__, pathname, contents, isdir) /* Create a directory, report error if it fails. */ #define assertMakeDir(dirname, mode) \ assertion_make_dir(__FILE__, __LINE__, dirname, mode) @@ -229,8 +231,8 @@ assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents) #define assertMakeHardlink(newfile, oldfile) \ assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile) -#define assertMakeSymlink(newfile, linkto) \ - assertion_make_symlink(__FILE__, __LINE__, newfile, linkto) +#define assertMakeSymlink(newfile, linkto, targetIsDir) \ + assertion_make_symlink(__FILE__, __LINE__, newfile, linkto, targetIsDir) #define assertSetNodump(path) \ assertion_set_nodump(__FILE__, __LINE__, path) #define assertUmask(mask) \ @@ -287,11 +289,11 @@ int assertion_is_dir(const char *, int, const char *, int); int assertion_is_hardlink(const char *, int, const char *, const char *); int assertion_is_not_hardlink(const char *, int, const char *, const char *); int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); +int assertion_is_symlink(const char *, int, const char *, const char *, int); int assertion_make_dir(const char *, int, const char *, int); int assertion_make_file(const char *, int, const char *, int, int, const void *); int assertion_make_hardlink(const char *, int, const char *newpath, const char *); -int assertion_make_symlink(const char *, int, const char *newpath, const char *); +int assertion_make_symlink(const char *, int, const char *newpath, const char *, int); int assertion_non_empty_file(const char *, int, const char *); int assertion_set_nodump(const char *, int, const char *); int assertion_text_file_contents(const char *, int, const char *buff, const char *f); diff --git a/test_utils/test_main.c b/test_utils/test_main.c index bb71217b..59c835ba 100644 --- a/test_utils/test_main.c +++ b/test_utils/test_main.c @@ -168,6 +168,32 @@ static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + static void * GetFunctionKernel32(const char *name) { @@ -185,15 +211,101 @@ GetFunctionKernel32(const char *name) } static int -my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) +my_CreateSymbolicLinkA(const char *linkname, const char *target, + int targetIsDir) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); + DWORD attrs; static int set; + int ret, tmpflags, llen, tlen; + int flags = 0; + char *src, *tgt, *p; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } - return f == NULL ? 0 : (*f)(linkname, target, flags); + if (f == NULL) + return (0); + + tlen = strlen(target); + llen = strlen(linkname); + + if (tlen == 0 || llen == 0) + return (0); + + tgt = malloc((tlen + 1) * sizeof(char)); + if (tgt == NULL) + return (0); + src = malloc((llen + 1) * sizeof(char)); + if (src == NULL) { + free(tgt); + return (0); + } + + /* + * Translate slashes to backslashes + */ + p = src; + while(*linkname != '\0') { + if (*linkname == '/') + *p = '\\'; + else + *p = *linkname; + linkname++; + p++; + } + *p = '\0'; + + p = tgt; + while(*target != '\0') { + if (*target == '/') + *p = '\\'; + else + *p = *target; + target++; + p++; + } + *p = '\0'; + + /* + * Each test has to specify if a file or a directory symlink + * should be created. + */ + if (targetIsDir) { +#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; +#else + flags |= 0x1; +#endif + } + +#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) + tmpflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; +#else + tmpflags = flags | 0x2; +#endif + /* + * Windows won't overwrite existing links + */ + attrs = GetFileAttributesA(linkname); + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + RemoveDirectoryA(linkname); + else + DeleteFileA(linkname); + } + + ret = (*f)(src, tgt, tmpflags); + /* + * Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + * is not undestood + */ + if (!ret) + ret = (*f)(src, tgt, flags); + + free(src); + free(tgt); + return (ret); } static int @@ -1599,26 +1711,146 @@ assertion_is_reg(const char *file, int line, const char *pathname, int mode) return (1); } -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ +/* + * Check whether 'pathname' is a symbolic link. If 'contents' is + * non-NULL, verify that the symlink has those contents. + * + * On platforms with directory symlinks, set isdir to 0 to test for a file + * symlink and to 1 to test for a directory symlink. On other platforms + * the variable is ignored. + */ static int is_symlink(const char *file, int line, - const char *pathname, const char *contents) + const char *pathname, const char *contents, int isdir) { #if defined(_WIN32) && !defined(__CYGWIN__) - (void)pathname; /* UNUSED */ - (void)contents; /* UNUSED */ - assertion_count(file, line); - /* Windows sort-of has real symlinks, but they're only usable - * by privileged users and are crippled even then, so there's - * really not much point in bothering with this. */ - return (0); + HANDLE h; + DWORD inbytes; + REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; + size_t len, len2; + wchar_t *linknamew, *contentsw; + const char *p; + char *s, *pn; + int ret = 0; + BYTE *indata; + const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OPEN_REPARSE_POINT; + + /* Replace slashes with backslashes in pathname */ + pn = malloc((strlen(pathname) + 1) * sizeof(char)); + p = pathname; + s = pn; + while(*p != '\0') { + if(*p == '/') + *s = '\\'; + else + *s = *p; + p++; + s++; + } + *s = '\0'; + + h = CreateFileA(pn, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + flag, NULL); + free(pn); + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); + return (0); + } + ret = GetFileInformationByHandle(h, &st); + if (ret == 0) { + failure_start(file, line, + "Can't stat: %s", pathname); + failure_finish(NULL); + } else if ((st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (isdir && ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) { + failure_start(file, line, + "Not a directory symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (!isdir && + ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { + failure_start(file, line, + "Not a file symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (ret == 0) { + CloseHandle(h); + return (0); + } + + indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); + ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, + 1024, &inbytes, NULL); + CloseHandle(h); + if (ret == 0) { + free(indata); + failure_start(file, line, + "Could not retrieve symlink target: %s", pathname); + failure_finish(NULL); + return (0); + } + + buf = (REPARSE_DATA_BUFFER *) indata; + if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + free(indata); + /* File is not a symbolic link */ + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); + return (0); + } + + if (contents == NULL) { + free(indata); + return (1); + } + + len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength; + + linknamew = malloc(len + sizeof(wchar_t)); + if (linknamew == NULL) { + free(indata); + return (0); + } + + memcpy(linknamew, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer) + [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len); + free(indata); + + linknamew[len / sizeof(wchar_t)] = L'\0'; + + contentsw = malloc(len + sizeof(wchar_t)); + if (contentsw == NULL) { + free(linknamew); + return (0); + } + + len2 = mbsrtowcs(contentsw, &contents, (len + sizeof(wchar_t) + / sizeof(wchar_t)), NULL); + + if (len2 > 0 && wcscmp(linknamew, contentsw) != 0) + ret = 1; + + free(linknamew); + free(contentsw); + return (ret); #else char buff[300]; struct stat st; ssize_t linklen; int r; + (void)isdir; /* UNUSED */ assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { @@ -1647,9 +1879,9 @@ is_symlink(const char *file, int line, /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) + const char *path, const char *contents, int isdir) { - if (is_symlink(file, line, path, contents)) + if (is_symlink(file, line, path, contents, isdir)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", @@ -1777,20 +2009,26 @@ assertion_make_hardlink(const char *file, int line, return(0); } -/* Create a symlink and report any failures. */ +/* + * Create a symlink and report any failures. + * + * Windows symlinks need to know if the target is a directory. + */ int assertion_make_symlink(const char *file, int line, - const char *newpath, const char *linkto) + const char *newpath, const char *linkto, int targetIsDir) { #if defined(_WIN32) && !defined(__CYGWIN__) - int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK + (void)targetIsDir; /* UNUSED */ assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); +#else + (void)targetIsDir; /* UNUSED */ #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); @@ -2217,10 +2455,12 @@ canSymlink(void) * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0", + 0); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0", + 0); #endif return (value); } @@ -2590,10 +2830,8 @@ sunacl_get(int cmd, int *aclcnt, int fd, const char *path) cnt = facl(fd, cmd, cnt, aclp); } } else { - if (aclp != NULL) { - free(aclp); - aclp = NULL; - } + free(aclp); + aclp = NULL; break; } } |