summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml108
-rw-r--r--.travis.yml30
-rw-r--r--CMakeLists.txt52
-rw-r--r--Makefile.am41
-rw-r--r--NEWS10
-rwxr-xr-xbuild/ci/build.sh (renamed from build/ci_build.sh)35
-rw-r--r--build/ci/cirrus_ci/Dockerfile.cygwin4
-rw-r--r--build/ci/cirrus_ci/Dockerfile.fedora293
-rw-r--r--build/ci/cirrus_ci/Dockerfile.mingw8
-rw-r--r--build/ci/cirrus_ci/Dockerfile.msvc9
-rw-r--r--build/ci/cirrus_ci/Dockerfile.windows12
-rwxr-xr-xbuild/ci/cirrus_ci/ci.cmd110
-rwxr-xr-xbuild/ci/cirrus_ci/ci.sh56
-rwxr-xr-xbuild/ci/test_driver (renamed from build/ci_test_driver)0
-rwxr-xr-xbuild/ci/travis_ci.sh33
-rw-r--r--contrib/shar/tree.c3
-rw-r--r--cpio/test/test_basic.c4
-rw-r--r--cpio/test/test_format_newc.c7
-rw-r--r--cpio/test/test_gcpio_compat.c2
-rw-r--r--cpio/test/test_option_L_upper.c14
-rw-r--r--cpio/test/test_option_a.c5
-rw-r--r--cpio/test/test_option_c.c2
-rw-r--r--cpio/test/test_option_t.c8
-rw-r--r--libarchive/CMakeLists.txt4
-rw-r--r--libarchive/archive.h4
-rw-r--r--libarchive/archive_acl.c24
-rw-r--r--libarchive/archive_blake2sp_ref.c6
-rw-r--r--libarchive/archive_disk_acl_sunos.c6
-rw-r--r--libarchive/archive_entry.c297
-rw-r--r--libarchive/archive_entry.h10
-rw-r--r--libarchive/archive_entry_misc.362
-rw-r--r--libarchive/archive_entry_private.h3
-rw-r--r--libarchive/archive_hmac.c1
-rw-r--r--libarchive/archive_match.c33
-rw-r--r--libarchive/archive_pack_dev.c3
-rw-r--r--libarchive/archive_platform.h2
-rw-r--r--libarchive/archive_ppmd8.c1287
-rw-r--r--libarchive/archive_ppmd8_private.h148
-rw-r--r--libarchive/archive_read.c9
-rw-r--r--libarchive/archive_read_disk_entry_from_file.c5
-rw-r--r--libarchive/archive_read_disk_posix.c70
-rw-r--r--libarchive/archive_read_disk_windows.c154
-rw-r--r--libarchive/archive_read_open_file.c3
-rw-r--r--libarchive/archive_read_private.h3
-rw-r--r--libarchive/archive_read_set_format.c3
-rw-r--r--libarchive/archive_read_support_filter_gzip.c46
-rw-r--r--libarchive/archive_read_support_format_7zip.c11
-rw-r--r--libarchive/archive_read_support_format_ar.c8
-rw-r--r--libarchive/archive_read_support_format_cpio.c3
-rw-r--r--libarchive/archive_read_support_format_iso9660.c17
-rw-r--r--libarchive/archive_read_support_format_rar5.c564
-rw-r--r--libarchive/archive_read_support_format_raw.c4
-rw-r--r--libarchive/archive_read_support_format_tar.c9
-rw-r--r--libarchive/archive_read_support_format_warc.c3
-rw-r--r--libarchive/archive_read_support_format_xar.c8
-rw-r--r--libarchive/archive_read_support_format_zip.c850
-rw-r--r--libarchive/archive_string.c6
-rw-r--r--libarchive/archive_util.c2
-rw-r--r--libarchive/archive_windows.h5
-rw-r--r--libarchive/archive_write_add_filter_xz.c7
-rw-r--r--libarchive/archive_write_disk_posix.c145
-rw-r--r--libarchive/archive_write_disk_set_standard_lookup.c6
-rw-r--r--libarchive/archive_write_disk_windows.c203
-rw-r--r--libarchive/archive_write_set_format_7zip.c3
-rw-r--r--libarchive/archive_write_set_format_ar.c5
-rw-r--r--libarchive/archive_write_set_format_cpio.c3
-rw-r--r--libarchive/archive_write_set_format_cpio_newc.c3
-rw-r--r--libarchive/archive_write_set_format_gnutar.c3
-rw-r--r--libarchive/archive_write_set_format_pax.c15
-rw-r--r--libarchive/archive_write_set_format_shar.c6
-rw-r--r--libarchive/archive_write_set_format_ustar.c9
-rw-r--r--libarchive/archive_write_set_format_v7tar.c9
-rw-r--r--libarchive/archive_write_set_format_xar.c7
-rw-r--r--libarchive/archive_write_set_format_zip.c9
-rw-r--r--libarchive/test/CMakeLists.txt1
-rw-r--r--libarchive/test/test_compat_pax_libarchive_2x.c153
-rw-r--r--libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu15
-rw-r--r--libarchive/test/test_entry.c2
-rw-r--r--libarchive/test/test_fuzz.c8
-rw-r--r--libarchive/test/test_read_disk_directory_traversals.c299
-rw-r--r--libarchive/test/test_read_extract.c2
-rw-r--r--libarchive/test/test_read_format_rar.c16
-rw-r--r--libarchive/test/test_read_format_rar5.c295
-rw-r--r--libarchive/test/test_read_format_rar5_extra_field_version.rar.uu10
-rw-r--r--libarchive/test/test_read_format_rar5_hardlink.rar.uu6
-rw-r--r--libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu9
-rw-r--r--libarchive/test/test_read_format_rar5_leftshift1.rar.uu9
-rw-r--r--libarchive/test/test_read_format_rar5_leftshift2.rar.uu6
-rw-r--r--libarchive/test/test_read_format_rar5_owner.rar.uu8
-rw-r--r--libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu15
-rw-r--r--libarchive/test/test_read_format_rar5_symlink.rar.uu8
-rw-r--r--libarchive/test/test_read_format_rar5_truncated_huff.rar.uu7
-rw-r--r--libarchive/test/test_read_format_rar5_win32.rar.uu131
-rw-r--r--libarchive/test/test_read_format_raw.c22
-rw-r--r--libarchive/test/test_read_format_raw.data.gz.uu4
-rw-r--r--libarchive/test/test_read_format_xar.c10
-rw-r--r--libarchive/test/test_read_format_zip.c604
-rw-r--r--libarchive/test/test_read_format_zip_bz2_hang.zip.uu5
-rw-r--r--libarchive/test/test_read_format_zip_bzip2.zipx.uu19
-rw-r--r--libarchive/test/test_read_format_zip_bzip2_multi.zipx.uu96
-rw-r--r--libarchive/test/test_read_format_zip_lzma.zipx.uu19
-rw-r--r--libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu5
-rw-r--r--libarchive/test/test_read_format_zip_lzma_multi.zipx.uu95
-rw-r--r--libarchive/test/test_read_format_zip_ppmd8.zipx.uu17
-rw-r--r--libarchive/test/test_read_format_zip_ppmd8_crash_1.zipx.uu4
-rw-r--r--libarchive/test/test_read_format_zip_ppmd8_crash_2.zipx.uu4
-rw-r--r--libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu84
-rw-r--r--libarchive/test/test_read_format_zip_xz_multi.zipx.uu125
-rw-r--r--libarchive/test/test_sparse_basic.c81
-rw-r--r--libarchive/test/test_write_disk_symlink.c156
-rw-r--r--tar/bsdtar.115
-rw-r--r--tar/bsdtar.c43
-rw-r--r--tar/bsdtar.h1
-rw-r--r--tar/cmdline.c1
-rw-r--r--tar/test/CMakeLists.txt1
-rw-r--r--tar/test/test_basic.c4
-rw-r--r--tar/test/test_copy.c4
-rw-r--r--tar/test/test_option_C_mtree.c17
-rw-r--r--tar/test/test_option_H_upper.c36
-rw-r--r--tar/test/test_option_L_upper.c28
-rw-r--r--tar/test/test_option_U_upper.c18
-rw-r--r--tar/test/test_option_exclude_vcs.c230
-rw-r--r--tar/test/test_option_n.c81
-rw-r--r--tar/test/test_option_s.c18
-rw-r--r--tar/test/test_strip_components.c11
-rw-r--r--tar/test/test_symlink_dir.c26
-rw-r--r--test_utils/test_common.h14
-rw-r--r--test_utils/test_main.c284
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 \
diff --git a/NEWS b/NEWS
index 5d782e6f..e2d96aeb 100644
--- a/NEWS
+++ b/NEWS
@@ -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(&times, 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, &times));
/* 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`
+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
+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
+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
+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*MZ&#04KA\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
+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
+"````
+`
+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
+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
+?`?#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;
}
}