aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2019-05-20 12:32:00 +0000
committerMartin Matuska <mm@FreeBSD.org>2019-05-20 12:32:00 +0000
commit8e97bbedaebab6c0c9e87545b8424c95f206f148 (patch)
treeebde61e13d7849a1bdad8146f7528a586b64ddcc
parentf9b2e63a44664e352e4a767f2822ca3eda65f540 (diff)
downloadsrc-8e97bbedaebab6c0c9e87545b8424c95f206f148.tar.gz
src-8e97bbedaebab6c0c9e87545b8424c95f206f148.zip
Update vendor/libarchive/dist to git b5818e39e128eca4951e2ab10467d4d850a2ba57
Relevant vendor changes: Issue #795: XAR - do not try to add xattrs without an allocated name PR #812: non-recursive option for extract and list PR #958: support reading metadata from compressed files PR #999: add --exclude-vcs option to bsdtar Issue #1062: treat empty archives with a GNU volume header as valid PR #1074: Handle ZIP files with trailing 0s in the extra fields (Android APK archives) PR #1109: Ignore padding in Zip extra field data (Android APK archives) PR #1167: fix problems related to unreadable directories Issue #1168: fix handling of strtol() and strtoul() PR #1172: RAR5 - fix invalid window buffer read in E8E9 filter PR #1174: ZIP reader - fix of MSZIP signature parsing PR #1175: gzip filter - fix reading files larger than 4GB from memory PR #1177: gzip filter - fix memory leak with repeated header reads PR #1180: ZIP reader - add support for Info-ZIP Unicode Path Extra Field PR #1181: RAR5 - fix merge_block() recursion (OSS-Fuzz 12999, 13029, 13144, 13478, 13490) PR #1183: fix memory leak when decompressing ZIP files with LZMA PR #1184: fix RAR5 OSS-Fuzz issues 12466, 14490, 14491, 12817 OSS-Fuzz 12466: RAR5 - fix buffer overflow when parsing huffman tables OSS-Fuzz 14490, 14491: RAR5 - fix bad shift-left operations OSS-Fuzz 12817: RAR5 - handle a case with truncated huffman tables PR #1186: RAR5 - fix invalid type used for dictionary size mask (OSS-Fuzz 14537) PR #1187: RAR5 - fix integer overflow (OSS-Fuzz 14555) PR #1190: RAR5 - RAR5 don't try to unpack entries marked as directories (OSS-Fuzz 14574) PR #1196: RAR5 - fix a potential SIGSEGV on 32-bit builds OSS-Fuzz 2582: RAR - fix use after free if there is an invalid entry OSS-Fuzz 14331: RAR5 - fix maximum owner name length OSS-Fuzz 13965: RAR5 - use unsigned int for volume number + range check Additional RAR5 reader changes: - support symlinks, hardlinks, file owner, file group, versioned files - change ARCHIVE_FORMAT_RAR_V5 to 0x100000 - set correct mode for readonly directories - support readonly, hidden and system Windows file attributes NOTE: a version bump of libarchive will happen in the following days
Notes
Notes: svn path=/vendor/libarchive/dist/; revision=347989
-rw-r--r--.cirrus.yml121
-rw-r--r--.travis.yml9
-rw-r--r--CMakeLists.txt7
-rw-r--r--Makefile.am55
-rw-r--r--NEWS10
-rwxr-xr-xbuild/ci/build.sh34
-rw-r--r--build/ci/cirrus_ci/Dockerfile.cygwin4
-rw-r--r--build/ci/cirrus_ci/Dockerfile.fc293
-rw-r--r--build/ci/cirrus_ci/Dockerfile.fc29.distcheck3
-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.cmd122
-rwxr-xr-xbuild/ci/cirrus_ci/ci.sh (renamed from build/ci/cirrus_ci.sh)11
-rwxr-xr-xbuild/ci/travis_ci.sh3
-rw-r--r--contrib/archivetest.c218
-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--libarchive/CMakeLists.txt1
-rw-r--r--libarchive/archive.h4
-rw-r--r--libarchive/archive_entry.c30
-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_platform.h2
-rw-r--r--libarchive/archive_read.c9
-rw-r--r--libarchive/archive_read_disk_entry_from_file.c2
-rw-r--r--libarchive/archive_read_disk_posix.c39
-rw-r--r--libarchive/archive_read_disk_windows.c180
-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.c52
-rw-r--r--libarchive/archive_read_support_format_cab.c4
-rw-r--r--libarchive/archive_read_support_format_mtree.c15
-rw-r--r--libarchive/archive_read_support_format_rar.c4
-rw-r--r--libarchive/archive_read_support_format_rar5.c5895
-rw-r--r--libarchive/archive_read_support_format_raw.c4
-rw-r--r--libarchive/archive_read_support_format_tar.c29
-rw-r--r--libarchive/archive_read_support_format_warc.c3
-rw-r--r--libarchive/archive_read_support_format_xar.c17
-rw-r--r--libarchive/archive_read_support_format_zip.c300
-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.c10
-rw-r--r--libarchive/archive_write_disk_windows.c259
-rw-r--r--libarchive/archive_write_set_format_pax.c15
-rw-r--r--libarchive/archive_write_set_format_xar.c7
-rw-r--r--libarchive/test/CMakeLists.txt3
-rw-r--r--libarchive/test/test_entry.c37
-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_mtree.c24
-rw-r--r--libarchive/test/test_read_format_mtree_noprint.mtree.uu4
-rw-r--r--libarchive/test/test_read_format_rar.c39
-rw-r--r--libarchive/test/test_read_format_rar5.c1622
-rw-r--r--libarchive/test/test_read_format_rar5_distance_overflow.rar.uu9
-rw-r--r--libarchive/test/test_read_format_rar5_extra_field_version.rar.uu10
-rw-r--r--libarchive/test/test_read_format_rar5_fileattr.rar.uu13
-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_nonempty_dir_stream.rar.uu9
-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_rar_ppmd_use_after_free.rar.uu10
-rw-r--r--libarchive/test/test_read_format_raw.c27
-rw-r--r--libarchive/test/test_read_format_raw.data.gz.uu4
-rw-r--r--libarchive/test/test_read_format_tar_empty_with_gnulabel.c53
-rw-r--r--libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu231
-rw-r--r--libarchive/test/test_read_format_zip.c165
-rw-r--r--libarchive/test/test_read_format_zip_7075_utf8_paths.c102
-rw-r--r--libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu20
-rw-r--r--libarchive/test/test_read_format_zip_extra_padding.c93
-rw-r--r--libarchive/test/test_read_format_zip_extra_padding.zip.uu7
-rw-r--r--libarchive/test/test_read_format_zip_lzma_alone_leak.zipx.uu5
-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.c18
-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.c278
107 files changed, 8011 insertions, 3721 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 28d38213f95b..21044fff79e0 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -2,25 +2,130 @@ env:
CIRRUS_CLONE_DEPTH: 1
ARCH: amd64
-task:
+FreeBSD_task:
+ matrix:
+ env:
+ BS: autotools
+ env:
+ BS: cmake
matrix:
- container:
- image: fedora:29
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
+ install_script:
+ - ./build/ci/build.sh -a install
+
+MacOS_task:
+ matrix:
+ env:
+ BS: autotools
+ env:
+ BS: cmake
+ matrix:
osx_instance:
- image: mojave-xcode-10.1
+ 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
+ install_script:
+ - ./build/ci/build.sh -a install
+
+Fedora_29_task:
+ container:
+ dockerfile: build/ci/cirrus_ci/Dockerfile.fc29
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
+ install_script:
+ - ./build/ci/build.sh -a install
+
+Fedora_29_distcheck_task:
+ container:
+ dockerfile: build/ci/cirrus_ci/Dockerfile.fc29.distcheck
+ env:
+ BS: autotools
+ configure_script:
+ - ./build/ci/build.sh -a autogen
+ - ./build/ci/build.sh -a configure
+ distcheck_script:
+ - ./build/ci/build.sh -a distcheck
+
+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
+ instal_script:
+ - build\ci\cirrus_ci\ci.cmd install
+
+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
+ install_script:
+ - build\ci\cirrus_ci\ci.cmd install
+
+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
install_script:
- - ./build/ci/cirrus_ci.sh install
- script:
- - ./build/ci/build.sh
- - ./build/ci/cirrus_ci.sh test
+ - build\ci\cirrus_ci\ci.cmd install
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 12ed598f6599..000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-language: C
-matrix:
- include:
- - os: windows
- env: BS=msbuild
- - os: windows
- env: BS=mingw
-script:
- - build/ci/travis_ci.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 133a5ddc50ac..0d6b8f14062b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -481,6 +481,7 @@ ENDIF()
IF(LIBLZMA_FOUND)
SET(HAVE_LIBLZMA 1)
SET(HAVE_LZMA_H 1)
+ CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR})
SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES})
INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIRS})
@@ -494,6 +495,7 @@ IF(LIBLZMA_FOUND)
IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
ADD_DEFINITIONS(-DLZMA_API_STATIC)
ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
+ CMAKE_POP_CHECK_STATE()
ELSE(LIBLZMA_FOUND)
# LZMA not found and will not be used.
ENDIF(LIBLZMA_FOUND)
@@ -549,9 +551,11 @@ IF(LIBB2_FOUND)
SET(HAVE_BLAKE2_H 1)
SET(ARCHIVE_BLAKE2 FALSE)
LIST(APPEND ADDITIONAL_LIBS ${LIBB2_LIBRARY})
+ CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_LIBRARIES ${LIBB2_LIBRARY})
SET(CMAKE_REQUIRED_INCLUDES ${LIBB2_INCLUDE_DIR})
CHECK_FUNCTION_EXISTS(blake2sp_init HAVE_LIBB2)
+ CMAKE_POP_CHECK_STATE()
ELSE(LIBB2_FOUND)
SET(ARCHIVE_BLAKE2 TRUE)
ENDIF(LIBB2_FOUND)
@@ -606,17 +610,18 @@ IF(ZSTD_FOUND)
SET(HAVE_ZSTD_H 1)
INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR})
LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY})
+ CMAKE_PUSH_CHECK_STATE()
SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY})
SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR})
CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD)
#
# TODO: test for static library.
#
+ CMAKE_POP_CHECK_STATE()
ENDIF(ZSTD_FOUND)
MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY)
-set(CMAKE_REQUIRED_LIBRARIES)
#
# Check headers
diff --git a/Makefile.am b/Makefile.am
index 186126bec280..da78b24acb51 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -288,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 \
@@ -501,6 +502,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_tar_concatenated.c \
libarchive/test/test_read_format_tar_empty_pax.c \
libarchive/test/test_read_format_tar_empty_filename.c \
+ libarchive/test/test_read_format_tar_empty_with_gnulabel.c \
libarchive/test/test_read_format_tar_filename.c \
libarchive/test/test_read_format_tbz.c \
libarchive/test/test_read_format_tgz.c \
@@ -511,10 +513,12 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_warc.c \
libarchive/test/test_read_format_xar.c \
libarchive/test/test_read_format_zip.c \
+ libarchive/test/test_read_format_zip_7075_utf8_paths.c \
libarchive/test/test_read_format_zip_comment_stored.c \
libarchive/test/test_read_format_zip_encryption_data.c \
libarchive/test/test_read_format_zip_encryption_partially.c \
libarchive/test/test_read_format_zip_encryption_header.c \
+ libarchive/test/test_read_format_zip_extra_padding.c \
libarchive/test/test_read_format_zip_filename.c \
libarchive/test/test_read_format_zip_high_compression.c \
libarchive/test/test_read_format_zip_jar.c \
@@ -721,6 +725,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_filter_lzop.tar.lzo.uu \
libarchive/test/test_read_filter_lzop_multiple_parts.tar.lzo.uu \
libarchive/test/test_read_format_mtree_crash747.mtree.bz2.uu \
+ libarchive/test/test_read_format_mtree_noprint.mtree.uu \
libarchive/test/test_read_format_7zip_bcj2_bzip2.7z.uu \
libarchive/test/test_read_format_7zip_bcj2_copy_1.7z.uu \
libarchive/test/test_read_format_7zip_bcj2_copy_2.7z.uu \
@@ -820,15 +825,51 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_rar_multivolume.part0004.rar.uu \
libarchive/test/test_read_format_rar_noeof.rar.uu \
libarchive/test/test_read_format_rar_ppmd_lzss_conversion.rar.uu \
+ libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu \
libarchive/test/test_read_format_rar_sfx.exe.uu \
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_distance_overflow.rar.uu \
+ libarchive/test/test_read_format_rar5_extra_field_version.rar.uu \
+ libarchive/test/test_read_format_rar5_fileattr.rar.uu \
+ libarchive/test/test_read_format_rar5_hardlink.rar.uu \
+ libarchive/test/test_read_format_rar5_invalid_dict_reference.rar.uu \
+ libarchive/test/test_read_format_rar5_leftshift1.rar.uu \
+ libarchive/test/test_read_format_rar5_leftshift2.rar.uu \
+ libarchive/test/test_read_format_rar5_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_nonempty_dir_stream.rar.uu \
+ libarchive/test/test_read_format_rar5_owner.rar.uu \
+ libarchive/test/test_read_format_rar5_readtables_overflow.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_truncated_huff.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.gz.uu \
libarchive/test/test_read_format_raw.data.Z.uu \
libarchive/test/test_read_format_raw.data.uu \
libarchive/test/test_read_format_tar_concatenated.tar.uu \
libarchive/test/test_read_format_tar_empty_filename.tar.uu \
+ libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu \
libarchive/test/test_read_format_tar_empty_pax.tar.Z.uu \
libarchive/test/test_read_format_tar_filename_koi8r.tar.Z.uu \
libarchive/test/test_read_format_ustar_filename_cp866.tar.Z.uu \
@@ -836,11 +877,16 @@ 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_7075_utf8_paths.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 \
libarchive/test/test_read_format_zip_encryption_header.zip.uu \
libarchive/test/test_read_format_zip_encryption_partially.zip.uu \
+ libarchive/test/test_read_format_zip_extra_padding.zip.uu \
libarchive/test/test_read_format_zip_filename_cp866.zip.uu \
libarchive/test/test_read_format_zip_filename_cp932.zip.uu \
libarchive/test/test_read_format_zip_filename_koi8r.zip.uu \
@@ -849,6 +895,9 @@ 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_lzma_alone_leak.zipx.uu \
+ libarchive/test/test_read_format_zip_lzma_multi.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 +907,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 +920,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 +1049,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 5d782e6fc356..e2d96aebd714 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 d61336eafb0e..64eb07fb23aa 100755
--- a/build/ci/build.sh
+++ b/build/ci/build.sh
@@ -8,11 +8,13 @@
# SRCDIR= # source directory
# CONFIGURE_ARGS= # configure arguments
# MAKE_ARGS= # make arguments
+# DEBUG= # set -g -fsanitize=address flags
ACTIONS=
if [ -n "${BUILD_SYSTEM}" ]; then
BS="${BUILD_SYSTEM}"
fi
+
BS="${BS:-autotools}"
MAKE="${MAKE:-make}"
CMAKE="${CMAKE:-cmake}"
@@ -21,7 +23,7 @@ SRCDIR="${SRCDIR:-`pwd`}"
RET=0
usage () {
- echo "Usage: $0 [-b autotools|cmake] [-a autogen|configure|build|test ] [ -a ... ] [ -d builddir ] [-s srcdir ]"
+ echo "Usage: $0 [-b autotools|cmake] [-a autogen|configure|build|test|install|distcheck ] [ -a ... ] [ -d builddir ] [-s srcdir ]"
}
inputerror () {
echo $1
@@ -36,6 +38,8 @@ while getopts a:b:d:s: opt; do
configure) ;;
build) ;;
test) ;;
+ install) ;;
+ distcheck) ;;
*) inputerror "Invalid action (-a)" ;;
esac
ACTIONS="${ACTIONS} ${OPTARG}"
@@ -58,8 +62,25 @@ while getopts a:b:d:s: opt; do
;;
esac
done
+if [ -z "${MAKE_ARGS}" ]; then
+ if [ "${BS}" = "autotools" ]; then
+ MAKE_ARGS="V=1"
+ elif [ "${BS}" = "cmake" ]; then
+ MAKE_ARGS="VERBOSE=1"
+ fi
+fi
+if [ -n "${DEBUG}" ]; then
+ if [ -n "${CFLAGS}" ]; then
+ export CFLAGS="${CFLAGS} -g -fsanitize=address"
+ else
+ export CFLAGS="-g -fsanitize=address"
+ fi
+ if ["${BS}" = "cmake" ]; then
+ CONFIGURE_ARGS="${CONFIGURE_ARGS} -DCMAKE_C_CFLAGS=-g -fsanitize=address"
+ fi
+fi
if [ -z "${ACTIONS}" ]; then
- ACTIONS="autogen configure build test"
+ ACTIONS="autogen configure build test install"
fi
if [ -z "${BS}" ]; then
inputerror "Missing build system (-b) parameter"
@@ -103,6 +124,15 @@ for action in ${ACTIONS}; do
RET="$?"
find ${TMPDIR:-/tmp} -path '*_test.*' -name '*.log' -print -exec cat {} \;
;;
+ install)
+ ${MAKE} ${MAKE_ARGS} install DESTDIR="${BUILDDIR}/destdir"
+ RET="$?"
+ cd ${BUILDDIR}/destdir && ls -lR .
+ ;;
+ distcheck)
+ ${MAKE} ${MAKE_ARGS} distcheck
+ RET="$?"
+ ;;
esac
if [ "${RET}" != "0" ]; then
exit "${RET}"
diff --git a/build/ci/cirrus_ci/Dockerfile.cygwin b/build/ci/cirrus_ci/Dockerfile.cygwin
new file mode 100644
index 000000000000..453503e0301c
--- /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.fc29 b/build/ci/cirrus_ci/Dockerfile.fc29
new file mode 100644
index 000000000000..d88176b174d8
--- /dev/null
+++ b/build/ci/cirrus_ci/Dockerfile.fc29
@@ -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.fc29.distcheck b/build/ci/cirrus_ci/Dockerfile.fc29.distcheck
new file mode 100644
index 000000000000..4470b5c39168
--- /dev/null
+++ b/build/ci/cirrus_ci/Dockerfile.fc29.distcheck
@@ -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 groff ghostscript
diff --git a/build/ci/cirrus_ci/Dockerfile.mingw b/build/ci/cirrus_ci/Dockerfile.mingw
new file mode 100644
index 000000000000..f14bb0bc73bd
--- /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 000000000000..c98318289217
--- /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 000000000000..34d6d32a7af6
--- /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 000000000000..654d704860ff
--- /dev/null
+++ b/build/ci/cirrus_ci/ci.cmd
@@ -0,0 +1,122 @@
+@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 IF "%1%"=="install" (
+ IF "%BE%"=="cygwin-gcc" (
+ SET BS=cmake
+ C:\tools\cygwin\bin\bash.exe --login -c "cd '%cd%'; ./build/ci/build.sh -a install"
+ ) 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 install DESTDIR=%cd%\destdir
+ ) ELSE IF "%BE%"=="msvc" (
+ SET PATH=%PATH%;C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin
+ cmake --build . --target INSTALL --config Release
+ )
+) ELSE (
+ ECHO "Usage: %0% prepare|deplibs|configure|build|test|install"
+ @EXIT /b 0
+)
+@EXIT /b 0
diff --git a/build/ci/cirrus_ci.sh b/build/ci/cirrus_ci/ci.sh
index 9db762f97cca..c07ebfe97bea 100755
--- a/build/ci/cirrus_ci.sh
+++ b/build/ci/cirrus_ci/ci.sh
@@ -1,6 +1,6 @@
#!/bin/sh
UNAME=`uname`
-if [ "$1" = "install" ]
+if [ "$1" = "prepare" ]
then
if [ "${UNAME}" = "FreeBSD" ]
then
@@ -17,8 +17,11 @@ then
elif [ "${UNAME}" = "Darwin" ]
then
set -x -e
- brew update
- brew install autoconf automake libtool pkg-config cmake xz lz4 zstd
+ 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" ]
@@ -48,6 +51,6 @@ then
TMPDIR=/tmp_acl_nfsv4 ${BIN_SUBDIR}/libarchive_test -r "${CURDIR}/libarchive/test" -v test_acl_platform_nfs4
fi
else
- echo "Usage $0 install | test_nfsv4_acls"
+ echo "Usage $0 prepare | test_nfsv4_acls"
exit 1
fi
diff --git a/build/ci/travis_ci.sh b/build/ci/travis_ci.sh
index e51a67931b9f..8ed0543dfdf9 100755
--- a/build/ci/travis_ci.sh
+++ b/build/ci/travis_ci.sh
@@ -15,13 +15,14 @@ case "$UNAME" in
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
- # The MinGW tests time out on Travis CI, disable for now
+ #export SKIP_TEST_FUZZ=1
#mingw32-make test
set +x
else
diff --git a/contrib/archivetest.c b/contrib/archivetest.c
new file mode 100644
index 000000000000..8002039ee4e2
--- /dev/null
+++ b/contrib/archivetest.c
@@ -0,0 +1,218 @@
+/*-
+ * 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.
+ */
+
+/*
+ * Archivetest verifies reading archives with libarchive
+ *
+ * It may be used to reproduce failures in testcases discovered by OSS-Fuzz
+ * https://github.com/google/oss-fuzz/blob/master/projects/libarchive
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+const char *errnostr(int errno)
+{
+ char *estr;
+ switch(errno) {
+ case ARCHIVE_EOF:
+ estr = "ARCHIVE_EOF";
+ break;
+ case ARCHIVE_OK:
+ estr = "ARCHIVE_OK";
+ break;
+ case ARCHIVE_WARN:
+ estr = "ARCHIVE_WARN";
+ break;
+ case ARCHIVE_RETRY:
+ estr = "ARCHIVE_RETRY";
+ break;
+ case ARCHIVE_FAILED:
+ estr = "ARCHIVE_FAILED";
+ break;
+ case ARCHIVE_FATAL:
+ estr = "ARCHIVE_FATAL";
+ break;
+ default:
+ estr = "Unknown";
+ break;
+ }
+ return (estr);
+}
+
+void usage(const char *prog)
+{
+ fprintf(stderr, "Usage: %s [-f filename] [-h] [-q] [-s]\n", prog);
+}
+
+void printhelp()
+{
+ fprintf(stdout, "archivetest: verify reading archives with "
+ "libarchive\n\n"
+ "Options:\n"
+ " -f filename Filename to verify\n"
+ " -h Show this help\n"
+ " -q Quiet mode\n"
+ " -s Verify only headers (skip data)\n\n"
+ "If no filename is specified, data is read from standard input.\n"
+ "\n%s\n", archive_version_details());
+}
+
+int v_print(int verbose, const char *format, ...)
+{
+ int r = 0;
+
+ if (verbose) {
+ va_list args;
+
+ va_start(args, format);
+ r = vfprintf(stdout, format, args);
+ va_end(args);
+ }
+ return (r);
+}
+
+int main(int argc, char *argv[])
+{
+ struct archive *a;
+ struct archive_entry *entry;
+ char *filename;
+ const char *p;
+ char buffer[4096];
+ int c;
+ int v, skip_data;
+ int r = ARCHIVE_OK;
+ int format_printed;
+
+ filename = NULL;
+ skip_data = 0;
+ v = 1;
+
+ while ((c = getopt (argc, argv, "f:hqs")) != -1) {
+ switch (c) {
+ case 'f':
+ filename = optarg;
+ break;
+ case 'h':
+ printhelp();
+ exit(0);
+ case 'q':
+ v = 0;
+ break;
+ case 's':
+ skip_data = 1;
+ break;
+ case '?':
+ if (optopt == 'f')
+ fprintf(stderr, "Option -%c requires "
+ "an argument.\n", optopt);
+ else if (isprint(optopt))
+ fprintf(stderr, "Unknown option '-%c'"
+ ".\n", optopt);
+ else
+ fprintf(stderr, "Unknown option "
+ "character '\\x%x'.\n", optopt);
+ usage(argv[0]);
+ default:
+ exit(1);
+ }
+ }
+
+ a = archive_read_new();
+
+ archive_read_support_filter_all(a);
+ archive_read_support_format_all(a);
+
+ v_print(v, "Data source: ");
+
+ if (filename == NULL) {
+ v_print(v, "standard input\n");
+ r = archive_read_open_fd(a, STDIN_FILENO, 4096);
+ } else {
+ v_print(v, "filename: %s\n", filename);
+ r = archive_read_open_filename(a, filename, 4096);
+ }
+
+ if (r != ARCHIVE_OK) {
+ archive_read_free(a);
+ fprintf(stderr, "Invalid or unsupported data source\n");
+ exit(1);
+ }
+
+ format_printed = 0;
+ c = 1;
+
+ while (1) {
+ r = archive_read_next_header(a, &entry);
+ if (r == ARCHIVE_FATAL) {
+ v_print(v, "Entry %d: fatal error reading "
+ "header\n", c);
+ break;
+ }
+ if (!format_printed) {
+ v_print(v, "Filter: %s\nFormat: %s\n",
+ archive_filter_name(a, 0), archive_format_name(a));
+ format_printed = 1;
+ }
+ if (r == ARCHIVE_RETRY)
+ continue;
+ if (r == ARCHIVE_EOF)
+ break;
+ p = archive_entry_pathname(entry);
+ v_print(v, "Entry %d: %s, pathname", c, errnostr(r));
+ if (p == NULL || p[0] == '\0')
+ v_print(v, " unreadable");
+ else
+ v_print(v, ": %s", p);
+ v_print(v, ", data: ");
+ if (skip_data) {
+ v_print(v, "skipping");
+ } else {
+ while ((r = archive_read_data(a, buffer, 4096) > 0))
+ ;
+ if (r == ARCHIVE_FATAL) {
+ v_print(v, "ERROR\nError string: %s\n",
+ archive_error_string(a));
+ break;
+ }
+ v_print(v, "OK");
+ }
+ v_print(v, "\n");
+ c++;
+ }
+
+ v_print(v, "Last return code: %s (%d)\n", errnostr(r), r);
+ if (r == ARCHIVE_EOF || r == ARCHIVE_OK) {
+ archive_read_free(a);
+ exit(0);
+ }
+ v_print(v, "Error string: %s\n", archive_error_string(a));
+ archive_read_free(a);
+ exit(2);
+}
diff --git a/cpio/test/test_basic.c b/cpio/test/test_basic.c
index 6e45d1856983..a8fedf89e968 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 258640443fe6..6c981f6ac139 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 461e427c2e88..9bb988990e4c 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 1774343f6732..cab41b61d749 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 296387777ccf..e96bdf3cab5b 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 fa47b7e277d8..013caed56030 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/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt
index 2bc69593d038..ec775bb49939 100644
--- a/libarchive/CMakeLists.txt
+++ b/libarchive/CMakeLists.txt
@@ -170,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
diff --git a/libarchive/archive.h b/libarchive/archive.h
index 438ce4680a9e..daaaf783a001 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_entry.c b/libarchive/archive_entry.c
index 34cd581155b2..72c644e6079b 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);
@@ -1749,6 +1766,10 @@ static const struct flag {
{ "nohidden", L"nohidden", UF_HIDDEN, 0},
{ "nouhidden", L"nouhidden", UF_HIDDEN, 0},
#endif
+#ifdef FILE_ATTRIBUTE_HIDDEN
+ { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0},
+ { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0},
+#endif
#ifdef UF_OFFLINE
{ "nooffline", L"nooffline", UF_OFFLINE, 0},
{ "nouoffline", L"nouoffline", UF_OFFLINE, 0},
@@ -1758,6 +1779,11 @@ static const struct flag {
{ "nourdonly", L"nourdonly", UF_READONLY, 0},
{ "noreadonly", L"noreadonly", UF_READONLY, 0},
#endif
+#ifdef FILE_ATTRIBUTE_READONLY
+ { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0},
+#endif
#ifdef UF_SPARSE
{ "nosparse", L"nosparse", UF_SPARSE, 0},
{ "nousparse", L"nousparse", UF_SPARSE, 0},
@@ -1770,6 +1796,10 @@ static const struct flag {
{ "nosystem", L"nosystem", UF_SYSTEM, 0},
{ "nousystem", L"nousystem", UF_SYSTEM, 0},
#endif
+#ifdef FILE_ATTRIBUTE_SYSTEM
+ { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0},
+ { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0},
+#endif
#if defined(FS_UNRM_FL) /* 'u' */
{ "noundel", L"noundel", FS_UNRM_FL, 0},
#elif defined(EXT2_UNRM_FL)
diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h
index 47653f37e5a0..f8a7e532f2b9 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 000000000000..9b1e3ea207d3
--- /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 c69233e68bd3..3d569bbfc6b5 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 f29965577f01..392916de5b7b 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 f150e8224c1c..04747b1f6663 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_platform.h b/libarchive/archive_platform.h
index 32b884c96ed3..b8bcb52bc90e 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_read.c b/libarchive/archive_read.c
index 0e56e76e7315..de964f253284 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 7b6102e5eecf..45417e9ac763 100644
--- a/libarchive/archive_read_disk_entry_from_file.c
+++ b/libarchive/archive_read_disk_entry_from_file.c
@@ -191,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 31d2429c41a5..c4df6c9436ba 100644
--- a/libarchive/archive_read_disk_posix.c
+++ b/libarchive/archive_read_disk_posix.c
@@ -909,7 +909,7 @@ next_entry(struct archive_read_disk *a, struct tree *t,
}
}
break;
- }
+ }
} while (lst == NULL);
#ifdef __APPLE__
@@ -1295,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);
@@ -2151,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;
@@ -2172,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);
@@ -2479,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 d82048de2995..4a5421f8f30e 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)
@@ -900,6 +1047,19 @@ next_entry(struct archive_read_disk *a, struct tree *t,
}
/*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = st->dwFileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
* Invoke a meta data filter callback.
*/
if (a->metadata_filter_func) {
@@ -1838,9 +1998,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 +2300,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;
}
@@ -2151,6 +2314,19 @@ archive_read_disk_entry_from_file(struct archive *_a,
archive_entry_copy_gname(entry, name);
/*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = fileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
* Can this file be sparse file ?
*/
if (archive_entry_filetype(entry) != AE_IFREG
diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h
index 78546dca34aa..bf04f6410438 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 190f4369d248..1d3e49d16477 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 fa8c675de124..458b6f729164 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);
@@ -374,7 +409,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
{
struct private_data *state;
size_t decompressed;
- ssize_t avail_in;
+ ssize_t avail_in, max_in;
int ret;
state = (struct private_data *)self->data;
@@ -408,6 +443,12 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
"truncated gzip input");
return (ARCHIVE_FATAL);
}
+ if (UINT_MAX >= SSIZE_MAX)
+ max_in = SSIZE_MAX;
+ else
+ max_in = UINT_MAX;
+ if (avail_in > max_in)
+ avail_in = max_in;
state->stream.avail_in = (uInt)avail_in;
/* Decompress and consume some of that data. */
@@ -469,6 +510,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_cab.c b/libarchive/archive_read_support_format_cab.c
index e5ff5a12cd9b..a6475308e016 100644
--- a/libarchive/archive_read_support_format_cab.c
+++ b/libarchive/archive_read_support_format_cab.c
@@ -1509,8 +1509,8 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
}
if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
goto nomszip;
- else if (cab->stream.next_in[0] != 0x43 ||
- cab->stream.next_in[1] != 0x4b)
+ else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||
+ cab->stream.next_in[1] != 0x4b))
goto nomszip;
cab->stream.next_in += mszip;
cab->stream.avail_in -= mszip;
diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c
index 5b0eadc0844c..a7331a2672c7 100644
--- a/libarchive/archive_read_support_format_mtree.c
+++ b/libarchive/archive_read_support_format_mtree.c
@@ -45,6 +45,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011
#ifdef HAVE_STRING_H
#include <string.h>
#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
#include "archive.h"
#include "archive_entry.h"
@@ -1011,7 +1014,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
{
ssize_t len;
uintmax_t counter;
- char *p;
+ char *p, *s;
struct mtree_option *global;
struct mtree_entry *last_entry;
int r, is_form_d;
@@ -1025,6 +1028,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
(void)detect_form(a, &is_form_d);
for (counter = 1; ; ++counter) {
+ r = ARCHIVE_OK;
len = readline(a, mtree, &p, 65536);
if (len == 0) {
mtree->this_entry = mtree->entries;
@@ -1045,6 +1049,15 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
continue;
if (*p == '\r' || *p == '\n' || *p == '\0')
continue;
+ /* Non-printable characters are not allowed */
+ for (s = p;s < p + len - 1; s++) {
+ if (!isprint(*s)) {
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ }
+ if (r != ARCHIVE_OK)
+ break;
if (*p != '/') {
r = process_add_entry(a, mtree, &global, p, len,
&last_entry, is_form_d);
diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c
index a8cc5c94d846..49360876c2ac 100644
--- a/libarchive/archive_read_support_format_rar.c
+++ b/libarchive/archive_read_support_format_rar.c
@@ -1024,8 +1024,10 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
case COMPRESS_METHOD_GOOD:
case COMPRESS_METHOD_BEST:
ret = read_data_compressed(a, buff, size, offset);
- if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN)
+ if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
__archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ rar->start_new_table = 1;
+ }
break;
default:
diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c
index 159c3b61f5ff..7c24627b186c 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,230 +81,275 @@
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;
- int64_t last_offset; /* Used in sanity checks. */
- int64_t last_size; /* Used in sanity checks. */
-
- uint8_t solid : 1; /* Is this a solid stream? */
- uint8_t service : 1; /* Is this file a service data? */
- uint8_t eof : 1; /* Did we finish unpacking the file? */
-
- /* Optional time fields. */
- uint64_t e_mtime;
- uint64_t e_ctime;
- uint64_t e_atime;
- uint32_t e_unix_ns;
-
- /* Optional hash fields. */
- uint32_t stored_crc32;
- uint32_t calculated_crc32;
- uint8_t blake2sp[32];
- blake2sp_state b2state;
- char has_blake2;
+ ssize_t bytes_remaining;
+ ssize_t unpacked_size;
+ int64_t last_offset; /* Used in sanity checks. */
+ int64_t last_size; /* Used in sanity checks. */
+
+ uint8_t solid : 1; /* Is this a solid stream? */
+ uint8_t service : 1; /* Is this file a service data? */
+ uint8_t eof : 1; /* Did we finish unpacking the file? */
+ uint8_t dir : 1; /* Is this file entry a directory? */
+
+ /* Optional time fields. */
+ uint64_t e_mtime;
+ uint64_t e_ctime;
+ uint64_t e_atime;
+ uint32_t e_unix_ns;
+
+ /* Optional hash fields. */
+ uint32_t stored_crc32;
+ uint32_t calculated_crc32;
+ uint8_t blake2sp[32];
+ blake2sp_state b2state;
+ char has_blake2;
+
+ /* Optional redir fields */
+ uint64_t redir_type;
+ uint64_t redir_flags;
};
+enum EXTRA {
+ EX_CRYPT = 0x01,
+ EX_HASH = 0x02,
+ EX_HTIME = 0x03,
+ EX_VERSION = 0x04,
+ EX_REDIR = 0x05,
+ EX_UOWNER = 0x06,
+ EX_SUBDATA = 0x07
+};
+
+#define REDIR_SYMLINK_IS_DIR 1
+
+enum REDIR_TYPE {
+ REDIR_TYPE_NONE = 0,
+ REDIR_TYPE_UNIXSYMLINK = 1,
+ REDIR_TYPE_WINSYMLINK = 2,
+ REDIR_TYPE_JUNCTION = 3,
+ REDIR_TYPE_HARDLINK = 4,
+ REDIR_TYPE_FILECOPY = 5,
+};
+
+#define OWNER_USER_NAME 0x01
+#define OWNER_GROUP_NAME 0x02
+#define OWNER_USER_UID 0x04
+#define OWNER_GROUP_GID 0x08
+#define OWNER_MAXNAMELEN 256
+
enum FILTER_TYPE {
- FILTER_DELTA = 0, /* Generic pattern. */
- FILTER_E8 = 1, /* Intel x86 code. */
- FILTER_E8E9 = 2, /* Intel x86 code. */
- FILTER_ARM = 3, /* ARM code. */
- FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */
- FILTER_RGB = 5, /* Color palette, not used in RARv5. */
- FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
- FILTER_PPM = 7, /* Predictive pattern matching, not used in RARv5. */
- FILTER_NONE = 8,
+ FILTER_DELTA = 0, /* Generic pattern. */
+ FILTER_E8 = 1, /* Intel x86 code. */
+ FILTER_E8E9 = 2, /* Intel x86 code. */
+ FILTER_ARM = 3, /* ARM code. */
+ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */
+ FILTER_RGB = 5, /* Color palette, not used in RARv5. */
+ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
+ FILTER_PPM = 7, /* Predictive pattern matching, not used in
+ RARv5. */
+ FILTER_NONE = 8,
};
struct filter_info {
- int type;
- int channels;
- int pos_r;
+ int type;
+ int channels;
+ int pos_r;
- int64_t block_start;
- ssize_t block_length;
- uint16_t width;
+ int64_t block_start;
+ ssize_t block_length;
+ uint16_t width;
};
struct data_ready {
- char used;
- const uint8_t* buf;
- size_t size;
- int64_t offset;
+ char used;
+ const uint8_t* buf;
+ size_t size;
+ int64_t offset;
};
struct cdeque {
- uint16_t beg_pos;
- uint16_t end_pos;
- uint16_t cap_mask;
- uint16_t size;
- size_t* arr;
+ uint16_t beg_pos;
+ uint16_t end_pos;
+ uint16_t cap_mask;
+ uint16_t size;
+ size_t* arr;
};
struct decode_table {
- uint32_t size;
- int32_t decode_len[16];
- uint32_t decode_pos[16];
- uint32_t quick_bits;
- uint8_t quick_len[1 << 10];
- uint16_t quick_num[1 << 10];
- uint16_t decode_num[306];
+ uint32_t size;
+ int32_t decode_len[16];
+ uint32_t decode_pos[16];
+ uint32_t quick_bits;
+ uint8_t quick_len[1 << 10];
+ uint16_t quick_num[1 << 10];
+ uint16_t decode_num[306];
};
struct comp_state {
- /* Flag used to specify if unpacker needs to reinitialize the uncompression
- * context. */
- uint8_t initialized : 1;
-
- /* Flag used when applying filters. */
- uint8_t all_filters_applied : 1;
-
- /* Flag used to skip file context reinitialization, used when unpacker is
- * skipping through different multivolume archives. */
- uint8_t switch_multivolume : 1;
-
- /* Flag used to specify if unpacker has processed the whole data block or
- * just a part of it. */
- uint8_t block_parsing_finished : 1;
-
- int notused : 4;
-
- int flags; /* Uncompression flags. */
- int method; /* Uncompression algorithm method. */
- int version; /* Uncompression algorithm version. */
- ssize_t window_size; /* Size of window_buf. */
- uint8_t* window_buf; /* Circular buffer used during
- decompression. */
- uint8_t* filtered_buf; /* Buffer used when applying filters. */
- const uint8_t* block_buf; /* Buffer used when merging blocks. */
- size_t window_mask; /* Convenience field; window_size - 1. */
- int64_t write_ptr; /* This amount of data has been unpacked in
- the window buffer. */
- int64_t last_write_ptr; /* This amount of data has been stored in
- the output file. */
- int64_t last_unstore_ptr; /* Counter of bytes extracted during
- unstoring. This is separate from
- last_write_ptr because of how SERVICE
- base blocks are handled during skipping
- in solid multiarchive archives. */
- int64_t solid_offset; /* Additional offset inside the window
- buffer, used in unpacking solid
- archives. */
- ssize_t cur_block_size; /* Size of current data block. */
- int last_len; /* Flag used in lzss decompression. */
-
- /* Decode tables used during lzss uncompression. */
+ /* Flag used to specify if unpacker needs to reinitialize the
+ uncompression context. */
+ uint8_t initialized : 1;
+
+ /* Flag used when applying filters. */
+ uint8_t all_filters_applied : 1;
+
+ /* Flag used to skip file context reinitialization, used when unpacker
+ is skipping through different multivolume archives. */
+ uint8_t switch_multivolume : 1;
+
+ /* Flag used to specify if unpacker has processed the whole data block
+ or just a part of it. */
+ uint8_t block_parsing_finished : 1;
+
+ int notused : 4;
+
+ int flags; /* Uncompression flags. */
+ int method; /* Uncompression algorithm method. */
+ int version; /* Uncompression algorithm version. */
+ ssize_t window_size; /* Size of window_buf. */
+ uint8_t* window_buf; /* Circular buffer used during
+ decompression. */
+ uint8_t* filtered_buf; /* Buffer used when applying filters. */
+ const uint8_t* block_buf; /* Buffer used when merging blocks. */
+ size_t window_mask; /* Convenience field; window_size - 1. */
+ int64_t write_ptr; /* This amount of data has been unpacked
+ in the window buffer. */
+ int64_t last_write_ptr; /* This amount of data has been stored in
+ the output file. */
+ int64_t last_unstore_ptr; /* Counter of bytes extracted during
+ unstoring. This is separate from
+ last_write_ptr because of how SERVICE
+ base blocks are handled during skipping
+ in solid multiarchive archives. */
+ int64_t solid_offset; /* Additional offset inside the window
+ buffer, used in unpacking solid
+ archives. */
+ ssize_t cur_block_size; /* Size of current data block. */
+ int last_len; /* Flag used in lzss decompression. */
+
+ /* Decode tables used during lzss uncompression. */
#define HUFF_BC 20
- struct decode_table bd; /* huffman bit lengths */
+ struct decode_table bd; /* huffman bit lengths */
#define HUFF_NC 306
- struct decode_table ld; /* literals */
+ struct decode_table ld; /* literals */
#define HUFF_DC 64
- struct decode_table dd; /* distances */
+ struct decode_table dd; /* distances */
#define HUFF_LDC 16
- struct decode_table ldd; /* lower bits of distances */
+ struct decode_table ldd; /* lower bits of distances */
#define HUFF_RC 44
- struct decode_table rd; /* repeating distances */
+ struct decode_table rd; /* repeating distances */
#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
- /* Circular deque for storing filters. */
- struct cdeque filters;
- int64_t last_block_start; /* Used for sanity checking. */
- ssize_t last_block_length; /* Used for sanity checking. */
+ /* Circular deque for storing filters. */
+ struct cdeque filters;
+ int64_t last_block_start; /* Used for sanity checking. */
+ ssize_t last_block_length; /* Used for sanity checking. */
- /* Distance cache used during lzss uncompression. */
- int dist_cache[4];
+ /* Distance cache used during lzss uncompression. */
+ int dist_cache[4];
- /* Data buffer stack. */
- struct data_ready dready[2];
+ /* Data buffer stack. */
+ struct data_ready dready[2];
};
/* Bit reader state. */
struct bit_reader {
- int8_t bit_addr; /* Current bit pointer inside current byte. */
- int in_addr; /* Current byte pointer. */
+ int8_t bit_addr; /* Current bit pointer inside current byte. */
+ int in_addr; /* Current byte pointer. */
};
/* RARv5 block header structure. Use bf_* functions to get values from
* block_flags_u8 field. I.e. bf_byte_count, etc. */
struct compressed_block_header {
- /* block_flags_u8 contain fields encoded in little-endian bitfield:
- *
- * - table present flag (shr 7, and 1),
- * - last block flag (shr 6, and 1),
- * - byte_count (shr 3, and 7),
- * - bit_size (shr 0, and 7).
- */
- uint8_t block_flags_u8;
- uint8_t block_cksum;
+ /* block_flags_u8 contain fields encoded in little-endian bitfield:
+ *
+ * - table present flag (shr 7, and 1),
+ * - last block flag (shr 6, and 1),
+ * - byte_count (shr 3, and 7),
+ * - bit_size (shr 0, and 7).
+ */
+ uint8_t block_flags_u8;
+ uint8_t block_cksum;
};
/* RARv5 main header structure. */
struct main_header {
- /* Does the archive contain solid streams? */
- uint8_t solid : 1;
+ /* Does the archive contain solid streams? */
+ uint8_t solid : 1;
- /* If this a multi-file archive? */
- uint8_t volume : 1;
- uint8_t endarc : 1;
- uint8_t notused : 5;
+ /* If this a multi-file archive? */
+ uint8_t volume : 1;
+ uint8_t endarc : 1;
+ uint8_t notused : 5;
- int vol_no;
+ unsigned int vol_no;
};
struct generic_header {
- uint8_t split_after : 1;
- uint8_t split_before : 1;
- uint8_t padding : 6;
- int size;
- int last_header_id;
+ uint8_t split_after : 1;
+ uint8_t split_before : 1;
+ uint8_t padding : 6;
+ int size;
+ int last_header_id;
};
struct multivolume {
- int expected_vol_no;
- uint8_t* push_buf;
+ unsigned int expected_vol_no;
+ uint8_t* push_buf;
};
/* Main context structure. */
struct rar5 {
- int header_initialized;
-
- /* Set to 1 if current file is positioned AFTER the magic value
- * of the archive file. This is used in header reading functions. */
- int skipped_magic;
-
- /* Set to not zero if we're in skip mode (either by calling rar5_data_skip
- * function or when skipping over solid streams). Set to 0 when in
- * extraction mode. This is used during checksum calculation functions. */
- int skip_mode;
-
- /* An offset to QuickOpen list. This is not supported by this unpacker,
- * because we're focusing on streaming interface. QuickOpen is designed
- * to make things quicker for non-stream interfaces, so it's not our
- * use case. */
- uint64_t qlist_offset;
-
- /* An offset to additional Recovery data. This is not supported by this
- * unpacker. Recovery data are additional Reed-Solomon codes that could
- * be used to calculate bytes that are missing in archive or are
- * corrupted. */
- uint64_t rr_offset;
-
- /* Various context variables grouped to different structures. */
- struct generic_header generic;
- struct main_header main;
- struct comp_state cstate;
- struct file_header file;
- struct bit_reader bits;
- struct multivolume vol;
-
- /* The header of currently processed RARv5 block. Used in main
- * decompression logic loop. */
- struct compressed_block_header last_block_hdr;
+ int header_initialized;
+
+ /* Set to 1 if current file is positioned AFTER the magic value
+ * of the archive file. This is used in header reading functions. */
+ int skipped_magic;
+
+ /* Set to not zero if we're in skip mode (either by calling
+ * rar5_data_skip function or when skipping over solid streams).
+ * Set to 0 when in * extraction mode. This is used during checksum
+ * calculation functions. */
+ int skip_mode;
+
+ /* Set to not zero if we're in block merging mode (i.e. when switching
+ * to another file in multivolume archive, last block from 1st archive
+ * needs to be merged with 1st block from 2nd archive). This flag
+ * guards against recursive use of the merging function, which doesn't
+ * support recursive calls. */
+ int merge_mode;
+
+ /* An offset to QuickOpen list. This is not supported by this unpacker,
+ * because we're focusing on streaming interface. QuickOpen is designed
+ * to make things quicker for non-stream interfaces, so it's not our
+ * use case. */
+ uint64_t qlist_offset;
+
+ /* An offset to additional Recovery data. This is not supported by this
+ * unpacker. Recovery data are additional Reed-Solomon codes that could
+ * be used to calculate bytes that are missing in archive or are
+ * corrupted. */
+ uint64_t rr_offset;
+
+ /* Various context variables grouped to different structures. */
+ struct generic_header generic;
+ struct main_header main;
+ struct comp_state cstate;
+ struct file_header file;
+ struct bit_reader bits;
+ struct multivolume vol;
+
+ /* The header of currently processed RARv5 block. Used in main
+ * decompression logic loop. */
+ struct compressed_block_header last_block_hdr;
};
/* Forward function declarations. */
@@ -309,332 +357,341 @@ struct rar5 {
static int verify_global_checksums(struct archive_read* a);
static int rar5_read_data_skip(struct archive_read *a);
static int push_data_ready(struct archive_read* a, struct rar5* rar,
- const uint8_t* buf, size_t size, int64_t offset);
+ const uint8_t* buf, size_t size, int64_t offset);
/* CDE_xxx = Circular Double Ended (Queue) return values. */
enum CDE_RETURN_VALUES {
- CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
+ CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
};
/* Clears the contents of this circular deque. */
static void cdeque_clear(struct cdeque* d) {
- d->size = 0;
- d->beg_pos = 0;
- d->end_pos = 0;
+ d->size = 0;
+ d->beg_pos = 0;
+ d->end_pos = 0;
}
/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
* 64, 256, etc. When the user will add another item above current capacity,
* the circular deque will overwrite the oldest entry. */
static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
- if(d == NULL || max_capacity_power_of_2 == 0)
- return CDE_PARAM;
+ if(d == NULL || max_capacity_power_of_2 == 0)
+ return CDE_PARAM;
- d->cap_mask = max_capacity_power_of_2 - 1;
- d->arr = NULL;
+ d->cap_mask = max_capacity_power_of_2 - 1;
+ d->arr = NULL;
- if((max_capacity_power_of_2 & d->cap_mask) > 0)
- return CDE_PARAM;
+ if((max_capacity_power_of_2 & d->cap_mask) > 0)
+ return CDE_PARAM;
- cdeque_clear(d);
- d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
+ cdeque_clear(d);
+ d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
- return d->arr ? CDE_OK : CDE_ALLOC;
+ return d->arr ? CDE_OK : CDE_ALLOC;
}
/* Return the current size (not capacity) of circular deque `d`. */
static size_t cdeque_size(struct cdeque* d) {
- return d->size;
+ return d->size;
}
/* Returns the first element of current circular deque. Note that this function
* doesn't perform any bounds checking. If you need bounds checking, use
* `cdeque_front()` function instead. */
static void cdeque_front_fast(struct cdeque* d, void** value) {
- *value = (void*) d->arr[d->beg_pos];
+ *value = (void*) d->arr[d->beg_pos];
}
/* Returns the first element of current circular deque. This function
* performs bounds checking. */
static int cdeque_front(struct cdeque* d, void** value) {
- if(d->size > 0) {
- cdeque_front_fast(d, value);
- return CDE_OK;
- } else
- return CDE_OUT_OF_BOUNDS;
+ if(d->size > 0) {
+ cdeque_front_fast(d, value);
+ return CDE_OK;
+ } else
+ return CDE_OUT_OF_BOUNDS;
}
/* Pushes a new element into the end of this circular deque object. If current
* size will exceed capacity, the oldest element will be overwritten. */
static int cdeque_push_back(struct cdeque* d, void* item) {
- if(d == NULL)
- return CDE_PARAM;
+ if(d == NULL)
+ return CDE_PARAM;
- if(d->size == d->cap_mask + 1)
- return CDE_OUT_OF_BOUNDS;
+ if(d->size == d->cap_mask + 1)
+ return CDE_OUT_OF_BOUNDS;
- d->arr[d->end_pos] = (size_t) item;
- d->end_pos = (d->end_pos + 1) & d->cap_mask;
- d->size++;
+ d->arr[d->end_pos] = (size_t) item;
+ d->end_pos = (d->end_pos + 1) & d->cap_mask;
+ d->size++;
- return CDE_OK;
+ return CDE_OK;
}
/* Pops a front element of this circular deque object and returns its value.
* This function doesn't perform any bounds checking. */
static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
- *value = (void*) d->arr[d->beg_pos];
- d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
- d->size--;
+ *value = (void*) d->arr[d->beg_pos];
+ d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
+ d->size--;
}
/* Pops a front element of this circular deque object and returns its value.
* This function performs bounds checking. */
static int cdeque_pop_front(struct cdeque* d, void** value) {
- if(!d || !value)
- return CDE_PARAM;
+ if(!d || !value)
+ return CDE_PARAM;
- if(d->size == 0)
- return CDE_OUT_OF_BOUNDS;
+ if(d->size == 0)
+ return CDE_OUT_OF_BOUNDS;
- cdeque_pop_front_fast(d, value);
- return CDE_OK;
+ cdeque_pop_front_fast(d, value);
+ return CDE_OK;
}
/* Convenience function to cast filter_info** to void **. */
static void** cdeque_filter_p(struct filter_info** f) {
- return (void**) (size_t) f;
+ return (void**) (size_t) f;
}
/* Convenience function to cast filter_info* to void *. */
static void* cdeque_filter(struct filter_info* f) {
- return (void**) (size_t) f;
+ return (void**) (size_t) f;
}
-/* Destroys this circular deque object. Deallocates the memory of the collection
- * buffer, but doesn't deallocate the memory of any pointer passed to this
- * deque as a value. */
+/* Destroys this circular deque object. Deallocates the memory of the
+ * collection buffer, but doesn't deallocate the memory of any pointer passed
+ * to this deque as a value. */
static void cdeque_free(struct cdeque* d) {
- if(!d)
- return;
+ if(!d)
+ return;
- if(!d->arr)
- return;
+ if(!d->arr)
+ return;
- free(d->arr);
+ free(d->arr);
- d->arr = NULL;
- d->beg_pos = -1;
- d->end_pos = -1;
- d->cap_mask = 0;
+ d->arr = NULL;
+ d->beg_pos = -1;
+ d->end_pos = -1;
+ d->cap_mask = 0;
}
static inline
uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
- return hdr->block_flags_u8 & 7;
+ return hdr->block_flags_u8 & 7;
}
static inline
uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
- return (hdr->block_flags_u8 >> 3) & 7;
+ return (hdr->block_flags_u8 >> 3) & 7;
}
static inline
uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
- return (hdr->block_flags_u8 >> 7) & 1;
+ return (hdr->block_flags_u8 >> 7) & 1;
}
static inline struct rar5* get_context(struct archive_read* a) {
- return (struct rar5*) a->format->data;
+ return (struct rar5*) a->format->data;
}
/* Convenience functions used by filter implementations. */
+static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask,
+ int64_t start, int64_t end)
+{
+ if((start & mask) > (end & mask)) {
+ ssize_t len1 = mask + 1 - (start & mask);
+ ssize_t len2 = end & mask;
+
+ memcpy(dst, &window[start & mask], len1);
+ memcpy(dst + len1, window, len2);
+ } else {
+ memcpy(dst, &window[start & mask], (size_t) (end - start));
+ }
+}
static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
- return archive_le32dec(&rar->cstate.window_buf[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)
+ uint32_t value)
{
- archive_le32enc(&rar->cstate.filtered_buf[offset], value);
-}
-
-static void circular_memcpy(uint8_t* dst, uint8_t* window, const int mask,
- int64_t start, int64_t end)
-{
- if((start & mask) > (end & mask)) {
- ssize_t len1 = mask + 1 - (start & mask);
- ssize_t len2 = end & mask;
-
- memcpy(dst, &window[start & mask], len1);
- memcpy(dst + len1, window, len2);
- } else {
- memcpy(dst, &window[start & mask], (size_t) (end - start));
- }
+ archive_le32enc(&rar->cstate.filtered_buf[offset], value);
}
/* Allocates a new filter descriptor and adds it to the filter array. */
static struct filter_info* add_new_filter(struct rar5* rar) {
- struct filter_info* f =
- (struct filter_info*) calloc(1, sizeof(struct filter_info));
+ struct filter_info* f =
+ (struct filter_info*) calloc(1, sizeof(struct filter_info));
- if(!f) {
- return NULL;
- }
+ if(!f) {
+ return NULL;
+ }
- cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
- return f;
+ cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
+ return f;
}
static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
- int i;
- ssize_t dest_pos, src_pos = 0;
+ int i;
+ ssize_t dest_pos, src_pos = 0;
- for(i = 0; i < flt->channels; i++) {
- uint8_t prev_byte = 0;
- for(dest_pos = i;
- dest_pos < flt->block_length;
- dest_pos += flt->channels)
- {
- uint8_t byte;
+ for(i = 0; i < flt->channels; i++) {
+ uint8_t prev_byte = 0;
+ for(dest_pos = i;
+ dest_pos < flt->block_length;
+ dest_pos += flt->channels)
+ {
+ uint8_t byte;
- byte = rar->cstate.window_buf[(rar->cstate.solid_offset +
- flt->block_start + src_pos) & rar->cstate.window_mask];
+ byte = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ src_pos) & rar->cstate.window_mask];
- prev_byte -= byte;
- rar->cstate.filtered_buf[dest_pos] = prev_byte;
- src_pos++;
- }
- }
+ prev_byte -= byte;
+ rar->cstate.filtered_buf[dest_pos] = prev_byte;
+ src_pos++;
+ }
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
- int extended)
+ int extended)
{
- const uint32_t file_size = 0x1000000;
- ssize_t i;
-
- const int mask = (int)rar->cstate.window_mask;
- circular_memcpy(rar->cstate.filtered_buf,
- rar->cstate.window_buf,
- mask,
- rar->cstate.solid_offset + flt->block_start,
- rar->cstate.solid_offset + flt->block_start + flt->block_length);
-
- for(i = 0; i < flt->block_length - 4;) {
- uint8_t b = rar->cstate.window_buf[(rar->cstate.solid_offset +
- flt->block_start + i++) & mask];
-
- /* 0xE8 = x86's call <relative_addr_uint32> (function call)
- * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump) */
- if(b == 0xE8 || (extended && b == 0xE9)) {
-
- uint32_t addr;
- uint32_t offset = (i + flt->block_start) % file_size;
-
- addr = read_filter_data(rar, (uint32_t)(rar->cstate.solid_offset +
- flt->block_start + i) & rar->cstate.window_mask);
-
- if(addr & 0x80000000) {
- if(((addr + offset) & 0x80000000) == 0) {
- write_filter_data(rar, (uint32_t)i, addr + file_size);
- }
- } else {
- if((addr - file_size) & 0x80000000) {
- uint32_t naddr = addr - offset;
- write_filter_data(rar, (uint32_t)i, naddr);
- }
- }
-
- i += 4;
- }
- }
-
- return ARCHIVE_OK;
+ const uint32_t file_size = 0x1000000;
+ ssize_t i;
+
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+ for(i = 0; i < flt->block_length - 4;) {
+ uint8_t b = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ i++) & rar->cstate.window_mask];
+
+ /*
+ * 0xE8 = x86's call <relative_addr_uint32> (function call)
+ * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
+ */
+ if(b == 0xE8 || (extended && b == 0xE9)) {
+
+ uint32_t addr;
+ uint32_t offset = (i + flt->block_start) % file_size;
+
+ addr = read_filter_data(rar,
+ (uint32_t)(rar->cstate.solid_offset +
+ flt->block_start + i) & rar->cstate.window_mask);
+
+ if(addr & 0x80000000) {
+ if(((addr + offset) & 0x80000000) == 0) {
+ write_filter_data(rar, (uint32_t)i,
+ addr + file_size);
+ }
+ } else {
+ if((addr - file_size) & 0x80000000) {
+ uint32_t naddr = addr - offset;
+ write_filter_data(rar, (uint32_t)i,
+ naddr);
+ }
+ }
+
+ i += 4;
+ }
+ }
+
+ return ARCHIVE_OK;
}
static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
- ssize_t i = 0;
- uint32_t offset;
- const int mask = (int)rar->cstate.window_mask;
+ ssize_t i = 0;
+ uint32_t offset;
- circular_memcpy(rar->cstate.filtered_buf,
- rar->cstate.window_buf,
- mask,
- rar->cstate.solid_offset + flt->block_start,
- rar->cstate.solid_offset + flt->block_start + flt->block_length);
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
- for(i = 0; i < flt->block_length - 3; i += 4) {
- uint8_t* b = &rar->cstate.window_buf[(rar->cstate.solid_offset +
- flt->block_start + i) & mask];
+ 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) & 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;
+ if(b[3] == 0xEB) {
+ /* 0xEB = ARM's BL (branch + link) instruction. */
+ offset = read_filter_data(rar,
+ (rar->cstate.solid_offset + flt->block_start + i) &
+ rar->cstate.window_mask) & 0x00ffffff;
- offset -= (uint32_t) ((i + flt->block_start) / 4);
- offset = (offset & 0x00ffffff) | 0xeb000000;
- write_filter_data(rar, (uint32_t)i, offset);
- }
- }
+ offset -= (uint32_t) ((i + flt->block_start) / 4);
+ offset = (offset & 0x00ffffff) | 0xeb000000;
+ write_filter_data(rar, (uint32_t)i, offset);
+ }
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int run_filter(struct archive_read* a, struct filter_info* flt) {
- int ret;
- struct rar5* rar = get_context(a);
-
- free(rar->cstate.filtered_buf);
-
- rar->cstate.filtered_buf = malloc(flt->block_length);
- if(!rar->cstate.filtered_buf) {
- archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for "
- "filter data.");
- return ARCHIVE_FATAL;
- }
-
- switch(flt->type) {
- case FILTER_DELTA:
- ret = run_delta_filter(rar, flt);
- break;
-
- case FILTER_E8:
- /* fallthrough */
- case FILTER_E8E9:
- ret = run_e8e9_filter(rar, flt, flt->type == FILTER_E8E9);
- break;
-
- case FILTER_ARM:
- ret = run_arm_filter(rar, flt);
- break;
-
- default:
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported filter type: 0x%02x", flt->type);
- return ARCHIVE_FATAL;
- }
-
- if(ret != ARCHIVE_OK) {
- /* Filter has failed. */
- return ret;
- }
-
- if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
- flt->block_length, rar->cstate.last_write_ptr))
- {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Stack overflow when submitting unpacked data");
-
- return ARCHIVE_FATAL;
- }
-
- rar->cstate.last_write_ptr += flt->block_length;
- return ARCHIVE_OK;
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ free(rar->cstate.filtered_buf);
+
+ rar->cstate.filtered_buf = malloc(flt->block_length);
+ if(!rar->cstate.filtered_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for filter data.");
+ return ARCHIVE_FATAL;
+ }
+
+ switch(flt->type) {
+ case FILTER_DELTA:
+ ret = run_delta_filter(rar, flt);
+ break;
+
+ case FILTER_E8:
+ /* fallthrough */
+ case FILTER_E8E9:
+ ret = run_e8e9_filter(rar, flt,
+ flt->type == FILTER_E8E9);
+ break;
+
+ case FILTER_ARM:
+ ret = run_arm_filter(rar, flt);
+ break;
+
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported filter type: 0x%x", flt->type);
+ return ARCHIVE_FATAL;
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Filter has failed. */
+ return ret;
+ }
+
+ if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
+ flt->block_length, rar->cstate.last_write_ptr))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Stack overflow when submitting unpacked data");
+
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.last_write_ptr += flt->block_length;
+ return ARCHIVE_OK;
}
/* The `push_data` function submits the selected data range to the user.
@@ -642,199 +699,205 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
* that are specified here. These arguments are pushed to the FIFO stack here,
* and popped from the stack by the `use_data` function. */
static void push_data(struct archive_read* a, struct rar5* rar,
- const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
+ const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
{
- const int wmask = (int)rar->cstate.window_mask;
- const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
- rar->cstate.last_write_ptr) & wmask;
+ const uint64_t wmask = rar->cstate.window_mask;
+ const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
+ rar->cstate.last_write_ptr) & wmask;
- idx_begin += rar->cstate.solid_offset;
- idx_end += rar->cstate.solid_offset;
+ idx_begin += rar->cstate.solid_offset;
+ idx_end += rar->cstate.solid_offset;
- /* Check if our unpacked data is wrapped inside the window circular buffer.
- * If it's not wrapped, it can be copied out by using a single memcpy,
- * but when it's wrapped, we need to copy the first part with one
- * memcpy, and the second part with another memcpy. */
+ /* Check if our unpacked data is wrapped inside the window circular
+ * buffer. If it's not wrapped, it can be copied out by using
+ * a single memcpy, but when it's wrapped, we need to copy the first
+ * part with one memcpy, and the second part with another memcpy. */
- if((idx_begin & wmask) > (idx_end & wmask)) {
- /* The data is wrapped (begin offset sis bigger than end offset). */
- const ssize_t frag1_size = rar->cstate.window_size - (idx_begin & wmask);
- const ssize_t frag2_size = idx_end & wmask;
+ if((idx_begin & wmask) > (idx_end & wmask)) {
+ /* The data is wrapped (begin offset sis bigger than end
+ * offset). */
+ const ssize_t frag1_size = rar->cstate.window_size -
+ (idx_begin & wmask);
+ const ssize_t frag2_size = idx_end & wmask;
- /* Copy the first part of the buffer first. */
- push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
- rar->cstate.last_write_ptr);
+ /* Copy the first part of the buffer first. */
+ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
+ rar->cstate.last_write_ptr);
- /* Copy the second part of the buffer. */
- push_data_ready(a, rar, buf, frag2_size,
- rar->cstate.last_write_ptr + frag1_size);
+ /* Copy the second part of the buffer. */
+ push_data_ready(a, rar, buf, frag2_size,
+ rar->cstate.last_write_ptr + frag1_size);
- rar->cstate.last_write_ptr += frag1_size + frag2_size;
- } else {
- /* Data is not wrapped, so we can just use one call to copy the
- * data. */
- push_data_ready(a, rar,
- buf + solid_write_ptr,
- (idx_end - idx_begin) & wmask,
- rar->cstate.last_write_ptr);
+ rar->cstate.last_write_ptr += frag1_size + frag2_size;
+ } else {
+ /* Data is not wrapped, so we can just use one call to copy the
+ * data. */
+ push_data_ready(a, rar,
+ buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
+ rar->cstate.last_write_ptr);
- rar->cstate.last_write_ptr += idx_end - idx_begin;
- }
+ rar->cstate.last_write_ptr += idx_end - idx_begin;
+ }
}
/* Convenience function that submits the data to the user. It uses the
* unpack window buffer as a source location. */
static void push_window_data(struct archive_read* a, struct rar5* rar,
- int64_t idx_begin, int64_t idx_end)
+ int64_t idx_begin, int64_t idx_end)
{
- push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
+ push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
}
static int apply_filters(struct archive_read* a) {
- struct filter_info* flt;
- struct rar5* rar = get_context(a);
- int ret;
-
- rar->cstate.all_filters_applied = 0;
-
- /* Get the first filter that can be applied to our data. The data needs to
- * be fully unpacked before the filter can be run. */
- if(CDE_OK ==
- cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt)))
- {
- /* Check if our unpacked data fully covers this filter's range. */
- if(rar->cstate.write_ptr > flt->block_start &&
- rar->cstate.write_ptr >= flt->block_start + flt->block_length)
- {
- /* Check if we have some data pending to be written right before
- * the filter's start offset. */
- if(rar->cstate.last_write_ptr == flt->block_start) {
- /* Run the filter specified by descriptor `flt`. */
- ret = run_filter(a, flt);
- if(ret != ARCHIVE_OK) {
- /* Filter failure, return error. */
- return ret;
- }
-
- /* Filter descriptor won't be needed anymore after it's used,
- * so remove it from the filter list and free its memory. */
- (void) cdeque_pop_front(&rar->cstate.filters,
- cdeque_filter_p(&flt));
-
- free(flt);
- } else {
- /* We can't run filters yet, dump the memory right before the
- * filter. */
- push_window_data(a, rar, rar->cstate.last_write_ptr,
- flt->block_start);
- }
-
- /* Return 'filter applied or not needed' state to the caller. */
- return ARCHIVE_RETRY;
- }
- }
-
- rar->cstate.all_filters_applied = 1;
- return ARCHIVE_OK;
+ struct filter_info* flt;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ rar->cstate.all_filters_applied = 0;
+
+ /* Get the first filter that can be applied to our data. The data
+ * needs to be fully unpacked before the filter can be run. */
+ if(CDE_OK == cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt))) {
+ /* Check if our unpacked data fully covers this filter's
+ * range. */
+ if(rar->cstate.write_ptr > flt->block_start &&
+ rar->cstate.write_ptr >= flt->block_start +
+ flt->block_length) {
+ /* Check if we have some data pending to be written
+ * right before the filter's start offset. */
+ if(rar->cstate.last_write_ptr == flt->block_start) {
+ /* Run the filter specified by descriptor
+ * `flt`. */
+ ret = run_filter(a, flt);
+ if(ret != ARCHIVE_OK) {
+ /* Filter failure, return error. */
+ return ret;
+ }
+
+ /* Filter descriptor won't be needed anymore
+ * after it's used, * so remove it from the
+ * filter list and free its memory. */
+ (void) cdeque_pop_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt));
+
+ free(flt);
+ } else {
+ /* We can't run filters yet, dump the memory
+ * right before the filter. */
+ push_window_data(a, rar,
+ rar->cstate.last_write_ptr,
+ flt->block_start);
+ }
+
+ /* Return 'filter applied or not needed' state to the
+ * caller. */
+ return ARCHIVE_RETRY;
+ }
+ }
+
+ rar->cstate.all_filters_applied = 1;
+ return ARCHIVE_OK;
}
static void dist_cache_push(struct rar5* rar, int value) {
- int* q = rar->cstate.dist_cache;
+ int* q = rar->cstate.dist_cache;
- q[3] = q[2];
- q[2] = q[1];
- q[1] = q[0];
- q[0] = value;
+ q[3] = q[2];
+ q[2] = q[1];
+ q[1] = q[0];
+ q[0] = value;
}
static int dist_cache_touch(struct rar5* rar, int idx) {
- int* q = rar->cstate.dist_cache;
- int i, dist = q[idx];
+ int* q = rar->cstate.dist_cache;
+ int i, dist = q[idx];
- for(i = idx; i > 0; i--)
- q[i] = q[i - 1];
+ for(i = idx; i > 0; i--)
+ q[i] = q[i - 1];
- q[0] = dist;
- return dist;
+ q[0] = dist;
+ return dist;
}
static void free_filters(struct rar5* rar) {
- struct cdeque* d = &rar->cstate.filters;
+ struct cdeque* d = &rar->cstate.filters;
- /* Free any remaining filters. All filters should be naturally consumed by
- * the unpacking function, so remaining filters after unpacking normally
- * mean that unpacking wasn't successful. But still of course we shouldn't
- * leak memory in such case. */
+ /* Free any remaining filters. All filters should be naturally
+ * consumed by the unpacking function, so remaining filters after
+ * unpacking normally mean that unpacking wasn't successful.
+ * But still of course we shouldn't leak memory in such case. */
- /* cdeque_size() is a fast operation, so we can use it as a loop
- * expression. */
- while(cdeque_size(d) > 0) {
- struct filter_info* f = NULL;
+ /* cdeque_size() is a fast operation, so we can use it as a loop
+ * expression. */
+ while(cdeque_size(d) > 0) {
+ struct filter_info* f = NULL;
- /* Pop_front will also decrease the collection's size. */
- if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
- free(f);
- }
+ /* Pop_front will also decrease the collection's size. */
+ if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
+ free(f);
+ }
- cdeque_clear(d);
+ cdeque_clear(d);
- /* Also clear out the variables needed for sanity checking. */
- rar->cstate.last_block_start = 0;
- rar->cstate.last_block_length = 0;
+ /* Also clear out the variables needed for sanity checking. */
+ rar->cstate.last_block_start = 0;
+ rar->cstate.last_block_length = 0;
}
static void reset_file_context(struct rar5* rar) {
- memset(&rar->file, 0, sizeof(rar->file));
- blake2sp_init(&rar->file.b2state, 32);
+ memset(&rar->file, 0, sizeof(rar->file));
+ blake2sp_init(&rar->file.b2state, 32);
+
+ if(rar->main.solid) {
+ rar->cstate.solid_offset += rar->cstate.write_ptr;
+ } else {
+ rar->cstate.solid_offset = 0;
+ }
- if(rar->main.solid) {
- rar->cstate.solid_offset += rar->cstate.write_ptr;
- } else {
- rar->cstate.solid_offset = 0;
- }
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
+ rar->cstate.last_unstore_ptr = 0;
- rar->cstate.write_ptr = 0;
- rar->cstate.last_write_ptr = 0;
- rar->cstate.last_unstore_ptr = 0;
+ rar->file.redir_type = REDIR_TYPE_NONE;
+ rar->file.redir_flags = 0;
- free_filters(rar);
+ free_filters(rar);
}
static inline int get_archive_read(struct archive* a,
- struct archive_read** ar)
+ struct archive_read** ar)
{
- *ar = (struct archive_read*) a;
- archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
- "archive_read_support_format_rar5");
+ *ar = (struct archive_read*) a;
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_support_format_rar5");
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int read_ahead(struct archive_read* a, size_t how_many,
- const uint8_t** ptr)
+ const uint8_t** ptr)
{
- if(!ptr)
- return 0;
+ if(!ptr)
+ return 0;
- ssize_t avail = -1;
- *ptr = __archive_read_ahead(a, how_many, &avail);
+ ssize_t avail = -1;
+ *ptr = __archive_read_ahead(a, how_many, &avail);
+ if(*ptr == NULL) {
+ return 0;
+ }
- if(*ptr == NULL) {
- return 0;
- }
-
- return 1;
+ return 1;
}
static int consume(struct archive_read* a, int64_t how_many) {
- int ret;
+ int ret;
- ret =
- how_many == __archive_read_consume(a, how_many)
- ? ARCHIVE_OK
- : ARCHIVE_FATAL;
+ ret = how_many == __archive_read_consume(a, how_many)
+ ? ARCHIVE_OK
+ : ARCHIVE_FATAL;
- return ret;
+ return ret;
}
/**
@@ -855,752 +918,1070 @@ static int consume(struct archive_read* a, int64_t how_many) {
*/
static int read_var(struct archive_read* a, uint64_t* pvalue,
- uint64_t* pvalue_len)
+ uint64_t* pvalue_len)
{
- uint64_t result = 0;
- size_t shift, i;
- const uint8_t* p;
- uint8_t b;
-
- /* We will read maximum of 8 bytes. We don't have to handle the situation
- * to read the RAR5 variable-sized value stored at the end of the file,
- * because such situation will never happen. */
- if(!read_ahead(a, 8, &p))
- return 0;
-
- for(shift = 0, i = 0; i < 8; i++, shift += 7) {
- b = p[i];
-
- /* Strip the MSB from the input byte and add the resulting number
- * to the `result`. */
- result += (b & (uint64_t)0x7F) << shift;
-
- /* MSB set to 1 means we need to continue decoding process. MSB set
- * to 0 means we're done.
- *
- * This conditional checks for the second case. */
- if((b & 0x80) == 0) {
- if(pvalue) {
- *pvalue = result;
- }
-
- /* If the caller has passed the `pvalue_len` pointer, store the
- * number of consumed bytes in it and do NOT consume those bytes,
- * since the caller has all the information it needs to perform
- * the consuming process itself. */
- if(pvalue_len) {
- *pvalue_len = 1 + i;
- } else {
- /* If the caller did not provide the `pvalue_len` pointer,
- * it will not have the possibility to advance the file
- * pointer, because it will not know how many bytes it needs
- * to consume. This is why we handle such situation here
- * automatically. */
- if(ARCHIVE_OK != consume(a, 1 + i)) {
- return 0;
- }
- }
-
- /* End of decoding process, return success. */
- return 1;
- }
- }
-
- /* The decoded value takes the maximum number of 8 bytes. It's a maximum
- * number of bytes, so end decoding process here even if the first bit
- * of last byte is 1. */
- if(pvalue) {
- *pvalue = result;
- }
-
- if(pvalue_len) {
- *pvalue_len = 9;
- } else {
- if(ARCHIVE_OK != consume(a, 9)) {
- return 0;
- }
- }
-
- return 1;
+ uint64_t result = 0;
+ size_t shift, i;
+ const uint8_t* p;
+ uint8_t b;
+
+ /* We will read maximum of 8 bytes. We don't have to handle the
+ * situation to read the RAR5 variable-sized value stored at the end of
+ * the file, because such situation will never happen. */
+ if(!read_ahead(a, 8, &p))
+ return 0;
+
+ for(shift = 0, i = 0; i < 8; i++, shift += 7) {
+ b = p[i];
+
+ /* Strip the MSB from the input byte and add the resulting
+ * number to the `result`. */
+ result += (b & (uint64_t)0x7F) << shift;
+
+ /* MSB set to 1 means we need to continue decoding process.
+ * MSB set to 0 means we're done.
+ *
+ * This conditional checks for the second case. */
+ if((b & 0x80) == 0) {
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ /* If the caller has passed the `pvalue_len` pointer,
+ * store the number of consumed bytes in it and do NOT
+ * consume those bytes, since the caller has all the
+ * information it needs to perform */
+ if(pvalue_len) {
+ *pvalue_len = 1 + i;
+ } else {
+ /* If the caller did not provide the
+ * `pvalue_len` pointer, it will not have the
+ * possibility to advance the file pointer,
+ * because it will not know how many bytes it
+ * needs to consume. This is why we handle
+ * such situation here automatically. */
+ if(ARCHIVE_OK != consume(a, 1 + i)) {
+ return 0;
+ }
+ }
+
+ /* End of decoding process, return success. */
+ return 1;
+ }
+ }
+
+ /* The decoded value takes the maximum number of 8 bytes.
+ * It's a maximum number of bytes, so end decoding process here
+ * even if the first bit of last byte is 1. */
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ if(pvalue_len) {
+ *pvalue_len = 9;
+ } else {
+ if(ARCHIVE_OK != consume(a, 9)) {
+ return 0;
+ }
+ }
+
+ return 1;
}
static int read_var_sized(struct archive_read* a, size_t* pvalue,
- size_t* pvalue_len)
+ size_t* pvalue_len)
{
- uint64_t v;
- uint64_t v_size = 0;
+ uint64_t v;
+ uint64_t v_size = 0;
- const int ret = pvalue_len
- ? read_var(a, &v, &v_size)
- : read_var(a, &v, NULL);
+ const int ret = pvalue_len ? read_var(a, &v, &v_size)
+ : read_var(a, &v, NULL);
- if(ret == 1 && pvalue) {
- *pvalue = (size_t) v;
- }
+ if(ret == 1 && pvalue) {
+ *pvalue = (size_t) v;
+ }
- if(pvalue_len) {
- /* Possible data truncation should be safe. */
- *pvalue_len = (size_t) v_size;
- }
+ if(pvalue_len) {
+ /* Possible data truncation should be safe. */
+ *pvalue_len = (size_t) v_size;
+ }
- return ret;
+ return ret;
}
static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {
- uint32_t bits = p[rar->bits.in_addr] << 24;
- bits |= p[rar->bits.in_addr + 1] << 16;
- bits |= p[rar->bits.in_addr + 2] << 8;
- bits |= p[rar->bits.in_addr + 3];
- bits <<= rar->bits.bit_addr;
- bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
- *value = bits;
- return ARCHIVE_OK;
+ uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
+ bits |= p[rar->bits.in_addr + 1] << 16;
+ bits |= p[rar->bits.in_addr + 2] << 8;
+ bits |= p[rar->bits.in_addr + 3];
+ bits <<= rar->bits.bit_addr;
+ bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
+ *value = bits;
+ return ARCHIVE_OK;
}
static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {
- int bits = (int) p[rar->bits.in_addr] << 16;
- bits |= (int) p[rar->bits.in_addr + 1] << 8;
- bits |= (int) p[rar->bits.in_addr + 2];
- bits >>= (8 - rar->bits.bit_addr);
- *value = bits & 0xffff;
- return ARCHIVE_OK;
+ int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
+ bits |= (int) p[rar->bits.in_addr + 1] << 8;
+ bits |= (int) p[rar->bits.in_addr + 2];
+ bits >>= (8 - rar->bits.bit_addr);
+ *value = bits & 0xffff;
+ return ARCHIVE_OK;
}
static void skip_bits(struct rar5* rar, int bits) {
- const int new_bits = rar->bits.bit_addr + bits;
- rar->bits.in_addr += new_bits >> 3;
- rar->bits.bit_addr = new_bits & 7;
+ const int new_bits = rar->bits.bit_addr + bits;
+ rar->bits.in_addr += new_bits >> 3;
+ rar->bits.bit_addr = new_bits & 7;
}
/* n = up to 16 */
static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,
- int* value)
+ int* value)
{
- uint16_t v;
- int ret, num;
+ uint16_t v;
+ int ret, num;
- if(n == 0 || n > 16) {
- /* This is a programmer error and should never happen in runtime. */
- return ARCHIVE_FATAL;
- }
+ if(n == 0 || n > 16) {
+ /* This is a programmer error and should never happen
+ * in runtime. */
+ return ARCHIVE_FATAL;
+ }
- ret = read_bits_16(rar, p, &v);
- if(ret != ARCHIVE_OK)
- return ret;
+ ret = read_bits_16(rar, p, &v);
+ if(ret != ARCHIVE_OK)
+ return ret;
- num = (int) v;
- num >>= 16 - n;
+ num = (int) v;
+ num >>= 16 - n;
- skip_bits(rar, n);
+ skip_bits(rar, n);
- if(value)
- *value = num;
+ if(value)
+ *value = num;
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int read_u32(struct archive_read* a, uint32_t* pvalue) {
- const uint8_t* p;
- if(!read_ahead(a, 4, &p))
- return 0;
+ const uint8_t* p;
+ if(!read_ahead(a, 4, &p))
+ return 0;
- *pvalue = archive_le32dec(p);
- return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
+ *pvalue = archive_le32dec(p);
+ return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
}
static int read_u64(struct archive_read* a, uint64_t* pvalue) {
- const uint8_t* p;
- if(!read_ahead(a, 8, &p))
- return 0;
+ const uint8_t* p;
+ if(!read_ahead(a, 8, &p))
+ return 0;
- *pvalue = archive_le64dec(p);
- return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
+ *pvalue = archive_le64dec(p);
+ return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
}
static int bid_standard(struct archive_read* a) {
- const uint8_t* p;
+ const uint8_t* p;
- if(!read_ahead(a, rar5_signature_size, &p))
- return -1;
+ if(!read_ahead(a, rar5_signature_size, &p))
+ return -1;
- if(!memcmp(rar5_signature, p, rar5_signature_size))
- return 30;
+ if(!memcmp(rar5_signature, p, rar5_signature_size))
+ return 30;
- return -1;
+ return -1;
}
static int rar5_bid(struct archive_read* a, int best_bid) {
- int my_bid;
+ int my_bid;
- if(best_bid > 30)
- return -1;
+ if(best_bid > 30)
+ return -1;
- my_bid = bid_standard(a);
- if(my_bid > -1) {
- return my_bid;
- }
+ my_bid = bid_standard(a);
+ if(my_bid > -1) {
+ return my_bid;
+ }
- return -1;
+ return -1;
}
-static int rar5_options(struct archive_read *a, const char *key, const char *val) {
- (void) a;
- (void) key;
- (void) val;
+static int rar5_options(struct archive_read *a, const char *key,
+ const char *val) {
+ (void) a;
+ (void) key;
+ (void) val;
- /* No options supported in this version. Return the ARCHIVE_WARN code to
- * signal the options supervisor that the unpacker didn't handle setting
- * this option. */
+ /* No options supported in this version. Return the ARCHIVE_WARN code
+ * to signal the options supervisor that the unpacker didn't handle
+ * setting this option. */
- return ARCHIVE_WARN;
+ return ARCHIVE_WARN;
}
static void init_header(struct archive_read* a) {
- a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
- a->archive.archive_format_name = "RAR5";
+ a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
+ a->archive.archive_format_name = "RAR5";
}
enum HEADER_FLAGS {
- HFL_EXTRA_DATA = 0x0001, HFL_DATA = 0x0002, HFL_SKIP_IF_UNKNOWN = 0x0004,
- HFL_SPLIT_BEFORE = 0x0008, HFL_SPLIT_AFTER = 0x0010, HFL_CHILD = 0x0020,
- HFL_INHERITED = 0x0040
+ HFL_EXTRA_DATA = 0x0001,
+ HFL_DATA = 0x0002,
+ HFL_SKIP_IF_UNKNOWN = 0x0004,
+ HFL_SPLIT_BEFORE = 0x0008,
+ HFL_SPLIT_AFTER = 0x0010,
+ HFL_CHILD = 0x0020,
+ HFL_INHERITED = 0x0040
};
static int process_main_locator_extra_block(struct archive_read* a,
- struct rar5* rar)
+ struct rar5* rar)
{
- uint64_t locator_flags;
+ uint64_t locator_flags;
- if(!read_var(a, &locator_flags, NULL)) {
- return ARCHIVE_EOF;
- }
+ if(!read_var(a, &locator_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
- enum LOCATOR_FLAGS {
- QLIST = 0x01, RECOVERY = 0x02,
- };
+ enum LOCATOR_FLAGS {
+ QLIST = 0x01, RECOVERY = 0x02,
+ };
- if(locator_flags & QLIST) {
- if(!read_var(a, &rar->qlist_offset, NULL)) {
- return ARCHIVE_EOF;
- }
+ if(locator_flags & QLIST) {
+ if(!read_var(a, &rar->qlist_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
- /* qlist is not used */
- }
+ /* qlist is not used */
+ }
- if(locator_flags & RECOVERY) {
- if(!read_var(a, &rar->rr_offset, NULL)) {
- return ARCHIVE_EOF;
- }
+ if(locator_flags & RECOVERY) {
+ if(!read_var(a, &rar->rr_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
- /* rr is not used */
- }
+ /* rr is not used */
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
- ssize_t* extra_data_size)
+ ssize_t* extra_data_size)
{
- size_t hash_type;
- size_t value_len;
+ size_t hash_type;
+ size_t value_len;
- if(!read_var_sized(a, &hash_type, &value_len))
- return ARCHIVE_EOF;
+ if(!read_var_sized(a, &hash_type, &value_len))
+ return ARCHIVE_EOF;
- *extra_data_size -= value_len;
- if(ARCHIVE_OK != consume(a, value_len)) {
- return ARCHIVE_EOF;
- }
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
- enum HASH_TYPE {
- BLAKE2sp = 0x00
- };
+ enum HASH_TYPE {
+ BLAKE2sp = 0x00
+ };
- /* The file uses BLAKE2sp checksum algorithm instead of plain old
- * CRC32. */
- if(hash_type == BLAKE2sp) {
- const uint8_t* p;
- const int hash_size = sizeof(rar->file.blake2sp);
+ /* The file uses BLAKE2sp checksum algorithm instead of plain old
+ * CRC32. */
+ if(hash_type == BLAKE2sp) {
+ const uint8_t* p;
+ const int hash_size = sizeof(rar->file.blake2sp);
- if(!read_ahead(a, hash_size, &p))
- return ARCHIVE_EOF;
+ if(!read_ahead(a, hash_size, &p))
+ return ARCHIVE_EOF;
- rar->file.has_blake2 = 1;
- memcpy(&rar->file.blake2sp, p, hash_size);
+ rar->file.has_blake2 = 1;
+ memcpy(&rar->file.blake2sp, p, hash_size);
- if(ARCHIVE_OK != consume(a, hash_size)) {
- return ARCHIVE_EOF;
- }
+ if(ARCHIVE_OK != consume(a, hash_size)) {
+ return ARCHIVE_EOF;
+ }
- *extra_data_size -= hash_size;
- } else {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported hash type (0x%02x)", (int) hash_type);
- return ARCHIVE_FATAL;
- }
+ *extra_data_size -= hash_size;
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported hash type (0x%x)", (int) hash_type);
+ return ARCHIVE_FATAL;
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static uint64_t time_win_to_unix(uint64_t win_time) {
- const size_t ns_in_sec = 10000000;
- const uint64_t sec_to_unix = 11644473600LL;
- return win_time / ns_in_sec - sec_to_unix;
+ const size_t ns_in_sec = 10000000;
+ const uint64_t sec_to_unix = 11644473600LL;
+ return win_time / ns_in_sec - sec_to_unix;
}
static int parse_htime_item(struct archive_read* a, char unix_time,
- uint64_t* where, ssize_t* extra_data_size)
+ uint64_t* where, ssize_t* extra_data_size)
{
- if(unix_time) {
- uint32_t time_val;
- if(!read_u32(a, &time_val))
- return ARCHIVE_EOF;
+ if(unix_time) {
+ uint32_t time_val;
+ if(!read_u32(a, &time_val))
+ return ARCHIVE_EOF;
- *extra_data_size -= 4;
- *where = (uint64_t) time_val;
- } else {
- uint64_t windows_time;
- if(!read_u64(a, &windows_time))
- return ARCHIVE_EOF;
+ *extra_data_size -= 4;
+ *where = (uint64_t) time_val;
+ } else {
+ uint64_t windows_time;
+ if(!read_u64(a, &windows_time))
+ return ARCHIVE_EOF;
- *where = time_win_to_unix(windows_time);
- *extra_data_size -= 8;
- }
+ *where = time_win_to_unix(windows_time);
+ *extra_data_size -= 8;
+ }
- return ARCHIVE_OK;
+ 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)
+static int parse_file_extra_version(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
{
- char unix_time = 0;
- size_t flags;
- size_t value_len;
+ size_t flags = 0;
+ size_t version = 0;
+ size_t value_len = 0;
+ struct archive_string version_string;
+ struct archive_string name_utf8_string;
- enum HTIME_FLAGS {
- IS_UNIX = 0x01,
- HAS_MTIME = 0x02,
- HAS_CTIME = 0x04,
- HAS_ATIME = 0x08,
- HAS_UNIX_NS = 0x10,
- };
+ /* Flags are ignored. */
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
- 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;
- *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;
- unix_time = flags & IS_UNIX;
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len))
+ return ARCHIVE_EOF;
- if(flags & HAS_MTIME) {
- parse_htime_item(a, unix_time, &rar->file.e_mtime, extra_data_size);
- archive_entry_set_mtime(e, rar->file.e_mtime, 0);
- }
+ /* extra_data_size should be zero here. */
- if(flags & HAS_CTIME) {
- parse_htime_item(a, unix_time, &rar->file.e_ctime, extra_data_size);
- archive_entry_set_ctime(e, rar->file.e_ctime, 0);
- }
+ 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;
+ }
- if(flags & HAS_ATIME) {
- parse_htime_item(a, unix_time, &rar->file.e_atime, extra_data_size);
- archive_entry_set_atime(e, rar->file.e_atime, 0);
- }
+ archive_string_init(&version_string);
+ archive_string_init(&name_utf8_string);
- if(flags & HAS_UNIX_NS) {
- if(!read_u32(a, &rar->file.e_unix_ns))
- return ARCHIVE_EOF;
+ /* Prepare a ;123 suffix for the filename, where '123' is the version
+ * value of this file. */
+ archive_string_sprintf(&version_string, ";%zu", version);
- *extra_data_size -= 4;
- }
+ /* Build the new filename. */
+ archive_strcat(&name_utf8_string, cur_filename);
+ archive_strcat(&name_utf8_string, version_string.s);
- return ARCHIVE_OK;
+ /* Apply the new filename into this file's context. */
+ archive_entry_update_pathname_utf8(e, name_utf8_string.s);
+
+ /* Free buffers. */
+ archive_string_free(&version_string);
+ archive_string_free(&name_utf8_string);
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_htime(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ char unix_time = 0;
+ size_t flags;
+ size_t value_len;
+
+ enum HTIME_FLAGS {
+ IS_UNIX = 0x01,
+ HAS_MTIME = 0x02,
+ HAS_CTIME = 0x04,
+ HAS_ATIME = 0x08,
+ HAS_UNIX_NS = 0x10,
+ };
+
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ unix_time = flags & IS_UNIX;
+
+ if(flags & HAS_MTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_mtime,
+ extra_data_size);
+ archive_entry_set_mtime(e, rar->file.e_mtime, 0);
+ }
+
+ if(flags & HAS_CTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_ctime,
+ extra_data_size);
+ archive_entry_set_ctime(e, rar->file.e_ctime, 0);
+ }
+
+ if(flags & HAS_ATIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_atime,
+ extra_data_size);
+ archive_entry_set_atime(e, rar->file.e_atime, 0);
+ }
+
+ if(flags & HAS_UNIX_NS) {
+ if(!read_u32(a, &rar->file.e_unix_ns))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= 4;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ uint64_t value_size = 0;
+ size_t target_size = 0;
+ char target_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ if(!read_var(a, &rar->file.redir_type, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var(a, &rar->file.redir_flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var_sized(a, &target_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= target_size + 1;
+
+ if(!read_ahead(a, target_size, &p))
+ return ARCHIVE_EOF;
+
+ if(target_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Link target is too long");
+ return ARCHIVE_FATAL;
+ }
+
+ if(target_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No link target specified");
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(target_utf8_buf, p, target_size);
+ target_utf8_buf[target_size] = 0;
+
+ if(ARCHIVE_OK != consume(a, (int64_t)target_size))
+ return ARCHIVE_EOF;
+
+ switch(rar->file.redir_type) {
+ case REDIR_TYPE_UNIXSYMLINK:
+ case REDIR_TYPE_WINSYMLINK:
+ archive_entry_set_filetype(e, AE_IFLNK);
+ archive_entry_update_symlink_utf8(e, target_utf8_buf);
+ if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ } else {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_FILE);
+ }
+ break;
+
+ case REDIR_TYPE_HARDLINK:
+ archive_entry_set_filetype(e, AE_IFREG);
+ archive_entry_update_hardlink_utf8(e, target_utf8_buf);
+ break;
+
+ default:
+ /* Unknown redir type, skip it. */
+ break;
+ }
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_owner(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
+{
+ uint64_t flags = 0;
+ uint64_t value_size = 0;
+ uint64_t id = 0;
+ size_t name_len = 0;
+ size_t name_size = 0;
+ char namebuf[OWNER_MAXNAMELEN];
+ const uint8_t* p;
+
+ if(!read_var(a, &flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if ((flags & OWNER_USER_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_uname(e, namebuf);
+ }
+ if ((flags & OWNER_GROUP_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_gname(e, namebuf);
+ }
+ if ((flags & OWNER_USER_UID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_uid(e, (la_int64_t)id);
+ }
+ if ((flags & OWNER_GROUP_GID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_gid(e, (la_int64_t)id);
+ }
+ return ARCHIVE_OK;
}
static int process_head_file_extra(struct archive_read* a,
- struct archive_entry* e, struct rar5* rar,
- ssize_t extra_data_size)
+ struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)
{
- size_t extra_field_size;
- size_t extra_field_id = 0;
- int ret = ARCHIVE_FATAL;
- size_t var_size;
-
- 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;
-
- extra_data_size -= var_size;
- if(ARCHIVE_OK != consume(a, var_size)) {
- return ARCHIVE_EOF;
- }
-
- if(!read_var_sized(a, &extra_field_id, &var_size))
- return ARCHIVE_EOF;
-
- extra_data_size -= var_size;
- if(ARCHIVE_OK != consume(a, var_size)) {
- return ARCHIVE_EOF;
- }
-
- switch(extra_field_id) {
- case HASH:
- ret = parse_file_extra_hash(a, rar, &extra_data_size);
- break;
- case HTIME:
- ret = parse_file_extra_htime(a, e, rar, &extra_data_size);
- break;
- case CRYPT:
- /* fallthrough */
- case VERSION_:
- /* fallthrough */
- case REDIR:
- /* fallthrough */
- case UOWNER:
- /* fallthrough */
- case 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;
- }
- }
-
- if(ret != ARCHIVE_OK) {
- /* Attribute not implemented. */
- return ret;
- }
-
- return ARCHIVE_OK;
+ size_t extra_field_size;
+ size_t extra_field_id = 0;
+ int ret = ARCHIVE_FATAL;
+ size_t var_size;
+
+ while(extra_data_size > 0) {
+ if(!read_var_sized(a, &extra_field_size, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ switch(extra_field_id) {
+ case EX_HASH:
+ ret = parse_file_extra_hash(a, rar,
+ &extra_data_size);
+ break;
+ case EX_HTIME:
+ ret = parse_file_extra_htime(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_REDIR:
+ ret = parse_file_extra_redir(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_UOWNER:
+ ret = parse_file_extra_owner(a, e,
+ &extra_data_size);
+ break;
+ case EX_VERSION:
+ ret = parse_file_extra_version(a, e,
+ &extra_data_size);
+ break;
+ case EX_CRYPT:
+ /* fallthrough */
+ case EX_SUBDATA:
+ /* fallthrough */
+ default:
+ /* Skip unsupported entry. */
+ return consume(a, extra_data_size);
+ }
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Attribute not implemented. */
+ return ret;
+ }
+
+ return ARCHIVE_OK;
}
static int process_head_file(struct archive_read* a, struct rar5* rar,
- struct archive_entry* entry, size_t block_flags)
+ struct archive_entry* entry, size_t block_flags)
{
- ssize_t extra_data_size = 0;
- size_t data_size = 0;
- size_t file_flags = 0;
- size_t file_attr = 0;
- size_t compression_info = 0;
- size_t host_os = 0;
- size_t name_size = 0;
- uint64_t unpacked_size;
- uint32_t mtime = 0, crc = 0;
- int c_method = 0, c_version = 0, is_dir;
- char name_utf8_buf[2048 * 4];
- const uint8_t* p;
-
- archive_entry_clear(entry);
-
- /* Do not reset file context if we're switching archives. */
- if(!rar->cstate.switch_multivolume) {
- reset_file_context(rar);
- }
-
- if(block_flags & HFL_EXTRA_DATA) {
- size_t edata_size = 0;
- if(!read_var_sized(a, &edata_size, NULL))
- return ARCHIVE_EOF;
-
- /* Intentional type cast from unsigned to signed. */
- extra_data_size = (ssize_t) edata_size;
- }
-
- if(block_flags & HFL_DATA) {
- if(!read_var_sized(a, &data_size, NULL))
- return ARCHIVE_EOF;
-
- rar->file.bytes_remaining = data_size;
- } else {
- rar->file.bytes_remaining = 0;
-
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "no data found in file/service block");
- return ARCHIVE_FATAL;
- }
-
- enum FILE_FLAGS {
- DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
- UNKNOWN_UNPACKED_SIZE = 0x0008,
- };
-
- enum COMP_INFO_FLAGS {
- SOLID = 0x0040,
- };
-
- if(!read_var_sized(a, &file_flags, NULL))
- return ARCHIVE_EOF;
-
- if(!read_var(a, &unpacked_size, NULL))
- return ARCHIVE_EOF;
-
- if(file_flags & UNKNOWN_UNPACKED_SIZE) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Files with unknown unpacked size are not supported");
- return ARCHIVE_FATAL;
- }
-
- is_dir = (int) (file_flags & DIRECTORY);
-
- if(!read_var_sized(a, &file_attr, NULL))
- return ARCHIVE_EOF;
-
- if(file_flags & UTIME) {
- if(!read_u32(a, &mtime))
- return ARCHIVE_EOF;
- }
-
- if(file_flags & CRC32) {
- if(!read_u32(a, &crc))
- return ARCHIVE_EOF;
- }
-
- if(!read_var_sized(a, &compression_info, NULL))
- return ARCHIVE_EOF;
-
- c_method = (int) (compression_info >> 7) & 0x7;
- c_version = (int) (compression_info & 0x3f);
-
- rar->cstate.window_size = is_dir ?
- 0 :
- g_unpack_window_size << ((compression_info >> 10) & 15);
- rar->cstate.method = c_method;
- rar->cstate.version = c_version + 50;
-
- rar->file.solid = (compression_info & SOLID) > 0;
- rar->file.service = 0;
-
- if(!read_var_sized(a, &host_os, NULL))
- return ARCHIVE_EOF;
-
- enum HOST_OS {
- HOST_WINDOWS = 0,
- HOST_UNIX = 1,
- };
-
- if(host_os == HOST_WINDOWS) {
- /* Host OS is Windows */
-
- unsigned short mode = 0660;
-
- if(is_dir)
- mode |= AE_IFDIR;
- else
- mode |= AE_IFREG;
-
- archive_entry_set_mode(entry, mode);
- } else if(host_os == HOST_UNIX) {
- /* Host OS is Unix */
- archive_entry_set_mode(entry, (unsigned short) file_attr);
- } else {
- /* Unknown host OS */
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported Host OS: 0x%02x", (int) host_os);
-
- return ARCHIVE_FATAL;
- }
-
- if(!read_var_sized(a, &name_size, NULL))
- return ARCHIVE_EOF;
-
- if(!read_ahead(a, name_size, &p))
- return ARCHIVE_EOF;
-
- if(name_size > 2047) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Filename is too long");
-
- return ARCHIVE_FATAL;
- }
-
- if(name_size == 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "No filename specified");
-
- return ARCHIVE_FATAL;
- }
-
- memcpy(name_utf8_buf, p, name_size);
- name_utf8_buf[name_size] = 0;
- if(ARCHIVE_OK != consume(a, name_size)) {
- return ARCHIVE_EOF;
- }
-
- if(extra_data_size > 0) {
- int ret = process_head_file_extra(a, entry, rar, extra_data_size);
-
- /* Sanity check. */
- if(extra_data_size < 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "File extra data size is not zero");
- return ARCHIVE_FATAL;
- }
-
- if(ret != ARCHIVE_OK)
- return ret;
- }
-
- if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
- rar->file.unpacked_size = (ssize_t) unpacked_size;
- archive_entry_set_size(entry, unpacked_size);
- }
-
- if(file_flags & UTIME) {
- archive_entry_set_mtime(entry, (time_t) mtime, 0);
- }
-
- if(file_flags & CRC32) {
- rar->file.stored_crc32 = crc;
- }
-
- 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;
- rar->cstate.all_filters_applied = 1;
- rar->cstate.initialized = 0;
- }
-
- if(rar->generic.split_before > 0) {
- /* If now we're standing on a header that has a 'split before' mark,
- * it means we're standing on a 'continuation' file header. Signal
- * the caller that if it wants to move to another file, it must call
- * rar5_read_header() function again. */
-
- return ARCHIVE_RETRY;
- } else {
- return ARCHIVE_OK;
- }
+ ssize_t extra_data_size = 0;
+ size_t data_size = 0;
+ size_t file_flags = 0;
+ size_t file_attr = 0;
+ size_t compression_info = 0;
+ size_t host_os = 0;
+ size_t name_size = 0;
+ uint64_t unpacked_size, window_size;
+ uint32_t mtime = 0, crc = 0;
+ int c_method = 0, c_version = 0;
+ char name_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ archive_entry_clear(entry);
+
+ /* Do not reset file context if we're switching archives. */
+ if(!rar->cstate.switch_multivolume) {
+ reset_file_context(rar);
+ }
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ size_t edata_size = 0;
+ if(!read_var_sized(a, &edata_size, NULL))
+ return ARCHIVE_EOF;
+
+ /* Intentional type cast from unsigned to signed. */
+ extra_data_size = (ssize_t) edata_size;
+ }
+
+ if(block_flags & HFL_DATA) {
+ if(!read_var_sized(a, &data_size, NULL))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining = data_size;
+ } else {
+ rar->file.bytes_remaining = 0;
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "no data found in file/service block");
+ return ARCHIVE_FATAL;
+ }
+
+ enum FILE_FLAGS {
+ DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+ UNKNOWN_UNPACKED_SIZE = 0x0008,
+ };
+
+ enum FILE_ATTRS {
+ ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+ ATTR_DIRECTORY = 0x10,
+ };
+
+ enum COMP_INFO_FLAGS {
+ SOLID = 0x0040,
+ };
+
+ if(!read_var_sized(a, &file_flags, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var(a, &unpacked_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UNKNOWN_UNPACKED_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Files with unknown unpacked size are not supported");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+
+ if(!read_var_sized(a, &file_attr, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UTIME) {
+ if(!read_u32(a, &mtime))
+ return ARCHIVE_EOF;
+ }
+
+ if(file_flags & CRC32) {
+ if(!read_u32(a, &crc))
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &compression_info, NULL))
+ return ARCHIVE_EOF;
+
+ c_method = (int) (compression_info >> 7) & 0x7;
+ c_version = (int) (compression_info & 0x3f);
+
+ /* RAR5 seems to limit the dictionary size to 64MB. */
+ window_size = (rar->file.dir > 0) ?
+ 0 :
+ g_unpack_window_size << ((compression_info >> 10) & 15);
+ rar->cstate.method = c_method;
+ rar->cstate.version = c_version + 50;
+
+ /* Check if window_size is a sane value. Also, if the file is not
+ * declared as a directory, disallow window_size == 0. */
+ if(window_size > (64 * 1024 * 1024) ||
+ (rar->file.dir == 0 && window_size == 0))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Declared dictionary size is not supported.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Values up to 64M should fit into ssize_t on every
+ * architecture. */
+ rar->cstate.window_size = (ssize_t) window_size;
+
+ rar->file.solid = (compression_info & SOLID) > 0;
+ rar->file.service = 0;
+
+ if(!read_var_sized(a, &host_os, NULL))
+ return ARCHIVE_EOF;
+
+ enum HOST_OS {
+ HOST_WINDOWS = 0,
+ HOST_UNIX = 1,
+ };
+
+ if(host_os == HOST_WINDOWS) {
+ /* Host OS is Windows */
+
+ __LA_MODE_T mode;
+
+ if(file_attr & ATTR_DIRECTORY) {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0555 | AE_IFDIR;
+ } else {
+ mode = 0755 | AE_IFDIR;
+ }
+ } else {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0444 | AE_IFREG;
+ } else {
+ mode = 0644 | AE_IFREG;
+ }
+ }
+
+ archive_entry_set_mode(entry, mode);
+
+ if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
+ char *fflags_text, *ptr;
+ /* allocate for "rdonly,hidden,system," */
+ fflags_text = malloc(22 * sizeof(char));
+ if (fflags_text != NULL) {
+ ptr = fflags_text;
+ if (file_attr & ATTR_READONLY) {
+ strcpy(ptr, "rdonly,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_HIDDEN) {
+ strcpy(ptr, "hidden,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_SYSTEM) {
+ strcpy(ptr, "system,");
+ ptr = ptr + 7;
+ }
+ if (ptr > fflags_text) {
+ /* Delete trailing comma */
+ *(ptr - 1) = '\0';
+ archive_entry_copy_fflags_text(entry,
+ fflags_text);
+ }
+ free(fflags_text);
+ }
+ }
+ } else if(host_os == HOST_UNIX) {
+ /* Host OS is Unix */
+ archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
+ } else {
+ /* Unknown host OS */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported Host OS: 0x%x", (int) host_os);
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if(name_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Filename is too long");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(name_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No filename specified");
+
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(name_utf8_buf, p, name_size);
+ name_utf8_buf[name_size] = 0;
+ if(ARCHIVE_OK != consume(a, name_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ archive_entry_update_pathname_utf8(entry, name_utf8_buf);
+
+ if(extra_data_size > 0) {
+ int ret = process_head_file_extra(a, entry, rar,
+ extra_data_size);
+
+ /* Sanity check. */
+ if(extra_data_size < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "File extra data size is not zero");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
+ rar->file.unpacked_size = (ssize_t) unpacked_size;
+ if(rar->file.redir_type == REDIR_TYPE_NONE)
+ archive_entry_set_size(entry, unpacked_size);
+ }
+
+ if(file_flags & UTIME) {
+ archive_entry_set_mtime(entry, (time_t) mtime, 0);
+ }
+
+ if(file_flags & CRC32) {
+ rar->file.stored_crc32 = crc;
+ }
+
+ if(!rar->cstate.switch_multivolume) {
+ /* Do not reinitialize unpacking state if we're switching
+ * archives. */
+ rar->cstate.block_parsing_finished = 1;
+ rar->cstate.all_filters_applied = 1;
+ rar->cstate.initialized = 0;
+ }
+
+ if(rar->generic.split_before > 0) {
+ /* If now we're standing on a header that has a 'split before'
+ * mark, it means we're standing on a 'continuation' file
+ * header. Signal the caller that if it wants to move to
+ * another file, it must call rar5_read_header() function
+ * again. */
+
+ return ARCHIVE_RETRY;
+ } else {
+ return ARCHIVE_OK;
+ }
}
static int process_head_service(struct archive_read* a, struct rar5* rar,
- struct archive_entry* entry, size_t block_flags)
+ struct archive_entry* entry, size_t block_flags)
{
- /* Process this SERVICE block the same way as FILE blocks. */
- int ret = process_head_file(a, rar, entry, block_flags);
- if(ret != ARCHIVE_OK)
- return ret;
+ /* Process this SERVICE block the same way as FILE blocks. */
+ int ret = process_head_file(a, rar, entry, block_flags);
+ if(ret != ARCHIVE_OK)
+ return ret;
- rar->file.service = 1;
+ rar->file.service = 1;
- /* But skip the data part automatically. It's no use for the user anyway.
- * It contains only service data, not even needed to properly unpack the
- * file. */
- ret = rar5_read_data_skip(a);
- if(ret != ARCHIVE_OK)
- return ret;
+ /* But skip the data part automatically. It's no use for the user
+ * anyway. It contains only service data, not even needed to
+ * properly unpack the file. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
- /* After skipping, try parsing another block automatically. */
- return ARCHIVE_RETRY;
+ /* After skipping, try parsing another block automatically. */
+ return ARCHIVE_RETRY;
}
static int process_head_main(struct archive_read* a, struct rar5* rar,
- struct archive_entry* entry, size_t block_flags)
+ struct archive_entry* entry, size_t block_flags)
{
- (void) entry;
-
- int ret;
- size_t extra_data_size = 0;
- size_t extra_field_size = 0;
- size_t extra_field_id = 0;
- size_t archive_flags = 0;
-
- if(block_flags & HFL_EXTRA_DATA) {
- if(!read_var_sized(a, &extra_data_size, NULL))
- return ARCHIVE_EOF;
- } else {
- extra_data_size = 0;
- }
-
- if(!read_var_sized(a, &archive_flags, NULL)) {
- return ARCHIVE_EOF;
- }
-
- enum MAIN_FLAGS {
- VOLUME = 0x0001, /* multi-volume archive */
- VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't have it */
- SOLID = 0x0004, /* solid archive */
- PROTECT = 0x0008, /* contains Recovery info */
- LOCK = 0x0010, /* readonly flag, not used */
- };
-
- rar->main.volume = (archive_flags & VOLUME) > 0;
- rar->main.solid = (archive_flags & SOLID) > 0;
-
- if(archive_flags & VOLUME_NUMBER) {
- size_t v = 0;
- if(!read_var_sized(a, &v, NULL)) {
- return ARCHIVE_EOF;
- }
-
- rar->main.vol_no = (int) v;
- } else {
- rar->main.vol_no = 0;
- }
-
- if(rar->vol.expected_vol_no > 0 &&
- rar->main.vol_no != rar->vol.expected_vol_no)
- {
- /* Returning EOF instead of FATAL because of strange libarchive
- * behavior. When opening multiple files via
- * archive_read_open_filenames(), after reading up the whole last file,
- * the __archive_read_ahead function wraps up to the first archive
- * instead of returning EOF. */
- return ARCHIVE_EOF;
- }
-
- if(extra_data_size == 0) {
- /* Early return. */
- return ARCHIVE_OK;
- }
-
- if(!read_var_sized(a, &extra_field_size, NULL)) {
- return ARCHIVE_EOF;
- }
-
- if(!read_var_sized(a, &extra_field_id, NULL)) {
- return ARCHIVE_EOF;
- }
-
- if(extra_field_size == 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Invalid extra field size");
- return ARCHIVE_FATAL;
- }
-
- enum MAIN_EXTRA {
- // Just one attribute here.
- LOCATOR = 0x01,
- };
-
- switch(extra_field_id) {
- case LOCATOR:
- ret = process_main_locator_extra_block(a, rar);
- if(ret != ARCHIVE_OK) {
- /* Error while parsing main locator extra block. */
- return ret;
- }
-
- break;
- default:
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported extra type (0x%02x)", (int) extra_field_id);
- return ARCHIVE_FATAL;
- }
-
- return ARCHIVE_OK;
+ (void) entry;
+
+ int ret;
+ size_t extra_data_size = 0;
+ size_t extra_field_size = 0;
+ size_t extra_field_id = 0;
+ size_t archive_flags = 0;
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ if(!read_var_sized(a, &extra_data_size, NULL))
+ return ARCHIVE_EOF;
+ } else {
+ extra_data_size = 0;
+ }
+
+ if(!read_var_sized(a, &archive_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ enum MAIN_FLAGS {
+ VOLUME = 0x0001, /* multi-volume archive */
+ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
+ * have it */
+ SOLID = 0x0004, /* solid archive */
+ PROTECT = 0x0008, /* contains Recovery info */
+ LOCK = 0x0010, /* readonly flag, not used */
+ };
+
+ rar->main.volume = (archive_flags & VOLUME) > 0;
+ rar->main.solid = (archive_flags & SOLID) > 0;
+
+ if(archive_flags & VOLUME_NUMBER) {
+ size_t v = 0;
+ if(!read_var_sized(a, &v, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if (v > UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid volume number");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->main.vol_no = (unsigned int) v;
+ } else {
+ rar->main.vol_no = 0;
+ }
+
+ if(rar->vol.expected_vol_no > 0 &&
+ rar->main.vol_no != rar->vol.expected_vol_no)
+ {
+ /* Returning EOF instead of FATAL because of strange
+ * libarchive behavior. When opening multiple files via
+ * archive_read_open_filenames(), after reading up the whole
+ * last file, the __archive_read_ahead function wraps up to
+ * the first archive instead of returning EOF. */
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_data_size == 0) {
+ /* Early return. */
+ return ARCHIVE_OK;
+ }
+
+ if(!read_var_sized(a, &extra_field_size, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_field_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid extra field size");
+ return ARCHIVE_FATAL;
+ }
+
+ enum MAIN_EXTRA {
+ // Just one attribute here.
+ LOCATOR = 0x01,
+ };
+
+ switch(extra_field_id) {
+ case LOCATOR:
+ ret = process_main_locator_extra_block(a, rar);
+ if(ret != ARCHIVE_OK) {
+ /* Error while parsing main locator extra
+ * block. */
+ return ret;
+ }
+
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported extra type (0x%x)",
+ (int) extra_field_id);
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int skip_unprocessed_bytes(struct archive_read* a) {
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ if(rar->file.bytes_remaining) {
+ /* Use different skipping method in block merging mode than in
+ * normal mode. If merge mode is active, rar5_read_data_skip
+ * can't be used, because it could allow recursive use of
+ * merge_block() * function, and this function doesn't support
+ * recursive use. */
+ if(rar->merge_mode) {
+ /* Discard whole merged block. This is valid in solid
+ * mode as well, because the code will discard blocks
+ * only if those blocks are safe to discard (i.e.
+ * they're not FILE blocks). */
+ ret = consume(a, rar->file.bytes_remaining);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ rar->file.bytes_remaining = 0;
+ } else {
+ /* If we're not in merge mode, use safe skipping code.
+ * This will ensure we'll handle solid archives
+ * properly. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
}
static int scan_for_signature(struct archive_read* a);
@@ -1651,1017 +2032,1117 @@ static int scan_for_signature(struct archive_read* a);
*/
static int process_base_block(struct archive_read* a,
- struct archive_entry* entry)
+ struct archive_entry* entry)
{
- struct rar5* rar = get_context(a);
- uint32_t hdr_crc, computed_crc;
- size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
- size_t header_id = 0;
- size_t header_flags = 0;
- const uint8_t* p;
- int ret;
-
- /* Skip any unprocessed data for this file. */
- if(rar->file.bytes_remaining) {
- ret = rar5_read_data_skip(a);
- if(ret != ARCHIVE_OK) {
- return ret;
- }
- }
-
- /* Read the expected CRC32 checksum. */
- if(!read_u32(a, &hdr_crc)) {
- return ARCHIVE_EOF;
- }
-
- /* Read header size. */
- if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
- return ARCHIVE_EOF;
- }
-
- /* Sanity check, maximum header size for RAR5 is 2MB. */
- if(raw_hdr_size > (2 * 1024 * 1024)) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Base block header is too large");
-
- return ARCHIVE_FATAL;
- }
-
- hdr_size = raw_hdr_size + hdr_size_len;
-
- /* Read the whole header data into memory, maximum memory use here is
- * 2MB. */
- if(!read_ahead(a, hdr_size, &p)) {
- return ARCHIVE_EOF;
- }
-
- /* Verify the CRC32 of the header data. */
- computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
- if(computed_crc != hdr_crc) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Header CRC error");
-
- return ARCHIVE_FATAL;
- }
-
- /* If the checksum is OK, we proceed with parsing. */
- if(ARCHIVE_OK != consume(a, hdr_size_len)) {
- return ARCHIVE_EOF;
- }
-
- if(!read_var_sized(a, &header_id, NULL))
- return ARCHIVE_EOF;
-
- if(!read_var_sized(a, &header_flags, NULL))
- return ARCHIVE_EOF;
-
- rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
- rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
- rar->generic.size = (int)hdr_size;
- rar->generic.last_header_id = (int)header_id;
- rar->main.endarc = 0;
-
- /* Those are possible header ids in RARv5. */
- enum HEADER_TYPE {
- HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
- HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
- HEAD_UNKNOWN = 0xff,
- };
-
- switch(header_id) {
- case HEAD_MAIN:
- ret = process_head_main(a, rar, entry, header_flags);
-
- /* Main header doesn't have any files in it, so it's pointless
- * to return to the caller. Retry to next header, which should be
- * HEAD_FILE/HEAD_SERVICE. */
- if(ret == ARCHIVE_OK)
- return ARCHIVE_RETRY;
-
- return ret;
- case HEAD_SERVICE:
- ret = process_head_service(a, rar, entry, header_flags);
- return ret;
- case HEAD_FILE:
- ret = process_head_file(a, rar, entry, header_flags);
- return ret;
- case HEAD_CRYPT:
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Encryption is not supported");
- return ARCHIVE_FATAL;
- case HEAD_ENDARC:
- rar->main.endarc = 1;
-
- /* After encountering an end of file marker, we need to take
- * into consideration if this archive is continued in another
- * file (i.e. is it part01.rar: is there a part02.rar?) */
- if(rar->main.volume) {
- /* In case there is part02.rar, position the read pointer
- * in a proper place, so we can resume parsing. */
-
- ret = scan_for_signature(a);
- if(ret == ARCHIVE_FATAL) {
- return ARCHIVE_EOF;
- } else {
- rar->vol.expected_vol_no = rar->main.vol_no + 1;
- return ARCHIVE_OK;
- }
- } else {
- return ARCHIVE_EOF;
- }
- case HEAD_MARK:
- return ARCHIVE_EOF;
- default:
- if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Header type error");
- return ARCHIVE_FATAL;
- } else {
- /* If the block is marked as 'skip if unknown', do as the flag
- * says: skip the block instead on failing on it. */
- return ARCHIVE_RETRY;
- }
- }
+ struct rar5* rar = get_context(a);
+ uint32_t hdr_crc, computed_crc;
+ size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
+ size_t header_id = 0;
+ size_t header_flags = 0;
+ const uint8_t* p;
+ int ret;
+
+ /* Skip any unprocessed data for this file. */
+ ret = skip_unprocessed_bytes(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ /* Read the expected CRC32 checksum. */
+ if(!read_u32(a, &hdr_crc)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Read header size. */
+ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Sanity check, maximum header size for RAR5 is 2MB. */
+ if(raw_hdr_size > (2 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Base block header is too large");
+
+ return ARCHIVE_FATAL;
+ }
+
+ hdr_size = raw_hdr_size + hdr_size_len;
+
+ /* Read the whole header data into memory, maximum memory use here is
+ * 2MB. */
+ if(!read_ahead(a, hdr_size, &p)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Verify the CRC32 of the header data. */
+ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
+ if(computed_crc != hdr_crc) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* If the checksum is OK, we proceed with parsing. */
+ if(ARCHIVE_OK != consume(a, hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &header_id, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var_sized(a, &header_flags, NULL))
+ return ARCHIVE_EOF;
+
+ rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+ rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+ rar->generic.size = (int)hdr_size;
+ rar->generic.last_header_id = (int)header_id;
+ rar->main.endarc = 0;
+
+ /* Those are possible header ids in RARv5. */
+ enum HEADER_TYPE {
+ HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
+ HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+ HEAD_UNKNOWN = 0xff,
+ };
+
+ switch(header_id) {
+ case HEAD_MAIN:
+ ret = process_head_main(a, rar, entry, header_flags);
+
+ /* Main header doesn't have any files in it, so it's
+ * pointless to return to the caller. Retry to next
+ * header, which should be HEAD_FILE/HEAD_SERVICE. */
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+
+ return ret;
+ case HEAD_SERVICE:
+ ret = process_head_service(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_FILE:
+ ret = process_head_file(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_CRYPT:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encryption is not supported");
+ return ARCHIVE_FATAL;
+ case HEAD_ENDARC:
+ rar->main.endarc = 1;
+
+ /* After encountering an end of file marker, we need
+ * to take into consideration if this archive is
+ * continued in another file (i.e. is it part01.rar:
+ * is there a part02.rar?) */
+ if(rar->main.volume) {
+ /* In case there is part02.rar, position the
+ * read pointer in a proper place, so we can
+ * resume parsing. */
+ ret = scan_for_signature(a);
+ if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_EOF;
+ } else {
+ if(rar->vol.expected_vol_no ==
+ UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header error");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->vol.expected_vol_no =
+ rar->main.vol_no + 1;
+ return ARCHIVE_OK;
+ }
+ } else {
+ return ARCHIVE_EOF;
+ }
+ case HEAD_MARK:
+ return ARCHIVE_EOF;
+ default:
+ if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header type error");
+ return ARCHIVE_FATAL;
+ } else {
+ /* If the block is marked as 'skip if unknown',
+ * do as the flag says: skip the block
+ * instead on failing on it. */
+ return ARCHIVE_RETRY;
+ }
+ }
#if !defined WIN32
- // Not reached.
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Internal unpacker error");
- return ARCHIVE_FATAL;
+ // Not reached.
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal unpacker error");
+ return ARCHIVE_FATAL;
#endif
}
static int skip_base_block(struct archive_read* a) {
- int ret;
- struct rar5* rar = get_context(a);
+ int ret;
+ struct rar5* rar = get_context(a);
- /* Create a new local archive_entry structure that will be operated on
- * by header reader; operations on this archive_entry will be discarded.
- */
- struct archive_entry* entry = archive_entry_new();
- ret = process_base_block(a, entry);
+ /* 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);
+ /* Discard operations on this archive_entry structure. */
+ archive_entry_free(entry);
+ if(ret == ARCHIVE_FATAL)
+ return ret;
- if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
- return ARCHIVE_OK;
+ if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
+ return ARCHIVE_OK;
- if(ret == ARCHIVE_OK)
- return ARCHIVE_RETRY;
- else
- return ret;
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+ else
+ return ret;
}
static int rar5_read_header(struct archive_read *a,
- struct archive_entry *entry)
+ struct archive_entry *entry)
{
- struct rar5* rar = get_context(a);
- int ret;
+ struct rar5* rar = get_context(a);
+ int ret;
- if(rar->header_initialized == 0) {
- init_header(a);
- rar->header_initialized = 1;
- }
+ if(rar->header_initialized == 0) {
+ init_header(a);
+ rar->header_initialized = 1;
+ }
- if(rar->skipped_magic == 0) {
- if(ARCHIVE_OK != consume(a, rar5_signature_size)) {
- return ARCHIVE_EOF;
- }
+ if(rar->skipped_magic == 0) {
+ if(ARCHIVE_OK != consume(a, rar5_signature_size)) {
+ return ARCHIVE_EOF;
+ }
- rar->skipped_magic = 1;
- }
+ rar->skipped_magic = 1;
+ }
- do {
- ret = process_base_block(a, entry);
- } while(ret == ARCHIVE_RETRY ||
- (rar->main.endarc > 0 && ret == ARCHIVE_OK));
+ do {
+ ret = process_base_block(a, entry);
+ } while(ret == ARCHIVE_RETRY ||
+ (rar->main.endarc > 0 && ret == ARCHIVE_OK));
- return ret;
+ return ret;
}
static void init_unpack(struct rar5* rar) {
- rar->file.calculated_crc32 = 0;
- if (rar->cstate.window_size)
- rar->cstate.window_mask = rar->cstate.window_size - 1;
- else
- rar->cstate.window_mask = 0;
-
- free(rar->cstate.window_buf);
+ rar->file.calculated_crc32 = 0;
+ if (rar->cstate.window_size)
+ rar->cstate.window_mask = rar->cstate.window_size - 1;
+ else
+ rar->cstate.window_mask = 0;
- free(rar->cstate.filtered_buf);
+ free(rar->cstate.window_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);
+ if(rar->cstate.window_size > 0) {
+ rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
+ rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
+ } else {
+ rar->cstate.window_buf = NULL;
+ rar->cstate.filtered_buf = NULL;
+ }
- rar->cstate.write_ptr = 0;
- rar->cstate.last_write_ptr = 0;
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
- memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
- memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
- memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
- memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
- memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
+ memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
+ memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
+ memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
+ memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
+ memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
}
static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
int verify_crc;
- if(rar->skip_mode) {
+ if(rar->skip_mode) {
#if defined CHECK_CRC_ON_SOLID_SKIP
- verify_crc = 1;
+ verify_crc = 1;
#else
- verify_crc = 0;
+ verify_crc = 0;
#endif
- } else
- verify_crc = 1;
-
- if(verify_crc) {
- /* Don't update CRC32 if the file doesn't have the `stored_crc32` info
- filled in. */
- if(rar->file.stored_crc32 > 0) {
- rar->file.calculated_crc32 =
- crc32(rar->file.calculated_crc32, p, to_read);
- }
-
- /* Check if the file uses an optional BLAKE2sp checksum algorithm. */
- if(rar->file.has_blake2 > 0) {
- /* Return value of the `update` function is always 0, so we can
- * explicitly ignore it here. */
- (void) blake2sp_update(&rar->file.b2state, p, to_read);
- }
- }
+ } else
+ verify_crc = 1;
+
+ if(verify_crc) {
+ /* Don't update CRC32 if the file doesn't have the
+ * `stored_crc32` info filled in. */
+ if(rar->file.stored_crc32 > 0) {
+ rar->file.calculated_crc32 =
+ crc32(rar->file.calculated_crc32, p, to_read);
+ }
+
+ /* Check if the file uses an optional BLAKE2sp checksum
+ * algorithm. */
+ if(rar->file.has_blake2 > 0) {
+ /* Return value of the `update` function is always 0,
+ * so we can explicitly ignore it here. */
+ (void) blake2sp_update(&rar->file.b2state, p, to_read);
+ }
+ }
}
static int create_decode_tables(uint8_t* bit_length,
- struct decode_table* table,
- int size)
+ struct decode_table* table, int size)
{
- int code, upper_limit = 0, i, lc[16];
- uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
- ssize_t cur_len, quick_data_size;
+ int code, upper_limit = 0, i, lc[16];
+ uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
+ ssize_t cur_len, quick_data_size;
- memset(&lc, 0, sizeof(lc));
- memset(table->decode_num, 0, sizeof(table->decode_num));
- table->size = size;
- table->quick_bits = size == HUFF_NC ? 10 : 7;
+ memset(&lc, 0, sizeof(lc));
+ memset(table->decode_num, 0, sizeof(table->decode_num));
+ table->size = size;
+ table->quick_bits = size == HUFF_NC ? 10 : 7;
- for(i = 0; i < size; i++) {
- lc[bit_length[i] & 15]++;
- }
+ for(i = 0; i < size; i++) {
+ lc[bit_length[i] & 15]++;
+ }
- lc[0] = 0;
- table->decode_pos[0] = 0;
- table->decode_len[0] = 0;
+ lc[0] = 0;
+ table->decode_pos[0] = 0;
+ table->decode_len[0] = 0;
- for(i = 1; i < 16; i++) {
- upper_limit += lc[i];
+ for(i = 1; i < 16; i++) {
+ upper_limit += lc[i];
- table->decode_len[i] = upper_limit << (16 - i);
- table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
+ table->decode_len[i] = upper_limit << (16 - i);
+ table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
- upper_limit <<= 1;
- }
+ upper_limit <<= 1;
+ }
- memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
+ memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
- for(i = 0; i < size; i++) {
- uint8_t clen = bit_length[i] & 15;
- if(clen > 0) {
- int last_pos = decode_pos_clone[clen];
- table->decode_num[last_pos] = i;
- decode_pos_clone[clen]++;
- }
- }
+ for(i = 0; i < size; i++) {
+ uint8_t clen = bit_length[i] & 15;
+ if(clen > 0) {
+ int last_pos = decode_pos_clone[clen];
+ table->decode_num[last_pos] = i;
+ decode_pos_clone[clen]++;
+ }
+ }
- quick_data_size = (int64_t)1 << table->quick_bits;
- cur_len = 1;
- for(code = 0; code < quick_data_size; code++) {
- int bit_field = code << (16 - table->quick_bits);
- int dist, pos;
+ quick_data_size = (int64_t)1 << table->quick_bits;
+ cur_len = 1;
+ for(code = 0; code < quick_data_size; code++) {
+ int bit_field = code << (16 - table->quick_bits);
+ int dist, pos;
- while(cur_len < rar5_countof(table->decode_len) &&
- bit_field >= table->decode_len[cur_len]) {
- cur_len++;
- }
+ while(cur_len < rar5_countof(table->decode_len) &&
+ bit_field >= table->decode_len[cur_len]) {
+ cur_len++;
+ }
- table->quick_len[code] = (uint8_t) cur_len;
+ table->quick_len[code] = (uint8_t) cur_len;
- dist = bit_field - table->decode_len[cur_len - 1];
- dist >>= (16 - cur_len);
+ dist = bit_field - table->decode_len[cur_len - 1];
+ dist >>= (16 - cur_len);
- pos = table->decode_pos[cur_len & 15] + dist;
- if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
- table->quick_num[code] = table->decode_num[pos];
- } else {
- table->quick_num[code] = 0;
- }
- }
+ pos = table->decode_pos[cur_len & 15] + dist;
+ if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
+ table->quick_num[code] = table->decode_num[pos];
+ } else {
+ table->quick_num[code] = 0;
+ }
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int decode_number(struct archive_read* a, struct decode_table* table,
- const uint8_t* p, uint16_t* num)
+ const uint8_t* p, uint16_t* num)
{
- int i, bits, dist;
- uint16_t bitfield;
- uint32_t pos;
- struct rar5* rar = get_context(a);
+ int i, bits, dist;
+ uint16_t bitfield;
+ uint32_t pos;
+ struct rar5* rar = get_context(a);
- if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
- return ARCHIVE_EOF;
- }
+ if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
+ return ARCHIVE_EOF;
+ }
- bitfield &= 0xfffe;
+ bitfield &= 0xfffe;
- if(bitfield < table->decode_len[table->quick_bits]) {
- int code = bitfield >> (16 - table->quick_bits);
- skip_bits(rar, table->quick_len[code]);
- *num = table->quick_num[code];
- return ARCHIVE_OK;
- }
+ if(bitfield < table->decode_len[table->quick_bits]) {
+ int code = bitfield >> (16 - table->quick_bits);
+ skip_bits(rar, table->quick_len[code]);
+ *num = table->quick_num[code];
+ return ARCHIVE_OK;
+ }
- bits = 15;
+ bits = 15;
- for(i = table->quick_bits + 1; i < 15; i++) {
- if(bitfield < table->decode_len[i]) {
- bits = i;
- break;
- }
- }
+ for(i = table->quick_bits + 1; i < 15; i++) {
+ if(bitfield < table->decode_len[i]) {
+ bits = i;
+ break;
+ }
+ }
- skip_bits(rar, bits);
+ skip_bits(rar, bits);
- dist = bitfield - table->decode_len[bits - 1];
- dist >>= (16 - bits);
- pos = table->decode_pos[bits] + dist;
+ dist = bitfield - table->decode_len[bits - 1];
+ dist >>= (16 - bits);
+ pos = table->decode_pos[bits] + dist;
- if(pos >= table->size)
- pos = 0;
+ if(pos >= table->size)
+ pos = 0;
- *num = table->decode_num[pos];
- return ARCHIVE_OK;
+ *num = table->decode_num[pos];
+ return ARCHIVE_OK;
}
/* Reads and parses Huffman tables from the beginning of the block. */
static int parse_tables(struct archive_read* a, struct rar5* rar,
- const uint8_t* p)
+ const uint8_t* p)
{
- int ret, value, i, w, idx = 0;
- uint8_t bit_length[HUFF_BC],
- table[HUFF_TABLE_SIZE],
- nibble_mask = 0xF0,
- nibble_shift = 4;
-
- enum { ESCAPE = 15 };
-
- /* The data for table generation is compressed using a simple RLE-like
- * algorithm when storing zeroes, so we need to unpack it first. */
- for(w = 0, i = 0; w < HUFF_BC;) {
- value = (p[i] & nibble_mask) >> nibble_shift;
-
- if(nibble_mask == 0x0F)
- ++i;
-
- nibble_mask ^= 0xFF;
- nibble_shift ^= 4;
-
- /* Values smaller than 15 is data, so we write it directly. Value 15
- * is a flag telling us that we need to unpack more bytes. */
- if(value == ESCAPE) {
- value = (p[i] & nibble_mask) >> nibble_shift;
- if(nibble_mask == 0x0F)
- ++i;
- nibble_mask ^= 0xFF;
- nibble_shift ^= 4;
-
- if(value == 0) {
- /* We sometimes need to write the actual value of 15, so this
- * case handles that. */
- bit_length[w++] = ESCAPE;
- } else {
- int k;
-
- /* Fill zeroes. */
- for(k = 0; k < value + 2; k++) {
- bit_length[w++] = 0;
- }
- }
- } else {
- bit_length[w++] = value;
- }
- }
-
- rar->bits.in_addr = i;
- rar->bits.bit_addr = nibble_shift ^ 4;
-
- ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Decoding huffman tables failed");
- return ARCHIVE_FATAL;
- }
-
- for(i = 0; i < HUFF_TABLE_SIZE;) {
- uint16_t num;
-
- ret = decode_number(a, &rar->cstate.bd, p, &num);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Decoding huffman tables failed");
- return ARCHIVE_FATAL;
- }
-
- if(num < 16) {
- /* 0..15: store directly */
- table[i] = (uint8_t) num;
- i++;
- continue;
- }
-
- if(num < 18) {
- /* 16..17: repeat previous code */
- uint16_t n;
- if(ARCHIVE_OK != read_bits_16(rar, p, &n))
- return ARCHIVE_EOF;
-
- if(num == 16) {
- n >>= 13;
- n += 3;
- skip_bits(rar, 3);
- } else {
- n >>= 9;
- n += 11;
- skip_bits(rar, 7);
- }
-
- if(i > 0) {
- while(n-- > 0 && i < HUFF_TABLE_SIZE) {
- table[i] = table[i - 1];
- i++;
- }
- } else {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unexpected error when decoding huffman tables");
- return ARCHIVE_FATAL;
- }
-
- continue;
- }
-
- /* other codes: fill with zeroes `n` times */
- uint16_t n;
- if(ARCHIVE_OK != read_bits_16(rar, p, &n))
- return ARCHIVE_EOF;
-
- if(num == 18) {
- n >>= 13;
- n += 3;
- skip_bits(rar, 3);
- } else {
- n >>= 9;
- n += 11;
- skip_bits(rar, 7);
- }
-
- while(n-- > 0 && i < HUFF_TABLE_SIZE)
- table[i++] = 0;
- }
-
- ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to create literal table");
- return ARCHIVE_FATAL;
- }
-
- idx += HUFF_NC;
-
- ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to create distance table");
- return ARCHIVE_FATAL;
- }
-
- idx += HUFF_DC;
-
- ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to create lower bits of distances table");
- return ARCHIVE_FATAL;
- }
-
- idx += HUFF_LDC;
-
- ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
- if(ret != ARCHIVE_OK) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Failed to create repeating distances table");
- return ARCHIVE_FATAL;
- }
-
- return ARCHIVE_OK;
+ int ret, value, i, w, idx = 0;
+ uint8_t bit_length[HUFF_BC],
+ table[HUFF_TABLE_SIZE],
+ nibble_mask = 0xF0,
+ nibble_shift = 4;
+
+ enum { ESCAPE = 15 };
+
+ /* The data for table generation is compressed using a simple RLE-like
+ * algorithm when storing zeroes, so we need to unpack it first. */
+ for(w = 0, i = 0; w < HUFF_BC;) {
+ if(i >= rar->cstate.cur_block_size) {
+ /* Truncated data, can't continue. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated data in huffman tables");
+ return ARCHIVE_FATAL;
+ }
+
+ value = (p[i] & nibble_mask) >> nibble_shift;
+
+ if(nibble_mask == 0x0F)
+ ++i;
+
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ /* Values smaller than 15 is data, so we write it directly.
+ * Value 15 is a flag telling us that we need to unpack more
+ * bytes. */
+ if(value == ESCAPE) {
+ value = (p[i] & nibble_mask) >> nibble_shift;
+ if(nibble_mask == 0x0F)
+ ++i;
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ if(value == 0) {
+ /* We sometimes need to write the actual value
+ * of 15, so this case handles that. */
+ bit_length[w++] = ESCAPE;
+ } else {
+ int k;
+
+ /* Fill zeroes. */
+ for(k = 0; (k < value + 2) && (w < HUFF_BC);
+ k++) {
+ bit_length[w++] = 0;
+ }
+ }
+ } else {
+ bit_length[w++] = value;
+ }
+ }
+
+ rar->bits.in_addr = i;
+ rar->bits.bit_addr = nibble_shift ^ 4;
+
+ ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < HUFF_TABLE_SIZE;) {
+ uint16_t num;
+
+ if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {
+ /* Truncated data, can't continue. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated data in huffman tables (#2)");
+ return ARCHIVE_FATAL;
+ }
+
+ ret = decode_number(a, &rar->cstate.bd, p, &num);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ if(num < 16) {
+ /* 0..15: store directly */
+ table[i] = (uint8_t) num;
+ i++;
+ continue;
+ }
+
+ if(num < 18) {
+ /* 16..17: repeat previous code */
+ uint16_t n;
+ if(ARCHIVE_OK != read_bits_16(rar, p, &n))
+ return ARCHIVE_EOF;
+
+ if(num == 16) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ if(i > 0) {
+ while(n-- > 0 && i < HUFF_TABLE_SIZE) {
+ table[i] = table[i - 1];
+ i++;
+ }
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unexpected error when decoding "
+ "huffman tables");
+ return ARCHIVE_FATAL;
+ }
+
+ continue;
+ }
+
+ /* other codes: fill with zeroes `n` times */
+ uint16_t n;
+ if(ARCHIVE_OK != read_bits_16(rar, p, &n))
+ return ARCHIVE_EOF;
+
+ if(num == 18) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ while(n-- > 0 && i < HUFF_TABLE_SIZE)
+ table[i++] = 0;
+ }
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create literal table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_NC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create distance table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_DC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create lower bits of distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_LDC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create repeating distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
}
/* Parses the block header, verifies its CRC byte, and saves the header
* fields inside the `hdr` pointer. */
static int parse_block_header(struct archive_read* a, const uint8_t* p,
- ssize_t* block_size, struct compressed_block_header* hdr)
+ ssize_t* block_size, struct compressed_block_header* hdr)
{
- memcpy(hdr, p, sizeof(struct compressed_block_header));
-
- if(bf_byte_count(hdr) > 2) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported block header size (was %d, max is 2)",
- bf_byte_count(hdr));
- return ARCHIVE_FATAL;
- }
-
- /* This should probably use bit reader interface in order to be more
- * future-proof. */
- *block_size = 0;
- switch(bf_byte_count(hdr)) {
- /* 1-byte block size */
- case 0:
- *block_size = *(const uint8_t*) &p[2];
- break;
-
- /* 2-byte block size */
- case 1:
- *block_size = archive_le16dec(&p[2]);
- break;
-
- /* 3-byte block size */
- case 2:
- *block_size = archive_le32dec(&p[2]);
- *block_size &= 0x00FFFFFF;
- break;
-
- /* Other block sizes are not supported. This case is not reached,
- * because we have an 'if' guard before the switch that makes sure
- * of it. */
- default:
- return ARCHIVE_FATAL;
- }
-
- /* Verify the block header checksum. 0x5A is a magic value and is always
- * constant. */
- uint8_t calculated_cksum = 0x5A
- ^ (uint8_t) hdr->block_flags_u8
- ^ (uint8_t) *block_size
- ^ (uint8_t) (*block_size >> 8)
- ^ (uint8_t) (*block_size >> 16);
-
- if(calculated_cksum != hdr->block_cksum) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Block checksum error: got 0x%02x, expected 0x%02x",
- hdr->block_cksum, calculated_cksum);
-
- return ARCHIVE_FATAL;
- }
-
- return ARCHIVE_OK;
+ memcpy(hdr, p, sizeof(struct compressed_block_header));
+
+ if(bf_byte_count(hdr) > 2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported block header size (was %d, max is 2)",
+ bf_byte_count(hdr));
+ return ARCHIVE_FATAL;
+ }
+
+ /* This should probably use bit reader interface in order to be more
+ * future-proof. */
+ *block_size = 0;
+ switch(bf_byte_count(hdr)) {
+ /* 1-byte block size */
+ case 0:
+ *block_size = *(const uint8_t*) &p[2];
+ break;
+
+ /* 2-byte block size */
+ case 1:
+ *block_size = archive_le16dec(&p[2]);
+ break;
+
+ /* 3-byte block size */
+ case 2:
+ *block_size = archive_le32dec(&p[2]);
+ *block_size &= 0x00FFFFFF;
+ break;
+
+ /* Other block sizes are not supported. This case is not
+ * reached, because we have an 'if' guard before the switch
+ * that makes sure of it. */
+ default:
+ return ARCHIVE_FATAL;
+ }
+
+ /* Verify the block header checksum. 0x5A is a magic value and is
+ * always * constant. */
+ uint8_t calculated_cksum = 0x5A
+ ^ (uint8_t) hdr->block_flags_u8
+ ^ (uint8_t) *block_size
+ ^ (uint8_t) (*block_size >> 8)
+ ^ (uint8_t) (*block_size >> 16);
+
+ if(calculated_cksum != hdr->block_cksum) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Block checksum error: got 0x%x, expected 0x%x",
+ hdr->block_cksum, calculated_cksum);
+
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
}
/* Convenience function used during filter processing. */
static int parse_filter_data(struct rar5* rar, const uint8_t* p,
- uint32_t* filter_data)
+ uint32_t* filter_data)
{
- int i, bytes;
- uint32_t data = 0;
+ int i, bytes;
+ uint32_t data = 0;
- if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
- return ARCHIVE_EOF;
+ if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
+ return ARCHIVE_EOF;
- bytes++;
+ bytes++;
- for(i = 0; i < bytes; i++) {
- uint16_t byte;
+ for(i = 0; i < bytes; i++) {
+ uint16_t byte;
- if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
- return ARCHIVE_EOF;
- }
+ if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
+ return ARCHIVE_EOF;
+ }
- data += (byte >> 8) << (i * 8);
- skip_bits(rar, 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);
+ }
- *filter_data = data;
- return ARCHIVE_OK;
+ *filter_data = data;
+ return ARCHIVE_OK;
}
/* Function is used during sanity checking. */
static int is_valid_filter_block_start(struct rar5* rar,
- uint32_t start)
+ uint32_t start)
{
- const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
- const int64_t last_bs = rar->cstate.last_block_start;
- const ssize_t last_bl = rar->cstate.last_block_length;
+ const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
+ const int64_t last_bs = rar->cstate.last_block_start;
+ const ssize_t last_bl = rar->cstate.last_block_length;
- if(last_bs == 0 || last_bl == 0) {
- /* We didn't have any filters yet, so accept this offset. */
- return 1;
- }
+ if(last_bs == 0 || last_bl == 0) {
+ /* We didn't have any filters yet, so accept this offset. */
+ return 1;
+ }
- if(block_start >= last_bs + last_bl) {
- /* Current offset is bigger than last block's end offset, so
- * accept current offset. */
- return 1;
- }
+ if(block_start >= last_bs + last_bl) {
+ /* Current offset is bigger than last block's end offset, so
+ * accept current offset. */
+ return 1;
+ }
- /* Any other case is not a normal situation and we should fail. */
- return 0;
+ /* Any other case is not a normal situation and we should fail. */
+ return 0;
}
/* The function will create a new filter, read its parameters from the input
* stream and add it to the filter collection. */
static int parse_filter(struct archive_read* ar, const uint8_t* p) {
- uint32_t block_start, block_length;
- uint16_t filter_type;
- struct rar5* rar = get_context(ar);
+ uint32_t block_start, block_length;
+ uint16_t filter_type;
+ struct rar5* rar = get_context(ar);
- /* Read the parameters from the input stream. */
- if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
- return ARCHIVE_EOF;
+ /* Read the parameters from the input stream. */
+ if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
+ return ARCHIVE_EOF;
- if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
- return ARCHIVE_EOF;
+ if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
+ return ARCHIVE_EOF;
- if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
- return ARCHIVE_EOF;
+ if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
+ return ARCHIVE_EOF;
- filter_type >>= 13;
- skip_bits(rar, 3);
+ filter_type >>= 13;
+ skip_bits(rar, 3);
- /* Perform some sanity checks on this filter parameters. Note that we
- * allow only DELTA, E8/E9 and ARM filters here, because rest of filters
- * are not used in RARv5. */
+ /* Perform some sanity checks on this filter parameters. Note that we
+ * allow only DELTA, E8/E9 and ARM filters here, because rest of
+ * filters are not used in RARv5. */
- if(block_length < 4 ||
- block_length > 0x400000 ||
- filter_type > FILTER_ARM ||
- !is_valid_filter_block_start(rar, block_start))
- {
- archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid "
- "filter encountered");
- return ARCHIVE_FATAL;
- }
+ if(block_length < 4 ||
+ block_length > 0x400000 ||
+ filter_type > FILTER_ARM ||
+ !is_valid_filter_block_start(rar, block_start))
+ {
+ archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filter encountered");
+ return ARCHIVE_FATAL;
+ }
- /* Allocate a new filter. */
- struct filter_info* filt = add_new_filter(rar);
- if(filt == NULL) {
- archive_set_error(&ar->archive, ENOMEM, "Can't allocate memory for a "
- "filter descriptor.");
- return ARCHIVE_FATAL;
- }
+ /* Allocate a new filter. */
+ struct filter_info* filt = add_new_filter(rar);
+ if(filt == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate memory for a filter descriptor.");
+ return ARCHIVE_FATAL;
+ }
- filt->type = filter_type;
- filt->block_start = rar->cstate.write_ptr + block_start;
- filt->block_length = block_length;
+ filt->type = filter_type;
+ filt->block_start = rar->cstate.write_ptr + block_start;
+ filt->block_length = block_length;
- rar->cstate.last_block_start = filt->block_start;
- rar->cstate.last_block_length = filt->block_length;
+ rar->cstate.last_block_start = filt->block_start;
+ rar->cstate.last_block_length = filt->block_length;
- /* Read some more data in case this is a DELTA filter. Other filter types
- * don't require any additional data over what was already read. */
- if(filter_type == FILTER_DELTA) {
- int channels;
+ /* Read some more data in case this is a DELTA filter. Other filter
+ * types don't require any additional data over what was already
+ * read. */
+ if(filter_type == FILTER_DELTA) {
+ int channels;
- if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
- return ARCHIVE_EOF;
+ if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
+ return ARCHIVE_EOF;
- filt->channels = channels + 1;
- }
+ filt->channels = channels + 1;
+ }
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int decode_code_length(struct rar5* rar, const uint8_t* p,
- uint16_t code)
+ uint16_t code)
{
- int lbits, length = 2;
- if(code < 8) {
- lbits = 0;
- length += code;
- } else {
- lbits = code / 4 - 1;
- length += (4 | (code & 3)) << lbits;
- }
+ int lbits, length = 2;
+ if(code < 8) {
+ lbits = 0;
+ length += code;
+ } else {
+ lbits = code / 4 - 1;
+ length += (4 | (code & 3)) << lbits;
+ }
- if(lbits > 0) {
- int add;
+ if(lbits > 0) {
+ int add;
- if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
- return -1;
+ if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
+ return -1;
- length += add;
- }
+ length += add;
+ }
- return length;
+ return length;
}
static int copy_string(struct archive_read* a, int len, int dist) {
- struct rar5* rar = get_context(a);
- const int cmask = (int)rar->cstate.window_mask;
- const int64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset;
- int i;
+ struct rar5* rar = get_context(a);
+ const uint64_t cmask = rar->cstate.window_mask;
+ const uint64_t write_ptr = rar->cstate.write_ptr +
+ rar->cstate.solid_offset;
+ int i;
- /* The unpacker spends most of the time in this function. It would be
- * a good idea to introduce some optimizations here.
- *
- * Just remember that this loop treats buffers that overlap differently
- * than buffers that do not overlap. This is why a simple memcpy(3) call
- * will not be enough. */
+ if (rar->cstate.window_buf == NULL)
+ return ARCHIVE_FATAL;
- for(i = 0; i < len; i++) {
- const ssize_t write_idx = (write_ptr + i) & cmask;
- const ssize_t read_idx = (write_ptr + i - dist) & cmask;
- rar->cstate.window_buf[write_idx] = rar->cstate.window_buf[read_idx];
- }
+ /* The unpacker spends most of the time in this function. It would be
+ * a good idea to introduce some optimizations here.
+ *
+ * Just remember that this loop treats buffers that overlap differently
+ * than buffers that do not overlap. This is why a simple memcpy(3)
+ * call will not be enough. */
- rar->cstate.write_ptr += len;
- return ARCHIVE_OK;
+ for(i = 0; i < len; i++) {
+ const ssize_t write_idx = (write_ptr + i) & cmask;
+ const ssize_t read_idx = (write_ptr + i - dist) & cmask;
+ rar->cstate.window_buf[write_idx] =
+ rar->cstate.window_buf[read_idx];
+ }
+
+ rar->cstate.write_ptr += len;
+ return ARCHIVE_OK;
}
static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
- struct rar5* rar = get_context(a);
- uint16_t num;
- int ret;
-
- const int cmask = (int)rar->cstate.window_mask;
- const struct compressed_block_header* hdr = &rar->last_block_hdr;
- const uint8_t bit_size = 1 + bf_bit_size(hdr);
-
- while(1) {
- if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
- (rar->cstate.window_size >> 1)) {
-
- /* Don't allow growing data by more than half of the window size
- * at a time. In such case, break the loop; next call to this
- * function will continue processing from this moment. */
-
- break;
- }
-
- if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
- (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
- rar->bits.bit_addr >= bit_size))
- {
- /* If the program counter is here, it means the function has
- * finished processing the block. */
- rar->cstate.block_parsing_finished = 1;
- break;
- }
-
- /* Decode the next literal. */
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
- return ARCHIVE_EOF;
- }
-
- /* Num holds a decompression literal, or 'command code'.
- *
- * - Values lower than 256 are just bytes. Those codes can be stored
- * in the output buffer directly.
- *
- * - Code 256 defines a new filter, which is later used to transform
- * the data block accordingly to the filter type. The data block
- * needs to be fully uncompressed first.
- *
- * - Code bigger than 257 and smaller than 262 define a repetition
- * pattern that should be copied from an already uncompressed chunk
- * of data.
- */
-
- if(num < 256) {
- /* Directly store the byte. */
-
- int64_t write_idx = rar->cstate.solid_offset +
- rar->cstate.write_ptr++;
-
- rar->cstate.window_buf[write_idx & cmask] = (uint8_t) num;
- continue;
- } else if(num >= 262) {
- uint16_t dist_slot;
- int len = decode_code_length(rar, p, num - 262),
- dbits,
- dist = 1;
-
- if(len == -1) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Failed to decode the code length");
-
- return ARCHIVE_FATAL;
- }
-
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, &dist_slot))
- {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Failed to decode the distance slot");
-
- return ARCHIVE_FATAL;
- }
-
- if(dist_slot < 4) {
- dbits = 0;
- dist += dist_slot;
- } else {
- dbits = dist_slot / 2 - 1;
- dist += (2 | (dist_slot & 1)) << dbits;
- }
-
- if(dbits > 0) {
- if(dbits >= 4) {
- uint32_t add = 0;
- uint16_t low_dist;
-
- if(dbits > 4) {
- if(ARCHIVE_OK != read_bits_32(rar, p, &add)) {
- /* Return EOF if we can't read more data. */
- return ARCHIVE_EOF;
- }
-
- skip_bits(rar, dbits - 4);
- add = (add >> (36 - dbits)) << 4;
- dist += add;
- }
-
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.ldd, p,
- &low_dist))
- {
- archive_set_error(&a->archive,
- ARCHIVE_ERRNO_PROGRAMMER,
- "Failed to decode the distance slot");
-
- return ARCHIVE_FATAL;
- }
-
- dist += low_dist;
- } else {
- /* dbits is one of [0,1,2,3] */
- int add;
-
- if(ARCHIVE_OK != read_consume_bits(rar, p, dbits, &add)) {
- /* Return EOF if we can't read more data. */
- return ARCHIVE_EOF;
- }
-
- dist += add;
- }
- }
-
- if(dist > 0x100) {
- len++;
-
- if(dist > 0x2000) {
- len++;
-
- if(dist > 0x40000) {
- len++;
- }
- }
- }
-
- dist_cache_push(rar, dist);
- rar->cstate.last_len = len;
-
- if(ARCHIVE_OK != copy_string(a, len, dist))
- return ARCHIVE_FATAL;
-
- continue;
- } else if(num == 256) {
- /* Create a filter. */
- ret = parse_filter(a, p);
- if(ret != ARCHIVE_OK)
- return ret;
-
- continue;
- } else if(num == 257) {
- if(rar->cstate.last_len != 0) {
- if(ARCHIVE_OK != copy_string(a, rar->cstate.last_len,
- rar->cstate.dist_cache[0]))
- {
- return ARCHIVE_FATAL;
- }
- }
-
- continue;
- } else if(num < 262) {
- const int idx = num - 258;
- const int dist = dist_cache_touch(rar, idx);
-
- uint16_t len_slot;
- int len;
-
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) {
- return ARCHIVE_FATAL;
- }
-
- len = decode_code_length(rar, p, len_slot);
- rar->cstate.last_len = len;
-
- if(ARCHIVE_OK != copy_string(a, len, dist))
- return ARCHIVE_FATAL;
-
- continue;
- }
-
- /* The program counter shouldn't reach here. */
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Unsupported block code: 0x%02x", num);
-
- return ARCHIVE_FATAL;
- }
-
- return ARCHIVE_OK;
+ struct rar5* rar = get_context(a);
+ uint16_t num;
+ int ret;
+
+ const uint64_t cmask = rar->cstate.window_mask;
+ const struct compressed_block_header* hdr = &rar->last_block_hdr;
+ const uint8_t bit_size = 1 + bf_bit_size(hdr);
+
+ while(1) {
+ if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
+ (rar->cstate.window_size >> 1)) {
+ /* Don't allow growing data by more than half of the
+ * window size at a time. In such case, break the loop;
+ * next call to this function will continue processing
+ * from this moment. */
+ break;
+ }
+
+ if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
+ (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
+ rar->bits.bit_addr >= bit_size))
+ {
+ /* If the program counter is here, it means the
+ * function has finished processing the block. */
+ rar->cstate.block_parsing_finished = 1;
+ break;
+ }
+
+ /* Decode the next literal. */
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Num holds a decompression literal, or 'command code'.
+ *
+ * - Values lower than 256 are just bytes. Those codes
+ * can be stored in the output buffer directly.
+ *
+ * - Code 256 defines a new filter, which is later used to
+ * ransform the data block accordingly to the filter type.
+ * The data block needs to be fully uncompressed first.
+ *
+ * - Code bigger than 257 and smaller than 262 define
+ * a repetition pattern that should be copied from
+ * an already uncompressed chunk of data.
+ */
+
+ if(num < 256) {
+ /* Directly store the byte. */
+ int64_t write_idx = rar->cstate.solid_offset +
+ rar->cstate.write_ptr++;
+
+ rar->cstate.window_buf[write_idx & cmask] =
+ (uint8_t) num;
+ continue;
+ } else if(num >= 262) {
+ uint16_t dist_slot;
+ int len = decode_code_length(rar, p, num - 262),
+ dbits,
+ dist = 1;
+
+ if(len == -1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the code length");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
+ &dist_slot))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist_slot < 4) {
+ dbits = 0;
+ dist += dist_slot;
+ } else {
+ dbits = dist_slot / 2 - 1;
+
+ /* Cast to uint32_t will make sure the shift
+ * left operation won't produce undefined
+ * result. Then, the uint32_t type will
+ * be implicitly casted to int. */
+ dist += (uint32_t) (2 |
+ (dist_slot & 1)) << dbits;
+ }
+
+ if(dbits > 0) {
+ if(dbits >= 4) {
+ uint32_t add = 0;
+ uint16_t low_dist;
+
+ if(dbits > 4) {
+ if(ARCHIVE_OK != read_bits_32(
+ rar, p, &add)) {
+ /* Return EOF if we
+ * can't read more
+ * data. */
+ return ARCHIVE_EOF;
+ }
+
+ skip_bits(rar, dbits - 4);
+ add = (add >> (
+ 36 - dbits)) << 4;
+ dist += add;
+ }
+
+ if(ARCHIVE_OK != decode_number(a,
+ &rar->cstate.ldd, p, &low_dist))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the "
+ "distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist >= INT_MAX - low_dist - 1) {
+ /* This only happens in
+ * invalid archives. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Distance pointer "
+ "overflow");
+ return ARCHIVE_FATAL;
+ }
+
+ dist += low_dist;
+ } else {
+ /* dbits is one of [0,1,2,3] */
+ int add;
+
+ if(ARCHIVE_OK != read_consume_bits(rar,
+ p, dbits, &add)) {
+ /* Return EOF if we can't read
+ * more data. */
+ return ARCHIVE_EOF;
+ }
+
+ dist += add;
+ }
+ }
+
+ if(dist > 0x100) {
+ len++;
+
+ if(dist > 0x2000) {
+ len++;
+
+ if(dist > 0x40000) {
+ len++;
+ }
+ }
+ }
+
+ dist_cache_push(rar, dist);
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ } else if(num == 256) {
+ /* Create a filter. */
+ ret = parse_filter(a, p);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ continue;
+ } else if(num == 257) {
+ if(rar->cstate.last_len != 0) {
+ if(ARCHIVE_OK != copy_string(a,
+ rar->cstate.last_len,
+ rar->cstate.dist_cache[0]))
+ {
+ return ARCHIVE_FATAL;
+ }
+ }
+
+ continue;
+ } else if(num < 262) {
+ const int idx = num - 258;
+ const int dist = dist_cache_touch(rar, idx);
+
+ uint16_t len_slot;
+ int len;
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
+ &len_slot)) {
+ return ARCHIVE_FATAL;
+ }
+
+ len = decode_code_length(rar, p, len_slot);
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ }
+
+ /* The program counter shouldn't reach here. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported block code: 0x%x", num);
+
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
}
/* Binary search for the RARv5 signature. */
static int scan_for_signature(struct archive_read* a) {
- const uint8_t* p;
- const int chunk_size = 512;
- ssize_t i;
-
- /* If we're here, it means we're on an 'unknown territory' data.
- * There's no indication what kind of data we're reading here. It could be
- * some text comment, any kind of binary data, digital sign, dragons, etc.
- *
- * We want to find a valid RARv5 magic header inside this unknown data. */
-
- /* Is it possible in libarchive to just skip everything until the
- * end of the file? If so, it would be a better approach than the
- * current implementation of this function. */
-
- while(1) {
- if(!read_ahead(a, chunk_size, &p))
- return ARCHIVE_EOF;
-
- for(i = 0; i < chunk_size - rar5_signature_size; i++) {
- if(memcmp(&p[i], rar5_signature, rar5_signature_size) == 0) {
- /* Consume the number of bytes we've used to search for the
- * signature, as well as the number of bytes used by the
- * signature itself. After this we should be standing on a
- * valid base block header. */
- (void) consume(a, i + rar5_signature_size);
- return ARCHIVE_OK;
- }
- }
-
- consume(a, chunk_size);
- }
-
- return ARCHIVE_FATAL;
+ const uint8_t* p;
+ const int chunk_size = 512;
+ ssize_t i;
+
+ /* If we're here, it means we're on an 'unknown territory' data.
+ * There's no indication what kind of data we're reading here.
+ * It could be some text comment, any kind of binary data,
+ * digital sign, dragons, etc.
+ *
+ * We want to find a valid RARv5 magic header inside this unknown
+ * data. */
+
+ /* Is it possible in libarchive to just skip everything until the
+ * end of the file? If so, it would be a better approach than the
+ * current implementation of this function. */
+
+ while(1) {
+ if(!read_ahead(a, chunk_size, &p))
+ return ARCHIVE_EOF;
+
+ for(i = 0; i < chunk_size - rar5_signature_size; i++) {
+ if(memcmp(&p[i], rar5_signature,
+ rar5_signature_size) == 0) {
+ /* Consume the number of bytes we've used to
+ * search for the signature, as well as the
+ * number of bytes used by the signature
+ * itself. After this we should be standing
+ * on a valid base block header. */
+ (void) consume(a, i + rar5_signature_size);
+ return ARCHIVE_OK;
+ }
+ }
+
+ consume(a, chunk_size);
+ }
+
+ return ARCHIVE_FATAL;
}
/* This function will switch the multivolume archive file to another file,
* i.e. from part03 to part 04. */
static int advance_multivolume(struct archive_read* a) {
- int lret;
- struct rar5* rar = get_context(a);
-
- /* A small state machine that will skip unnecessary data, needed to
- * switch from one multivolume to another. Such skipping is needed if
- * we want to be an stream-oriented (instead of file-oriented)
- * unpacker.
- *
- * The state machine starts with `rar->main.endarc` == 0. It also
- * assumes that current stream pointer points to some base block header.
- *
- * The `endarc` field is being set when the base block parsing function
- * encounters the 'end of archive' marker.
- */
-
- while(1) {
- if(rar->main.endarc == 1) {
- rar->main.endarc = 0;
- while(ARCHIVE_RETRY == skip_base_block(a));
- 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);
-
- /* The `skip_base_block` function tells us if we should continue
- * with skipping, or we should stop skipping. We're trying to skip
- * everything up to a base FILE block. */
-
- if(lret != ARCHIVE_RETRY) {
- /* If there was an error during skipping, or we have just
- * skipped a FILE base block... */
-
- if(rar->main.endarc == 0) {
- return lret;
- } else {
- continue;
- }
- }
- }
- }
-
- return ARCHIVE_OK;
+ int lret;
+ struct rar5* rar = get_context(a);
+
+ /* A small state machine that will skip unnecessary data, needed to
+ * switch from one multivolume to another. Such skipping is needed if
+ * we want to be an stream-oriented (instead of file-oriented)
+ * unpacker.
+ *
+ * The state machine starts with `rar->main.endarc` == 0. It also
+ * assumes that current stream pointer points to some base block
+ * header.
+ *
+ * The `endarc` field is being set when the base block parsing
+ * function encounters the 'end of archive' marker.
+ */
+
+ while(1) {
+ if(rar->main.endarc == 1) {
+ int looping = 1;
+
+ rar->main.endarc = 0;
+
+ while(looping) {
+ lret = skip_base_block(a);
+ switch(lret) {
+ case ARCHIVE_RETRY:
+ /* Continue looping. */
+ break;
+ case ARCHIVE_OK:
+ /* Break loop. */
+ looping = 0;
+ break;
+ default:
+ /* Forward any errors to the
+ * caller. */
+ return lret;
+ }
+ }
+
+ break;
+ } else {
+ /* Skip current base block. In order to properly skip
+ * it, we really need to simply parse it and discard
+ * the results. */
+
+ lret = skip_base_block(a);
+ if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
+ return lret;
+
+ /* The `skip_base_block` function tells us if we
+ * should continue with skipping, or we should stop
+ * skipping. We're trying to skip everything up to
+ * a base FILE block. */
+
+ if(lret != ARCHIVE_RETRY) {
+ /* If there was an error during skipping, or we
+ * have just skipped a FILE base block... */
+
+ if(rar->main.endarc == 0) {
+ return lret;
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
}
/* Merges the partial block from the first multivolume archive file, and
@@ -2669,234 +3150,253 @@ static int advance_multivolume(struct archive_read* a) {
* a chunk of memory containing the whole block, and the stream pointer
* is advanced to the next block in the second multivolume archive file. */
static int merge_block(struct archive_read* a, ssize_t block_size,
- const uint8_t** p)
+ const uint8_t** p)
{
- struct rar5* rar = get_context(a);
- ssize_t cur_block_size, partial_offset = 0;
- const uint8_t* lp;
- int ret;
-
- /* Set a flag that we're in the switching mode. */
- rar->cstate.switch_multivolume = 1;
-
- /* Reallocate the memory which will hold the whole block. */
- if(rar->vol.push_buf)
- free((void*) rar->vol.push_buf);
-
- /* Increasing the allocation block by 8 is due to bit reading functions,
- * which are using additional 2 or 4 bytes. Allocating the block size
- * by exact value would make bit reader perform reads from invalid memory
- * block when reading the last byte from the buffer. */
- rar->vol.push_buf = malloc(block_size + 8);
- if(!rar->vol.push_buf) {
- archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a "
- "merge block buffer.");
- return ARCHIVE_FATAL;
- }
-
- /* Valgrind complains if the extension block for bit reader is not
- * initialized, so initialize it. */
- memset(&rar->vol.push_buf[block_size], 0, 8);
-
- /* A single block can span across multiple multivolume archive files,
- * so we use a loop here. This loop will consume enough multivolume
- * archive files until the whole block is read. */
-
- while(1) {
- /* Get the size of current block chunk in this multivolume archive
- * file and read it. */
- cur_block_size =
- rar5_min(rar->file.bytes_remaining, block_size - partial_offset);
-
- if(cur_block_size == 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Encountered block size == 0 during block merge");
- return ARCHIVE_FATAL;
- }
-
- if(!read_ahead(a, cur_block_size, &lp))
- return ARCHIVE_EOF;
-
- /* Sanity check; there should never be a situation where this function
- * reads more data than the block's size. */
- if(partial_offset + cur_block_size > block_size) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Consumed too much data when merging blocks.");
- return ARCHIVE_FATAL;
- }
-
- /* Merge previous block chunk with current block chunk, or create
- * first block chunk if this is our first iteration. */
- memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
-
- /* Advance the stream read pointer by this block chunk size. */
- if(ARCHIVE_OK != consume(a, cur_block_size))
- return ARCHIVE_EOF;
-
- /* Update the pointers. `partial_offset` contains information about
- * the sum of merged block chunks. */
- partial_offset += cur_block_size;
- rar->file.bytes_remaining -= cur_block_size;
-
- /* If `partial_offset` is the same as `block_size`, this means we've
- * merged all block chunks and we have a valid full block. */
- if(partial_offset == block_size) {
- break;
- }
-
- /* If we don't have any bytes to read, this means we should switch
- * to another multivolume archive file. */
- if(rar->file.bytes_remaining == 0) {
- ret = advance_multivolume(a);
- if(ret != ARCHIVE_OK)
- return ret;
- }
- }
-
- *p = rar->vol.push_buf;
-
- /* If we're here, we can resume unpacking by processing the block pointed
- * to by the `*p` memory pointer. */
-
- return ARCHIVE_OK;
+ struct rar5* rar = get_context(a);
+ ssize_t cur_block_size, partial_offset = 0;
+ const uint8_t* lp;
+ int ret;
+
+ if(rar->merge_mode) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Recursive merge is not allowed");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Set a flag that we're in the switching mode. */
+ rar->cstate.switch_multivolume = 1;
+
+ /* Reallocate the memory which will hold the whole block. */
+ if(rar->vol.push_buf)
+ free((void*) rar->vol.push_buf);
+
+ /* Increasing the allocation block by 8 is due to bit reading functions,
+ * which are using additional 2 or 4 bytes. Allocating the block size
+ * by exact value would make bit reader perform reads from invalid
+ * memory block when reading the last byte from the buffer. */
+ rar->vol.push_buf = malloc(block_size + 8);
+ if(!rar->vol.push_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a merge block buffer.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Valgrind complains if the extension block for bit reader is not
+ * initialized, so initialize it. */
+ memset(&rar->vol.push_buf[block_size], 0, 8);
+
+ /* A single block can span across multiple multivolume archive files,
+ * so we use a loop here. This loop will consume enough multivolume
+ * archive files until the whole block is read. */
+
+ while(1) {
+ /* Get the size of current block chunk in this multivolume
+ * archive file and read it. */
+ cur_block_size = rar5_min(rar->file.bytes_remaining,
+ block_size - partial_offset);
+
+ if(cur_block_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encountered block size == 0 during block merge");
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_ahead(a, cur_block_size, &lp))
+ return ARCHIVE_EOF;
+
+ /* Sanity check; there should never be a situation where this
+ * function reads more data than the block's size. */
+ if(partial_offset + cur_block_size > block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Consumed too much data when merging blocks.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Merge previous block chunk with current block chunk,
+ * or create first block chunk if this is our first
+ * iteration. */
+ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
+
+ /* Advance the stream read pointer by this block chunk size. */
+ if(ARCHIVE_OK != consume(a, cur_block_size))
+ return ARCHIVE_EOF;
+
+ /* Update the pointers. `partial_offset` contains information
+ * about the sum of merged block chunks. */
+ partial_offset += cur_block_size;
+ rar->file.bytes_remaining -= cur_block_size;
+
+ /* If `partial_offset` is the same as `block_size`, this means
+ * we've merged all block chunks and we have a valid full
+ * block. */
+ if(partial_offset == block_size) {
+ break;
+ }
+
+ /* If we don't have any bytes to read, this means we should
+ * switch to another multivolume archive file. */
+ if(rar->file.bytes_remaining == 0) {
+ rar->merge_mode++;
+ ret = advance_multivolume(a);
+ rar->merge_mode--;
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ *p = rar->vol.push_buf;
+
+ /* If we're here, we can resume unpacking by processing the block
+ * pointed to by the `*p` memory pointer. */
+
+ return ARCHIVE_OK;
}
static int process_block(struct archive_read* a) {
- const uint8_t* p;
- struct rar5* rar = get_context(a);
- int ret;
-
- /* If we don't have any data to be processed, this most probably means
- * we need to switch to the next volume. */
- if(rar->main.volume && rar->file.bytes_remaining == 0) {
- ret = advance_multivolume(a);
- if(ret != ARCHIVE_OK)
- return ret;
- }
-
- if(rar->cstate.block_parsing_finished) {
- ssize_t block_size;
-
- 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. */
- return ARCHIVE_EOF;
- }
-
- /*
- * Read block_size by parsing block header. Validate the header by
- * calculating CRC byte stored inside the header. Size of the header is
- * not constant (block size can be stored either in 1 or 2 bytes),
- * that's why block size is left out from the `compressed_block_header`
- * structure and returned by `parse_block_header` as the second
- * argument. */
-
- ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr);
- if(ret != ARCHIVE_OK)
- return ret;
-
- /* Skip block header. Next data is huffman tables, if present. */
- ssize_t to_skip = sizeof(struct compressed_block_header) +
- bf_byte_count(&rar->last_block_hdr) + 1;
-
- if(ARCHIVE_OK != consume(a, to_skip))
- return ARCHIVE_EOF;
-
- rar->file.bytes_remaining -= to_skip;
-
- /* The block size gives information about the whole block size, but
- * the block could be stored in split form when using multi-volume
- * archives. In this case, the block size will be bigger than the
- * actual data stored in this file. Remaining part of the data will
- * be in another file. */
-
- ssize_t cur_block_size =
- rar5_min(rar->file.bytes_remaining, block_size);
-
- if(block_size > rar->file.bytes_remaining) {
- /* If current blocks' size is bigger than our data size, this
- * means we have a multivolume archive. In this case, skip
- * all base headers until the end of the file, proceed to next
- * "partXXX.rar" volume, find its signature, skip all headers up
- * to the first FILE base header, and continue from there.
- *
- * Note that `merge_block` will update the `rar` context structure
- * quite extensively. */
-
- ret = merge_block(a, block_size, &p);
- if(ret != ARCHIVE_OK) {
- return ret;
- }
-
- cur_block_size = block_size;
-
- /* Current stream pointer should be now directly *after* the
- * block that spanned through multiple archive files. `p` pointer
- * should have the data of the *whole* block (merged from
- * partial blocks stored in multiple archives files). */
- } else {
- rar->cstate.switch_multivolume = 0;
-
- /* Read the whole block size into memory. This can take up to
- * 8 megabytes of memory in theoretical cases. Might be worth to
- * optimize this and use a standard chunk of 4kb's. */
-
- if(!read_ahead(a, 4 + cur_block_size, &p)) {
- /* Failed to prefetch block data. */
- return ARCHIVE_EOF;
- }
- }
-
- rar->cstate.block_buf = p;
- rar->cstate.cur_block_size = cur_block_size;
-
- rar->bits.in_addr = 0;
- rar->bits.bit_addr = 0;
-
- if(bf_is_table_present(&rar->last_block_hdr)) {
- /* Load Huffman tables. */
- ret = parse_tables(a, rar, p);
- if(ret != ARCHIVE_OK) {
- /* Error during decompression of Huffman tables. */
- return ret;
- }
- }
- } else {
- p = rar->cstate.block_buf;
- }
-
- /* Uncompress the block, or a part of it, depending on how many bytes
- * will be generated by uncompressing the block.
- *
- * In case too many bytes will be generated, calling this function again
- * will resume the uncompression operation. */
- ret = do_uncompress_block(a, p);
- if(ret != ARCHIVE_OK) {
- return ret;
- }
-
- if(rar->cstate.block_parsing_finished &&
- rar->cstate.switch_multivolume == 0 &&
- rar->cstate.cur_block_size > 0)
- {
- /* If we're processing a normal block, consume the whole block. We
- * can do this because we've already read the whole block to memory.
- */
- if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
- return ARCHIVE_FATAL;
-
- rar->file.bytes_remaining -= rar->cstate.cur_block_size;
- } else if(rar->cstate.switch_multivolume) {
- /* Don't consume the block if we're doing multivolume processing.
- * The volume switching function will consume the proper count of
- * bytes instead. */
-
- rar->cstate.switch_multivolume = 0;
- }
-
- return ARCHIVE_OK;
+ const uint8_t* p;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ /* If we don't have any data to be processed, this most probably means
+ * we need to switch to the next volume. */
+ if(rar->main.volume && rar->file.bytes_remaining == 0) {
+ ret = advance_multivolume(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished) {
+ ssize_t block_size;
+
+ /* The header size won't be bigger than 6 bytes. */
+ if(!read_ahead(a, 6, &p)) {
+ /* Failed to prefetch data block header. */
+ return ARCHIVE_EOF;
+ }
+
+ /*
+ * Read block_size by parsing block header. Validate the header
+ * by calculating CRC byte stored inside the header. Size of
+ * the header is not constant (block size can be stored either
+ * in 1 or 2 bytes), that's why block size is left out from the
+ * `compressed_block_header` structure and returned by
+ * `parse_block_header` as the second argument. */
+
+ ret = parse_block_header(a, p, &block_size,
+ &rar->last_block_hdr);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ /* Skip block header. Next data is huffman tables,
+ * if present. */
+ ssize_t to_skip = sizeof(struct compressed_block_header) +
+ bf_byte_count(&rar->last_block_hdr) + 1;
+
+ if(ARCHIVE_OK != consume(a, to_skip))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining -= to_skip;
+
+ /* The block size gives information about the whole block size,
+ * but the block could be stored in split form when using
+ * multi-volume archives. In this case, the block size will be
+ * bigger than the actual data stored in this file. Remaining
+ * part of the data will be in another file. */
+
+ ssize_t cur_block_size =
+ rar5_min(rar->file.bytes_remaining, block_size);
+
+ if(block_size > rar->file.bytes_remaining) {
+ /* If current blocks' size is bigger than our data
+ * size, this means we have a multivolume archive.
+ * In this case, skip all base headers until the end
+ * of the file, proceed to next "partXXX.rar" volume,
+ * find its signature, skip all headers up to the first
+ * FILE base header, and continue from there.
+ *
+ * Note that `merge_block` will update the `rar`
+ * context structure quite extensively. */
+
+ ret = merge_block(a, block_size, &p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ cur_block_size = block_size;
+
+ /* Current stream pointer should be now directly
+ * *after* the block that spanned through multiple
+ * archive files. `p` pointer should have the data of
+ * the *whole* block (merged from partial blocks
+ * stored in multiple archives files). */
+ } else {
+ rar->cstate.switch_multivolume = 0;
+
+ /* Read the whole block size into memory. This can take
+ * up to 8 megabytes of memory in theoretical cases.
+ * Might be worth to optimize this and use a standard
+ * chunk of 4kb's. */
+ if(!read_ahead(a, 4 + cur_block_size, &p)) {
+ /* Failed to prefetch block data. */
+ return ARCHIVE_EOF;
+ }
+ }
+
+ rar->cstate.block_buf = p;
+ rar->cstate.cur_block_size = cur_block_size;
+ rar->cstate.block_parsing_finished = 0;
+
+ rar->bits.in_addr = 0;
+ rar->bits.bit_addr = 0;
+
+ if(bf_is_table_present(&rar->last_block_hdr)) {
+ /* Load Huffman tables. */
+ ret = parse_tables(a, rar, p);
+ if(ret != ARCHIVE_OK) {
+ /* Error during decompression of Huffman
+ * tables. */
+ return ret;
+ }
+ }
+ } else {
+ /* Block parsing not finished, reuse previous memory buffer. */
+ p = rar->cstate.block_buf;
+ }
+
+ /* Uncompress the block, or a part of it, depending on how many bytes
+ * will be generated by uncompressing the block.
+ *
+ * In case too many bytes will be generated, calling this function
+ * again will resume the uncompression operation. */
+ ret = do_uncompress_block(a, p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished &&
+ rar->cstate.switch_multivolume == 0 &&
+ rar->cstate.cur_block_size > 0)
+ {
+ /* If we're processing a normal block, consume the whole
+ * block. We can do this because we've already read the whole
+ * block to memory. */
+ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
+ return ARCHIVE_FATAL;
+
+ rar->file.bytes_remaining -= rar->cstate.cur_block_size;
+ } else if(rar->cstate.switch_multivolume) {
+ /* Don't consume the block if we're doing multivolume
+ * processing. The volume switching function will consume
+ * the proper count of bytes instead. */
+ rar->cstate.switch_multivolume = 0;
+ }
+
+ return ARCHIVE_OK;
}
/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
@@ -2904,78 +3404,78 @@ static int process_block(struct archive_read* a) {
* Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
* when there is no data on the stack. */
static int use_data(struct rar5* rar, const void** buf, size_t* size,
- int64_t* offset)
+ int64_t* offset)
{
- int i;
+ int i;
- for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
- struct data_ready *d = &rar->cstate.dready[i];
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready *d = &rar->cstate.dready[i];
- if(d->used) {
- if(buf) *buf = d->buf;
- if(size) *size = d->size;
- if(offset) *offset = d->offset;
+ if(d->used) {
+ if(buf) *buf = d->buf;
+ if(size) *size = d->size;
+ if(offset) *offset = d->offset;
- d->used = 0;
- return ARCHIVE_OK;
- }
- }
+ d->used = 0;
+ return ARCHIVE_OK;
+ }
+ }
- return ARCHIVE_RETRY;
+ return ARCHIVE_RETRY;
}
/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
* FIFO stack. Those values will be popped from this stack by the `use_data`
* function. */
static int push_data_ready(struct archive_read* a, struct rar5* rar,
- const uint8_t* buf, size_t size, int64_t offset)
+ const uint8_t* buf, size_t size, int64_t offset)
{
- int i;
-
- /* Don't push if we're in skip mode. This is needed because solid
- * streams need full processing even if we're skipping data. After fully
- * processing the stream, we need to discard the generated bytes, because
- * we're interested only in the side effect: building up the internal
- * window circular buffer. This window buffer will be used later during
- * unpacking of requested data. */
- if(rar->skip_mode)
- return ARCHIVE_OK;
-
- /* Sanity check. */
- if(offset != rar->file.last_offset + rar->file.last_size) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity "
- "check error: output stream is not continuous");
- return ARCHIVE_FATAL;
- }
-
- for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
- struct data_ready* d = &rar->cstate.dready[i];
- if(!d->used) {
- d->used = 1;
- d->buf = buf;
- d->size = size;
- d->offset = offset;
-
- /* These fields are used only in sanity checking. */
- rar->file.last_offset = offset;
- rar->file.last_size = size;
-
- /* Calculate the checksum of this new block before submitting
- * data to libarchive's engine. */
- update_crc(rar, d->buf, d->size);
-
- return ARCHIVE_OK;
- }
- }
-
- /* Program counter will reach this code if the `rar->cstate.data_ready`
- * stack will be filled up so that no new entries will be allowed. The
- * code shouldn't allow such situation to occur. So we treat this case
- * as an internal error. */
-
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Error: "
- "premature end of data_ready stack");
- return ARCHIVE_FATAL;
+ int i;
+
+ /* Don't push if we're in skip mode. This is needed because solid
+ * streams need full processing even if we're skipping data. After
+ * fully processing the stream, we need to discard the generated bytes,
+ * because we're interested only in the side effect: building up the
+ * internal window circular buffer. This window buffer will be used
+ * later during unpacking of requested data. */
+ if(rar->skip_mode)
+ return ARCHIVE_OK;
+
+ /* Sanity check. */
+ if(offset != rar->file.last_offset + rar->file.last_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Sanity check error: output stream is not continuous");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready* d = &rar->cstate.dready[i];
+ if(!d->used) {
+ d->used = 1;
+ d->buf = buf;
+ d->size = size;
+ d->offset = offset;
+
+ /* These fields are used only in sanity checking. */
+ rar->file.last_offset = offset;
+ rar->file.last_size = size;
+
+ /* Calculate the checksum of this new block before
+ * submitting data to libarchive's engine. */
+ update_crc(rar, d->buf, d->size);
+
+ return ARCHIVE_OK;
+ }
+ }
+
+ /* Program counter will reach this code if the `rar->cstate.data_ready`
+ * stack will be filled up so that no new entries will be allowed. The
+ * code shouldn't allow such situation to occur. So we treat this case
+ * as an internal error. */
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Error: premature end of data_ready stack");
+ return ARCHIVE_FATAL;
}
/* This function uncompresses the data that is stored in the <FILE> base
@@ -3021,477 +3521,510 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar,
* */
static int do_uncompress_file(struct archive_read* a) {
- struct rar5* rar = get_context(a);
- int ret;
- int64_t max_end_pos;
-
- if(!rar->cstate.initialized) {
- /* Don't perform full context reinitialization if we're processing
- * a solid archive. */
- if(!rar->main.solid || !rar->cstate.window_buf) {
- init_unpack(rar);
- }
-
- rar->cstate.initialized = 1;
- }
-
- if(rar->cstate.all_filters_applied == 1) {
- /* We use while(1) here, but standard case allows for just 1 iteration.
- * The loop will iterate if process_block() didn't generate any data at
- * all. This can happen if the block contains only filter definitions
- * (this is common in big files). */
-
- while(1) {
- ret = process_block(a);
- if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
- return ret;
-
- if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) {
- /* The block didn't generate any new data, so just process
- * a new block. */
- continue;
- }
-
- /* The block has generated some new data, so break the loop. */
- break;
- }
- }
-
- /* Try to run filters. If filters won't be applied, it means that
- * insufficient data was generated. */
- ret = apply_filters(a);
- if(ret == ARCHIVE_RETRY) {
- return ARCHIVE_OK;
- } else if(ret == ARCHIVE_FATAL) {
- return ARCHIVE_FATAL;
- }
-
- /* If apply_filters() will return ARCHIVE_OK, we can continue here. */
-
- if(cdeque_size(&rar->cstate.filters) > 0) {
- /* Check if we can write something before hitting first filter. */
- struct filter_info* flt;
-
- /* Get the block_start offset from the first filter. */
- if(CDE_OK != cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt)))
- {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Can't read first filter");
- return ARCHIVE_FATAL;
- }
-
- max_end_pos = rar5_min(flt->block_start, rar->cstate.write_ptr);
- } else {
- /* There are no filters defined, or all filters were applied. This
- * means we can just store the data without any postprocessing. */
- max_end_pos = rar->cstate.write_ptr;
- }
-
- if(max_end_pos == rar->cstate.last_write_ptr) {
- /* We can't write anything yet. The block uncompression function did
- * not generate enough data, and no filter can be applied. At the same
- * time we don't have any data that can be stored without filter
- * postprocessing. This means we need to wait for more data to be
- * generated, so we can apply the filters.
- *
- * Signal the caller that we need more data to be able to do anything.
- */
- return ARCHIVE_RETRY;
- } else {
- /* We can write the data before hitting the first filter. So let's
- * do it. The push_window_data() function will effectively return
- * the selected data block to the user application. */
- push_window_data(a, rar, rar->cstate.last_write_ptr, max_end_pos);
- rar->cstate.last_write_ptr = max_end_pos;
- }
-
- return ARCHIVE_OK;
+ struct rar5* rar = get_context(a);
+ int ret;
+ int64_t max_end_pos;
+
+ if(!rar->cstate.initialized) {
+ /* Don't perform full context reinitialization if we're
+ * processing a solid archive. */
+ if(!rar->main.solid || !rar->cstate.window_buf) {
+ init_unpack(rar);
+ }
+
+ rar->cstate.initialized = 1;
+ }
+
+ if(rar->cstate.all_filters_applied == 1) {
+ /* We use while(1) here, but standard case allows for just 1
+ * iteration. The loop will iterate if process_block() didn't
+ * generate any data at all. This can happen if the block
+ * contains only filter definitions (this is common in big
+ * files). */
+ while(1) {
+ ret = process_block(a);
+ if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
+ return ret;
+
+ if(rar->cstate.last_write_ptr ==
+ rar->cstate.write_ptr) {
+ /* The block didn't generate any new data,
+ * so just process a new block. */
+ continue;
+ }
+
+ /* The block has generated some new data, so break
+ * the loop. */
+ break;
+ }
+ }
+
+ /* Try to run filters. If filters won't be applied, it means that
+ * insufficient data was generated. */
+ ret = apply_filters(a);
+ if(ret == ARCHIVE_RETRY) {
+ return ARCHIVE_OK;
+ } else if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_FATAL;
+ }
+
+ /* If apply_filters() will return ARCHIVE_OK, we can continue here. */
+
+ if(cdeque_size(&rar->cstate.filters) > 0) {
+ /* Check if we can write something before hitting first
+ * filter. */
+ struct filter_info* flt;
+
+ /* Get the block_start offset from the first filter. */
+ if(CDE_OK != cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt)))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Can't read first filter");
+ return ARCHIVE_FATAL;
+ }
+
+ max_end_pos = rar5_min(flt->block_start,
+ rar->cstate.write_ptr);
+ } else {
+ /* There are no filters defined, or all filters were applied.
+ * This means we can just store the data without any
+ * postprocessing. */
+ max_end_pos = rar->cstate.write_ptr;
+ }
+
+ if(max_end_pos == rar->cstate.last_write_ptr) {
+ /* We can't write anything yet. The block uncompression
+ * function did not generate enough data, and no filter can be
+ * applied. At the same time we don't have any data that can be
+ * stored without filter postprocessing. This means we need to
+ * wait for more data to be generated, so we can apply the
+ * filters.
+ *
+ * Signal the caller that we need more data to be able to do
+ * anything.
+ */
+ return ARCHIVE_RETRY;
+ } else {
+ /* We can write the data before hitting the first filter.
+ * So let's do it. The push_window_data() function will
+ * effectively return the selected data block to the user
+ * application. */
+ push_window_data(a, rar, rar->cstate.last_write_ptr,
+ max_end_pos);
+ rar->cstate.last_write_ptr = max_end_pos;
+ }
+
+ return ARCHIVE_OK;
}
static int uncompress_file(struct archive_read* a) {
- int ret;
+ int ret;
- while(1) {
- /* Sometimes the uncompression function will return a 'retry' signal.
- * If this will happen, we have to retry the function. */
- ret = do_uncompress_file(a);
- if(ret != ARCHIVE_RETRY)
- return ret;
- }
+ while(1) {
+ /* Sometimes the uncompression function will return a
+ * 'retry' signal. If this will happen, we have to retry
+ * the function. */
+ ret = do_uncompress_file(a);
+ if(ret != ARCHIVE_RETRY)
+ return ret;
+ }
}
static int do_unstore_file(struct archive_read* a,
- struct rar5* rar,
- const void** buf,
- size_t* size,
- int64_t* offset)
+ struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
{
- const uint8_t* p;
+ const uint8_t* p;
- if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
- rar->generic.split_after > 0)
- {
- int ret;
+ if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
+ rar->generic.split_after > 0)
+ {
+ int ret;
- rar->cstate.switch_multivolume = 1;
- ret = advance_multivolume(a);
- rar->cstate.switch_multivolume = 0;
+ rar->cstate.switch_multivolume = 1;
+ ret = advance_multivolume(a);
+ rar->cstate.switch_multivolume = 0;
- if(ret != ARCHIVE_OK) {
- /* Failed to advance to next multivolume archive file. */
- return ret;
- }
- }
+ if(ret != ARCHIVE_OK) {
+ /* Failed to advance to next multivolume archive
+ * file. */
+ return ret;
+ }
+ }
- size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
- if(to_read == 0) {
- return ARCHIVE_EOF;
- }
+ size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+ if(to_read == 0) {
+ return ARCHIVE_EOF;
+ }
- if(!read_ahead(a, to_read, &p)) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "I/O error "
- "when unstoring file");
- return ARCHIVE_FATAL;
- }
+ if(!read_ahead(a, to_read, &p)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "I/O error when unstoring file");
+ return ARCHIVE_FATAL;
+ }
- if(ARCHIVE_OK != consume(a, to_read)) {
- return ARCHIVE_EOF;
- }
+ if(ARCHIVE_OK != consume(a, to_read)) {
+ return ARCHIVE_EOF;
+ }
- if(buf) *buf = p;
- if(size) *size = to_read;
- if(offset) *offset = rar->cstate.last_unstore_ptr;
+ if(buf) *buf = p;
+ if(size) *size = to_read;
+ if(offset) *offset = rar->cstate.last_unstore_ptr;
- rar->file.bytes_remaining -= to_read;
- rar->cstate.last_unstore_ptr += to_read;
+ rar->file.bytes_remaining -= to_read;
+ rar->cstate.last_unstore_ptr += to_read;
- update_crc(rar, p, to_read);
- return ARCHIVE_OK;
+ update_crc(rar, p, to_read);
+ return ARCHIVE_OK;
}
static int do_unpack(struct archive_read* a, struct rar5* rar,
- const void** buf, size_t* size, int64_t* offset)
+ const void** buf, size_t* size, int64_t* offset)
{
- enum COMPRESSION_METHOD {
- STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, BEST = 5
- };
-
- if(rar->file.service > 0) {
- return do_unstore_file(a, rar, buf, size, offset);
- } else {
- switch(rar->cstate.method) {
- case STORE:
- return do_unstore_file(a, rar, buf, size, offset);
- case FASTEST:
- /* fallthrough */
- case FAST:
- /* fallthrough */
- case NORMAL:
- /* fallthrough */
- case GOOD:
- /* fallthrough */
- case BEST:
- return uncompress_file(a);
- default:
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Compression method not supported: 0x%08x",
- rar->cstate.method);
-
- return ARCHIVE_FATAL;
- }
- }
+ enum COMPRESSION_METHOD {
+ STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
+ BEST = 5
+ };
+
+ if(rar->file.service > 0) {
+ return do_unstore_file(a, rar, buf, size, offset);
+ } else {
+ switch(rar->cstate.method) {
+ case STORE:
+ return do_unstore_file(a, rar, buf, size,
+ offset);
+ case FASTEST:
+ /* fallthrough */
+ case FAST:
+ /* fallthrough */
+ case NORMAL:
+ /* fallthrough */
+ case GOOD:
+ /* fallthrough */
+ case BEST:
+ return uncompress_file(a);
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Compression method not supported: 0x%x",
+ rar->cstate.method);
+
+ return ARCHIVE_FATAL;
+ }
+ }
#if !defined WIN32
- /* Not reached. */
- return ARCHIVE_OK;
+ /* Not reached. */
+ return ARCHIVE_OK;
#endif
}
static int verify_checksums(struct archive_read* a) {
- int verify_crc;
- struct rar5* rar = get_context(a);
-
- /* Check checksums only when actually unpacking the data. There's no need
- * to calculate checksum when we're skipping data in solid archives
- * (skipping in solid archives is the same thing as unpacking compressed
- * data and discarding the result). */
-
- if(!rar->skip_mode) {
- /* Always check checksums if we're not in skip mode */
- verify_crc = 1;
- } else {
- /* We can override the logic above with a compile-time option
- * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, and it
- * will check checksums of unpacked data even when we're skipping it.
- */
+ int verify_crc;
+ struct rar5* rar = get_context(a);
+
+ /* Check checksums only when actually unpacking the data. There's no
+ * need to calculate checksum when we're skipping data in solid archives
+ * (skipping in solid archives is the same thing as unpacking compressed
+ * data and discarding the result). */
+
+ if(!rar->skip_mode) {
+ /* Always check checksums if we're not in skip mode */
+ verify_crc = 1;
+ } else {
+ /* We can override the logic above with a compile-time option
+ * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
+ * and it will check checksums of unpacked data even when
+ * we're skipping it. */
#if defined CHECK_CRC_ON_SOLID_SKIP
- /* Debug case */
- verify_crc = 1;
+ /* Debug case */
+ verify_crc = 1;
#else
- /* Normal case */
- verify_crc = 0;
+ /* Normal case */
+ verify_crc = 0;
#endif
- }
-
- if(verify_crc) {
- /* During unpacking, on each unpacked block we're calling the
- * update_crc() function. Since we are here, the unpacking process is
- * already over and we can check if calculated checksum (CRC32 or
- * BLAKE2sp) is the same as what is stored in the archive.
- */
- if(rar->file.stored_crc32 > 0) {
- /* Check CRC32 only when the file contains a CRC32 value for this
- * file. */
-
- if(rar->file.calculated_crc32 != rar->file.stored_crc32) {
- /* Checksums do not match; the unpacked file is corrupted. */
-
- DEBUG_CODE {
- printf("Checksum error: CRC32 (was: %08x, expected: %08x)\n",
- rar->file.calculated_crc32, rar->file.stored_crc32);
- }
+ }
+
+ if(verify_crc) {
+ /* During unpacking, on each unpacked block we're calling the
+ * update_crc() function. Since we are here, the unpacking
+ * process is already over and we can check if calculated
+ * checksum (CRC32 or BLAKE2sp) is the same as what is stored
+ * in the archive. */
+ if(rar->file.stored_crc32 > 0) {
+ /* Check CRC32 only when the file contains a CRC32
+ * value for this file. */
+
+ if(rar->file.calculated_crc32 !=
+ rar->file.stored_crc32) {
+ /* Checksums do not match; the unpacked file
+ * is corrupted. */
+
+ DEBUG_CODE {
+ printf("Checksum error: CRC32 "
+ "(was: %08x, expected: %08x)\n",
+ rar->file.calculated_crc32,
+ rar->file.stored_crc32);
+ }
#ifndef DONT_FAIL_ON_CRC_ERROR
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Checksum error: CRC32");
- return ARCHIVE_FATAL;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: CRC32");
+ return ARCHIVE_FATAL;
#endif
- } else {
- DEBUG_CODE {
- printf("Checksum OK: CRC32 (%08x/%08x)\n",
- rar->file.stored_crc32,
- rar->file.calculated_crc32);
- }
- }
- }
-
- if(rar->file.has_blake2 > 0) {
- /* BLAKE2sp is an optional checksum algorithm that is added to
- * RARv5 archives when using the `-htb` switch during creation of
- * archive.
- *
- * We now finalize the hash calculation by calling the `final`
- * function. This will generate the final hash value we can use to
- * compare it with the BLAKE2sp checksum that is stored in the
- * archive.
- *
- * The return value of this `final` function is not very helpful,
- * as it guards only against improper use. This is why we're
- * explicitly ignoring it. */
-
- uint8_t b2_buf[32];
- (void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
-
- if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
+ } else {
+ DEBUG_CODE {
+ printf("Checksum OK: CRC32 "
+ "(%08x/%08x)\n",
+ rar->file.stored_crc32,
+ rar->file.calculated_crc32);
+ }
+ }
+ }
+
+ if(rar->file.has_blake2 > 0) {
+ /* BLAKE2sp is an optional checksum algorithm that is
+ * added to RARv5 archives when using the `-htb` switch
+ * during creation of archive.
+ *
+ * We now finalize the hash calculation by calling the
+ * `final` function. This will generate the final hash
+ * value we can use to compare it with the BLAKE2sp
+ * checksum that is stored in the archive.
+ *
+ * The return value of this `final` function is not
+ * very helpful, as it guards only against improper use.
+ * This is why we're explicitly ignoring it. */
+
+ uint8_t b2_buf[32];
+ (void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
+
+ if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
#ifndef DONT_FAIL_ON_CRC_ERROR
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Checksum error: BLAKE2");
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: BLAKE2");
- return ARCHIVE_FATAL;
+ return ARCHIVE_FATAL;
#endif
- }
- }
- }
+ }
+ }
+ }
- /* Finalization for this file has been successfully completed. */
- return ARCHIVE_OK;
+ /* Finalization for this file has been successfully completed. */
+ return ARCHIVE_OK;
}
static int verify_global_checksums(struct archive_read* a) {
- return verify_checksums(a);
+ return verify_checksums(a);
}
static int rar5_read_data(struct archive_read *a, const void **buff,
- size_t *size, int64_t *offset) {
- int ret;
- struct rar5* rar = get_context(a);
-
- if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Unpacker has written too many bytes");
- return ARCHIVE_FATAL;
- }
-
- ret = use_data(rar, buff, size, offset);
- if(ret == ARCHIVE_OK) {
- return ret;
- }
-
- if(rar->file.eof == 1) {
- return ARCHIVE_EOF;
- }
-
- ret = do_unpack(a, rar, buff, size, offset);
- if(ret != ARCHIVE_OK) {
- return ret;
- }
-
- if(rar->file.bytes_remaining == 0 &&
- rar->cstate.last_write_ptr == rar->file.unpacked_size)
- {
- /* If all bytes of current file were processed, run finalization.
- *
- * Finalization will check checksum against proper values. If
- * some of the checksums will not match, we'll return an error
- * value in the last `archive_read_data` call to signal an error
- * to the user. */
-
- rar->file.eof = 1;
- return verify_global_checksums(a);
- }
-
- return ARCHIVE_OK;
+ size_t *size, int64_t *offset) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ if(rar->file.dir > 0) {
+ /* Don't process any data if this file entry was declared
+ * as a directory. This is needed, because entries marked as
+ * directory doesn't have any dictionary buffer allocated, so
+ * it's impossible to perform any decompression. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't decompress an entry marked as a directory");
+ return ARCHIVE_FAILED;
+ }
+
+ if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Unpacker has written too many bytes");
+ return ARCHIVE_FATAL;
+ }
+
+ ret = use_data(rar, buff, size, offset);
+ if(ret == ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.eof == 1) {
+ return ARCHIVE_EOF;
+ }
+
+ ret = do_unpack(a, rar, buff, size, offset);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.bytes_remaining == 0 &&
+ rar->cstate.last_write_ptr == rar->file.unpacked_size)
+ {
+ /* If all bytes of current file were processed, run
+ * finalization.
+ *
+ * Finalization will check checksum against proper values. If
+ * some of the checksums will not match, we'll return an error
+ * value in the last `archive_read_data` call to signal an error
+ * to the user. */
+
+ rar->file.eof = 1;
+ return verify_global_checksums(a);
+ }
+
+ return ARCHIVE_OK;
}
static int rar5_read_data_skip(struct archive_read *a) {
- struct rar5* rar = get_context(a);
-
- if(rar->main.solid) {
- /* In solid archives, instead of skipping the data, we need to extract
- * it, and dispose the result. The side effect of this operation will
- * be setting up the initial window buffer state needed to be able to
- * extract the selected file. */
-
- int ret;
-
- /* Make sure to process all blocks in the compressed stream. */
- while(rar->file.bytes_remaining > 0) {
- /* Setting the "skip mode" will allow us to skip checksum checks
- * during data skipping. Checking the checksum of skipped data
- * isn't really necessary and it's only slowing things down.
- *
- * This is incremented instead of setting to 1 because this data
- * skipping function can be called recursively. */
- rar->skip_mode++;
-
- /* We're disposing 1 block of data, so we use triple NULLs in
- * arguments.
- */
- ret = rar5_read_data(a, NULL, NULL, NULL);
-
- /* Turn off "skip mode". */
- rar->skip_mode--;
-
- if(ret < 0) {
- /* Propagate any potential error conditions to the caller. */
- return ret;
- }
- }
- } else {
- /* In standard archives, we can just jump over the compressed stream.
- * Each file in non-solid archives starts from an empty window buffer.
- */
-
- if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
- return ARCHIVE_FATAL;
- }
-
- rar->file.bytes_remaining = 0;
- }
-
- return ARCHIVE_OK;
+ struct rar5* rar = get_context(a);
+
+ if(rar->main.solid) {
+ /* In solid archives, instead of skipping the data, we need to
+ * extract it, and dispose the result. The side effect of this
+ * operation will be setting up the initial window buffer state
+ * needed to be able to extract the selected file. */
+
+ int ret;
+
+ /* Make sure to process all blocks in the compressed stream. */
+ while(rar->file.bytes_remaining > 0) {
+ /* Setting the "skip mode" will allow us to skip
+ * checksum checks during data skipping. Checking the
+ * checksum of skipped data isn't really necessary and
+ * it's only slowing things down.
+ *
+ * This is incremented instead of setting to 1 because
+ * this data skipping function can be called
+ * recursively. */
+ rar->skip_mode++;
+
+ /* We're disposing 1 block of data, so we use triple
+ * NULLs in arguments. */
+ ret = rar5_read_data(a, NULL, NULL, NULL);
+
+ /* Turn off "skip mode". */
+ rar->skip_mode--;
+
+ if(ret < 0) {
+ /* Propagate any potential error conditions
+ * to the caller. */
+ return ret;
+ }
+ }
+ } else {
+ /* In standard archives, we can just jump over the compressed
+ * stream. Each file in non-solid archives starts from an empty
+ * window buffer. */
+
+ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.bytes_remaining = 0;
+ }
+
+ return ARCHIVE_OK;
}
static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
- int whence)
+ int whence)
{
- (void) a;
- (void) offset;
- (void) whence;
+ (void) a;
+ (void) offset;
+ (void) whence;
- /* We're a streaming unpacker, and we don't support seeking. */
+ /* We're a streaming unpacker, and we don't support seeking. */
- return ARCHIVE_FATAL;
+ return ARCHIVE_FATAL;
}
static int rar5_cleanup(struct archive_read *a) {
- struct rar5* rar = get_context(a);
-
- free(rar->cstate.window_buf);
+ struct rar5* rar = get_context(a);
- free(rar->cstate.filtered_buf);
+ free(rar->cstate.window_buf);
+ free(rar->cstate.filtered_buf);
- free(rar->vol.push_buf);
+ free(rar->vol.push_buf);
- free_filters(rar);
- cdeque_free(&rar->cstate.filters);
+ free_filters(rar);
+ cdeque_free(&rar->cstate.filters);
- free(rar);
- a->format->data = NULL;
+ free(rar);
+ a->format->data = NULL;
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
static int rar5_capabilities(struct archive_read * a) {
- (void) a;
- return 0;
+ (void) a;
+ return 0;
}
static int rar5_has_encrypted_entries(struct archive_read *_a) {
- (void) _a;
+ (void) _a;
- /* Unsupported for now. */
- return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+ /* Unsupported for now. */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
}
static int rar5_init(struct rar5* rar) {
- ssize_t i;
+ ssize_t i;
- memset(rar, 0, sizeof(struct rar5));
+ memset(rar, 0, sizeof(struct rar5));
- /* Decrypt the magic signature pattern. Check the comment near the
- * `rar5_signature` symbol to read the rationale behind this. */
+ /* Decrypt the magic signature pattern. Check the comment near the
+ * `rar5_signature` symbol to read the rationale behind this. */
- if(rar5_signature[0] == 243) {
- for(i = 0; i < rar5_signature_size; i++) {
- rar5_signature[i] ^= 0xA1;
- }
- }
+ if(rar5_signature[0] == 243) {
+ for(i = 0; i < rar5_signature_size; i++) {
+ rar5_signature[i] ^= 0xA1;
+ }
+ }
- if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
- return ARCHIVE_FATAL;
+ if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
+ return ARCHIVE_FATAL;
- return ARCHIVE_OK;
+ return ARCHIVE_OK;
}
int archive_read_support_format_rar5(struct archive *_a) {
- struct archive_read* ar;
- int ret;
- struct rar5* rar;
-
- if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
- return ret;
-
- rar = malloc(sizeof(*rar));
- if(rar == NULL) {
- archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data");
- return ARCHIVE_FATAL;
- }
-
- if(ARCHIVE_OK != rar5_init(rar)) {
- archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter "
- "buffer");
- return ARCHIVE_FATAL;
- }
-
- ret = __archive_read_register_format(ar,
- rar,
- "rar5",
- rar5_bid,
- rar5_options,
- rar5_read_header,
- rar5_read_data,
- rar5_read_data_skip,
- rar5_seek_data,
- rar5_cleanup,
- rar5_capabilities,
- rar5_has_encrypted_entries);
-
- if(ret != ARCHIVE_OK) {
- (void) rar5_cleanup(ar);
- }
-
- return ret;
+ struct archive_read* ar;
+ int ret;
+ struct rar5* rar;
+
+ if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
+ return ret;
+
+ rar = malloc(sizeof(*rar));
+ if(rar == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 data");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != rar5_init(rar)) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 filter buffer");
+ return ARCHIVE_FATAL;
+ }
+
+ ret = __archive_read_register_format(ar,
+ rar,
+ "rar5",
+ rar5_bid,
+ rar5_options,
+ rar5_read_header,
+ rar5_read_data,
+ rar5_read_data_skip,
+ rar5_seek_data,
+ rar5_cleanup,
+ rar5_capabilities,
+ rar5_has_encrypted_entries);
+
+ if(ret != ARCHIVE_OK) {
+ (void) rar5_cleanup(ar);
+ }
+
+ return ret;
}
diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c
index efa2c6a33c7e..ec0520b60a6c 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 60800bb812e5..c63d46fc0ce2 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -694,11 +694,13 @@ tar_read_header(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, size_t *unconsumed)
{
ssize_t bytes;
- int err;
+ int err, eof_vol_header;
const char *h;
const struct archive_entry_header_ustar *header;
const struct archive_entry_header_gnutar *gnuheader;
+ eof_vol_header = 0;
+
/* Loop until we find a workable header record. */
for (;;) {
tar_flush_unconsumed(a, unconsumed);
@@ -788,6 +790,8 @@ tar_read_header(struct archive_read *a, struct tar *tar,
break;
case 'V': /* GNU volume header */
err = header_volume(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ eof_vol_header = 1;
break;
case 'X': /* Used by SUN tar; same as 'x'. */
a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
@@ -862,9 +866,17 @@ tar_read_header(struct archive_read *a, struct tar *tar,
}
return (err);
}
- if (err == ARCHIVE_EOF)
- /* EOF when recursively reading a header is bad. */
- archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
+ if (err == ARCHIVE_EOF) {
+ if (!eof_vol_header) {
+ /* EOF when recursively reading a header is bad. */
+ archive_set_error(&a->archive, EINVAL,
+ "Damaged tar archive");
+ } else {
+ /* If we encounter just a GNU volume header treat
+ * this situation as an empty archive */
+ return (ARCHIVE_EOF);
+ }
+ }
return (ARCHIVE_FATAL);
}
@@ -1942,6 +1954,15 @@ pax_attribute(struct archive_read *a, struct tar *tar,
pax_time(value, &s, &n);
archive_entry_set_birthtime(entry, s, n);
}
+ if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {
+ if (strcmp(value, "file") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_FILE);
+ } else if (strcmp(value, "dir") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ }
+ }
if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
pax_attribute_xattr(entry, key, value);
break;
diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c
index e8fc8428b41e..c1c54450c396 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 6ff9cc4be5ff..34253a52fb75 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)
@@ -820,8 +821,18 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
r = checksum_final(a,
xattr->a_sum.val, xattr->a_sum.len,
xattr->e_sum.val, xattr->e_sum.len);
- if (r != ARCHIVE_OK)
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr checksum error");
+ r = ARCHIVE_WARN;
+ break;
+ }
+ if (xattr->name.s == NULL) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr name error");
+ r = ARCHIVE_WARN;
break;
+ }
archive_entry_xattr_add_entry(entry,
xattr->name.s, d, outbytes);
xattr = xattr->next;
@@ -847,7 +858,7 @@ xar_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
struct xar *xar;
- size_t used;
+ size_t used = 0;
int r;
xar = (struct xar *)(a->format->data);
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index 677c43709548..ab21e222f5af 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -472,27 +472,49 @@ zip_time(const char *p)
* triplets. id and size are 2 bytes each.
*/
static int
-process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry)
+process_extra(struct archive_read *a, struct archive_entry *entry,
+ const char *p, size_t extra_length, struct zip_entry* zip_entry)
{
unsigned offset = 0;
+ struct zip *zip = (struct zip *)(a->format->data);
if (extra_length == 0) {
return ARCHIVE_OK;
}
if (extra_length < 4) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length);
- return ARCHIVE_FAILED;
+ size_t i = 0;
+ /* Some ZIP files may have trailing 0 bytes. Let's check they
+ * are all 0 and ignore them instead of returning an error.
+ *
+ * This is not techincally correct, but some ZIP files look
+ * like this and other tools support those files - so let's
+ * also support them.
+ */
+ for (; i < extra_length; i++) {
+ if (p[i] != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too-small extra data: "
+ "Need at least 4 bytes, "
+ "but only found %d bytes",
+ (int)extra_length);
+ return ARCHIVE_FAILED;
+ }
+ }
+
+ return ARCHIVE_OK;
}
+
while (offset <= extra_length - 4) {
unsigned short headerid = archive_le16dec(p + offset);
unsigned short datasize = archive_le16dec(p + offset + 2);
offset += 4;
if (offset + datasize > extra_length) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Extra data overflow: Need %d bytes but only found %d bytes",
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "
+ "Need %d bytes but only found %d bytes",
(int)datasize, (int)(extra_length - offset));
return ARCHIVE_FAILED;
}
@@ -507,9 +529,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->uncompressed_size == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
- || (t = archive_le64dec(p + offset)) > INT64_MAX) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Malformed 64-bit uncompressed size");
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "uncompressed size");
return ARCHIVE_FAILED;
}
zip_entry->uncompressed_size = t;
@@ -519,9 +544,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->compressed_size == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
- || (t = archive_le64dec(p + offset)) > INT64_MAX) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Malformed 64-bit compressed size");
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "compressed size");
return ARCHIVE_FAILED;
}
zip_entry->compressed_size = t;
@@ -531,9 +559,12 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
if (zip_entry->local_header_offset == 0xffffffff) {
uint64_t t = 0;
if (datasize < 8
- || (t = archive_le64dec(p + offset)) > INT64_MAX) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Malformed 64-bit local header offset");
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "local header offset");
return ARCHIVE_FAILED;
}
zip_entry->local_header_offset = t;
@@ -566,7 +597,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
/* Extended time field "UT". */
int flags;
if (datasize == 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
"Incomplete extended time field");
return ARCHIVE_FAILED;
}
@@ -648,7 +680,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
* if bitmap & 1, 2 byte "version made by"
* if bitmap & 2, 2 byte "internal file attributes"
* if bitmap & 4, 4 byte "external file attributes"
- * if bitmap & 8, 2 byte comment length + n byte comment
+ * if bitmap & 8, 2 byte comment length + n byte
+ * comment
*/
int bitmap, bitmap_last;
@@ -699,13 +732,18 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
= external_attributes >> 16;
} else if (zip_entry->system == 0) {
// Interpret MSDOS directory bit
- if (0x10 == (external_attributes & 0x10)) {
- zip_entry->mode = AE_IFDIR | 0775;
+ if (0x10 == (external_attributes &
+ 0x10)) {
+ zip_entry->mode =
+ AE_IFDIR | 0775;
} else {
- zip_entry->mode = AE_IFREG | 0664;
+ zip_entry->mode =
+ AE_IFREG | 0664;
}
- if (0x01 == (external_attributes & 0x01)) {
- // Read-only bit; strip write permissions
+ if (0x01 == (external_attributes &
+ 0x01)) {
+ /* Read-only bit;
+ * strip write permissions */
zip_entry->mode &= 0555;
}
} else {
@@ -732,6 +770,59 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
break;
}
+ case 0x7075:
+ {
+ /* Info-ZIP Unicode Path Extra Field. */
+ if (datasize < 5 || entry == NULL)
+ break;
+ offset += 5;
+ datasize -= 5;
+
+ /* The path name in this field is always encoded
+ * in UTF-8. */
+ if (zip->sconv_utf8 == NULL) {
+ zip->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ /* If the converter from UTF-8 is not
+ * available, then the path name from the main
+ * field will more likely be correct. */
+ if (zip->sconv_utf8 == NULL)
+ break;
+ }
+
+ /* Make sure the CRC32 of the filename matches. */
+ if (!zip->ignore_crc32) {
+ const char *cp = archive_entry_pathname(entry);
+ if (cp) {
+ unsigned long file_crc =
+ zip->crc32func(0, cp, strlen(cp));
+ unsigned long utf_crc =
+ archive_le32dec(p + offset - 4);
+ if (file_crc != utf_crc) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "CRC filename mismatch; "
+ "CDE is %lx, but UTF8 "
+ "is outdated with %lx\n",
+ file_crc, utf_crc);
+#endif
+ break;
+ }
+ }
+ }
+
+ if (archive_entry_copy_pathname_l(entry,
+ p + offset, datasize, zip->sconv_utf8) != 0) {
+ /* Ignore the error, and fallback to the path
+ * name from the main field. */
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read the ZIP "
+ "0x7075 extra field path.\n");
+#endif
+ }
+ break;
+ }
case 0x7855:
/* Info-ZIP Unix Extra Field (type 2) "Ux". */
#ifdef DEBUG
@@ -766,7 +857,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
if (datasize >= (2 + uidsize + 3)) {
/* get a gid size. */
- gidsize = 0xff & (int)p[offset+2+uidsize];
+ gidsize = 0xff &
+ (int)p[offset+2+uidsize];
if (gidsize == 2)
zip_entry->gid =
archive_le16dec(
@@ -783,7 +875,8 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
case 0x9901:
/* WinZip AES extra data field. */
if (datasize < 6) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
"Incomplete AES field");
return ARCHIVE_FAILED;
}
@@ -803,12 +896,6 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct
}
offset += datasize;
}
- if (offset != extra_length) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Malformed extra data: Consumed %d bytes of %d bytes",
- (int)offset, (int)extra_length);
- return ARCHIVE_FAILED;
- }
return ARCHIVE_OK;
}
@@ -928,7 +1015,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
- if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) {
+ if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,
+ zip_entry)) {
return ARCHIVE_FATAL;
}
__archive_read_consume(a, extra_length);
@@ -945,8 +1033,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
zip_entry->mode |= 0664;
}
- /* Windows archivers sometimes use backslash as the directory separator.
- Normalize to slash. */
+ /* Windows archivers sometimes use backslash as the directory
+ * separator. Normalize to slash. */
if (zip_entry->system == 0 &&
(wp = archive_entry_pathname_w(entry)) != NULL) {
if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
@@ -1255,7 +1343,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff,
zip->entry->crc32 = archive_le32dec(p + 4);
compressed = archive_le64dec(p + 8);
uncompressed = archive_le64dec(p + 16);
- if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
+ if (compressed > INT64_MAX || uncompressed >
+ INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Overflow of 64-bit file sizes");
@@ -1372,7 +1461,8 @@ consume_optional_marker(struct archive_read *a, struct zip *zip)
zip->entry->crc32 = archive_le32dec(p);
compressed = archive_le64dec(p + 4);
uncompressed = archive_le64dec(p + 12);
- if (compressed > INT64_MAX || uncompressed > INT64_MAX) {
+ if (compressed > INT64_MAX ||
+ uncompressed > INT64_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Overflow of 64-bit file sizes");
@@ -1444,12 +1534,16 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
} alone_header;
#pragma pack(pop)
- /* 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. */
+ 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);
@@ -1477,8 +1571,8 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
* 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.
+ * 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:
*
@@ -1488,17 +1582,17 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
* 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.
+ * 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.
+ * 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. */
@@ -1514,8 +1608,8 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
return (ARCHIVE_FATAL);
}
- /* Prepare an lzma alone header: copy the lzma_params blob into a proper
- * place into the lzma alone header. */
+ /* 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
@@ -1541,8 +1635,9 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
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. */
+ /* 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,
@@ -1617,7 +1712,8 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
if((int64_t) zip->zipx_lzma_stream.total_in !=
zip->entry_bytes_remaining)
{
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
"xz premature end of stream");
return (ARCHIVE_FATAL);
}
@@ -1662,12 +1758,13 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
return (ret);
}
- /* Fetch more compressed data. The same note as in deflate handler applies
- * here as well:
+ /* 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.
+ * 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) {
@@ -1684,8 +1781,9 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
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. */
+ /* 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);
@@ -1810,7 +1908,8 @@ zipx_ppmd8_init(struct archive_read *a, struct zip *zip)
return (ARCHIVE_FATAL);
}
- __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, restore_method);
+ __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,
+ restore_method);
/* Allocate the buffer that will hold uncompressed data. */
free(zip->uncompressed_buffer);
@@ -1856,8 +1955,8 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
return ret;
}
- /* Fetch for more data. We're reading 1 byte here, but libarchive should
- * prefetch more bytes. */
+ /* 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,
@@ -1871,7 +1970,8 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
/* Decompression loop. */
do {
- int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(&zip->ppmd8);
+ int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(
+ &zip->ppmd8);
if(sym < 0) {
zip->end_of_entry = 1;
break;
@@ -1880,8 +1980,9 @@ zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
/* 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");
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated PPMd8 file body");
return (ARCHIVE_FATAL);
}
@@ -1985,9 +2086,10 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
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. */
+ /* 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");
@@ -2014,16 +2116,18 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
case BZ_OK:
break;
default:
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Failed to clean up bzip2 decompressor");
+ 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. */
+ /* 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,
@@ -2131,8 +2235,10 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
if (zip->tctx_valid || zip->cctx_valid) {
if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
size_t buff_remaining =
- (zip->decrypted_buffer + zip->decrypted_buffer_size)
- - (zip->decrypted_ptr + zip->decrypted_bytes_remaining);
+ (zip->decrypted_buffer +
+ zip->decrypted_buffer_size)
+ - (zip->decrypted_ptr +
+ zip->decrypted_bytes_remaining);
if (buff_remaining > (size_t)bytes_avail)
buff_remaining = (size_t)bytes_avail;
@@ -2143,12 +2249,12 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
+ buff_remaining)
> zip->entry_bytes_remaining) {
if (zip->entry_bytes_remaining <
- (int64_t)zip->decrypted_bytes_remaining)
+ (int64_t)zip->decrypted_bytes_remaining)
buff_remaining = 0;
else
buff_remaining =
(size_t)zip->entry_bytes_remaining
- - zip->decrypted_bytes_remaining;
+ - zip->decrypted_bytes_remaining;
}
}
if (buff_remaining > 0) {
@@ -2167,7 +2273,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
+ zip->decrypted_bytes_remaining,
&dsize);
}
- zip->decrypted_bytes_remaining += buff_remaining;
+ zip->decrypted_bytes_remaining +=
+ buff_remaining;
}
}
bytes_avail = zip->decrypted_bytes_remaining;
@@ -2751,7 +2858,7 @@ archive_read_format_zip_cleanup(struct archive_read *a)
inflateEnd(&zip->stream);
#endif
-#if HAVA_LZMA_H && HAVE_LIBLZMA
+#if HAVE_LZMA_H && HAVE_LIBLZMA
if (zip->zipx_lzma_valid) {
lzma_end(&zip->zipx_lzma_stream);
}
@@ -3391,7 +3498,8 @@ expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)
}
static int
-slurp_central_directory(struct archive_read *a, struct zip *zip)
+slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
+ struct zip *zip)
{
ssize_t i;
unsigned found;
@@ -3501,8 +3609,10 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
filename_length = archive_le16dec(p + 28);
extra_length = archive_le16dec(p + 30);
comment_length = archive_le16dec(p + 32);
- /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */
- /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */
+ /* disk_start = archive_le16dec(p + 34);
+ * Better be zero.
+ * internal_attributes = archive_le16dec(p + 36);
+ * text bit */
external_attributes = archive_le32dec(p + 38);
zip_entry->local_header_offset =
archive_le32dec(p + 42) + correction;
@@ -3538,7 +3648,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
"Truncated ZIP file header");
return ARCHIVE_FATAL;
}
- if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) {
+ if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,
+ extra_length, zip_entry)) {
return ARCHIVE_FATAL;
}
@@ -3560,7 +3671,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip)
* a directory. We should treat it as a non
* resource fork file to expose it. */
if (name[filename_length-1] != '/' &&
- (r - name < 3 || r[0] != '.' || r[1] != '_')) {
+ (r - name < 3 || r[0] != '.' ||
+ r[1] != '_')) {
__archive_rb_tree_insert_node(
&zip->tree, &zip_entry->node);
/* Expose its parent directories. */
@@ -3637,8 +3749,10 @@ zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
switch(rsrc->compression) {
case 0: /* No compression. */
if (rsrc->uncompressed_size != rsrc->compressed_size) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Malformed OS X metadata entry: inconsistent size");
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed OS X metadata entry: "
+ "inconsistent size");
return (ARCHIVE_FATAL);
}
#ifdef HAVE_ZLIB_H
@@ -3797,7 +3911,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a,
a->archive.archive_format_name = "ZIP";
if (zip->zip_entries == NULL) {
- r = slurp_central_directory(a, zip);
+ r = slurp_central_directory(a, entry, zip);
if (r != ARCHIVE_OK)
return r;
/* Get first entry whose local header offset is lower than
@@ -3827,8 +3941,8 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a,
__archive_read_reset_passphrase(a);
/* File entries are sorted by the header offset, we should mostly
- * use __archive_read_consume to advance a read point to avoid redundant
- * data reading. */
+ * use __archive_read_consume to advance a read point to avoid
+ * redundant data reading. */
offset = archive_filter_bytes(&a->archive, 0);
if (offset < zip->entry->local_header_offset)
__archive_read_consume(a,
diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c
index 96d61456d339..3399c0b5f492 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 e77cd08fc3b4..87d8c891bb0b 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 b0f25a6ef0ed..0f7c8cfc31a5 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 ed15451762bf..84e8f80eed7c 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -2032,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.
@@ -2198,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;
@@ -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'",
diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c
index 975555f2e732..3d4d577f081f 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 *,
@@ -219,7 +221,10 @@ static int restore_entry(struct archive_write_disk *);
static int set_acls(struct archive_write_disk *, HANDLE h,
const wchar_t *, struct archive_acl *);
static int set_xattrs(struct archive_write_disk *);
+static int clear_nochange_fflags(struct archive_write_disk *);
static int set_fflags(struct archive_write_disk *);
+static int set_fflags_platform(const wchar_t *, unsigned long,
+ unsigned long);
static int set_ownership(struct archive_write_disk *);
static int set_mode(struct archive_write_disk *, int mode);
static int set_times(struct archive_write_disk *, HANDLE, int,
@@ -556,8 +561,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
@@ -582,6 +589,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)
{
@@ -863,9 +967,11 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
}
if (a->deferred & TODO_FFLAGS) {
+ unsigned long set, clear;
+
fe = current_fixup(a, archive_entry_pathname_w(entry));
- fe->fixup |= TODO_FFLAGS;
- /* TODO: Complete this.. defer fflags from below. */
+ archive_entry_fflags(entry, &set, &clear);
+ fe->fflags_set = set;
}
/*
@@ -1240,7 +1346,7 @@ archive_write_disk_new(void)
}
static int
-disk_unlink(wchar_t *path)
+disk_unlink(const wchar_t *path)
{
wchar_t *fullname;
int r;
@@ -1255,7 +1361,7 @@ disk_unlink(wchar_t *path)
}
static int
-disk_rmdir(wchar_t *path)
+disk_rmdir(const wchar_t *path)
{
wchar_t *fullname;
int r;
@@ -1286,6 +1392,8 @@ restore_entry(struct archive_write_disk *a)
* object is a dir, but that doesn't mean the old
* object isn't a dir.
*/
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
if (disk_unlink(a->name) == 0) {
/* We removed it, reset cached stat. */
a->pst = NULL;
@@ -1360,28 +1468,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;
}
/*
@@ -1405,8 +1530,19 @@ 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 (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ 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);
@@ -1416,6 +1552,8 @@ restore_entry(struct archive_write_disk *a)
en = create_filesystem_object(a);
} else if (!S_ISDIR(a->mode)) {
/* A dir is in the way of a non-dir, rmdir it. */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
if (disk_rmdir(a->name) != 0) {
archive_set_error(&a->archive, errno,
"Can't remove already-existing dir");
@@ -1515,7 +1653,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
}
@@ -1668,6 +1815,8 @@ _archive_write_disk_close(struct archive *_a)
la_chmod(p->name, p->mode);
if (p->fixup & TODO_ACLS)
set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);
+ if (p->fixup & TODO_FFLAGS)
+ set_fflags_platform(p->name, p->fflags_set, 0);
next = p->next;
archive_acl_clear(&p->acl);
free(p->name);
@@ -1784,6 +1933,7 @@ new_fixup(struct archive_write_disk *a, const wchar_t *pathname)
a->fixup_list = fe;
fe->fixup = 0;
fe->name = _wcsdup(pathname);
+ fe->fflags_set = 0;
return (fe);
}
@@ -1827,6 +1977,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 +1997,21 @@ 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 (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ 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 +2035,17 @@ 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 (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ 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 +2061,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. */
@@ -2438,10 +2613,56 @@ set_mode(struct archive_write_disk *a, int mode)
return (r);
}
+static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,
+ unsigned long fflags_clear)
+{
+ DWORD oldflags, newflags;
+ wchar_t *fullname;
+
+ const DWORD settable_flags =
+ FILE_ATTRIBUTE_ARCHIVE |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_NORMAL |
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
+ FILE_ATTRIBUTE_OFFLINE |
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_TEMPORARY;
+
+ oldflags = GetFileAttributesW(name);
+ if (oldflags == (DWORD)-1 &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(name);
+ oldflags = GetFileAttributesW(fullname);
+ }
+ if (oldflags == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ return (ARCHIVE_WARN);
+ }
+ newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;
+ if(SetFileAttributesW(name, newflags) == 0)
+ return (ARCHIVE_WARN);
+ return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+ return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));
+}
+
static int
set_fflags(struct archive_write_disk *a)
{
- (void)a; /* UNUSED */
+ unsigned long set, clear;
+
+ if (a->todo & TODO_FFLAGS) {
+ archive_entry_fflags(a->entry, &set, &clear);
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ return (set_fflags_platform(a->name, set, clear));
+
+ }
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c
index 5a4c45a11e02..cf2a1f959e34 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_xar.c b/libarchive/archive_write_set_format_xar.c
index 36d4a615e2ae..5e4b90e06b3f 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/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
index 690a83c5cd60..2f451d2a0649 100644
--- a/libarchive/test/CMakeLists.txt
+++ b/libarchive/test/CMakeLists.txt
@@ -154,6 +154,7 @@ IF(ENABLE_TEST)
test_read_format_tar.c
test_read_format_tar_concatenated.c
test_read_format_tar_empty_filename.c
+ test_read_format_tar_empty_with_gnulabel.c
test_read_format_tar_empty_pax.c
test_read_format_tar_filename.c
test_read_format_tbz.c
@@ -165,10 +166,12 @@ IF(ENABLE_TEST)
test_read_format_warc.c
test_read_format_xar.c
test_read_format_zip.c
+ test_read_format_zip_7075_utf8_paths.c
test_read_format_zip_comment_stored.c
test_read_format_zip_encryption_data.c
test_read_format_zip_encryption_header.c
test_read_format_zip_encryption_partially.c
+ test_read_format_zip_extra_padding.c
test_read_format_zip_filename.c
test_read_format_zip_high_compression.c
test_read_format_zip_jar.c
diff --git a/libarchive/test/test_entry.c b/libarchive/test/test_entry.c
index a0a2607a156c..0cf13e825afc 100644
--- a/libarchive/test/test_entry.c
+++ b/libarchive/test/test_entry.c
@@ -27,6 +27,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/test/test_entry.c 201247 2009-12-30 05:5
#include <locale.h>
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+
#ifndef HAVE_WCSCPY
static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
{
@@ -337,16 +341,37 @@ DEFINE_TEST(test_entry)
/* TODO: Make this system-independent. */
assertEqualString(archive_entry_fflags_text(e),
"uappnd,nouchg,nodump,noopaque,uunlnk,nosystem");
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
/* Test archive_entry_copy_fflags_text_w() */
- archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,uunlnk");
+ archive_entry_copy_fflags_text_w(e, L" ,nouappnd, nouchg, dump,hidden");
archive_entry_fflags(e, &set, &clear);
- assertEqualInt(16, set);
- assertEqualInt(7, clear);
+ assertEqualInt(UF_HIDDEN, set);
+ assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear);
/* Test archive_entry_copy_fflags_text() */
- archive_entry_copy_fflags_text(e, " ,nouappnd, nouchg, dump,uunlnk");
+ archive_entry_copy_fflags_text(e, " ,nouappnd, nouchg, dump,hidden");
+ archive_entry_fflags(e, &set, &clear);
+ assertEqualInt(UF_HIDDEN, set);
+ assertEqualInt(UF_NODUMP | UF_IMMUTABLE | UF_APPEND, clear);
+#elif defined(_WIN32) && !defined(CYGWIN)
+ archive_entry_copy_fflags_text_w(e, L"rdonly,hidden,nosystem");
+ archive_entry_fflags(e, &set, &clear);
+ assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set);
+ assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear);
+ archive_entry_copy_fflags_text(e, "rdonly,hidden,nosystem");
+ archive_entry_fflags(e, &set, &clear);
+ assertEqualInt(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, set);
+ assertEqualInt(FILE_ATTRIBUTE_SYSTEM, clear);
+#elif defined FS_IOC_GETFLAGS /* Linux */
+ archive_entry_copy_fflags_text_w(e, L"sappnd,schg,dump,noundel");
+ archive_entry_fflags(e, &set, &clear);
+ assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set);
+ assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear);
+ archive_entry_copy_fflags_text(e, "sappnd,schg,dump,noundel");
archive_entry_fflags(e, &set, &clear);
- assertEqualInt(16, set);
- assertEqualInt(7, clear);
+ assertEqualInt(FS_APPEND_FL | FS_IMMUTABLE_FL, set);
+ assertEqualInt(FS_NODUMP_FL | FS_UNRM_FL, clear);
#endif
/* See test_acl_basic.c for tests of ACL set/get consistency. */
diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c
index 2025834ca424..d02fd993f972 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 705b3d989cd5..7dd19157d4d2 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 c537e4f936c4..cd06096eff69 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_mtree.c b/libarchive/test/test_read_format_mtree.c
index 8576d579f9c6..df6f6cde0cdb 100644
--- a/libarchive/test/test_read_format_mtree.c
+++ b/libarchive/test/test_read_format_mtree.c
@@ -717,4 +717,28 @@ DEFINE_TEST(test_read_format_mtree_nonexistent_contents_file)
assertEqualInt(ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+/*
+ * Check mtree file with non-printable ascii characters
+ */
+DEFINE_TEST(test_read_format_mtree_noprint)
+{
+ const char reffile[] = "test_read_format_mtree_noprint.mtree";
+ struct archive_entry *ae;
+ struct archive *a;
+ extract_reference_file(reffile);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, reffile, 11));
+
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+ assertEqualString("Can't parse line 3", archive_error_string(a));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_mtree_noprint.mtree.uu b/libarchive/test/test_read_format_mtree_noprint.mtree.uu
new file mode 100644
index 000000000000..7965cdf29aec
--- /dev/null
+++ b/libarchive/test/test_read_format_mtree_noprint.mtree.uu
@@ -0,0 +1,4 @@
+begin 644 test_read_format_mtree_noprint.mtree
+K(VUT<F5E"F1I<E]O:R!T>7!E/61I<@ID:7)?P[;%H<2'('1Y<&4]9&ER"@``
+`
+end
diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c
index 6392d8f50ff8..f08b06bc69a3 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];
@@ -3740,3 +3756,26 @@ DEFINE_TEST(test_read_format_rar_multivolume_uncompressed_files)
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
}
+
+DEFINE_TEST(test_read_format_rar_ppmd_use_after_free)
+{
+ uint8_t buf[16];
+ const char* reffile = "test_read_format_rar_ppmd_use_after_free.rar";
+
+ struct archive_entry *ae;
+ struct archive *a;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+ assertA(0 == archive_read_support_format_all(a));
+ assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+ assertA(ARCHIVE_OK == archive_read_next_header(a, &ae));
+ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
+ assertA(ARCHIVE_OK == archive_read_next_header(a, &ae));
+ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c
index 0be9c45a5d49..1408f37c49dc 100644
--- a/libarchive/test/test_read_format_rar5.c
+++ b/libarchive/test/test_read_format_rar5.c
@@ -31,171 +31,186 @@
#include <archive_endian.h>
#define PROLOGUE(reffile) \
- struct archive_entry *ae; \
- struct archive *a; \
- \
- (void) a; /* Make the compiler happy if we won't use this variables */ \
- (void) ae; /* in the test cases. */ \
- \
- extract_reference_file(reffile); \
- assert((a = archive_read_new()) != NULL); \
- assertA(0 == archive_read_support_filter_all(a)); \
- assertA(0 == archive_read_support_format_all(a)); \
- assertA(0 == archive_read_open_filename(a, reffile, 10240))
+ struct archive_entry *ae; \
+ struct archive *a; \
+ \
+ (void) a; /* Make the compiler happy if we won't use this variables */ \
+ (void) ae; /* in the test cases. */ \
+ \
+ extract_reference_file(reffile); \
+ assert((a = archive_read_new()) != NULL); \
+ assertA(0 == archive_read_support_filter_all(a)); \
+ assertA(0 == archive_read_support_format_all(a)); \
+ assertA(0 == archive_read_open_filename(a, reffile, 10240))
#define PROLOGUE_MULTI(reffile) \
- struct archive_entry *ae; \
- struct archive *a; \
- \
- (void) a; \
- (void) ae; \
- \
- extract_reference_files(reffile); \
- assert((a = archive_read_new()) != NULL); \
- assertA(0 == archive_read_support_filter_all(a)); \
- assertA(0 == archive_read_support_format_all(a)); \
- assertA(0 == archive_read_open_filenames(a, reffile, 10240))
+ struct archive_entry *ae; \
+ struct archive *a; \
+ \
+ (void) a; \
+ (void) ae; \
+ \
+ extract_reference_files(reffile); \
+ assert((a = archive_read_new()) != NULL); \
+ assertA(0 == archive_read_support_filter_all(a)); \
+ assertA(0 == archive_read_support_format_all(a)); \
+ assertA(0 == archive_read_open_filenames(a, reffile, 10240))
#define EPILOGUE() \
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); \
- assertEqualInt(ARCHIVE_OK, archive_read_free(a))
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); \
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a))
static
int verify_data(const uint8_t* data_ptr, int magic, int size) {
- int i = 0;
+ int i = 0;
- /* This is how the test data inside test files was generated;
- * we are re-generating it here and we check if our re-generated
- * test data is the same as in the test file. If this test is
- * failing it's either because there's a bug in the test case,
- * or the unpacked data is corrupted. */
+ /* This is how the test data inside test files was generated;
+ * we are re-generating it here and we check if our re-generated
+ * test data is the same as in the test file. If this test is
+ * failing it's either because there's a bug in the test case,
+ * or the unpacked data is corrupted. */
- for(i = 0; i < size / 4; ++i) {
- const int k = i + 1;
- const signed int* lptr = (const signed int*) &data_ptr[i * 4];
- signed int val = k * k - 3 * k + (1 + magic);
+ for(i = 0; i < size / 4; ++i) {
+ const int k = i + 1;
+ const signed int* lptr = (const signed int*) &data_ptr[i * 4];
+ signed int val = k * k - 3 * k + (1 + magic);
- if(val < 0)
- val = 0;
+ if(val < 0)
+ val = 0;
- /* *lptr is a value inside unpacked test file, val is the
- * value that should be in the unpacked test file. */
+ /* *lptr is a value inside unpacked test file, val is the
+ * value that should be in the unpacked test file. */
- if(archive_le32dec(lptr) != (uint32_t) val)
- return 0;
- }
+ if(archive_le32dec(lptr) != (uint32_t) val)
+ return 0;
+ }
- return 1;
+ return 1;
}
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;
+ 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;
+ free(buf);
+ return ret;
}
-DEFINE_TEST(test_read_format_rar5_stored)
+DEFINE_TEST(test_read_format_rar5_set_format)
{
- const char helloworld_txt[] = "hello libarchive test suite!\n";
- la_ssize_t file_size = sizeof(helloworld_txt) - 1;
- char buff[64];
-
- PROLOGUE("test_read_format_rar5_stored.rar");
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("helloworld.txt", archive_entry_pathname(ae));
- assertA((int) archive_entry_mtime(ae) > 0);
- assertA((int) archive_entry_ctime(ae) == 0);
- assertA((int) archive_entry_atime(ae) == 0);
- assertEqualInt(file_size, archive_entry_size(ae));
- assertEqualInt(33188, archive_entry_mode(ae));
- assertA(file_size == archive_read_data(a, buff, file_size));
- assertEqualMem(buff, helloworld_txt, file_size);
- assertEqualInt(archive_entry_is_encrypted(ae), 0);
-
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ 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();
+}
- 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;
+ char buff[64];
+
+ PROLOGUE("test_read_format_rar5_stored.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("helloworld.txt", archive_entry_pathname(ae));
+ assertA((int) archive_entry_mtime(ae) > 0);
+ assertA((int) archive_entry_ctime(ae) == 0);
+ assertA((int) archive_entry_atime(ae) == 0);
+ assertEqualInt(file_size, archive_entry_size(ae));
+ assertEqualInt(33188, archive_entry_mode(ae));
+ assertA(file_size == archive_read_data(a, buff, file_size));
+ assertEqualMem(buff, helloworld_txt, file_size);
+ assertEqualInt(archive_entry_is_encrypted(ae), 0);
+
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_compressed)
{
- const int DATA_SIZE = 1200;
- uint8_t buff[1200];
+ const int DATA_SIZE = 1200;
+ uint8_t buff[1200];
- PROLOGUE("test_read_format_rar5_compressed.rar");
+ PROLOGUE("test_read_format_rar5_compressed.rar");
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(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));
- verify_data(buff, 0, DATA_SIZE);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(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));
+ verify_data(buff, 0, DATA_SIZE);
- EPILOGUE();
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiple_files)
{
- const int DATA_SIZE = 4096;
- uint8_t buff[4096];
-
- PROLOGUE("test_read_format_rar5_multiple_files.rar");
-
- /* There should be 4 files inside this test file. Check for their
- * existence, and also check the contents of those test files. */
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- 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));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 2, DATA_SIZE));
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertEqualInt(DATA_SIZE, archive_entry_size(ae));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 3, DATA_SIZE));
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertEqualInt(DATA_SIZE, archive_entry_size(ae));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 4, DATA_SIZE));
-
- /* There should be no more files in this archive. */
-
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const int DATA_SIZE = 4096;
+ uint8_t buff[4096];
+
+ PROLOGUE("test_read_format_rar5_multiple_files.rar");
+
+ /* There should be 4 files inside this test file. Check for their
+ * existence, and also check the contents of those test files. */
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ 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));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 2, DATA_SIZE));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertEqualInt(DATA_SIZE, archive_entry_size(ae));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 3, DATA_SIZE));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertEqualInt(DATA_SIZE, archive_entry_size(ae));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 4, DATA_SIZE));
+
+ /* There should be no more files in this archive. */
+
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
/* This test is really the same as the test above, but it deals with a solid
@@ -206,291 +221,342 @@ 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[4096];
-
- PROLOGUE("test_read_format_rar5_multiple_files_solid.rar");
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- 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));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 2, DATA_SIZE));
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertEqualInt(DATA_SIZE, archive_entry_size(ae));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 3, DATA_SIZE));
-
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertEqualInt(DATA_SIZE, archive_entry_size(ae));
- assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
- assertA(verify_data(buff, 4, DATA_SIZE));
-
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const int DATA_SIZE = 4096;
+ uint8_t buff[4096];
+
+ PROLOGUE("test_read_format_rar5_multiple_files_solid.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ 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));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 2, DATA_SIZE));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertEqualInt(DATA_SIZE, archive_entry_size(ae));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 3, DATA_SIZE));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertEqualInt(DATA_SIZE, archive_entry_size(ae));
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(verify_data(buff, 4, DATA_SIZE));
+
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all)
{
- const char* reffiles[] = {
- "test_read_format_rar5_multiarchive.part01.rar",
- "test_read_format_rar5_multiarchive.part02.rar",
- "test_read_format_rar5_multiarchive.part03.rar",
- "test_read_format_rar5_multiarchive.part04.rar",
- "test_read_format_rar5_multiarchive.part05.rar",
- "test_read_format_rar5_multiarchive.part06.rar",
- "test_read_format_rar5_multiarchive.part07.rar",
- "test_read_format_rar5_multiarchive.part08.rar",
- NULL
- };
-
- PROLOGUE_MULTI(reffiles);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdtar_test", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffiles[] = {
+ "test_read_format_rar5_multiarchive.part01.rar",
+ "test_read_format_rar5_multiarchive.part02.rar",
+ "test_read_format_rar5_multiarchive.part03.rar",
+ "test_read_format_rar5_multiarchive.part04.rar",
+ "test_read_format_rar5_multiarchive.part05.rar",
+ "test_read_format_rar5_multiarchive.part06.rar",
+ "test_read_format_rar5_multiarchive.part07.rar",
+ "test_read_format_rar5_multiarchive.part08.rar",
+ NULL
+ };
+
+ PROLOGUE_MULTI(reffiles);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdcat_test", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("home/antek/temp/build/unrar5/libarchive/bin/bsdtar_test", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_first)
{
- const char* reffiles[] = {
- "test_read_format_rar5_multiarchive.part01.rar",
- "test_read_format_rar5_multiarchive.part02.rar",
- "test_read_format_rar5_multiarchive.part03.rar",
- "test_read_format_rar5_multiarchive.part04.rar",
- "test_read_format_rar5_multiarchive.part05.rar",
- "test_read_format_rar5_multiarchive.part06.rar",
- "test_read_format_rar5_multiarchive.part07.rar",
- "test_read_format_rar5_multiarchive.part08.rar",
- NULL
- };
-
- PROLOGUE_MULTI(reffiles);
- assertA(0 == archive_read_next_header(a, &ae));
- assertA(0 == extract_one(a, ae, 0x35277473));
- assertA(0 == archive_read_next_header(a, &ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffiles[] = {
+ "test_read_format_rar5_multiarchive.part01.rar",
+ "test_read_format_rar5_multiarchive.part02.rar",
+ "test_read_format_rar5_multiarchive.part03.rar",
+ "test_read_format_rar5_multiarchive.part04.rar",
+ "test_read_format_rar5_multiarchive.part05.rar",
+ "test_read_format_rar5_multiarchive.part06.rar",
+ "test_read_format_rar5_multiarchive.part07.rar",
+ "test_read_format_rar5_multiarchive.part08.rar",
+ NULL
+ };
+
+ PROLOGUE_MULTI(reffiles);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertA(0 == extract_one(a, ae, 0x35277473));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_skip_all_but_second)
{
- const char* reffiles[] = {
- "test_read_format_rar5_multiarchive.part01.rar",
- "test_read_format_rar5_multiarchive.part02.rar",
- "test_read_format_rar5_multiarchive.part03.rar",
- "test_read_format_rar5_multiarchive.part04.rar",
- "test_read_format_rar5_multiarchive.part05.rar",
- "test_read_format_rar5_multiarchive.part06.rar",
- "test_read_format_rar5_multiarchive.part07.rar",
- "test_read_format_rar5_multiarchive.part08.rar",
- NULL
- };
-
- PROLOGUE_MULTI(reffiles);
- assertA(0 == archive_read_next_header(a, &ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertA(0 == extract_one(a, ae, 0xE59665F8));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffiles[] = {
+ "test_read_format_rar5_multiarchive.part01.rar",
+ "test_read_format_rar5_multiarchive.part02.rar",
+ "test_read_format_rar5_multiarchive.part03.rar",
+ "test_read_format_rar5_multiarchive.part04.rar",
+ "test_read_format_rar5_multiarchive.part05.rar",
+ "test_read_format_rar5_multiarchive.part06.rar",
+ "test_read_format_rar5_multiarchive.part07.rar",
+ "test_read_format_rar5_multiarchive.part08.rar",
+ NULL
+ };
+
+ PROLOGUE_MULTI(reffiles);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertA(0 == extract_one(a, ae, 0xE59665F8));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_blake2)
{
- const la_ssize_t proper_size = 814;
- uint8_t buf[814];
+ const la_ssize_t proper_size = 814;
+ uint8_t buf[814];
- PROLOGUE("test_read_format_rar5_blake2.rar");
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualInt(proper_size, archive_entry_size(ae));
+ PROLOGUE("test_read_format_rar5_blake2.rar");
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(proper_size, archive_entry_size(ae));
- /* Should blake2 calculation fail, we'll get a failure return
- * value from archive_read_data(). */
+ /* Should blake2 calculation fail, we'll get a failure return
+ * value from archive_read_data(). */
- assertA(proper_size == archive_read_data(a, buf, proper_size));
+ assertA(proper_size == archive_read_data(a, buf, proper_size));
- /* To be extra pedantic, let's also check crc32 of the poem. */
- assertEqualInt(crc32(0, buf, proper_size), 0x7E5EC49E);
+ /* To be extra pedantic, let's also check crc32 of the poem. */
+ assertEqualInt(crc32(0, buf, proper_size), 0x7E5EC49E);
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_arm_filter)
{
- /* This test unpacks a file that uses an ARM filter. The DELTA
- * and X86 filters are tested implicitly in the "multiarchive_skip"
- * test. */
+ /* This test unpacks a file that uses an ARM filter. The DELTA
+ * and X86 filters are tested implicitly in the "multiarchive_skip"
+ * test. */
- const la_ssize_t proper_size = 90808;
- uint8_t buf[90808];
+ const la_ssize_t proper_size = 90808;
+ uint8_t buf[90808];
- PROLOGUE("test_read_format_rar5_arm.rar");
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualInt(proper_size, archive_entry_size(ae));
- assertA(proper_size == archive_read_data(a, buf, proper_size));
+ PROLOGUE("test_read_format_rar5_arm.rar");
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(proper_size, archive_entry_size(ae));
+ assertA(proper_size == archive_read_data(a, buf, proper_size));
- /* Yes, RARv5 unpacker itself should calculate the CRC, but in case
- * the DONT_FAIL_ON_CRC_ERROR define option is enabled during compilation,
- * let's still fail the test if the unpacked data is wrong. */
- assertEqualInt(crc32(0, buf, proper_size), 0x886F91EB);
+ /* Yes, RARv5 unpacker itself should calculate the CRC, but in case
+ * the DONT_FAIL_ON_CRC_ERROR define option is enabled during compilation,
+ * let's still fail the test if the unpacked data is wrong. */
+ assertEqualInt(crc32(0, buf, proper_size), 0x886F91EB);
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_stored_skip_all)
{
- const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
-
- PROLOGUE(fname);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("cebula.txt", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
+
+ PROLOGUE(fname);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("cebula.txt", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_stored_skip_in_part)
{
- const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
- char buf[6];
-
- /* Skip first, extract in part rest. */
-
- PROLOGUE(fname);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("cebula.txt", archive_entry_pathname(ae));
- assertA(6 == archive_read_data(a, buf, 6));
- assertEqualInt(0, memcmp(buf, "Cebula", 6));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(4 == archive_read_data(a, buf, 4));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
+ char buf[6];
+
+ /* Skip first, extract in part rest. */
+
+ PROLOGUE(fname);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("cebula.txt", archive_entry_pathname(ae));
+ assertA(6 == archive_read_data(a, buf, 6));
+ assertEqualInt(0, memcmp(buf, "Cebula", 6));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(4 == archive_read_data(a, buf, 4));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_stored_skip_all_but_first)
{
- const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
- char buf[405];
-
- /* Extract first, skip rest. */
-
- PROLOGUE(fname);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
- assertA(405 == archive_read_data(a, buf, sizeof(buf)));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("cebula.txt", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
+ char buf[405];
+
+ /* Extract first, skip rest. */
+
+ PROLOGUE(fname);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
+ assertA(405 == archive_read_data(a, buf, sizeof(buf)));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("cebula.txt", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_stored_skip_all_in_part)
{
- const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
- char buf[4];
-
- /* Extract in part all */
+ const char* fname = "test_read_format_rar5_stored_manyfiles.rar";
+ char buf[4];
+
+ /* Extract in part all */
+
+ PROLOGUE(fname);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
+ assertA(4 == archive_read_data(a, buf, 4));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("cebula.txt", archive_entry_pathname(ae));
+ assertA(4 == archive_read_data(a, buf, 4));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(4 == archive_read_data(a, buf, 4));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
+}
- PROLOGUE(fname);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("make_uue.tcl", archive_entry_pathname(ae));
- assertA(4 == archive_read_data(a, buf, 4));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("cebula.txt", archive_entry_pathname(ae));
- assertA(4 == archive_read_data(a, buf, 4));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(4 == archive_read_data(a, buf, 4));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- 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[] = {
- "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 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_first)
{
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
/* "skip_all_but_scnd" -> am I hitting the test name limit here after
@@ -498,273 +564,633 @@ DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_first)
DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_scnd)
{
- 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 == 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 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ 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 == 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_third)
{
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("elf-Linux-ARMv7-ls", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_multiarchive_solid_skip_all_but_last)
{
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- 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();
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ 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_solid_skip_all)
{
- const char* reffile = "test_read_format_rar5_solid.rar";
-
- /* Skip all */
-
- PROLOGUE(reffile);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffile = "test_read_format_rar5_solid.rar";
+
+ /* Skip all */
+
+ PROLOGUE(reffile);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_first)
{
- const char* reffile = "test_read_format_rar5_solid.rar";
-
- /* Extract first, skip rest */
-
- PROLOGUE(reffile);
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffile = "test_read_format_rar5_solid.rar";
+
+ /* Extract first, skip rest */
+
+ PROLOGUE(reffile);
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_second)
{
- const char* reffile = "test_read_format_rar5_solid.rar";
-
- /* Skip first, extract second, skip rest */
-
- PROLOGUE(reffile);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- 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 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffile = "test_read_format_rar5_solid.rar";
+
+ /* Skip first, extract second, skip rest */
+
+ PROLOGUE(reffile);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ 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 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_solid_skip_all_but_last)
{
- const char* reffile = "test_read_format_rar5_solid.rar";
-
- /* Skip all but last, extract last */
-
- PROLOGUE(reffile);
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test1.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test2.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test3.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test4.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test5.bin", archive_entry_pathname(ae));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test6.bin", archive_entry_pathname(ae));
- assertA(0 == extract_one(a, ae, 0x36A448FF));
- assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
- EPILOGUE();
+ const char* reffile = "test_read_format_rar5_solid.rar";
+
+ /* Skip all but last, extract last */
+
+ PROLOGUE(reffile);
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test1.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test2.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test3.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test4.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test5.bin", archive_entry_pathname(ae));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test6.bin", archive_entry_pathname(ae));
+ assertA(0 == extract_one(a, ae, 0x36A448FF));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ EPILOGUE();
}
DEFINE_TEST(test_read_format_rar5_extract_win32)
{
- PROLOGUE("test_read_format_rar5_win32.rar");
- 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));
- EPILOGUE();
+ 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();
}
DEFINE_TEST(test_read_format_rar5_block_by_block)
{
- /* This test uses strange buffer sizes intentionally. */
-
- struct archive_entry *ae;
- struct archive *a;
- uint8_t buf[173];
- int bytes_read;
- uint32_t computed_crc = 0;
-
- extract_reference_file("test_read_format_rar5_compressed.rar");
- assert((a = archive_read_new()) != NULL);
- assertA(0 == archive_read_support_filter_all(a));
- assertA(0 == archive_read_support_format_all(a));
- assertA(0 == archive_read_open_filename(a, "test_read_format_rar5_compressed.rar", 130));
- assertA(0 == archive_read_next_header(a, &ae));
- assertEqualString("test.bin", archive_entry_pathname(ae));
- assertEqualInt(1200, archive_entry_size(ae));
-
- /* File size is 1200 bytes, we're reading it using a buffer of 173 bytes.
- * Libarchive is configured to use a buffer of 130 bytes. */
-
- while(1) {
- /* archive_read_data should return one of:
- * a) 0, if there is no more data to be read,
- * b) negative value, if there was an error,
- * c) positive value, meaning how many bytes were read.
- */
-
- bytes_read = archive_read_data(a, buf, sizeof(buf));
- assertA(bytes_read >= 0);
- if(bytes_read <= 0)
- break;
-
- computed_crc = crc32(computed_crc, buf, bytes_read);
- }
-
- assertEqualInt(computed_crc, 0x7CCA70CD);
- EPILOGUE();
+ /* This test uses strange buffer sizes intentionally. */
+
+ struct archive_entry *ae;
+ struct archive *a;
+ uint8_t buf[173];
+ int bytes_read;
+ uint32_t computed_crc = 0;
+
+ extract_reference_file("test_read_format_rar5_compressed.rar");
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+ assertA(0 == archive_read_support_format_all(a));
+ assertA(0 == archive_read_open_filename(a, "test_read_format_rar5_compressed.rar", 130));
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+ assertEqualInt(1200, archive_entry_size(ae));
+
+ /* File size is 1200 bytes, we're reading it using a buffer of 173 bytes.
+ * Libarchive is configured to use a buffer of 130 bytes. */
+
+ while(1) {
+ /* archive_read_data should return one of:
+ * a) 0, if there is no more data to be read,
+ * b) negative value, if there was an error,
+ * c) positive value, meaning how many bytes were read.
+ */
+
+ bytes_read = archive_read_data(a, buf, sizeof(buf));
+ assertA(bytes_read >= 0);
+ if(bytes_read <= 0)
+ break;
+
+ computed_crc = crc32(computed_crc, buf, bytes_read);
+ }
+
+ 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(archive_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != 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_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != 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_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != 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_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != 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");
+
+ /* This test should fail on parsing the header. */
+ assertA(archive_read_next_header(a, &ae) != ARCHIVE_OK);
+
+ /* This archive is invalid. However, processing it shouldn't cause any
+ * errors related to buffer underflow when using -fsanitize. */
+ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != archive_read_next_header(a, &ae));
+
+ EPILOGUE();
+}
+
+DEFINE_TEST(test_read_format_rar5_distance_overflow)
+{
+ uint8_t buf[16];
+
+ PROLOGUE("test_read_format_rar5_distance_overflow.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+
+ /* This archive is invalid. However, processing it shouldn't cause any
+ * errors related to variable overflows when using -fsanitize. */
+ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != archive_read_next_header(a, &ae));
+
+ EPILOGUE();
+}
+
+DEFINE_TEST(test_read_format_rar5_nonempty_dir_stream)
+{
+ uint8_t buf[16];
+
+ PROLOGUE("test_read_format_rar5_nonempty_dir_stream.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+
+ /* This archive is invalid. However, processing it shouldn't cause any
+ * errors related to buffer overflows when using -fsanitize. */
+ assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
+
+ /* This test only cares about not returning success here. */
+ assertA(ARCHIVE_OK != archive_read_next_header(a, &ae));
+
+ EPILOGUE();
+}
+
+DEFINE_TEST(test_read_format_rar5_fileattr)
+{
+ unsigned long set, clear, flag;
+
+ flag = 0;
+
+ PROLOGUE("test_read_format_rar5_fileattr.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG);
+ assertEqualString("readonly.txt", archive_entry_pathname(ae));
+ assertEqualString("rdonly", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_READONLY;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_READONLY;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG);
+ assertEqualString("hidden.txt", archive_entry_pathname(ae));
+ assertEqualString("hidden", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_HIDDEN;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_HIDDEN;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0644 | AE_IFREG);
+ assertEqualString("system.txt", archive_entry_pathname(ae));
+ assertEqualString("system", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_SYSTEM;;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_SYSTEM;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0444 | AE_IFREG);
+ assertEqualString("ro_hidden.txt", archive_entry_pathname(ae));
+ assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_READONLY | UF_HIDDEN;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR);
+ assertEqualString("dir_readonly", archive_entry_pathname(ae));
+ assertEqualString("rdonly", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_READONLY;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_READONLY;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR);
+ assertEqualString("dir_hidden", archive_entry_pathname(ae));
+ assertEqualString("hidden", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_HIDDEN;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_HIDDEN;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0755 | AE_IFDIR);
+ assertEqualString("dir_system", archive_entry_pathname(ae));
+ assertEqualString("system", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_SYSTEM;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_SYSTEM;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualInt(archive_entry_mode(ae), 0555 | AE_IFDIR);
+ assertEqualString("dir_rohidden", archive_entry_pathname(ae));
+ assertEqualString("rdonly,hidden", archive_entry_fflags_text(ae));
+ archive_entry_fflags(ae, &set, &clear);
+#if defined(__FreeBSD__)
+ flag = UF_READONLY | UF_HIDDEN;
+#elif defined(_WIN32) && !defined(CYGWIN)
+ flag = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN;
+#endif
+ assertEqualInt(flag, set & flag);
+
+ EPILOGUE();
}
diff --git a/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu b/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu
new file mode 100644
index 000000000000..8fefa282e733
--- /dev/null
+++ b/libarchive/test/test_read_format_rar5_distance_overflow.rar.uu
@@ -0,0 +1,9 @@
+begin 644 test_read_format_rar5_distance_overflow.rar
+M4F%R(1H'`0"-[P+2``(''/\@("`@_R4``B`@("`@("`@("`@(/__("`@("`@
+M(/\@("`@("`@((9ML63,PX"&AK%:S+?_(/\@_R#_(/\@_R#_(/\@`"``!R`@
+MR<G)``#_(,G)R?___R#___\@____(/___R#___\@____R4#)R<G___\@____
+M(/\@____("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(/__________
+M____________________________________________________("`@("`@
+.("`@("`@("`@("#_("``
+`
+end
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 000000000000..f9a5ce6926a8
--- /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_fileattr.rar.uu b/libarchive/test/test_read_format_rar5_fileattr.rar.uu
new file mode 100644
index 000000000000..456b02504066
--- /dev/null
+++ b/libarchive/test/test_read_format_rar5_fileattr.rar.uu
@@ -0,0 +1,13 @@
+begin 640 test_read_format_rar5_fileattr.rar
+M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``-$;*1"@"`PN+``2+`"&+W8Z;@```
+M#')E861O;FQY+G1X=`H#`J*C5`BE!M4!,3(S-#4V-S@@#0JYZ,#-)@(#"XL`
+M!(L`(F^$/86````*:&ED9&5N+G1X=`H#`G(Q.0^E!M4!,C,T-38W.#D@#0H>
+M%_EV)@(#"XL`!(L`)"V,TBB````*<WES=&5M+G1X=`H#`F:K01.E!M4!,S0U
+M-C<X.3`@#0HQ>$42*0(#"XL`!(L`(RZ?$!V````-<F]?:&ED9&5N+G1X=`H#
+M`JIEBD2M!M4!-C<X.3`Q,C,@#0HQAL?6)@(#"P`%`!$`````@```#&1I<E]R
+M96%D;VYL>0H#`HS,%ABE!M4!Q!O^+"0"`PL`!0`2`````(````ID:7)?:&ED
+M9&5N"@,">JRV&:4&U0'&L*8=)`(#"P`%`!0`````@```"F1I<E]S>7-T96T*
+M`P(@+D4;I0;5`2YJ1$0F`@,+``4`$P````"````,9&ER7W)O:&ED9&5N"@,"
+0CW7S@JT&U0$==U91`P4$````
+`
+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 000000000000..0ec085d478d5
--- /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 000000000000..9b78c9b36cef
--- /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 000000000000..694a27f6ff6c
--- /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 000000000000..57ffad725643
--- /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_nonempty_dir_stream.rar.uu b/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu
new file mode 100644
index 000000000000..c508c1f97ffd
--- /dev/null
+++ b/libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu
@@ -0,0 +1,9 @@
+begin 644 test_read_format_rar5_nonempty_dir_stream.rar
+M4F%R(1H'`0"-[P+2``(''($'$7\`_R4``BP<`0(`(0#_Y@```"#2````____
+M_P`(`/__^P#_W0`"(8#_`(:&;;%DS+?,L8```!;(&P#>``#__^_P```4```&
+M`````````````+`!`@`A`/_F````(-(```#_____``@`___[`/_=``(A``++
+M``"`]/^`!P!#^_____\"(2$!`@`A````_R4``B$A`0(`@````0```"#&`/_=
+M``(A@/\`AH9ML63,M\R`_P```,@;`````!@`````_0`````````!87(A&@<`
+5`(WO`M(``O8'&X`'`#C[_X`E``(A
+`
+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 000000000000..285bdb91caf9
--- /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 000000000000..611c2af237d2
--- /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 000000000000..f603e3c04db5
--- /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 000000000000..12d9e2550d30
--- /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 59920490e998..16d9fce1d381 100644
--- a/libarchive/test/test_read_format_rar5_win32.rar.uu
+++ b/libarchive/test/test_read_format_rar5_win32.rar.uu
@@ -1,68 +1,69 @@
begin 644 test_read_format_rar5_win32.rar
-M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``/^"F5B0"`POI`@2P"2#-<,I\@`4`
-M"'1E<W0N8FEN"@,"/.U\@0U:U`'*]&4!)V5@5!]5=EV_E))QR<]EEED,<-EE
-MDQC%DA.&0DDDX9"0DDDD)..222$(220A"220DDA"$A+-M6][?3,.@'T=`ZX_
-MGKJ?]T'^]]KWW7FO.E_H`$`!`,@@GY^/;U]/+Q[^[LZ^KIZ.?EY./BX>#?W=
-MS:V=?6U=33TM#/S<S*R<C'QL7$P\&_O;R[N;BWMK6TL[*QKZZMJZJIIZ6DHZ
-M*AH)^=G)N9EY:4DTLE(R$>A0'XZ,BHF'.G#9J&A(*`?GQ[>GAV='-R<6]M:V
-MEG96-A7UU;65=54R$W]?R/T"4^B4Y_A$ET6*,&>6%VHX*:8IE!?F0M%VG48R
-MPSC`J$?_H)3&:0I?95"L5E8+BP&1:#<N!W7A!L`/,02,@GF85C0+IJ&6V#>;
-MAX.`8'(/KF$3H*9V&`\#;>A[O8=7T(W\6$"-&#'G"AY-"8-B^.#F.APB!*GA
-MA18[8T/Y\5"`;"$-"(32,9R0+R4`8B?AZB4"`POG`@2`("#&LA-^@`4`"71E
-M<W0Q+F)I;@H#`A*5H8`-6M0!R/!C`1!D168O5P5_4DDDDDDDDDDDDDDDDDDD
-MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD^_/D;F=O/:[?;Z2Z_N
-M[6[OF=&;N]._TF,S#.GS#C<WS/.'/XKG;\@`@`@!L,/'3AM\>_MZ^GGY>/AW
-M]W;V=?5T]'/S<O)Q\7#P;^]N[FWM;.QKZVKJ:>EHZ&?G9N9EY63D8^-BXF'A
-M8.!?WU[>7=U<W%O;6MI9V5C85]=6UE755-13TU+24=%0T$_/3LY-S4S,2\M*
-MRDG)2,A'QT;&1<5$Q$/#0L)!P4#`/[\^^]['VA2X4M7TE0H#.Q@!;-"-XC0$
-M.A"U@6`X1/;M+3@AZ=F@%F`%LT(W2-`0Z$+6!8#A$]N4M."'IR:`68`6S0C<
-M(T!#H0M8%@.$3VW2TX(>FYH!9@!;-"-LC0$.A"U@6`X1-8__0K^QS*_>TU/6
-MRJ_(8IKGE-.XIH6GK_HGV@`)6BG`)0(#"^8"!(`@(,NO9O&`!0`)=&5S=#(N
-M8FEN"@,"A/;"@`U:U`'/]F(!$&1%9B]7!7]2222222222222222222222222
-M2222222222222222222222222222222222223[\^1N9V\]KM]OI+K^[M;N^9
-MT9N[T[_28S,,Z?,.-S?,\X<_BN=OR`"`"`&IAXZ<-OCW]O7T\_+Q\._N[>SK
-MZNGHY^;EY./BX>#?WMW<V]K9V-?6U=33TM'0S\[-S,O*R<C'QL7$P\+!P+^^
-MO;R[NKFXM[:UM+.RL;"OKJVLJZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64DY*1
-MD(^.C8R+BHF(AX:%A(."@8!_?GWWO8^T*7"EJ^DJ%`9V,`+9H1O$:`AT(6L"
-MP'")[=I:<$/3LT`LP`MFA&Z1H"'0A:P+`<(GMREIP0].30"S`"V:$;A&@(="
-M%K`L!PB>VZ6G!#TW-`+,`+9H1MD:`AT(6L"P'")K'_Z%?V.97[VFIZV57Y#%
-M-<\IIW%-"T]?]$^TVMWAVR4"`POT`@2`("#9([&?@`4`"71E<W0S+F)I;@H#
-M`H1PWX`-6M0!RN%P`1!D554O5P5^))))))))))))))))))))))))))))))))
-M))))))))))))))))))))))))))))))SO.\[S<:UGCPYS=>O+LEY]]M9^&\\,
-MWO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#848765^/?V]?3S\O'P[^[M[.OJ
-MZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z]
-MO+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923DI&0
-MCXZ-C(N*B8B'AH6$@X*!@']^??=]3[8AOPALOH4&#J;"`FDX$V"95@TP3;@P
-MF6DRPI=O")0\&G@!T,0$TG`FN3*KFER;<&$RTF6%+MV1*'@T[`.AB`FDX$UB
-M958T/&FX`#:3+"EVZ(E#P:=`'0Q`32<":I,JJ:'C3<`!M)EA2^4__8C^QK(_
-M>\:'K=J/R*(<ZY#CV(<&IZ_]"_:`$R,0$"4"`POT`@2`("#4/L00@`4`"71E
-M<W0T+F)I;@H#`@*,&8$-6M0!R^!P`1!D554O5P5^I)))))))))))))))))))
-M))))))))))))))))))))))))))))))))))))))))))SO.\YS<:UGCPYS=>O+
-MLEY]]M9^&\\,WO7CO^DQF89X_9MAKF??:-?Q7/7Y`!`!`#7=,+*J?'O[>OIY
-M^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV;F9>5DY&/C
-M8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3<U,
-MS$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_/ON^Z^V(;\(;+Z%!@ZFP@)I.
-M!-@F58-,$VX,)EI,L*7:Y$H>#2X!T,0$TG`FL3*K&EB;<&$RTF6%+MX1*'@T
-M\`.AB`FDX$U2954T/&FX`#:3+"EV[(E#P:=@'0Q`32<":A,JH:'C3<`!M)EA
-M2^=?_L1_8UD?O>-#UNU'Y%$.=<AQ[$.#4]?^A?M`Y!DD9R4"`PO\`@2`("#R
-M5=&Y@`4`"71E<W0U+F)I;@H#`BI\2($-6M0!S.]X`1!D554O=6ZDDDDDDDDD
-MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG.\[SO-Q
-MFM>/..;N7KV[)>??;>LS[/'-[UX[_28S,,\?L,TUS/OM&_Q7/7Y`!`!`#6<+
-MK*J?'O[>OIY^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGY
-MV;F9>5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14
-M-!/ST[.3<U,S$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_/ON^X^W$)PA)M
-MD@@5^H.&2UDI2.+AC"BYI<-/"!MIL`Z/;L$0M;)V8*FSADM9*4CBP8PHL:6#
-M3P@;:;`.CVZ!$+6R=&"ILX9+62E(XJ&,**FE0T\(&VFP#H]N01"ULG)@J;.&
-M2UDI2.*!C"BAI0-/"!MIL`Z/G'_[B/[&LC][QH>MVH_(HAYUR'CV(>#4]?^@
-M_:`_"MBZ)0(#"_P"!(`@(/](I#:`!0`)=&5S=#8N8FEN"@,"%79G@0U:U`'-
-M[G@!$&1552]U;J2222222222222222222222222222222222222222222222
-M222222222222222<[SO.\W&:UX\XYNY>O;LEY]]MZS/L\<WO7CO])C,PSQ^P
-MS37,^^T;_%<]?D`$`$`-TX7654^/?V]?3S\O'P[^[M[.OJZ>CGYN7DX^+AX-
-M_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`O[Z]O+NZN;BWMK6TL[
-M*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923DI&0CXZ-C(N*B8B'AH
-M6$@X*!@']^??=]Q]N(3A"3;)!`K]0<,EK)2D<7#&%%S2X:>$#;38!T>W8(A:
-MV3LP5-G#):R4I'%@QA18TL&GA`VTV`='MT"(6MDZ,%39PR6LE*1Q4,845-*A
-MIX0-M-@'1[<@B%K9.3!4V<,EK)2D<4#&%%#2@:>$#;38!T?./_W$?V-9'[WC
-;0];M1^11#SKD/'L0\&IZ_]!^T!UW5E$#!00`
+M4F%R(1H'`0#SX8+K"P$%!P`&`0&`@(``EV&"+"$"`PL`!0`0`````(````=T
+M97-T9&ER"@,"5N<QTHH!U0&_NE<D)`(#"^D"!+`)(,UPRGR``P`(=&5S="YB
+M:6X*`P(\[7R!#5K4`<KT90$G96!4'U5V7;^4DG')SV6660QPV663&,62$X9"
+M223AD)"2220DXY))(0A))"$)))"22$(2$LVU;WM],PZ`?1T#KC^>NI_W0?[W
+MVO?=>:\Z7^@`0`$`R""?GX]O7T\O'O[NSKZNGHY^7DX^+AX-_=W-K9U];5U-
+M/2T,_-S,K)R,?&Q<3#P;^]O+NYN+>VM;2SLK&OKJVKJJFGI:2CHJ&@GYV<FY
+MF7EI232R4C(1Z%`?CHR*B8<Z<-FH:$@H!^?'MZ>'9T<W)Q;VUK:6=E8V%?75
+MM95U53(3?U_(_0)3Z)3G^$2718HP9Y87:C@IIBF4%^9"T7:=1C+#.,"H1_^@
+ME,9I"E]E4*Q65@N+`9%H-RX'=>$&P`\Q!(R">9A6-`NFH9;8-YN'@X!@<@^N
+M81.@IG88#P-MZ'N]AU?0C?Q80(T8,><*'DT)@V+XX.8Z'"($J>&%%CMC0_GQ
+M4(!L(0T(A-(QG)`O)0`QGD%J)0(#"^L"!(`@(,:R$WZ``P`)=&5S=#$N8FEN
+M"@,"$I6A@`U:U`'-\6<!$&1&92]7!7^22222222222222222222222222222
+M222222222222222222222222222222223GY^RW,[??J[><7#G7SNUN[YG1F[
+MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&PPZ<-FGU\_'O[>OIY^7CX=_=V
+M]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV;F9>5DY&/C8N)AX6#@
+M7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!/ST[.3<U,S$O+2LI)
+MR4C(1\=&QD7%1,1#PT+"0<%`P#_[WL?R%+A2U?J5"@,[&`%LT(W2-`0Z$+6!
+M8#A$]N4M."'IR:`68`6S0C<(T!#H0M8%@.$3VW2TX(>FYH!9@!;-"-LC0$.A
+M"U@6`X1/;5+3@AZ:F@%F`%LT(VB-`0Z$+6!8#A$UA]Z%,?GS*>SVE?X8*>=C
+M4\-BG=.J=HXIZ?]R?M!(:PP')0(#"^L"!(`@(<NO9O&``P`)=&5S=#(N8FEN
+M"@,"A/;"@`U:U`',\&<!$&1&92]7!7^22222222222222222222222222222
+M222222222222222222222222222222223GY^RW,[??J[><7#G7SNUN[YG1F[
+MO3O]%F,,Z?#,/6YOF>>GO;]_`_=OV`"`"`&IATX;-/KY^/?V]?3S\O'P[^[M
+M[.OJZ>CGYN7DX^+AX-_>W=S;VMG8U];5U-/2T=#/SLW,R\K)R,?&Q<3#PL'`
+MO[Z]O+NZN;BWMK6TL[*QL*^NK:RKJJFHIZ:EI*.BH:"?GIV<FYJ9F)>6E923
+MDI&0CXZ-C(N*B8B'AH6$@X*!@'_WO8_D*7"EJ_4J%`9V,`+9H1ND:`AT(6L"
+MP'")[<I:<$/3DT`LP`MFA&X1H"'0A:P+`<(GMNEIP0]-S0"S`"V:$;9&@(="
+M%K`L!PB>VJ6G!#TU-`+,`+9H1M$:`AT(6L"P'")K#[T*8_/F4]GM*_PP4\[&
+MIX;%.Z=4[1Q3T_[D_:#Z7*U;)0(#"_<"!(`@(-DCL9^``P`)=&5S=#,N8FEN
+M"@,"A'#?@`U:U`'.YG,!$&1552]7!7XDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG>\[SO-RUK/'AS>Z]>]72\[VUGX;
+MSPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!LK,L+N/CW]O7T\_+Q\._N
+M[>SKZNGHY^;EY./BX>#?WMW<V]K9V-?6U=33TM'0S\[-S,O*R<C'QL7$P\+!
+MP+^^O;R[NKFXM[:UM+.RL;"OKJVLJZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64
+MDY*1D(^.C8R+BHF(AX:%A(."@8!_?O_[[UOQB$^$)+[%!@ZJP@)I6!-DF59-
+M,DTP85+::84MW9$H?1AV`=#$!-*P)L$RK!I@FF#"I;33"ENZ(E#Z,.@#H8@)
+MI6!-<F57-#AI,`!MIIA2W<D2A]&'(!T,0$TK`FX)E7!H<-)@`-M-,*7RW][$
+M+?7[(?3[2/T-$/>UH>G1#RK4/$L0^/_<7F@SHER0)0(#"_<"!(`@(-0^Q!"`
+M`P`)=&5S=#0N8FEN"@,"`HP9@0U:U`'/YW,!$&1552]7!7XDDDDDDDDDDDDD
+MDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDG.\[SG-RUK/'
+MASFZ]>]72\[VUGX;SPS6]^._Z+,89X_&9MAOF??:->O/X'?K\@`@`@!L*,KK
+M*_'O[>OIY^7CX=_=V]G7U=/1S\W+R<?%P\&_O;NYM[6SL:^MJZFGI:.AGYV;
+MF9>5DY&/C8N)AX6#@7]]>WEW=7-Q;VUK:6=E8V%?75M95U5344]-2TE'14-!
+M/ST[.3<U,S$O+2LI)R4C(1\=&QD7%1,1#PT+"0<%`P#^_?_WWJ?C$.>$.%]B
+M@P=380$TG`FR3*LFF2;D&$RVFF%+=@B4/HPP`=#$!-)P)KDRJYI<FY!A,MII
+MA2W=D2A]&'8!T,0$TG`FL3*K&APTY``;::84MW1$H?1AT`=#$!-)P)JDRJIH
+M<-.0`&VFF%+Y3^]B%/K]D/I]I'Z&B'O:T/3HAY5J'B6(?'_N+S0M?'0I)0(#
+M"X`#!(`@(/)5T;F``P`)=&5S=#4N8FEN"@,"*GQ(@0U:U`'([WP!$&1552]U
+M;J2222222222222222222222222222222222222222222222222222222222
+M222<[SO.\W+-:\><<W<O7O5TO.]MYF?9XZUO?CO]%F,,\?C,,TUW/OM&_7OX
+M'GK\@`@`@!K.%UE5/CW]O7T\_+Q\._N[>SKZNGHY^;EY./BX>#?WMW<V]K9V
+M-?6U=33TM'0S\[-S,O*R<C'QL7$P\+!P+^^O;R[NKFXM[:UM+.RL;"OKJVLJ
+MZJIJ*>FI:2CHJ&@GYZ=G)N:F9B7EI64DY*1D(^.C8R+BHF(AX:%A(."@8!_?
+MO_[[W'XXA.D)1LT$"OW!PR6LE*QQ<,847-+AIH@;:C`.CV[!$+6S=F"ILX9+
+M62E8XL&,*+&E@TT0-M1@'1[=`B%K9NC!4V<,EK)2L<5#&%%32H::(&VHP#H]
+MN01"ULW)@J;.&2UDI6.*!C"BAI0--$#;48!T?G'][B''U_9#Z?:1^AHA[VM#
+MT\$/*RH>)D0^/_</F@#V;XCT)0(#"X`#!(`@(/](I#:``P`)=&5S=#8N8FEN
+M"@,"%79G@0U:U`')[GP!$&1552]U;J222222222222222222222222222222
+M2222222222222222222222222222222<[SO.\W+-:\><<W<O7O5TO.]MYF?9
+MXZUO?CO]%F,,\?C,,TUW/OM&_7OX'GK\@`@`@!NG"ZRJGQ[^WKZ>?EX^'?W=
+MO9U]73T<_-R\G'Q</!O[V[N;>UL[&OK:NIIZ6CH9^=FYF7E9.1CXV+B8>%@X
+M%_?7MY=W5S<6]M:VEG96-A7UU;65=54U%/34M)1T5#03\].SDW-3,Q+RTK*2
+M<E(R$?'1L9%Q43$0\-"PD'!0,`_OW_]][C\<0G2$HV:"!7[@X9+62E8XN&,*
+M+FEPTT0-M1@'1[=@B%K9NS!4V<,EK)2L<6#&%%C2P::(&VHP#H]N@1"ULW1@
+MJ;.&2UDI6.*AC"BII4--$#;48!T>W((A:V;DP5-G#):R4K'%`QA10TH&FB!M
+KJ,`Z/SC^]Q#CZ_LA]/M(_0T0][6AZ>"'E94/$R(?'_N'S0`==U91`P4$````
`
end
diff --git a/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu b/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu
new file mode 100644
index 000000000000..1363386530bb
--- /dev/null
+++ b/libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu
@@ -0,0 +1,10 @@
+begin 644 test_read_format_rar_ppmd_use_after_free.rar
+M4F%R(1H'``1G=$Q26`!W````>U!+`P0R`'#_J7\`+@TU'`#]`0`7__]"0D)"
+M+W5N<V5T"6=I9`UD#1T+``!"`````````&%R(1H'``1G>TQ26`!W=&@`[E!+
+M`P0Q`'#_(````"`@(+<@!/T`("`@("`@("`@("`@("`@("`@("`@("`@("`@
+M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@
+M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(`1G=$Q26`!W````
+M>U!+`P0R`'#_J7\`+@TU'`#]`0`7__]"0D)"+W5N<V5T"6=I9`UD#1T+``!"
+@`````````&%R(1H'``1G>TQ26`!W=&@`[E!+`P0Q`'``
+`
+end
diff --git a/libarchive/test/test_read_format_raw.c b/libarchive/test/test_read_format_raw.c
index 831bcec11de3..0dac8bfbab4a 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);
@@ -117,4 +118,30 @@ DEFINE_TEST(test_read_format_raw)
assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* 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));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(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 000000000000..cf1f7b307d4e
--- /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_tar_empty_with_gnulabel.c b/libarchive/test/test_read_format_tar_empty_with_gnulabel.c
new file mode 100644
index 000000000000..83f20bb97115
--- /dev/null
+++ b/libarchive/test/test_read_format_tar_empty_with_gnulabel.c
@@ -0,0 +1,53 @@
+/*-
+ * 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$");
+
+/*
+ * Tar archives with with just a GNU label (or ending with one) should
+ * be treated as valid (empty) archives
+ */
+DEFINE_TEST(test_read_format_tar_empty_with_gnulabel)
+{
+ char name[] = "test_read_format_tar_empty_with_gnulabel.tar";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ extract_reference_file(name);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 10240));
+
+ /* Read first entry. */
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+ /* Verify that the format detection worked. */
+ assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_NONE);
+ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu b/libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu
new file mode 100644
index 000000000000..c5aa393db8ec
--- /dev/null
+++ b/libarchive/test/test_read_format_tar_empty_with_gnulabel.tar.uu
@@ -0,0 +1,231 @@
+begin 664 test_read_format_tar_empty_with_gnulabel.tar
+M;&%B96P`````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`#$S-#8W-S,V,C`S`#`P,C8R,``@5@``````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+9````````````````````````````````````
+`
+end
diff --git a/libarchive/test/test_read_format_zip.c b/libarchive/test/test_read_format_zip.c
index 14c5ada81053..b6e957a96cec 100644
--- a/libarchive/test/test_read_format_zip.c
+++ b/libarchive/test/test_read_format_zip.c
@@ -32,29 +32,29 @@ __FBSDID("$FreeBSD: head/lib/libarchive/test/test_read_format_zip.c 189482 2009-
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;
+ 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;
+ free(buf);
+ return ret;
}
static
@@ -111,7 +111,7 @@ verify_basic(struct archive *a, int seek_checks)
int64_t o;
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
- assertEqualString("ZIP 1.0 (uncompressed)", archive_format_name(a));
+ assertEqualString("ZIP 1.0 (uncompressed)", archive_format_name(a));
assertEqualString("dir/", archive_entry_pathname(ae));
assertEqualInt(1179604249, archive_entry_mtime(ae));
assertEqualInt(0, archive_entry_size(ae));
@@ -124,7 +124,7 @@ verify_basic(struct archive *a, int seek_checks)
assertEqualInt((int)s, 0);
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
- assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
+ assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
assertEqualString("file1", archive_entry_pathname(ae));
assertEqualInt(1179604289, archive_entry_mtime(ae));
if (seek_checks)
@@ -144,7 +144,7 @@ verify_basic(struct archive *a, int seek_checks)
}
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
- assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
+ assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
assertEqualString("file2", archive_entry_pathname(ae));
assertEqualInt(1179605932, archive_entry_mtime(ae));
assertEqualInt(archive_entry_is_encrypted(ae), 0);
@@ -166,7 +166,7 @@ verify_basic(struct archive *a, int seek_checks)
assert(archive_errno(a) != 0);
}
assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae));
- assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
+ assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a));
/* Verify the number of files read. */
failure("the archive file has three files");
assertEqualInt(3, archive_file_count(a));
@@ -493,9 +493,14 @@ DEFINE_TEST(test_read_format_zip_lzma_one_file)
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);
- 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));
@@ -513,9 +518,14 @@ DEFINE_TEST(test_read_format_zip_lzma_one_file_blockread)
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);
- 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));
@@ -533,9 +543,14 @@ DEFINE_TEST(test_read_format_zip_lzma_multi)
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);
- 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));
@@ -565,9 +580,14 @@ DEFINE_TEST(test_read_format_zip_lzma_multi_blockread)
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);
- 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));
@@ -598,9 +618,14 @@ DEFINE_TEST(test_read_format_zip_bzip2_one_file)
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);
- 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));
@@ -618,9 +643,14 @@ DEFINE_TEST(test_read_format_zip_bzip2_one_file_blockread)
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);
- 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));
@@ -638,9 +668,14 @@ DEFINE_TEST(test_read_format_zip_bzip2_multi)
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);
- 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));
@@ -670,9 +705,14 @@ DEFINE_TEST(test_read_format_zip_bzip2_multi_blockread)
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);
- 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));
@@ -702,9 +742,14 @@ DEFINE_TEST(test_read_format_zip_xz_multi)
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);
- 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));
@@ -734,9 +779,14 @@ DEFINE_TEST(test_read_format_zip_xz_multi_blockread)
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);
- 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));
@@ -789,9 +839,14 @@ DEFINE_TEST(test_read_format_zip_bz2_hang_on_invalid)
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);
- 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));
@@ -823,3 +878,41 @@ DEFINE_TEST(test_read_format_zip_ppmd8_crash_2)
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_7075_utf8_paths.c b/libarchive/test/test_read_format_zip_7075_utf8_paths.c
new file mode 100644
index 000000000000..7b78770aae43
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_7075_utf8_paths.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * Copyright (c) 2019 Mike Frysinger
+ * 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>
+
+static void
+verify(struct archive *a) {
+ struct archive_entry *ae;
+ const char *p;
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assert((p = archive_entry_pathname_utf8(ae)) != NULL);
+ assertEqualUTF8String(p, "File 1.txt");
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assert((p = archive_entry_pathname_utf8(ae)) != NULL);
+#if defined(__APPLE__)
+ /* Compare NFD string. */
+ assertEqualUTF8String(p, "File 2 - o\xCC\x88.txt");
+#else
+ /* Compare NFC string. */
+ assertEqualUTF8String(p, "File 2 - \xC3\xB6.txt");
+#endif
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assert((p = archive_entry_pathname_utf8(ae)) != NULL);
+#if defined(__APPLE__)
+ /* Compare NFD string. */
+ assertEqualUTF8String(p, "File 3 - a\xCC\x88.txt");
+#else
+ /* Compare NFC string. */
+ assertEqualUTF8String(p, "File 3 - \xC3\xA4.txt");
+#endif
+
+ /* The CRC of the filename fails, so fall back to CDE. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assert((p = archive_entry_pathname_utf8(ae)) != NULL);
+ assertEqualUTF8String(p, "File 4 - xx.txt");
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+}
+
+DEFINE_TEST(test_read_format_zip_utf8_paths)
+{
+ const char *refname = "test_read_format_zip_7075_utf8_paths.zip";
+ struct archive *a;
+ char *p;
+ size_t s;
+
+ extract_reference_file(refname);
+
+ if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) {
+ skipping("en_US.UTF-8 locale not available on this system.");
+ return;
+ }
+
+ /* Verify with seeking reader. */
+ 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));
+ verify(a);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+
+ /* Verify with streaming reader. */
+ p = slurpfile(&s, 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, read_open_memory(a, p, s, 31));
+ verify(a);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_free(a));
+ free(p);
+}
diff --git a/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu b/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu
new file mode 100644
index 000000000000..332f996898e0
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu
@@ -0,0 +1,20 @@
+begin 644 test_read_format_zip_7075_utf8_paths.zip
+M4$L#!!0````(`,$^9D5BZ95P"P````D````*````1FEL92`Q+G1X=`M)+2Y1
+M2,O,204`4$L#!!0````(`,$^9D5BZ95P"P````D````/`!@`1FEL92`R("T@
+M>'@N='AT=7`4``$NSPQQ1FEL92`R("T@P[8N='AT"TDM+E%(R\Q)!0!02P,$
+M%`````@`P3YF16+IE7`+````"0````\`&`!&:6QE(#,@+2!X>"YT>'1U<!0`
+M`1"DSIY&:6QE(#,@+2##I"YT>'0+22TN44C+S$D%`%!+`P04````"`#!/F9%
+M8NF5<`L````)````#P`8`$9I;&4@-"`M('AX+G1X='5P%``!G[AP'$9I;&4@
+M-"`M(,.E+G1X=`M)+2Y12,O,204`4$L!`A\`%`````@`P3YF16+IE7`+````
+M"0````H`)``````````@`````````$9I;&4@,2YT>'0*`"````````$`&``Q
+M6UASCOG/`5^OQVV.^<\!7Z_';8[YSP%02P$"'P`4````"`#!/F9%8NF5<`L`
+M```)````#@`\`````````"`````S````1FEL92`R("T@E"YT>'0*`"``````
+M``$`&``Q6UASCOG/`2M.B72.^<\!*TZ)=([YSP%U<!0``2[/#'%&:6QE(#(@
+M+2##MBYT>'102P$"'P`4````"`#!/F9%8NF5<`L````)````#@`\````````
+M`"````"#````1FEL92`S("T@A"YT>'0*`"````````$`&``Q6UASCOG/`5<$
+M&W>.^<\!5P0;=X[YSP%U<!0``1"DSIY&:6QE(#,@+2##I"YT>'102P$"'P`4
+M````"`#!/F9%8NF5<`L````)````#@`\`````````"````#3````1FEL92`T
+M("T@ABYT>'0*`"````````$`&``Q6UASCOG/`6#)ZG:.^<\!8,GJ=H[YSP%U
+M<!0``9^X<!Q&:6QE(#0@+2##I2YT>'102P4&``````0`!`#$`0``(P$`````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_extra_padding.c b/libarchive/test/test_read_format_zip_extra_padding.c
new file mode 100644
index 000000000000..54f7fa04ee89
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_extra_padding.c
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2003-2018 Tim Kientzle
+ * 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"
+
+/*
+ * Test archive verifies that we ignore padding in the extra field.
+ *
+ * APPNOTE.txt does not provide any provision for padding the extra
+ * field, so libarchive used to error when there were unconsumed
+ * bytes. Apparently, some Zip writers do routinely put zero padding
+ * in the extra field.
+ *
+ * The extra fields in this test (for both the local file header
+ * and the central directory entry) are formatted as follows:
+ *
+ * 0000 0000 - unrecognized field with type zero, zero bytes
+ * 5554 0900 03d258155cdb58155c - UX field with length 9
+ * 0000 0400 00000000 - unrecognized field with type zero, four bytes
+ * 000000 - three bytes padding
+ *
+ * The two valid type zero fields should be skipped and ignored, as
+ * should the three bytes padding (which is too short to be a valid
+ * extra data object). If there were no errors and we read the UX
+ * field correctly, then we've correctly handled all of the padding
+ * fields above.
+ */
+
+
+static void verify(struct archive *a) {
+ struct archive_entry *ae;
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("a", archive_entry_pathname(ae));
+ assertEqualInt(AE_IFREG | 0664, archive_entry_mode(ae));
+ assertEqualInt(0x5c1558d2, archive_entry_mtime(ae));
+ assertEqualInt(0, archive_entry_ctime(ae));
+ assertEqualInt(0x5c1558db, archive_entry_atime(ae));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+}
+
+DEFINE_TEST(test_read_format_zip_extra_padding)
+{
+ const char *refname = "test_read_format_zip_extra_padding.zip";
+ struct archive *a;
+ char *p;
+ size_t s;
+
+ extract_reference_file(refname);
+
+ /* Verify with seeking reader. */
+ 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, 7));
+ verify(a);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* Verify with streaming reader. */
+ p = slurpfile(&s, 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, read_open_memory(a, p, s, 3));
+ verify(a);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ free(p);
+}
diff --git a/libarchive/test/test_read_format_zip_extra_padding.zip.uu b/libarchive/test/test_read_format_zip_extra_padding.zip.uu
new file mode 100644
index 000000000000..496bd3c49434
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_extra_padding.zip.uu
@@ -0,0 +1,7 @@
+begin 644 test_read_format_zip_extra_padding.zip
+M4$L#!`H``````"-=CTW$\L?V`@````(````!`!P`80````!55`D``])8%5S;
+M6!5<```$``````````!B"E!+`0(>`PH``````"-=CTW$\L?V`@````(````!
+M`!@```````$```"D@0````!A`````%54"0`#TE@57-M8%5P```0``````$L%
+3!@`````!``$`1P```#T`````````
+`
+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 000000000000..5fce21355069
--- /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_sparse_basic.c b/libarchive/test/test_sparse_basic.c
index 0df0f1d321ac..f12b6af4862a 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 13089c78d894..aeadfee8a4e9 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 132e1145794c..82840547007a 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 e70b3929dea4..b59963d0f822 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 543a228c9072..c61d568fd748 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 66cf4c2d196f..21558e12df42 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 d7de42d6ab62..459d9dcb1eff 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 0008e1cfb70d..9bb966a0cf86 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 e6e31f452285..b828666b93fd 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 caf8044bf932..ccadc389974c 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");
@@ -68,6 +83,7 @@ DEFINE_TEST(test_option_C_mtree)
assertEqualMem(p0 + 1536, "\0\0\0\0\0\0\0\0", 8);
done:
free(p0);
+ free(absolute_path);
}
diff --git a/tar/test/test_option_H_upper.c b/tar/test/test_option_H_upper.c
index adc294b55b26..2c2ad33ce75b 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 f5a3c5ab4cba..5697b0f293c2 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 2c43e002df5b..d864e13c4f6b 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 000000000000..202151139996
--- /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 18ab6142d0f2..e474ac1d5754 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 ee8332f34fd5..09c72ee7d63d 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 d195af1b3526..090fb0dbf913 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 852e00b37c64..5836647c1a23 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 dd7e41016d52..7538d8cb7b5a 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 defdd34446c4..59c835ba81fa 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);
}