aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2026-07-30 08:07:58 +0000
committerMartin Matuska <mm@FreeBSD.org>2026-07-30 08:08:17 +0000
commit6d46c752afc81c22e51b47e020e2a898e9384afd (patch)
tree7e56e62d504013b00eae01739d2fe01e45dbbe84
parent77a6696abb6946edaa8cef0fcd9f666d3c69514b (diff)
Update vendor/libarchive to 3.8.9vendor/libarchive
-rw-r--r--.cirrus.yml51
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--.github/workflows/codeql.yml8
-rw-r--r--.github/workflows/lint.yml34
-rw-r--r--.github/workflows/scorecard.yml6
-rw-r--r--CMakeLists.txt18
-rw-r--r--Makefile.am106
-rw-r--r--NEWS2
-rw-r--r--build/ci/cirrus_ci/Dockerfile.cygwin4
-rw-r--r--build/ci/cirrus_ci/Dockerfile.fc313
-rw-r--r--build/ci/cirrus_ci/Dockerfile.fc31.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.sh66
-rw-r--r--build/cmake/config.h.in3
-rw-r--r--build/la-lint/.clang-format46
-rw-r--r--build/la-lint/CMakeLists.txt25
-rw-r--r--build/la-lint/main.cpp190
-rw-r--r--build/version2
-rw-r--r--configure.ac12
-rw-r--r--libarchive/archive.h4
-rw-r--r--libarchive/archive_acl.c15
-rw-r--r--libarchive/archive_acl_private.h2
-rw-r--r--libarchive/archive_check_magic.c1
-rw-r--r--libarchive/archive_entry.c26
-rw-r--r--libarchive/archive_entry.h4
-rw-r--r--libarchive/archive_entry_link_resolver.c6
-rw-r--r--libarchive/archive_entry_xattr.c10
-rw-r--r--libarchive/archive_integer.h20
-rw-r--r--libarchive/archive_options.c12
-rw-r--r--libarchive/archive_pathmatch.c2
-rw-r--r--libarchive/archive_private.h26
-rw-r--r--libarchive/archive_read.c384
-rw-r--r--libarchive/archive_read_append_filter.c54
-rw-r--r--libarchive/archive_read_data_into_fd.c54
-rw-r--r--libarchive/archive_read_disk_posix.c4
-rw-r--r--libarchive/archive_read_disk_set_standard_lookup.c7
-rw-r--r--libarchive/archive_read_disk_windows.c47
-rw-r--r--libarchive/archive_read_open_fd.c5
-rw-r--r--libarchive/archive_read_open_file.c5
-rw-r--r--libarchive/archive_read_open_memory.c5
-rw-r--r--libarchive/archive_read_private.h15
-rw-r--r--libarchive/archive_read_support_filter_bzip2.c120
-rw-r--r--libarchive/archive_read_support_filter_compress.c217
-rw-r--r--libarchive/archive_read_support_filter_grzip.c16
-rw-r--r--libarchive/archive_read_support_filter_gzip.c193
-rw-r--r--libarchive/archive_read_support_filter_lrzip.c16
-rw-r--r--libarchive/archive_read_support_filter_lz4.c335
-rw-r--r--libarchive/archive_read_support_filter_lzop.c216
-rw-r--r--libarchive/archive_read_support_filter_program.c183
-rw-r--r--libarchive/archive_read_support_filter_rpm.c67
-rw-r--r--libarchive/archive_read_support_filter_uu.c250
-rw-r--r--libarchive/archive_read_support_filter_xz.c291
-rw-r--r--libarchive/archive_read_support_filter_zstd.c98
-rw-r--r--libarchive/archive_read_support_format_7zip.c867
-rw-r--r--libarchive/archive_read_support_format_all.c2
-rw-r--r--libarchive/archive_read_support_format_ar.c147
-rw-r--r--libarchive/archive_read_support_format_cab.c844
-rw-r--r--libarchive/archive_read_support_format_cpio.c35
-rw-r--r--libarchive/archive_read_support_format_iso9660.c153
-rw-r--r--libarchive/archive_read_support_format_lha.c33
-rw-r--r--libarchive/archive_read_support_format_mtree.c87
-rw-r--r--libarchive/archive_read_support_format_rar.c108
-rw-r--r--libarchive/archive_read_support_format_rar5.c1098
-rw-r--r--libarchive/archive_read_support_format_raw.c65
-rw-r--r--libarchive/archive_read_support_format_tar.c144
-rw-r--r--libarchive/archive_read_support_format_warc.c547
-rw-r--r--libarchive/archive_read_support_format_xar.c237
-rw-r--r--libarchive/archive_read_support_format_zip.c287
-rw-r--r--libarchive/archive_string.c5
-rw-r--r--libarchive/archive_write.c113
-rw-r--r--libarchive/archive_write_add_filter_b64encode.c115
-rw-r--r--libarchive/archive_write_add_filter_bzip2.c200
-rw-r--r--libarchive/archive_write_add_filter_compress.c238
-rw-r--r--libarchive/archive_write_add_filter_grzip.c68
-rw-r--r--libarchive/archive_write_add_filter_gzip.c256
-rw-r--r--libarchive/archive_write_add_filter_lrzip.c88
-rw-r--r--libarchive/archive_write_add_filter_lz4.c474
-rw-r--r--libarchive/archive_write_add_filter_lzop.c260
-rw-r--r--libarchive/archive_write_add_filter_program.c77
-rw-r--r--libarchive/archive_write_add_filter_uuencode.c117
-rw-r--r--libarchive/archive_write_add_filter_xz.c276
-rw-r--r--libarchive/archive_write_add_filter_zstd.c268
-rw-r--r--libarchive/archive_write_disk_posix.c48
-rw-r--r--libarchive/archive_write_disk_set_standard_lookup.c7
-rw-r--r--libarchive/archive_write_disk_windows.c146
-rw-r--r--libarchive/archive_write_private.h4
-rw-r--r--libarchive/archive_write_set_format_7zip.c55
-rw-r--r--libarchive/archive_write_set_format_ar.c97
-rw-r--r--libarchive/archive_write_set_format_cpio_binary.c38
-rw-r--r--libarchive/archive_write_set_format_cpio_newc.c24
-rw-r--r--libarchive/archive_write_set_format_cpio_odc.c39
-rw-r--r--libarchive/archive_write_set_format_gnutar.c44
-rw-r--r--libarchive/archive_write_set_format_iso9660.c65
-rw-r--r--libarchive/archive_write_set_format_mtree.c69
-rw-r--r--libarchive/archive_write_set_format_pax.c244
-rw-r--r--libarchive/archive_write_set_format_raw.c8
-rw-r--r--libarchive/archive_write_set_format_shar.c68
-rw-r--r--libarchive/archive_write_set_format_ustar.c20
-rw-r--r--libarchive/archive_write_set_format_v7tar.c20
-rw-r--r--libarchive/archive_write_set_format_warc.c535
-rw-r--r--libarchive/archive_write_set_format_xar.c193
-rw-r--r--libarchive/archive_write_set_format_zip.c50
-rw-r--r--libarchive/archive_write_set_options.328
-rw-r--r--libarchive/test/CMakeLists.txt36
-rw-r--r--libarchive/test/read_open_memory.c8
-rw-r--r--libarchive/test/test_acl_from_text_overread.c98
-rw-r--r--libarchive/test/test_archive_pathmatch_oob.c41
-rw-r--r--libarchive/test/test_archive_read_close_twice.c42
-rw-r--r--libarchive/test/test_archive_read_set_filter_option.c54
-rw-r--r--libarchive/test/test_archive_read_set_option.c68
-rw-r--r--libarchive/test/test_archive_read_set_options.c150
-rw-r--r--libarchive/test/test_archive_seek_data_unsupported.c95
-rw-r--r--libarchive/test/test_archive_string_conversion_overread.c87
-rw-r--r--libarchive/test/test_archive_write_set_format_option.c66
-rw-r--r--libarchive/test/test_archive_write_set_option.c68
-rw-r--r--libarchive/test/test_archive_write_set_options.c150
-rw-r--r--libarchive/test/test_entry.c28
-rw-r--r--libarchive/test/test_fuzz.c2
-rw-r--r--libarchive/test/test_fuzz_consumer.h6
-rw-r--r--libarchive/test/test_gnutar_filename_encoding.c59
-rw-r--r--libarchive/test/test_pax_filename_encoding.c8
-rw-r--r--libarchive/test/test_read_data_into_fd_size_exceeds_declared.c119
-rw-r--r--libarchive/test/test_read_data_into_fd_size_exceeds_declared_deflate.zip.uu23
-rw-r--r--libarchive/test/test_read_data_into_fd_size_exceeds_declared_stored.zip.uu9
-rw-r--r--libarchive/test/test_read_data_large.c92
-rw-r--r--libarchive/test/test_read_filter_program.c2
-rw-r--r--libarchive/test/test_read_format_7zip.c67
-rw-r--r--libarchive/test/test_read_format_7zip_archive_properties.7z.uu5
-rw-r--r--libarchive/test/test_read_format_7zip_folders_oom.7z.uu8
-rw-r--r--libarchive/test/test_read_format_7zip_folders_oom.c (renamed from libarchive/test/test_archive_read_close_twice_open_filename.c)34
-rw-r--r--libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu22
-rw-r--r--libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu22
-rw-r--r--libarchive/test/test_read_format_cab_lzx_16bit.c (renamed from libarchive/test/test_archive_read_close_twice_open_fd.c)34
-rw-r--r--libarchive/test/test_read_format_cab_lzx_16bit.cab.uu6
-rw-r--r--libarchive/test/test_read_format_cab_lzx_e8.c (renamed from libarchive/test/test_archive_read_set_format_option.c)61
-rw-r--r--libarchive/test/test_read_format_cab_lzx_e8.cab.uu6
-rw-r--r--libarchive/test/test_read_format_cpio_symlink_trailer.c58
-rw-r--r--libarchive/test/test_read_format_cpio_symlink_trailer.cpio.uu2919
-rw-r--r--libarchive/test/test_read_format_gtar_sparse.c54
-rw-r--r--libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu48
-rw-r--r--libarchive/test/test_read_format_iso_rockridge_zf_overflow.c77
-rw-r--r--libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu1096
-rw-r--r--libarchive/test/test_read_format_isojoliet_compress.c (renamed from libarchive/test/test_read_format_isojoliet_bz2.c)6
-rw-r--r--libarchive/test/test_read_format_isorr_compress.c (renamed from libarchive/test/test_read_format_isorr_bz2.c)6
-rw-r--r--libarchive/test/test_read_format_isorr_new_compress.c (renamed from libarchive/test/test_read_format_isorr_new_bz2.c)6
-rw-r--r--libarchive/test/test_read_format_isozisofs_compress.c (renamed from libarchive/test/test_read_format_isozisofs_bz2.c)4
-rw-r--r--libarchive/test/test_read_format_lha_symlink_missing_target.c159
-rw-r--r--libarchive/test/test_read_format_lha_symlink_missing_target.lzh.uu11
-rw-r--r--libarchive/test/test_read_format_mtree.c32
-rw-r--r--libarchive/test/test_read_format_rar.c22
-rw-r--r--libarchive/test/test_read_format_rar5.c42
-rw-r--r--libarchive/test/test_read_format_rar5_unpacked_size_exceeds_declared.rar.uu10
-rw-r--r--libarchive/test/test_read_format_rar_seek_data_cursor0.rar.uu164
-rw-r--r--libarchive/test/test_read_format_tar_empty_pax.c69
-rw-r--r--libarchive/test/test_read_format_tar_pax_sun_holesdata.c76
-rw-r--r--libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu95
-rw-r--r--libarchive/test/test_read_format_warc.c100
-rw-r--r--libarchive/test/test_read_format_xar.c106
-rw-r--r--libarchive/test/test_read_format_xar_atou64_overread.xar.uu7
-rw-r--r--libarchive/test/test_read_format_xar_base64_oob.xar.uu7
-rw-r--r--libarchive/test/test_read_format_xar_parse_time_overread.xar.uu8
-rw-r--r--libarchive/test/test_read_format_zip.c64
-rw-r--r--libarchive/test/test_read_format_zip_7075_utf8_paths.c2
-rw-r--r--libarchive/test/test_read_format_zip_filename.c2
-rw-r--r--libarchive/test/test_read_format_zip_pkware_unix.zip.uu7
-rw-r--r--libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.zipx.uu16
-rw-r--r--libarchive/test/test_read_format_zip_size_exceeds_declared.c145
-rw-r--r--libarchive/test/test_read_format_zip_size_exceeds_declared_deflate.zip.uu23
-rw-r--r--libarchive/test/test_read_format_zip_size_exceeds_declared_stored.zip.uu9
-rw-r--r--libarchive/test/test_read_format_zip_symlink_unsupported_compression.c168
-rw-r--r--libarchive/test/test_read_format_zip_symlink_unsupported_compression.zip.uu16
-rw-r--r--libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c106
-rw-r--r--libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.zip.uu7
-rw-r--r--libarchive/test/test_read_format_zip_zipx_encrypted.c186
-rw-r--r--libarchive/test/test_sparse_basic.c17
-rw-r--r--libarchive/test/test_ustar_filename_encoding.c10
-rw-r--r--libarchive/test/test_write_disk_secure.c292
-rw-r--r--libarchive/test/test_write_disk_secure744.c11
-rw-r--r--libarchive/test/test_write_disk_secure745.c1
-rw-r--r--libarchive/test/test_write_disk_secure746.c9
-rw-r--r--libarchive/test/test_write_format_7zip.c39
-rw-r--r--libarchive/test/test_write_format_gnutar.c11
-rw-r--r--libarchive/test/test_write_format_gnutar_huge.c (renamed from libarchive/test/test_archive_write_set_filter_option.c)49
-rw-r--r--libarchive/test/test_write_format_iso9660_rockridge_overflow.c74
-rw-r--r--libarchive/test/test_write_format_iso9660_zisofs_overflow.c127
-rw-r--r--libarchive/test/test_write_format_pax.c31
-rw-r--r--libarchive/test/test_write_format_pax_align.c246
-rw-r--r--libarchive/test/test_write_format_tar_ustar.c14
-rw-r--r--libarchive/test/test_write_format_warc.c106
-rw-r--r--libarchive/test/test_write_format_xar.c69
-rw-r--r--libarchive/test/test_write_format_zip.c4
-rw-r--r--libarchive/test/test_write_format_zip_compression_lzma_xz.c (renamed from libarchive/test/test_write_format_zip_compression_lzmaxz.c)2
-rw-r--r--libarchive/test/test_write_read_format_zip.c4
-rw-r--r--tar/CMakeLists.txt13
-rw-r--r--tar/bsdtar.exe.manifest15
-rw-r--r--tar/bsdtar.h1
-rw-r--r--tar/bsdtar.rc20
-rw-r--r--tar/read.c12
-rw-r--r--tar/subst.c2
-rw-r--r--tar/test/CMakeLists.txt1
-rw-r--r--tar/test/test_option_stdout_size_exceeds_declared.c50
-rw-r--r--tar/test/test_option_stdout_size_exceeds_declared_deflate.zip.uu23
-rw-r--r--tar/test/test_option_stdout_size_exceeds_declared_stored.zip.uu9
-rw-r--r--tar/test/test_stdio.c2
-rw-r--r--tar/util.c25
-rw-r--r--tar/write.c30
-rw-r--r--test_utils/test_main.c77
-rw-r--r--unzip/CMakeLists.txt17
-rw-r--r--unzip/bsdunzip.c76
-rw-r--r--unzip/bsdunzip.exe.manifest15
-rw-r--r--unzip/bsdunzip.rc19
-rw-r--r--unzip/bsdunzip_platform.h4
-rw-r--r--unzip/bsdunzip_windows.c728
-rw-r--r--unzip/bsdunzip_windows.h61
-rw-r--r--unzip/test/CMakeLists.txt4
-rw-r--r--unzip/test/test_I.c8
-rw-r--r--unzip/test/test_f.c39
-rw-r--r--unzip/test/test_f_new.zip.uu8
-rw-r--r--unzip/test/test_f_old.zip.uu8
-rw-r--r--unzip/test/test_symlink.c53
-rw-r--r--unzip/test/test_symlink_1.zip.uu21
-rw-r--r--unzip/test/test_symlink_2.zip.uu12
-rw-r--r--unzip/test/test_symlink_dotdot.c36
-rw-r--r--unzip/test/test_symlink_dotdot.zip.uu10
-rw-r--r--unzip/test/test_u.c32
-rw-r--r--unzip/test/test_u_new.zip.uu8
-rw-r--r--unzip/test/test_u_old.zip.uu8
230 files changed, 16151 insertions, 6382 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
deleted file mode 100644
index 3c99d430a348..000000000000
--- a/.cirrus.yml
+++ /dev/null
@@ -1,51 +0,0 @@
----
-env:
- CIRRUS_CLONE_DEPTH: 1
- ARCH: amd64
-
-FreeBSD_task:
- matrix:
- - name: 15.0-RELEASE (UFS) cmake
- freebsd_instance:
- image_family: freebsd-15-0-amd64-ufs
- env:
- BS: cmake
- - name: 15.0-RELEASE (ZFS)
- freebsd_instance:
- image_family: freebsd-15-0-amd64-zfs
- env:
- BS: autotools
- - name: 14.3-RELEASE
- freebsd_instance:
- image_family: freebsd-14-3
- env:
- BS: cmake
- prepare_script:
- - ./build/ci/cirrus_ci/ci.sh prepare
- configure_script:
- - env CFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./build/ci/build.sh -a autogen
- - env MAKE=gmake CFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib ./build/ci/build.sh -a configure
- build_script:
- - env MAKE=gmake MAKE_ARGS="-j 2" ./build/ci/build.sh -a build
- test_script:
- - env MAKE=gmake SKIP_TEST_RESTORE_ATIME=1 MAKE_ARGS="-j 2" ./build/ci/build.sh -a test
- - ./build/ci/cirrus_ci/ci.sh test
- install_script:
- - env MAKE=gmake MAKE_ARGS="-j 2" ./build/ci/build.sh -a 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\ci.cmd install
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dcd948275bfb..8d8796999f6b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,7 +13,7 @@ jobs:
bs: [autotools, cmake]
steps:
- name: Build and test on a FreeBSD 15 firecracker VM
- uses: acj/freebsd-firecracker-action@ff79eb955fd19c697a708a02834995e47a6e58ed # v0.10.0
+ uses: acj/freebsd-firecracker-action@e04896d2dd91f7ec7381e5f363c6fbe83ee445c3 # v0.11.0
with:
run-in-vm: |
chflags -R noschg / && \
@@ -33,7 +33,7 @@ jobs:
matrix:
bs: [autotools, cmake]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install dependencies
run: ./build/ci/github_actions/install-macos-dependencies.sh
- name: Autogen
@@ -80,7 +80,7 @@ jobs:
bs: [autotools, cmake]
crypto: [mbedtls, nettle, openssl]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Update apt cache
run: sudo apt-get update
- name: Install dependencies
@@ -121,7 +121,7 @@ jobs:
Ubuntu-distcheck:
runs-on: ubuntu-24.04
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Update package definitions
run: sudo apt-get update
- name: Install dependencies
@@ -148,7 +148,7 @@ jobs:
matrix:
be: [mingw-gcc, cygwin-gcc, msvc]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Cygwin
uses: cygwin/cygwin-install-action@3f0a3f9f988f7e96b8c18098ae05eaec175f5b52 # v6
if: ${{ matrix.be=='cygwin-gcc' }}
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 98de73181557..9387827285cd 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -26,18 +26,18 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Initialize CodeQL
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
- uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ uses: github/codeql-action/autobuild@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 000000000000..69ee68f8fc63
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,34 @@
+name: Lint
+
+on: [push, pull_request]
+
+permissions:
+ contents: read
+
+jobs:
+ Lint:
+ runs-on: ubuntu-26.04
+ env:
+ BS: cmake
+ CRYPTO: openssl
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ - name: Update apt cache
+ run: sudo apt-get update
+ - name: Install dependencies
+ run: sudo apt-get install -y autoconf automake build-essential cmake git libssl-dev nettle-dev libmbedtls-dev libacl1-dev libbz2-dev liblzma-dev liblz4-dev libzstd-dev lzop pkg-config zlib1g-dev libclang-22-dev libclang-cpp22-dev clang-22 ninja-build
+ - name: Install Lint
+ run: |-
+ set -euo pipefail
+ cmake -S build/la-lint -B /tmp/lint-build -GNinja
+ cmake --build /tmp/lint-build
+ cmake --install /tmp/lint-build
+ - name: Configure
+ run: ./build/ci/build.sh -a configure
+ env:
+ CMAKE_ARGS: -GNinja -DENABLE_CLANG_TIDY=YES -DCLANG_TIDY_PATH=la-lint
+ - name: Build
+ run: ./build/ci/build.sh -a build
+ env:
+ MAKE: ninja
+ MAKE_ARGS: --
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index b8835e7ed7a5..5751f9cc30da 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -29,12 +29,12 @@ jobs:
steps:
- name: "Checkout code"
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: "Run analysis"
- uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
+ uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
@@ -60,6 +60,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
+ uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
sarif_file: results.sarif
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8591b6323f82..7cb92764676a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -246,19 +246,15 @@ OPTION(ENABLE_CPIO "Enable cpio building" ON)
OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE)
OPTION(ENABLE_CAT "Enable cat building" ON)
OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE)
-IF(WIN32 AND NOT CYGWIN)
- SET(ENABLE_UNZIP FALSE)
- SET(ENABLE_UNZIP_SHARED FALSE)
-ELSE()
- OPTION(ENABLE_UNZIP "Enable unzip building" ON)
- OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE)
-ENDIF()
+OPTION(ENABLE_UNZIP "Enable unzip building" ON)
+OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE)
OPTION(ENABLE_XATTR "Enable extended attribute support" ON)
OPTION(ENABLE_ACL "Enable ACL support" ON)
OPTION(ENABLE_ICONV "Enable iconv support" ON)
OPTION(ENABLE_TEST "Enable unit and regression tests" ON)
OPTION(ENABLE_COVERAGE "Enable code coverage (GCC only, automatically sets ENABLE_TEST to ON)" FALSE)
OPTION(ENABLE_INSTALL "Enable installing of libraries" ON)
+OPTION(ENABLE_CLANG_TIDY "Enable custom build-time code health checks" OFF)
IF(WIN32 AND MSVC)
OPTION(MSVC_USE_STATIC_CRT "Use static CRT" OFF)
@@ -1500,6 +1496,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R)
CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R)
CHECK_FUNCTION_EXISTS_GLIBC(getresgid HAVE_GETRESGID)
CHECK_FUNCTION_EXISTS_GLIBC(getresuid HAVE_GETRESUID)
+CHECK_FUNCTION_EXISTS_GLIBC(gettimeofday HAVE_GETTIMEOFDAY)
CHECK_FUNCTION_EXISTS_GLIBC(getvfsbyname HAVE_GETVFSBYNAME)
CHECK_FUNCTION_EXISTS_GLIBC(gmtime_r HAVE_GMTIME_R)
CHECK_FUNCTION_EXISTS_GLIBC(issetugid HAVE_ISSETUGID)
@@ -2233,6 +2230,13 @@ IF(ENABLE_TEST)
ADD_CUSTOM_TARGET(run_all_tests)
ENDIF(ENABLE_TEST)
+IF(ENABLE_CLANG_TIDY)
+ IF(NOT CLANG_TIDY_PATH)
+ SET(CLANG_TIDY_PATH clang-tidy)
+ ENDIF(NOT CLANG_TIDY_PATH)
+ SET(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_PATH})
+ENDIF(ENABLE_CLANG_TIDY)
+
add_subdirectory(libarchive)
add_subdirectory(cat)
add_subdirectory(tar)
diff --git a/Makefile.am b/Makefile.am
index 58bc73ec227f..2fb4fdf4b1e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -74,6 +74,8 @@ distcheck-hook:
#
# Extra rules for cleanup
#
+CLEANFILES=
+
DISTCLEANFILES= \
libarchive/test/list.h \
tar/test/list.h \
@@ -379,6 +381,7 @@ libarchive_test_SOURCES= \
$(test_utils_SOURCES) \
libarchive/test/read_open_memory.c \
libarchive/test/test_7zip_filename_encoding.c \
+ libarchive/test/test_acl_from_text_overread.c \
libarchive/test/test_acl_nfs4.c \
libarchive/test/test_acl_nfs4_null_id_overflow.c \
libarchive/test/test_acl_nfs4_text.c \
@@ -402,27 +405,21 @@ libarchive_test_SOURCES= \
libarchive/test/test_archive_read.c \
libarchive/test/test_archive_read_add_passphrase.c \
libarchive/test/test_archive_read_close_twice.c \
- libarchive/test/test_archive_read_close_twice_open_fd.c \
- libarchive/test/test_archive_read_close_twice_open_filename.c \
libarchive/test/test_archive_read_multiple_data_objects.c \
libarchive/test/test_archive_read_next_header_empty.c \
libarchive/test/test_archive_read_next_header_raw.c \
libarchive/test/test_archive_read_open2.c \
- libarchive/test/test_archive_read_set_filter_option.c \
- libarchive/test/test_archive_read_set_format_option.c \
- libarchive/test/test_archive_read_set_option.c \
libarchive/test/test_archive_read_set_options.c \
libarchive/test/test_archive_read_support.c \
+ libarchive/test/test_archive_seek_data_unsupported.c \
libarchive/test/test_archive_set_error.c \
libarchive/test/test_archive_string.c \
libarchive/test/test_archive_string_conversion.c \
+ libarchive/test/test_archive_string_conversion_overread.c \
libarchive/test/test_archive_write_add_filter.c \
libarchive/test/test_archive_write_add_filter_by_name.c \
- libarchive/test/test_archive_write_set_filter_option.c \
libarchive/test/test_archive_write_set_format_by_name.c \
libarchive/test/test_archive_write_set_format_filter_by_ext.c \
- libarchive/test/test_archive_write_set_format_option.c \
- libarchive/test/test_archive_write_set_option.c \
libarchive/test/test_archive_write_set_options.c \
libarchive/test/test_archive_write_set_passphrase.c \
libarchive/test/test_bad_fd.c \
@@ -462,6 +459,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_open_filename.c \
libarchive/test/test_pax_filename_encoding.c \
libarchive/test/test_pax_xattr_header.c \
+ libarchive/test/test_read_data_into_fd_size_exceeds_declared.c \
libarchive/test/test_read_data_large.c \
libarchive/test/test_read_disk.c \
libarchive/test/test_read_disk_directory_traversals.c \
@@ -486,12 +484,15 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_7zip_encryption_partially.c \
libarchive/test/test_read_format_7zip_encryption_header.c \
libarchive/test/test_read_format_7zip_entries_oom.c \
+ libarchive/test/test_read_format_7zip_folders_oom.c \
libarchive/test/test_read_format_7zip_issue2765.c \
libarchive/test/test_read_format_7zip_malformed.c \
libarchive/test/test_read_format_7zip_packinfo_digests.c \
libarchive/test/test_read_format_ar.c \
libarchive/test/test_read_format_cab.c \
libarchive/test/test_read_format_cab_filename.c \
+ libarchive/test/test_read_format_cab_lzx_16bit.c \
+ libarchive/test/test_read_format_cab_lzx_e8.c \
libarchive/test/test_read_format_cab_lzx_oob.c \
libarchive/test/test_read_format_cab_mszip_oob.c \
libarchive/test/test_read_format_cab_multi.c \
@@ -512,6 +513,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_cpio_svr4_gzip.c \
libarchive/test/test_read_format_cpio_svr4_gzip_rpm.c \
libarchive/test/test_read_format_cpio_svr4c_Z.c \
+ libarchive/test/test_read_format_cpio_symlink_trailer.c \
libarchive/test/test_read_format_empty.c \
libarchive/test/test_read_format_gtar_filename.c \
libarchive/test/test_read_format_gtar_gz.c \
@@ -525,22 +527,24 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_iso_joliet_utf16be_overflow.c \
libarchive/test/test_read_format_iso_multi_extent.c \
libarchive/test/test_read_format_iso_xorriso.c \
- libarchive/test/test_read_format_isojoliet_bz2.c \
+ libarchive/test/test_read_format_isojoliet_compress.c \
libarchive/test/test_read_format_isojoliet_long.c \
libarchive/test/test_read_format_isojoliet_rr.c \
libarchive/test/test_read_format_isojoliet_versioned.c \
- libarchive/test/test_read_format_isorr_bz2.c \
+ libarchive/test/test_read_format_isorr_compress.c \
libarchive/test/test_read_format_isorr_ce.c \
libarchive/test/test_read_format_isorr_ce_loop.c \
- libarchive/test/test_read_format_isorr_new_bz2.c \
+ libarchive/test/test_read_format_isorr_new_compress.c \
libarchive/test/test_read_format_isorr_rr_moved.c \
- libarchive/test/test_read_format_isozisofs_bz2.c \
+ libarchive/test/test_read_format_isozisofs_compress.c \
+ libarchive/test/test_read_format_iso_rockridge_zf_overflow.c \
libarchive/test/test_read_format_iso_zisofs_overflow.c \
libarchive/test/test_read_format_lha.c \
libarchive/test/test_read_format_lha_bugfix_0.c \
libarchive/test/test_read_format_lha_filename.c \
libarchive/test/test_read_format_lha_filename_utf16.c \
libarchive/test/test_read_format_lha_oversize_header.c \
+ libarchive/test/test_read_format_lha_symlink_missing_target.c \
libarchive/test/test_read_format_mtree.c \
libarchive/test/test_read_format_mtree_crash747.c \
libarchive/test/test_read_format_pax_bz2.c \
@@ -571,6 +575,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_tar_pax_g_large.c \
libarchive/test/test_read_format_tar_pax_large_attr.c \
libarchive/test/test_read_format_tar_pax_negative_time.c \
+ libarchive/test/test_read_format_tar_pax_sun_holesdata.c \
libarchive/test/test_read_format_tar_pax_timestamps.c \
libarchive/test/test_read_format_tar_timestamp_overflow.c \
libarchive/test/test_read_format_tbz.c \
@@ -599,7 +604,10 @@ libarchive_test_SOURCES= \
libarchive/test/test_read_format_zip_nofiletype.c \
libarchive/test/test_read_format_zip_padded.c \
libarchive/test/test_read_format_zip_sfx.c \
+ libarchive/test/test_read_format_zip_size_exceeds_declared.c \
+ libarchive/test/test_read_format_zip_symlink_unsupported_compression.c \
libarchive/test/test_read_format_zip_traditional_encryption_data.c \
+ libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c \
libarchive/test/test_read_format_zip_winzip_aes.c \
libarchive/test/test_read_format_zip_winzip_aes_large.c \
libarchive/test/test_read_format_zip_zip64.c \
@@ -666,6 +674,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_write_format_cpio_odc.c \
libarchive/test/test_write_format_gnutar.c \
libarchive/test/test_write_format_gnutar_filenames.c \
+ libarchive/test/test_write_format_gnutar_huge.c \
libarchive/test/test_write_format_iso9660.c \
libarchive/test/test_write_format_iso9660_boot.c \
libarchive/test/test_write_format_iso9660_bugs.c \
@@ -673,7 +682,9 @@ libarchive_test_SOURCES= \
libarchive/test/test_write_format_iso9660_filename.c \
libarchive/test/test_write_format_iso9660_joliet_id.c \
libarchive/test/test_write_format_iso9660_rockridge.c \
+ libarchive/test/test_write_format_iso9660_rockridge_overflow.c \
libarchive/test/test_write_format_iso9660_zisofs.c \
+ libarchive/test/test_write_format_iso9660_zisofs_overflow.c \
libarchive/test/test_write_format_mtree.c \
libarchive/test/test_write_format_mtree_absolute.c \
libarchive/test/test_write_format_mtree_absolute_path.c \
@@ -686,6 +697,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_write_format_mtree_quoted_filename.c\
libarchive/test/test_write_format_mtree_null_deref.c \
libarchive/test/test_write_format_pax.c \
+ libarchive/test/test_write_format_pax_align.c \
libarchive/test/test_write_format_raw.c \
libarchive/test/test_write_format_raw_b64.c \
libarchive/test/test_write_format_shar_empty.c \
@@ -706,7 +718,7 @@ libarchive_test_SOURCES= \
libarchive/test/test_write_format_zip_compression_store.c \
libarchive/test/test_write_format_zip_compression_zstd.c \
libarchive/test/test_write_format_zip_compression_bzip2.c \
- libarchive/test/test_write_format_zip_compression_lzmaxz.c \
+ libarchive/test/test_write_format_zip_compression_lzma_xz.c \
libarchive/test/test_write_format_zip_empty.c \
libarchive/test/test_write_format_zip_empty_pathname.c \
libarchive/test/test_write_format_zip_empty_zip64.c \
@@ -827,6 +839,8 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_rar_multivolume_uncompressed_files.part08.rar.uu \
libarchive/test/test_rar_multivolume_uncompressed_files.part09.rar.uu \
libarchive/test/test_rar_multivolume_uncompressed_files.part10.rar.uu \
+ libarchive/test/test_read_data_into_fd_size_exceeds_declared_deflate.zip.uu \
+ libarchive/test/test_read_data_into_fd_size_exceeds_declared_stored.zip.uu \
libarchive/test/test_read_files_compressed.part01.uu \
libarchive/test/test_read_files_compressed.part02.uu \
libarchive/test/test_read_filter_grzip.tar.grz.uu \
@@ -866,10 +880,12 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_7zip_delta4_lzma2.7z.uu \
libarchive/test/test_read_format_7zip_empty_archive.7z.uu \
libarchive/test/test_read_format_7zip_empty_file.7z.uu \
+ libarchive/test/test_read_format_7zip_archive_properties.7z.uu \
libarchive/test/test_read_format_7zip_encryption.7z.uu \
libarchive/test/test_read_format_7zip_encryption_header.7z.uu \
libarchive/test/test_read_format_7zip_encryption_partially.7z.uu \
libarchive/test/test_read_format_7zip_entries_oom.7z.uu \
+ libarchive/test/test_read_format_7zip_folders_oom.7z.uu \
libarchive/test/test_read_format_7zip_extract_second.7z.uu \
libarchive/test/test_read_format_7zip_issue2765.7z.uu \
libarchive/test/test_read_format_7zip_lzma1.7z.uu \
@@ -888,6 +904,8 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_7zip_malformed_numfiles_oom.7z.uu \
libarchive/test/test_read_format_7zip_packinfo_digests.7z.uu \
libarchive/test/test_read_format_7zip_ppmd.7z.uu \
+ libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu \
+ libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu \
libarchive/test/test_read_format_7zip_sfx_elf.elf.uu \
libarchive/test/test_read_format_7zip_sfx_elf64trunc.elf.uu \
libarchive/test/test_read_format_7zip_sfx_modified_pe.exe.uu \
@@ -905,6 +923,8 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_cab_2.cab.uu \
libarchive/test/test_read_format_cab_3.cab.uu \
libarchive/test/test_read_format_cab_filename_cp932.cab.uu \
+ libarchive/test/test_read_format_cab_lzx_16bit.cab.uu \
+ libarchive/test/test_read_format_cab_lzx_e8.cab.uu \
libarchive/test/test_read_format_cab_lzx_oob.cab.uu \
libarchive/test/test_read_format_cab_multi.p1.cab.uu \
libarchive/test/test_read_format_cab_multi.p2.cab.uu \
@@ -920,6 +940,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_cpio_filename_utf8_ru.cpio.uu \
libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.rpm.uu \
libarchive/test/test_read_format_cpio_svr4_gzip_rpm.rpm.uu \
+ libarchive/test/test_read_format_cpio_symlink_trailer.cpio.uu \
libarchive/test/test_read_format_gtar_filename_cp866.tar.Z.uu \
libarchive/test/test_read_format_gtar_filename_eucjp.tar.Z.uu \
libarchive/test/test_read_format_gtar_filename_koi8r.tar.Z.uu \
@@ -930,6 +951,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_gtar_sparse_1_17_posix01.tar.uu \
libarchive/test/test_read_format_gtar_sparse_1_17_posix10.tar.uu \
libarchive/test/test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu \
+ libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu \
libarchive/test/test_read_format_gtar_sparse_length.tar.Z.uu \
libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu \
libarchive/test/test_read_format_huge_rpm.rpm.uu \
@@ -947,6 +969,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_iso_rockridge_ce_loop.iso.Z.uu \
libarchive/test/test_read_format_iso_rockridge_new.iso.Z.uu \
libarchive/test/test_read_format_iso_rockridge_rr_moved.iso.Z.uu \
+ libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu \
libarchive/test/test_read_format_iso_xorriso.iso.Z.uu \
libarchive/test/test_read_format_iso_zisofs.iso.Z.uu \
libarchive/test/test_read_format_iso_zisofs_overflow.iso.uu \
@@ -961,6 +984,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_lha_lh6.lzh.uu \
libarchive/test/test_read_format_lha_lh7.lzh.uu \
libarchive/test/test_read_format_lha_oversize_header.lzh.uu \
+ libarchive/test/test_read_format_lha_symlink_missing_target.lzh.uu \
libarchive/test/test_read_format_lha_withjunk.lzh.uu \
libarchive/test/test_read_format_mtree.mtree.uu \
libarchive/test/test_read_format_mtree_nomagic.mtree.uu \
@@ -990,6 +1014,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu \
libarchive/test/test_read_format_rar_ppmd_use_after_free2.rar.uu \
libarchive/test/test_read_format_rar_sfx.exe.uu \
+ libarchive/test/test_read_format_rar_seek_data_cursor0.rar.uu \
libarchive/test/test_read_format_rar_subblock.rar.uu \
libarchive/test/test_read_format_rar_symlink_huge.rar.uu \
libarchive/test/test_read_format_rar_unbound_staticdata.rar.uu \
@@ -1046,6 +1071,7 @@ libarchive_test_EXTRA_DIST=\
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_unpacked_size_exceeds_declared.rar.uu \
libarchive/test/test_read_format_rar5_unicode.rar.uu \
libarchive/test/test_read_format_rar5_win32.rar.uu \
libarchive/test/test_read_format_rar5_arm_filter_on_window_boundary.rar.uu \
@@ -1072,6 +1098,7 @@ libarchive_test_EXTRA_DIST=\
libarchive/test/test_read_format_tar_pax_g_large.tar.uu \
libarchive/test/test_read_format_tar_pax_large_attr.tar.Z.uu \
libarchive/test/test_read_format_tar_pax_negative_time.tar.uu \
+ libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu \
libarchive/test/test_read_format_tar_pax_timestamps.tar.uu \
libarchive/test/test_read_format_tar_timestamp_overflow.tar.uu \
libarchive/test/test_read_format_ustar_filename_cp866.tar.Z.uu \
@@ -1079,9 +1106,12 @@ 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_warc_incomplete.warc.uu \
+ libarchive/test/test_read_format_xar_atou64_overread.xar.uu \
+ libarchive/test/test_read_format_xar_base64_oob.xar.uu \
libarchive/test/test_read_format_xar_doublelink.xar.uu \
libarchive/test/test_read_format_xar_duplicate_filename_node.xar.uu \
libarchive/test/test_read_format_xar_large_mode.xar.uu \
+ libarchive/test/test_read_format_xar_parse_time_overread.xar.uu \
libarchive/test/test_read_format_zip.zip.uu \
libarchive/test/test_read_format_zip_7075_utf8_paths.zip.uu \
libarchive/test/test_read_format_zip_7z_deflate.zip.uu \
@@ -1116,13 +1146,19 @@ 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_pkware_unix.zip.uu \
libarchive/test/test_read_format_zip_ppmd8.zipx.uu \
+ libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.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_size_exceeds_declared_deflate.zip.uu \
+ libarchive/test/test_read_format_zip_size_exceeds_declared_stored.zip.uu \
libarchive/test/test_read_format_zip_symlink.zip.uu \
+ libarchive/test/test_read_format_zip_symlink_unsupported_compression.zip.uu \
libarchive/test/test_read_format_zip_traditional_encryption_data.zip.uu \
+ libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.zip.uu \
libarchive/test/test_read_format_zip_ux.zip.uu \
libarchive/test/test_read_format_zip_winzip_aes128.zip.uu \
libarchive/test/test_read_format_zip_winzip_aes256.zip.uu \
@@ -1227,10 +1263,21 @@ bsdtar_LDADD= libarchive.la libarchive_fe.la $(LTLIBICONV)
bsdtar_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdtar_ccstatic) $(PLATFORMCPPFLAGS)
bsdtar_LDFLAGS= $(bsdtar_ldstatic) $(DEAD_CODE_REMOVAL)
+if INC_WINDOWS_FILES
+# Embed an application manifest marking bsdtar as long-path aware.
+tar/bsdtar_rc.$(OBJEXT): $(top_srcdir)/tar/bsdtar.rc $(top_srcdir)/tar/bsdtar.exe.manifest
+ $(AM_V_GEN)$(WINDRES) -I $(top_srcdir)/tar -i $(top_srcdir)/tar/bsdtar.rc -o $@
+bsdtar_LDADD += tar/bsdtar_rc.$(OBJEXT)
+bsdtar_DEPENDENCIES += tar/bsdtar_rc.$(OBJEXT)
+CLEANFILES += tar/bsdtar_rc.$(OBJEXT)
+endif
+
bsdtar_EXTRA_DIST= \
tar/bsdtar.1 \
tar/bsdtar_windows.h \
tar/bsdtar_windows.c \
+ tar/bsdtar.rc \
+ tar/bsdtar.exe.manifest \
tar/CMakeLists.txt \
tar/config_freebsd.h
@@ -1311,6 +1358,7 @@ bsdtar_test_SOURCES= \
tar/test/test_option_r.c \
tar/test/test_option_s.c \
tar/test/test_option_safe_writes.c \
+ tar/test/test_option_stdout_size_exceeds_declared.c \
tar/test/test_option_uid_uname.c \
tar/test/test_option_uuencode.c \
tar/test/test_option_xattrs.c \
@@ -1362,6 +1410,8 @@ bsdtar_test_EXTRA_DIST= \
tar/test/test_option_keep_newer_files.tar.Z.uu \
tar/test/test_option_passphrase.zip.uu \
tar/test/test_option_s.tar.Z.uu \
+ tar/test/test_option_stdout_size_exceeds_declared_deflate.zip.uu \
+ tar/test/test_option_stdout_size_exceeds_declared_stored.zip.uu \
tar/test/test_patterns_2.tar.uu \
tar/test/test_patterns_3.tar.uu \
tar/test/test_patterns_4.tar.uu \
@@ -1653,8 +1703,10 @@ bsdunzip_SOURCES= \
unzip/cmdline.c
if INC_WINDOWS_FILES
-noinst_HEADERS+=
-bsdunzip_SOURCES+=
+noinst_HEADERS+= \
+ unzip/bsdunzip_windows.h
+bsdunzip_SOURCES+= \
+ unzip/bsdunzip_windows.c
endif
bsdunzip_DEPENDENCIES = libarchive.la libarchive_fe.la
@@ -1672,7 +1724,20 @@ bsdunzip_LDADD= libarchive_fe.la libarchive.la $(LTLIBICONV)
bsdunzip_CPPFLAGS= -I$(top_srcdir)/libarchive -I$(top_srcdir)/libarchive_fe $(bsdunzip_ccstatic) $(PLATFORMCPPFLAGS)
bsdunzip_LDFLAGS= $(bsdunzip_ldstatic) $(DEAD_CODE_REMOVAL)
+if INC_WINDOWS_FILES
+# Embed an application manifest marking bsdunzip as long-path aware.
+unzip/bsdunzip_rc.$(OBJEXT): $(top_srcdir)/unzip/bsdunzip.rc $(top_srcdir)/unzip/bsdunzip.exe.manifest
+ $(AM_V_GEN)$(WINDRES) -I $(top_srcdir)/unzip -i $(top_srcdir)/unzip/bsdunzip.rc -o $@
+bsdunzip_LDADD += unzip/bsdunzip_rc.$(OBJEXT)
+bsdunzip_DEPENDENCIES += unzip/bsdunzip_rc.$(OBJEXT)
+CLEANFILES += unzip/bsdunzip_rc.$(OBJEXT)
+endif
+
bsdunzip_EXTRA_DIST= \
+ unzip/bsdunzip_windows.h \
+ unzip/bsdunzip_windows.c \
+ unzip/bsdunzip.rc \
+ unzip/bsdunzip.exe.manifest \
unzip/bsdunzip.1 \
unzip/CMakeLists.txt
@@ -1697,6 +1762,7 @@ bsdunzip_test_SOURCES= \
unzip/test/test_0.c \
unzip/test/test_basic.c \
unzip/test/test_doubledash.c \
+ unzip/test/test_f.c \
unzip/test/test_glob.c \
unzip/test/test_not_exist.c \
unzip/test/test_singlefile.c \
@@ -1708,8 +1774,11 @@ bsdunzip_test_SOURCES= \
unzip/test/test_n.c \
unzip/test/test_o.c \
unzip/test/test_q.c \
+ unzip/test/test_symlink.c \
+ unzip/test/test_symlink_dotdot.c \
unzip/test/test_t.c \
unzip/test/test_t_bad.c \
+ unzip/test/test_u.c \
unzip/test/test_version.c \
unzip/test/test_x.c \
unzip/test/test_Z1.c \
@@ -1740,7 +1809,14 @@ bsdunzip_test_EXTRA_DIST= \
unzip/test/list.h \
unzip/test/test_basic.zip.uu \
unzip/test/test_encrypted.zip.uu \
+ unzip/test/test_f_new.zip.uu \
+ unzip/test/test_f_old.zip.uu \
unzip/test/test_singlefile.zip.uu \
+ unzip/test/test_symlink_1.zip.uu \
+ unzip/test/test_symlink_2.zip.uu \
+ unzip/test/test_symlink_dotdot.zip.uu \
unzip/test/test_t_bad.zip.uu \
+ unzip/test/test_u_new.zip.uu \
+ unzip/test/test_u_old.zip.uu \
unzip/test/test_I.zip.uu \
unzip/test/CMakeLists.txt
diff --git a/NEWS b/NEWS
index 5c64b9dddb66..a46b69aaf3e9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,5 @@
+Jul 28, 2026: libarchive 3.8.9 released
+
Jun 23, 2026: libarchive 3.8.8 released
Apr 13, 2026: libarchive 3.8.7 released
diff --git a/build/ci/cirrus_ci/Dockerfile.cygwin b/build/ci/cirrus_ci/Dockerfile.cygwin
deleted file mode 100644
index be9aca13b2ad..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.cygwin
+++ /dev/null
@@ -1,4 +0,0 @@
-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,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel
diff --git a/build/ci/cirrus_ci/Dockerfile.fc31 b/build/ci/cirrus_ci/Dockerfile.fc31
deleted file mode 100644
index 0285497234ff..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.fc31
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM fedora:31
-
-RUN dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison pkgconf libacl-devel libasan librichacl-devel bzip2-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel nettle-devel mbedtls-devel
diff --git a/build/ci/cirrus_ci/Dockerfile.fc31.distcheck b/build/ci/cirrus_ci/Dockerfile.fc31.distcheck
deleted file mode 100644
index 10064e4ec274..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.fc31.distcheck
+++ /dev/null
@@ -1,3 +0,0 @@
-FROM fedora:31
-
-RUN dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison pkgconf libacl-devel libasan librichacl-devel bzip2-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel groff ghostscript xz zip
diff --git a/build/ci/cirrus_ci/Dockerfile.mingw b/build/ci/cirrus_ci/Dockerfile.mingw
deleted file mode 100644
index f14bb0bc73bd..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.mingw
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644
index c98318289217..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.msvc
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644
index 5b6cab99a852..000000000000
--- a/build/ci/cirrus_ci/Dockerfile.windows
+++ /dev/null
@@ -1,12 +0,0 @@
-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,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
deleted file mode 100755
index 6fc1ea7c8141..000000000000
--- a/build/ci/cirrus_ci/ci.cmd
+++ /dev/null
@@ -1,122 +0,0 @@
-@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,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/ci.sh b/build/ci/cirrus_ci/ci.sh
deleted file mode 100755
index 231205723410..000000000000
--- a/build/ci/cirrus_ci/ci.sh
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh
-set -eu
-
-if [ $# != 1 ]
-then
- echo "Usage: $0 prepare | test"
- exit 1
-fi
-
-UNAME=`uname`
-if [ "$1" = "prepare" ]
-then
- if [ "${UNAME}" = "FreeBSD" ]
- then
- set -x -e
- env ASSUME_ALWAYS_YES=yes pkg bootstrap -f
- sed -i.bak -e 's,pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly,pkg+http://pkg.FreeBSD.org/\${ABI}/latest,' /etc/pkg/FreeBSD.conf
- pkg update
- mount -u -o acls /
- mkdir /tmp_acl_nfsv4
- MD=`mdconfig -a -t swap -s 128M`
- newfs /dev/$MD
- tunefs -N enable /dev/$MD
- mount /dev/$MD /tmp_acl_nfsv4
- chmod 1777 /tmp_acl_nfsv4
- pkg install -y autoconf automake cmake libiconv libtool pkgconf expat libxml2 liblz4 zstd gmake
- elif [ "${UNAME}" = "Darwin" ]
- then
- set -x -e
- brew update > /dev/null
- for pkg in autoconf automake libtool pkg-config cmake xz lz4 zstd
- do
- brew list $pkg > /dev/null && brew upgrade $pkg || brew install $pkg
- done
- elif [ "${UNAME}" = "Linux" ]
- then
- if [ -f "/etc/debian_version" ]
- then
- apt-get -y update
- apt-get -y install build-essential locales automake libtool bison pkgconf libacl1-dev libbz2-dev zlib1g-dev liblzma-dev liblz4-dev libzstd-dev libssl-dev lrzip cmake
- elif [ -f "/etc/fedora-release" ]
- then
- dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison pkgconf libacl-devel librichacl-devel bzip2-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel
- fi
- fi
-elif [ "$1" = "test" ]
-then
- if [ "${UNAME}" = "FreeBSD" ]
- then
- set -e
- echo "Additional NFSv4 ACL tests"
- CURDIR=`pwd`
- if [ "${BS}" = "cmake" ]
- then
- BIN_SUBDIR="bin"
- else
- BIN_SUBDIR=.
- fi
- BUILDDIR="${CURDIR}/build_ci/${BS}"
- cd "$BUILDDIR"
- TMPDIR=/tmp_acl_nfsv4 ${BIN_SUBDIR}/libarchive_test -r "${CURDIR}/libarchive/test" -v test_acl_platform_nfs4
- fi
-else
- echo "Usage: $0 prepare | test"
- exit 1
-fi
diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in
index a082e6298652..76864a909e44 100644
--- a/build/cmake/config.h.in
+++ b/build/cmake/config.h.in
@@ -666,6 +666,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `getpwuid_r' function. */
#cmakedefine HAVE_GETPWUID_R 1
+/* Define to 1 if you have the `gettimeofday' function. */
+#cmakedefine HAVE_GETTIMEOFDAY 1
+
/* Define to 1 if you have the `getvfsbyname' function. */
#cmakedefine HAVE_GETVFSBYNAME 1
diff --git a/build/la-lint/.clang-format b/build/la-lint/.clang-format
new file mode 100644
index 000000000000..613db0ca3c0a
--- /dev/null
+++ b/build/la-lint/.clang-format
@@ -0,0 +1,46 @@
+---
+BasedOnStyle: Google
+AlignAfterOpenBracket: DontAlign
+AlignArrayOfStructures: Left
+AlignConsecutiveAssignments:
+ AlignFunctionDeclarations: true
+AlignConsecutiveMacros:
+ Enabled: true
+AlignOperands: DontAlign
+AllowAllArgumentsOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Empty
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakBeforeMultilineStrings: false
+BinPackParameters: OnePerLine
+BreakBeforeTemplateCloser: true
+BreakConstructorInitializers: AfterColon
+BreakInheritanceList: AfterComma
+BreakStringLiterals: false
+ColumnLimit: 120
+ConstructorInitializerIndentWidth: 8
+EnumTrailingComma: Insert
+IncludeBlocks: Preserve
+IndentCaseLabels: false
+IndentGotoLabels: false
+IndentPPDirectives: AfterHash
+IndentWidth: 8
+PPIndentWidth: 1
+PackConstructorInitializers: BinPack
+PointerAlignment: Right
+QualifierAlignment: Left
+RemoveEmptyLinesInUnwrappedLines: true
+SeparateDefinitionBlocks: Always
+SortIncludes:
+ Enabled: false
+SpaceBeforeParens: Custom
+SpaceBeforeParensOptions:
+ AfterControlStatements: false
+ AfterForeachMacros: false
+ AfterIfMacros: false
+SpacesInContainerLiterals: false
+UseTab: ForIndentation
+
diff --git a/build/la-lint/CMakeLists.txt b/build/la-lint/CMakeLists.txt
new file mode 100644
index 000000000000..9480adc8c688
--- /dev/null
+++ b/build/la-lint/CMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.12)
+set(CMAKE_CXX_STANDARD 23)
+
+project(libarchive-clang-tools)
+find_package(zstd REQUIRED)
+find_package(Clang REQUIRED)
+add_executable(la-lint)
+
+target_sources(la-lint
+ PRIVATE
+ main.cpp
+)
+
+target_include_directories(la-lint
+ PRIVATE
+ ${CLANG_INCLUDE_DIRS}
+)
+
+target_link_libraries(la-lint
+ PRIVATE
+ LLVM LLVMCore LLVMSupport
+ libclang clang-cpp
+)
+
+install(TARGETS la-lint RUNTIME DESTINATION bin)
diff --git a/build/la-lint/main.cpp b/build/la-lint/main.cpp
new file mode 100644
index 000000000000..077b16399cf7
--- /dev/null
+++ b/build/la-lint/main.cpp
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2026 Dustin L. Howett
+ * 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
+ * in this position and unchanged.
+ * 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 <clang/Frontend/FrontendActions.h>
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <clang/Frontend/ChainedDiagnosticConsumer.h>
+#include <clang/Tooling/CommonOptionsParser.h>
+#include <clang/Tooling/Tooling.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+#include <llvm/Support/CommandLine.h>
+
+#include <print>
+
+using namespace clang::tooling;
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+static llvm::cl::OptionCategory LaToolCategory("la-clang-tool options");
+static llvm::cl::opt<std::string> Prefix("prefix",
+ llvm::cl::desc("Project path prefix"),
+ llvm::cl::init(""),
+ llvm::cl::cat(LaToolCategory));
+static bool IsGithubCiBuild{};
+
+struct MemCmpNeedleHandler : public MatchFinder::MatchCallback {
+ virtual void run(const MatchFinder::MatchResult &result) {
+ auto needle = result.Nodes.getNodeAs<clang::StringLiteral>("needle");
+ auto lenExpr = result.Nodes.getNodeAs<clang::IntegerLiteral>("length");
+
+ /*
+ * This check uses [less than] rather than [less equal] because in some formats we want to verify the
+ * presence of the null terminator and in others we do not.
+ */
+ if(lenExpr->getValue().ult(needle->getLength())) {
+ auto &diag = result.Context->getDiagnostics();
+ auto did = diag.getCustomDiagID(clang::DiagnosticsEngine::Level::Error,
+ "memcmp needle has length `%0', but is checked with length `%1'");
+ clang::DiagnosticBuilder db = diag.Report(lenExpr->getLocation(), did);
+ db.AddTaggedVal(needle->getLength(), clang::DiagnosticsEngine::ArgumentKind::ak_uint);
+ db.AddTaggedVal(lenExpr->getValue().getSExtValue(),
+ clang::DiagnosticsEngine::ArgumentKind::ak_sint);
+ db.AddSourceRange(clang::CharSourceRange::getCharRange(needle->getSourceRange()));
+ }
+ }
+};
+
+struct AssertUAFHandler : public MatchFinder::MatchCallback {
+ virtual void run(const MatchFinder::MatchResult &result) {
+ /*
+ * This code uses getFileLoc to specify locations disregarding macro expansion
+ * and to avoid Clang printing "macro expanded from here".
+ */
+ auto &SM = *result.SourceManager;
+ auto uaf = result.Nodes.getNodeAs<clang::DeclRefExpr>("uaf");
+ auto assertion = result.Nodes.getNodeAs<clang::CallExpr>("assertion");
+ auto freed = result.Nodes.getNodeAs<clang::CallExpr>("free");
+
+ auto &diag = result.Context->getDiagnostics();
+ {
+ auto did = diag.getCustomDiagID(clang::DiagnosticsEngine::Level::Error,
+ "test assertion uses `%0' after free; prefer assertEqualInt");
+ clang::DiagnosticBuilder db = diag.Report(SM.getFileLoc(uaf->getBeginLoc()), did);
+ db.AddString(uaf->getNameInfo().getAsString());
+ db << clang::FixItHint::CreateReplacement(
+ clang::SourceRange{SM.getFileLoc(assertion->getBeginLoc()),
+ SM.getFileLoc(uaf->getBeginLoc())},
+ "assertEqualInt(");
+ }
+
+ {
+ auto did = diag.getCustomDiagID(clang::DiagnosticsEngine::Level::Note, "`%0' freed here");
+ clang::DiagnosticBuilder db = diag.Report(freed->getBeginLoc(), did);
+ db.AddString(uaf->getNameInfo().getAsString());
+ }
+ }
+};
+
+class GithubActionDiagnosticConsumer : public clang::DiagnosticConsumer {
+ virtual void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) {
+ static constexpr const char *levels[] = {nullptr, "notice", "notice", "warning", "error", "error"};
+
+ const auto level = levels[DiagLevel];
+ if(!level)
+ return;
+
+ auto &SM = Info.getSourceManager();
+ auto loc = Info.getLocation();
+ loc = SM.getFileLoc(loc);
+
+ auto file = SM.getFilename(loc);
+ if(file.empty()) {
+ return;
+ }
+ file.consume_front_insensitive(Prefix);
+ while(file.consume_front("/")) {}
+
+ bool lineInvalid = false;
+ auto line = SM.getLineNumber(loc, &lineInvalid);
+ if(lineInvalid) {
+ return;
+ }
+
+ llvm::SmallVector<char> diagString;
+ Info.FormatDiagnostic(diagString);
+
+ std::print("::{0} file={1},line={2},title=Lint::{3}\n", level, std::string_view{file}, line,
+ std::string_view{diagString});
+ }
+};
+
+int main(int argc, const char **argv) {
+ auto exp = CommonOptionsParser::create(argc, argv, LaToolCategory);
+ auto &optionsParser = exp.get();
+ ClangTool tool(optionsParser.getCompilations(), optionsParser.getSourcePathList());
+
+ if(const char *e = getenv("GITHUB_WORKSPACE")) {
+ IsGithubCiBuild = true;
+
+ if(Prefix.c_str()[0] == '\0') {
+ Prefix.assign(e);
+ Prefix.append("/");
+ }
+ }
+
+ MatchFinder finder;
+
+ /*
+ * Find every assertion_equal_mem that passes a string literal.
+ * --> assertEqualMem(foo, "literal", 7);
+ */
+ MemCmpNeedleHandler memCmpHandler;
+ finder.addMatcher(callExpr(callee(functionDecl(hasName("assertion_equal_mem"))),
+ hasArgument(4, stringLiteral().bind("needle")),
+ hasArgument(6, integerLiteral(anything()).bind("length"))),
+ &memCmpHandler);
+
+ /*
+ * Find every assertion_equal_int whose call site contains a `free` function,
+ * and then passes the freed decl to assertion_equal_int as context.
+ * --> assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a));
+ * ^-- must be the same as ----------^
+ */
+ AssertUAFHandler uafHandler;
+ finder.addMatcher(
+ callExpr(callee(functionDecl(hasName("assertion_equal_int"))),
+ hasArgument(4,
+ callExpr(callee(functionDecl(matchesName("archive.*_free$"))),
+ hasArgument(0, declRefExpr(to(varDecl(anything()).bind("freed")))))
+ .bind("free")),
+ hasArgument(6, declRefExpr(to(varDecl(declaresSameEntityAsBoundNode("freed")))).bind("uaf")))
+ .bind("assertion"),
+ &uafHandler);
+
+ std::unique_ptr<clang::DiagnosticConsumer> diagnosticConsumer;
+
+ if(IsGithubCiBuild) {
+ /* Emit GitHub-formatted error messages by default when we are building in GitHub Actions */
+ clang::DiagnosticOptions diagOptions;
+ diagOptions.ShowColors = true;
+ clang::TextDiagnosticPrinter defaultDiag(llvm::errs(), diagOptions, false);
+ diagnosticConsumer = std::make_unique<clang::ChainedDiagnosticConsumer>(&defaultDiag,
+ std::make_unique<GithubActionDiagnosticConsumer>());
+ tool.setDiagnosticConsumer(diagnosticConsumer.get());
+ }
+
+ return tool.run(newFrontendActionFactory(&finder).get());
+}
diff --git a/build/version b/build/version
index 07e7c74e793a..c9c7ecb809f5 100644
--- a/build/version
+++ b/build/version
@@ -1 +1 @@
-3008008
+3008009
diff --git a/configure.ac b/configure.ac
index 1ef0ef8bbbf1..a2a6d7c213ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,8 +4,8 @@ dnl First, define all of the version numbers up front.
dnl In particular, this allows the version macro to be used in AC_INIT
dnl These first two version numbers are updated automatically on each release.
-m4_define([LIBARCHIVE_VERSION_S],[3.8.8])
-m4_define([LIBARCHIVE_VERSION_N],[3008008])
+m4_define([LIBARCHIVE_VERSION_S],[3.8.9])
+m4_define([LIBARCHIVE_VERSION_N],[3008009])
dnl bsdtar and bsdcpio versioning tracks libarchive
m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S())
@@ -101,6 +101,9 @@ esac
AM_CONDITIONAL([INC_WINDOWS_FILES], [test $inc_windows_files = yes])
AM_CONDITIONAL([INC_CYGWIN_FILES], [test $inc_cygwin_files = yes])
+dnl On native Windows (MinGW), embedding the manifest needs windres
+AS_IF([test $inc_windows_files = yes], [AC_CHECK_TOOL([WINDRES], [windres])])
+
dnl Defines that are required for specific platforms (e.g. -D_POSIX_SOURCE, etc)
PLATFORMCPPFLAGS=
case "$host_os" in
@@ -828,7 +831,7 @@ AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate])
AC_CHECK_FUNCS([futimens futimes futimesat])
AC_CHECK_FUNCS([getegid geteuid getline getpid getresgid getresuid])
AC_CHECK_FUNCS([getgrgid_r getgrnam_r getpwnam_r getpwuid_r])
-AC_CHECK_FUNCS([getvfsbyname gmtime_r])
+AC_CHECK_FUNCS([gettimeofday getvfsbyname gmtime_r])
AC_CHECK_FUNCS([issetugid])
AC_CHECK_FUNCS([lchflags lchmod lchown link linkat localtime_r lstat lutimes])
AC_CHECK_FUNCS([mbrtowc memmove memset])
@@ -1375,9 +1378,8 @@ AC_SUBST(LIBSREQUIRED)
# Probe libmd AFTER OpenSSL/libcrypto.
# The two are incompatible and OpenSSL is more complete.
-AC_CHECK_HEADERS([md5.h ripemd.h sha.h sha256.h sha512.h])
saved_LIBS=$LIBS
-AC_CHECK_LIB(md,MD5Init)
+AC_CHECK_LIB(md, MD5Init)
CRYPTO_CHECK(MD5, LIBMD, md5)
CRYPTO_CHECK(RMD160, LIBMD, rmd160)
CRYPTO_CHECK(SHA1, LIBMD, sha1)
diff --git a/libarchive/archive.h b/libarchive/archive.h
index 159a3d561a18..a7de6f6dd85b 100644
--- a/libarchive/archive.h
+++ b/libarchive/archive.h
@@ -34,7 +34,7 @@
* assert that ARCHIVE_VERSION_NUMBER >= 2012108.
*/
/* Note: Compiler will complain if this does not match archive_entry.h! */
-#define ARCHIVE_VERSION_NUMBER 3008008
+#define ARCHIVE_VERSION_NUMBER 3008009
#include <sys/stat.h>
#include <stddef.h> /* for wchar_t */
@@ -177,7 +177,7 @@ __LA_DECL int archive_version_number(void);
/*
* Textual name/version of the library, useful for version displays.
*/
-#define ARCHIVE_VERSION_ONLY_STRING "3.8.8"
+#define ARCHIVE_VERSION_ONLY_STRING "3.8.9"
#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
__LA_DECL const char * archive_version_string(void);
diff --git a/libarchive/archive_acl.c b/libarchive/archive_acl.c
index f8d85b4bfabc..f1b1232f5eef 100644
--- a/libarchive/archive_acl.c
+++ b/libarchive/archive_acl.c
@@ -1516,7 +1516,7 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
const char *end;
} field[6], name;
- const char *s, *st;
+ const char *s, *st, *text_end;
int numfields, fields, n, r, sol, ret;
int type, types, tag, permset, id;
size_t len;
@@ -1539,6 +1539,7 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
ret = ARCHIVE_OK;
types = 0;
+ text_end = (text == NULL) ? text : text + length;
while (text != NULL && length > 0 && *text != '\0') {
/*
@@ -1565,6 +1566,18 @@ archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
return (ARCHIVE_FATAL);
}
+ if (field[0].start == text_end) {
+ /*
+ * Empty entry: next_field() consumed the rest of the
+ * buffer as separators or whitespace, leaving
+ * field[0].start one past the end. 'text' is not
+ * guaranteed to be NUL terminated, so the byte after it
+ * must not be dereferenced.
+ */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
if (*(field[0].start) == '#') {
/* Comment, skip entry */
continue;
diff --git a/libarchive/archive_acl_private.h b/libarchive/archive_acl_private.h
index 54e4439692ed..4df8a0ac3a5c 100644
--- a/libarchive/archive_acl_private.h
+++ b/libarchive/archive_acl_private.h
@@ -27,8 +27,10 @@
#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
#error This header is only to be used internally to libarchive.
#endif
+#endif
#include "archive_string.h"
diff --git a/libarchive/archive_check_magic.c b/libarchive/archive_check_magic.c
index b6e1257949e1..788a76070b7a 100644
--- a/libarchive/archive_check_magic.c
+++ b/libarchive/archive_check_magic.c
@@ -85,6 +85,7 @@ state_name(unsigned s)
case ARCHIVE_STATE_NEW: return ("new");
case ARCHIVE_STATE_HEADER: return ("header");
case ARCHIVE_STATE_DATA: return ("data");
+ case ARCHIVE_STATE_DATA_RECOVERY: return ("data_recovery");
case ARCHIVE_STATE_EOF: return ("eof");
case ARCHIVE_STATE_CLOSED: return ("closed");
case ARCHIVE_STATE_FATAL: return ("fatal");
diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c
index 90cf5cc829ea..5b8351b4a190 100644
--- a/libarchive/archive_entry.c
+++ b/libarchive/archive_entry.c
@@ -1562,17 +1562,21 @@ void
archive_entry_copy_mac_metadata(struct archive_entry *entry,
const void *p, size_t s)
{
- free(entry->mac_metadata);
- if (p == NULL || s == 0) {
- entry->mac_metadata = NULL;
- entry->mac_metadata_size = 0;
- } else {
- entry->mac_metadata_size = s;
- entry->mac_metadata = malloc(s);
- if (entry->mac_metadata == NULL)
- abort();
- memcpy(entry->mac_metadata, p, s);
- }
+ void *metadata;
+
+ if (p == NULL || s == 0) {
+ free(entry->mac_metadata);
+ entry->mac_metadata = NULL;
+ entry->mac_metadata_size = 0;
+ } else {
+ metadata = malloc(s);
+ if (metadata == NULL)
+ abort();
+ memcpy(metadata, p, s);
+ free(entry->mac_metadata);
+ entry->mac_metadata = metadata;
+ entry->mac_metadata_size = s;
+ }
}
/* Digest handling */
diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h
index aa4f4dfb366f..9c859a44e340 100644
--- a/libarchive/archive_entry.h
+++ b/libarchive/archive_entry.h
@@ -28,7 +28,7 @@
#define ARCHIVE_ENTRY_H_INCLUDED
/* Note: Compiler will complain if this does not match archive.h! */
-#define ARCHIVE_VERSION_NUMBER 3008008
+#define ARCHIVE_VERSION_NUMBER 3008009
/*
* Note: archive_entry.h is for use outside of libarchive; the
@@ -666,7 +666,7 @@ __LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *,
la_int64_t /* offset */, la_int64_t /* length */);
/*
- * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * To retrieve the sparse list, first "reset", then repeatedly ask for the
* "next" entry.
*/
diff --git a/libarchive/archive_entry_link_resolver.c b/libarchive/archive_entry_link_resolver.c
index 233c46aa892e..947c65fca5fe 100644
--- a/libarchive/archive_entry_link_resolver.c
+++ b/libarchive/archive_entry_link_resolver.c
@@ -41,6 +41,7 @@
#include "archive.h"
#include "archive_entry.h"
+#include "archive_integer.h"
/*
* This is mostly a pretty straightforward hash table implementation.
@@ -387,7 +388,7 @@ insert_entry(struct archive_entry_linkresolver *res,
}
/* If the links cache is getting too full, enlarge the hash table. */
- if (res->number_entries > res->number_buckets * 2)
+ if (res->number_entries / 2 > res->number_buckets)
grow_hash(res);
hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
@@ -413,8 +414,7 @@ grow_hash(struct archive_entry_linkresolver *res)
size_t i, bucket;
/* Try to enlarge the bucket list. */
- new_size = res->number_buckets * 2;
- if (new_size < res->number_buckets)
+ if (archive_ckd_mul_size(&new_size, res->number_buckets, 2))
return;
new_buckets = calloc(new_size, sizeof(struct links_entry *));
diff --git a/libarchive/archive_entry_xattr.c b/libarchive/archive_entry_xattr.c
index b92e1878bc43..7a78f0c3e1ab 100644
--- a/libarchive/archive_entry_xattr.c
+++ b/libarchive/archive_entry_xattr.c
@@ -96,11 +96,11 @@ archive_entry_xattr_add_entry(struct archive_entry *entry,
if ((xp->name = strdup(name)) == NULL)
__archive_errx(1, "Out of memory");
- if ((xp->value = malloc(size)) != NULL) {
- memcpy(xp->value, value, size);
- xp->size = size;
- } else
- xp->size = 0;
+ if ((xp->value = malloc(size)) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ memcpy(xp->value, value, size);
+ xp->size = size;
xp->next = entry->xattr_head;
entry->xattr_head = xp;
diff --git a/libarchive/archive_integer.h b/libarchive/archive_integer.h
index 6427c622cd29..1e715927901e 100644
--- a/libarchive/archive_integer.h
+++ b/libarchive/archive_integer.h
@@ -49,6 +49,9 @@
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -244,4 +247,21 @@ archive_ckd_sub_i64(int64_t *result, int64_t a, int64_t b)
#endif
}
+#if !defined(TIME_MAX)
+#define TIME_MAX (((time_t)0 < (time_t)-1) ? (time_t)~0 : \
+ sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MAX : \
+ sizeof(time_t) == sizeof(long) ? (time_t)LONG_MAX : \
+ sizeof(time_t) == sizeof(int) ? (time_t)INT_MAX : \
+ sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MAX : \
+ 1 /* I give up */)
+#endif
+#if !defined(TIME_MIN)
+#define TIME_MIN (((time_t)0 < (time_t)-1) ? (time_t)0 : \
+ sizeof(time_t) == sizeof(long long) ? (time_t)LLONG_MIN : \
+ sizeof(time_t) == sizeof(long) ? (time_t)LONG_MIN : \
+ sizeof(time_t) == sizeof(int) ? (time_t)INT_MIN : \
+ sizeof(time_t) == sizeof(short) ? (time_t)SHRT_MIN : \
+ -1 /* I give up */)
+#endif
+
#endif
diff --git a/libarchive/archive_options.c b/libarchive/archive_options.c
index 6190bb8ef7ee..24480b97e25d 100644
--- a/libarchive/archive_options.c
+++ b/libarchive/archive_options.c
@@ -33,7 +33,7 @@
static char *
parse_option(char **str,
- char **mod, char **opt, char **val);
+ const char **mod, const char **opt, const char **val);
int
_archive_set_option(struct archive *a,
@@ -102,8 +102,8 @@ _archive_set_options(struct archive *a, const char *options,
unsigned int magic, const char *fn, option_handler use_option)
{
int allok = 1, anyok = 0, ignore_mod_err = 0, r;
- char *data;
- char *s, *mod, *opt, *val;
+ char *data, *s;
+ const char *mod, *opt, *val;
archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
@@ -168,10 +168,10 @@ _archive_set_options(struct archive *a, const char *options,
}
static char *
-parse_option(char **s, char **m, char **o, char **v)
+parse_option(char **s, const char **m, const char **o, const char **v)
{
- char *end, *mod, *opt, *val;
- char *p;
+ const char *mod, *val;
+ char *end, *opt, *p;
end = NULL;
mod = NULL;
diff --git a/libarchive/archive_pathmatch.c b/libarchive/archive_pathmatch.c
index 4b69ae1e4afb..46bdb51056a7 100644
--- a/libarchive/archive_pathmatch.c
+++ b/libarchive/archive_pathmatch.c
@@ -337,7 +337,7 @@ pm_w(const wchar_t *p, const wchar_t *s, int flags, int depth)
}
if (*end == L']') {
/* We found [...], try to match it. */
- if (!pm_list_w(p + 1, end, *s, flags))
+ if (*s == L'\0' || !pm_list_w(p + 1, end, *s, flags))
return (0);
p = end; /* Jump to trailing ']' char. */
break;
diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h
index 3a926c6886ad..0f068639b9cc 100644
--- a/libarchive/archive_private.h
+++ b/libarchive/archive_private.h
@@ -61,12 +61,38 @@
#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
#define ARCHIVE_MATCH_MAGIC (0xcad11c9U)
+/*
+ * Having the state be a bitmask makes it easy to check
+ * for combinations of allowed states. These should
+ * generally only be used in public API entry points,
+ * primarily in archive_read.c, archive_write.c, etc.
+ * Internal callbacks can rely on the core machinery
+ * to only call them when appropriate.
+ *
+ * Generally checked via `__archive_check_magic()`.
+ */
+
+/* Newly created archive object, not yet opened */
#define ARCHIVE_STATE_NEW 1U
+/* Archive is ready to read a header. */
#define ARCHIVE_STATE_HEADER 2U
+/* A header has been read: client can ask for data
+ * or they can ask for the next header and
+ * we'll automatically skip the remaining data. */
#define ARCHIVE_STATE_DATA 4U
+/* Similar to STATE_DATA but after a FAILED header:
+ * the client may not read data, but they may ask
+ * for the next header and we'll recover. */
+#define ARCHIVE_STATE_DATA_RECOVERY 8U
+/* End-of-archive has been reached. Client can only
+ * close or free the archive. */
#define ARCHIVE_STATE_EOF 0x10U
+/* Archive is closed; client is only allowed to free the archive. */
#define ARCHIVE_STATE_CLOSED 0x20U
+/* Archive is in a FATAL error state: a close request
+ * is permitted but ignored. */
#define ARCHIVE_STATE_FATAL 0x8000U
+/* Any valid (non-fatal) state. */
#define ARCHIVE_STATE_ANY (0xFFFFU & ~ARCHIVE_STATE_FATAL)
struct archive_vtable {
diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c
index b23d99dd7b27..e844e42d1ac9 100644
--- a/libarchive/archive_read.c
+++ b/libarchive/archive_read.c
@@ -103,7 +103,12 @@ archive_read_new(void)
a->archive.state = ARCHIVE_STATE_NEW;
a->entry = archive_entry_new2(&a->archive);
+ if (a->entry == NULL) {
+ free(a);
+ return (NULL);
+ }
a->archive.vtable = &archive_read_vtable;
+ a->entry_bytes_declared = -1;
a->passphrases.last = &a->passphrases.first;
@@ -135,12 +140,16 @@ archive_read_open(struct archive *a, void *client_data,
archive_open_callback *client_opener, archive_read_callback *client_reader,
archive_close_callback *client_closer)
{
+ int r;
+
/* Old archive_read_open() is just a thin shell around
* archive_read_open1. */
archive_read_set_open_callback(a, client_opener);
archive_read_set_read_callback(a, client_reader);
archive_read_set_close_callback(a, client_closer);
- archive_read_set_callback_data(a, client_data);
+ r = archive_read_set_callback_data(a, client_data);
+ if (r < 0)
+ return (r);
return archive_read_open1(a);
}
@@ -152,9 +161,13 @@ archive_read_open2(struct archive *a, void *client_data,
archive_skip_callback *client_skipper,
archive_close_callback *client_closer)
{
+ int r;
+
/* Old archive_read_open2() is just a thin shell around
* archive_read_open1. */
- archive_read_set_callback_data(a, client_data);
+ r = archive_read_set_callback_data(a, client_data);
+ if (r < 0)
+ return (r);
archive_read_set_open_callback(a, client_opener);
archive_read_set_read_callback(a, client_reader);
archive_read_set_skip_callback(a, client_skipper);
@@ -163,28 +176,28 @@ archive_read_open2(struct archive *a, void *client_data,
}
static ssize_t
-client_read_proxy(struct archive_read_filter *self, const void **buff)
+client_read_proxy(struct archive_read_filter *f, const void **buff)
{
ssize_t r;
- r = (self->archive->client.reader)(&self->archive->archive,
- self->data, buff);
+ r = (f->archive->client.reader)(&f->archive->archive,
+ f->data, buff);
return (r);
}
static int64_t
-client_skip_proxy(struct archive_read_filter *self, int64_t request)
+client_skip_proxy(struct archive_read_filter *f, int64_t request)
{
if (request < 0)
__archive_errx(1, "Negative skip requested");
if (request == 0)
return 0;
- if (self->archive->client.skipper != NULL) {
+ if (f->archive->client.skipper != NULL) {
int64_t total = 0;
for (;;) {
int64_t get, ask = request;
- get = (self->archive->client.skipper)
- (&self->archive->archive, self->data, ask);
+ get = (f->archive->client.skipper)
+ (&f->archive->archive, f->data, ask);
total += get;
if (get == 0 || get == request)
return (total);
@@ -192,7 +205,7 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request)
return ARCHIVE_FATAL;
request -= get;
}
- } else if (self->archive->client.seeker != NULL
+ } else if (f->archive->client.seeker != NULL
&& request > 64 * 1024) {
/* If the client provided a seeker but not a skipper,
* we can use the seeker to skip forward.
@@ -205,9 +218,9 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request)
* to just reading and discarding. That's why we
* only do this for skips of over 64k.
*/
- int64_t before = self->position;
- int64_t after = (self->archive->client.seeker)
- (&self->archive->archive, self->data, request, SEEK_CUR);
+ int64_t before = f->position;
+ int64_t after = (f->archive->client.seeker)
+ (&f->archive->archive, f->data, request, SEEK_CUR);
if (after != before + request)
return ARCHIVE_FATAL;
return after - before;
@@ -216,20 +229,20 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request)
}
static int64_t
-client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+client_seek_proxy(struct archive_read_filter *f, int64_t offset, int whence)
{
/* DO NOT use the skipper here! If we transparently handled
* forward seek here by using the skipper, that will break
* other libarchive code that assumes a successful forward
* seek means it can also seek backwards.
*/
- if (self->archive->client.seeker == NULL) {
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ if (f->archive->client.seeker == NULL) {
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Current client reader does not support seeking a device");
return (ARCHIVE_FAILED);
}
- return (self->archive->client.seeker)(&self->archive->archive,
- self->data, offset, whence);
+ return (f->archive->client.seeker)(&f->archive->archive,
+ f->data, offset, whence);
}
static int
@@ -251,21 +264,21 @@ read_client_close_proxy(struct archive_read *a)
}
static int
-client_close_proxy(struct archive_read_filter *self)
+client_close_proxy(struct archive_read_filter *f)
{
- return read_client_close_proxy(self->archive);
+ return read_client_close_proxy(f->archive);
}
static int
-client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+client_switch_proxy(struct archive_read_filter *f, unsigned int iindex)
{
struct archive_read *a;
int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
void *data2;
- while (self->upstream != NULL)
- self = self->upstream;
- a = self->archive;
+ while (f->upstream != NULL)
+ f = f->upstream;
+ a = f->archive;
/* Don't do anything if already in the specified data node */
if (a->client.cursor == iindex)
@@ -276,19 +289,19 @@ client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
if (a->client.switcher != NULL)
{
r1 = r2 = (a->client.switcher)
- ((struct archive *)a, self->data, data2);
- self->data = data2;
+ ((struct archive *)a, f->data, data2);
+ f->data = data2;
}
else
{
/* Attempt to call close and open instead */
if (a->client.closer != NULL)
r1 = (a->client.closer)
- ((struct archive *)a, self->data);
- self->data = data2;
+ ((struct archive *)a, f->data);
+ f->data = data2;
if (a->client.opener != NULL)
r2 = (a->client.opener)
- ((struct archive *)a, self->data);
+ ((struct archive *)a, f->data);
}
return (r1 < r2) ? r1 : r2;
}
@@ -469,7 +482,7 @@ int
archive_read_open1(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
- struct archive_read_filter *filter, *tmp;
+ struct archive_read_filter *f, *tmp;
int slot, e = ARCHIVE_OK;
archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
@@ -493,23 +506,23 @@ archive_read_open1(struct archive *_a)
}
}
- filter = calloc(1, sizeof(*filter));
- if (filter == NULL)
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
return (ARCHIVE_FATAL);
- filter->bidder = NULL;
- filter->upstream = NULL;
- filter->archive = a;
- filter->data = a->client.dataset[0].data;
- filter->vtable = &none_reader_vtable;
- filter->name = "none";
- filter->code = ARCHIVE_FILTER_NONE;
- filter->can_skip = 1;
- filter->can_seek = 1;
+ f->bidder = NULL;
+ f->upstream = NULL;
+ f->archive = a;
+ f->data = a->client.dataset[0].data;
+ f->vtable = &none_reader_vtable;
+ f->name = "none";
+ f->code = ARCHIVE_FILTER_NONE;
+ f->can_skip = 1;
+ f->can_seek = 1;
a->client.dataset[0].begin_position = 0;
if (!a->filter || !a->bypass_filter_bidding)
{
- a->filter = filter;
+ a->filter = f;
/* Build out the input pipeline. */
e = choose_filters(a);
if (e < ARCHIVE_WARN) {
@@ -523,7 +536,7 @@ archive_read_open1(struct archive *_a)
tmp = a->filter;
while (tmp->upstream)
tmp = tmp->upstream;
- tmp->upstream = filter;
+ tmp->upstream = f;
}
if (!a->format)
@@ -558,7 +571,7 @@ choose_filters(struct archive_read *a)
{
int number_bidders, i, bid, best_bid, number_filters;
struct archive_read_filter_bidder *bidder, *best_bidder;
- struct archive_read_filter *filter;
+ struct archive_read_filter *f;
ssize_t avail;
int r;
@@ -590,13 +603,13 @@ choose_filters(struct archive_read *a)
return (ARCHIVE_OK);
}
- filter = calloc(1, sizeof(*filter));
- if (filter == NULL)
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
return (ARCHIVE_FATAL);
- filter->bidder = best_bidder;
- filter->archive = a;
- filter->upstream = a->filter;
- a->filter = filter;
+ f->bidder = best_bidder;
+ f->archive = a;
+ f->upstream = a->filter;
+ a->filter = f;
r = (best_bidder->vtable->init)(a->filter);
if (r != ARCHIVE_OK) {
__archive_read_free_filters(a);
@@ -626,7 +639,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
int r1 = ARCHIVE_OK, r2;
archive_check_magic(_a, ARCHIVE_READ_MAGIC,
- ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA |
+ ARCHIVE_STATE_DATA_RECOVERY,
"archive_read_next_header");
archive_entry_clear(entry);
@@ -635,8 +649,12 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
/*
* If client didn't consume entire data, skip any remainder
* (This is especially important for GNU incremental directories.)
+ * A header that failed to parse (DATA_RECOVERY) still needs the
+ * same treatment: whatever of its body the format reader left
+ * unconsumed must be skipped before we can read the next header.
*/
- if (a->archive.state == ARCHIVE_STATE_DATA) {
+ if (a->archive.state == ARCHIVE_STATE_DATA ||
+ a->archive.state == ARCHIVE_STATE_DATA_RECOVERY) {
r1 = archive_read_data_skip(&a->archive);
if (r1 == ARCHIVE_EOF)
archive_set_error(&a->archive, EIO,
@@ -674,8 +692,26 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
case ARCHIVE_FATAL:
a->archive.state = ARCHIVE_STATE_FATAL;
break;
+ case ARCHIVE_FAILED:
+ /*
+ * This entry's header could not be parsed, so its metadata
+ * cannot be trusted. ARCHIVE_STATE_DATA_RECOVERY still
+ * permits skipping past it (the format reader is
+ * responsible for ensuring that's actually possible), but
+ * blocks archive_read_data() and friends, which all check
+ * for ARCHIVE_STATE_DATA specifically and would otherwise
+ * return content for an entry we don't actually understand.
+ */
+ a->archive.state = ARCHIVE_STATE_DATA_RECOVERY;
+ break;
}
+ if (r2 == ARCHIVE_OK || r2 == ARCHIVE_WARN)
+ a->entry_bytes_declared = archive_entry_size_is_set(entry)
+ ? archive_entry_size(entry) : -1;
+ else
+ a->entry_bytes_declared = -1;
+
__archive_reset_read_data(&a->archive);
a->data_start_node = a->client.cursor;
@@ -764,7 +800,7 @@ archive_read_header_position(struct archive *_a)
/*
* Returns 1 if the archive contains at least one encrypted entry.
- * If the archive format not support encryption at all
+ * If the archive format does not support encryption at all,
* ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
* If for any other reason (e.g. not enough data read so far)
* we cannot say whether there are encrypted entries, then
@@ -934,7 +970,8 @@ archive_read_data_skip(struct archive *_a)
size_t size;
int64_t offset;
- archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_DATA | ARCHIVE_STATE_DATA_RECOVERY,
"archive_read_data_skip");
if (a->format->read_data_skip != NULL)
@@ -966,11 +1003,9 @@ archive_seek_data(struct archive *_a, int64_t offset, int whence)
"archive_seek_data_block");
if (a->format->seek_data == NULL) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
- "Internal error: "
- "No format_seek_data_block function registered");
- a->archive.state = ARCHIVE_STATE_FATAL;
- return (ARCHIVE_FATAL);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot seek data with this format");
+ return (ARCHIVE_FAILED);
}
r = (a->format->seek_data)(a, offset, whence);
@@ -1347,13 +1382,13 @@ __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
}
const void *
-__archive_read_filter_ahead(struct archive_read_filter *filter,
+__archive_read_filter_ahead(struct archive_read_filter *f,
size_t min, ssize_t *avail)
{
ssize_t bytes_read;
size_t tocopy;
- if (filter->fatal) {
+ if (f->fatal) {
if (avail)
*avail = ARCHIVE_FATAL;
return (NULL);
@@ -1370,79 +1405,79 @@ __archive_read_filter_ahead(struct archive_read_filter *filter,
* note that min == 0 is a perfectly well-defined
* request.
*/
- if (filter->avail >= min && filter->avail > 0) {
+ if (f->avail >= min && f->avail > 0) {
if (avail != NULL)
- *avail = filter->avail;
- return (filter->next);
+ *avail = f->avail;
+ return (f->next);
}
/*
* We can satisfy directly from client buffer if everything
* currently in the copy buffer is still in the client buffer.
*/
- if (filter->client_total >= filter->client_avail + filter->avail
- && filter->client_avail + filter->avail >= min) {
+ if (f->client_total >= f->client_avail + f->avail
+ && f->client_avail + f->avail >= min) {
/* "Roll back" to client buffer. */
- filter->client_avail += filter->avail;
- filter->client_next -= filter->avail;
+ f->client_avail += f->avail;
+ f->client_next -= f->avail;
/* Copy buffer is now empty. */
- filter->avail = 0;
- filter->next = filter->buffer;
+ f->avail = 0;
+ f->next = f->buffer;
/* Return data from client buffer. */
if (avail != NULL)
- *avail = filter->client_avail;
- return (filter->client_next);
+ *avail = f->client_avail;
+ return (f->client_next);
}
/* Move data forward in copy buffer if necessary. */
- if (filter->next > filter->buffer &&
- min > filter->buffer_size - (filter->next - filter->buffer)) {
- if (filter->avail > 0)
- memmove(filter->buffer, filter->next,
- filter->avail);
- filter->next = filter->buffer;
+ if (f->next > f->buffer &&
+ min > f->buffer_size - (f->next - f->buffer)) {
+ if (f->avail > 0)
+ memmove(f->buffer, f->next,
+ f->avail);
+ f->next = f->buffer;
}
/* If we've used up the client data, get more. */
- if (filter->client_avail <= 0) {
- if (filter->end_of_file) {
+ if (f->client_avail <= 0) {
+ if (f->end_of_file) {
if (avail != NULL)
- *avail = filter->avail;
+ *avail = f->avail;
return (NULL);
}
- bytes_read = (filter->vtable->read)(filter,
- &filter->client_buff);
+ bytes_read = (f->vtable->read)(f,
+ &f->client_buff);
if (bytes_read < 0) { /* Read error. */
- filter->client_total = filter->client_avail = 0;
- filter->client_next =
- filter->client_buff = NULL;
- filter->fatal = 1;
+ f->client_total = f->client_avail = 0;
+ f->client_next =
+ f->client_buff = NULL;
+ f->fatal = 1;
if (avail != NULL)
*avail = ARCHIVE_FATAL;
return (NULL);
}
if (bytes_read == 0) {
/* Check for another client object first */
- if (filter->archive->client.cursor !=
- filter->archive->client.nodes - 1) {
- if (client_switch_proxy(filter,
- filter->archive->client.cursor + 1)
+ if (f->archive->client.cursor !=
+ f->archive->client.nodes - 1) {
+ if (client_switch_proxy(f,
+ f->archive->client.cursor + 1)
== ARCHIVE_OK)
continue;
}
/* Premature end-of-file. */
- filter->client_total = filter->client_avail = 0;
- filter->client_next =
- filter->client_buff = NULL;
- filter->end_of_file = 1;
+ f->client_total = f->client_avail = 0;
+ f->client_next =
+ f->client_buff = NULL;
+ f->end_of_file = 1;
/* Return whatever we do have. */
if (avail != NULL)
- *avail = filter->avail;
+ *avail = f->avail;
return (NULL);
}
- filter->client_total = bytes_read;
- filter->client_avail = filter->client_total;
- filter->client_next = filter->client_buff;
+ f->client_total = bytes_read;
+ f->client_avail = f->client_total;
+ f->client_next = f->client_buff;
} else {
/*
* We can't satisfy the request from the copy
@@ -1452,67 +1487,66 @@ __archive_read_filter_ahead(struct archive_read_filter *filter,
*/
/* Ensure the buffer is big enough. */
- if (min > filter->buffer_size) {
- size_t s, t;
+ if (min > f->buffer_size) {
+ size_t s;
char *p;
/* Double the buffer; watch for overflow. */
- s = t = filter->buffer_size;
+ s = f->buffer_size;
if (s == 0)
s = min;
while (s < min) {
- t *= 2;
- if (t <= s) { /* Integer overflow! */
+ if (archive_ckd_mul_size(&s, s, 2)) {
+ /* Integer overflow! */
archive_set_error(
- &filter->archive->archive,
+ &f->archive->archive,
ENOMEM,
"Unable to allocate copy"
" buffer");
- filter->fatal = 1;
+ f->fatal = 1;
if (avail != NULL)
*avail = ARCHIVE_FATAL;
return (NULL);
}
- s = t;
}
/* Now s >= min, so allocate a new buffer. */
p = malloc(s);
if (p == NULL) {
archive_set_error(
- &filter->archive->archive,
+ &f->archive->archive,
ENOMEM,
"Unable to allocate copy buffer");
- filter->fatal = 1;
+ f->fatal = 1;
if (avail != NULL)
*avail = ARCHIVE_FATAL;
return (NULL);
}
/* Move data into newly-enlarged buffer. */
- if (filter->avail > 0)
- memmove(p, filter->next, filter->avail);
- free(filter->buffer);
- filter->next = filter->buffer = p;
- filter->buffer_size = s;
+ if (f->avail > 0)
+ memmove(p, f->next, f->avail);
+ free(f->buffer);
+ f->next = f->buffer = p;
+ f->buffer_size = s;
}
/* We can add client data to copy buffer. */
/* First estimate: copy to fill rest of buffer. */
- tocopy = (filter->buffer + filter->buffer_size)
- - (filter->next + filter->avail);
+ tocopy = (f->buffer + f->buffer_size)
+ - (f->next + f->avail);
/* Don't waste time buffering more than we need to. */
- if (tocopy + filter->avail > min)
- tocopy = min - filter->avail;
+ if (tocopy + f->avail > min)
+ tocopy = min - f->avail;
/* Don't copy more than is available. */
- if (tocopy > filter->client_avail)
- tocopy = filter->client_avail;
+ if (tocopy > f->client_avail)
+ tocopy = f->client_avail;
- memcpy(filter->next + filter->avail,
- filter->client_next, tocopy);
+ memcpy(f->next + f->avail,
+ f->client_next, tocopy);
/* Remove this data from client buffer. */
- filter->client_next += tocopy;
- filter->client_avail -= tocopy;
+ f->client_next += tocopy;
+ f->client_avail -= tocopy;
/* add it to copy buffer. */
- filter->avail += tocopy;
+ f->avail += tocopy;
}
}
}
@@ -1527,7 +1561,7 @@ __archive_read_consume(struct archive_read *a, int64_t request)
}
int64_t
-__archive_read_filter_consume(struct archive_read_filter * filter,
+__archive_read_filter_consume(struct archive_read_filter *f,
int64_t request)
{
int64_t skipped;
@@ -1537,13 +1571,13 @@ __archive_read_filter_consume(struct archive_read_filter * filter,
if (request == 0)
return 0;
- skipped = advance_file_pointer(filter, request);
+ skipped = advance_file_pointer(f, request);
if (skipped == request)
return (skipped);
/* We hit EOF before we satisfied the skip request. */
if (skipped < 0) /* Map error code to 0 for error message below. */
skipped = 0;
- archive_set_error(&filter->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Truncated input file (needed %jd bytes, only %jd available)",
(intmax_t)request, (intmax_t)skipped);
@@ -1557,45 +1591,45 @@ __archive_read_filter_consume(struct archive_read_filter * filter,
* Returns a negative value if there's an I/O error.
*/
static int64_t
-advance_file_pointer(struct archive_read_filter *filter, int64_t request)
+advance_file_pointer(struct archive_read_filter *f, int64_t request)
{
int64_t bytes_skipped, total_bytes_skipped = 0;
ssize_t bytes_read;
size_t min;
- if (filter->fatal)
+ if (f->fatal)
return (-1);
/* Use up the copy buffer first. */
- if (filter->avail > 0) {
- min = (size_t)minimum(request, (int64_t)filter->avail);
- filter->next += min;
- filter->avail -= min;
+ if (f->avail > 0) {
+ min = (size_t)minimum(request, (int64_t)f->avail);
+ f->next += min;
+ f->avail -= min;
request -= min;
- filter->position += min;
+ f->position += min;
total_bytes_skipped += min;
}
/* Then use up the client buffer. */
- if (filter->client_avail > 0) {
- min = (size_t)minimum(request, (int64_t)filter->client_avail);
- filter->client_next += min;
- filter->client_avail -= min;
+ if (f->client_avail > 0) {
+ min = (size_t)minimum(request, (int64_t)f->client_avail);
+ f->client_next += min;
+ f->client_avail -= min;
request -= min;
- filter->position += min;
+ f->position += min;
total_bytes_skipped += min;
}
if (request == 0)
return (total_bytes_skipped);
/* If there's an optimized skip function, use it. */
- if (filter->can_skip != 0) {
- bytes_skipped = client_skip_proxy(filter, request);
+ if (f->can_skip != 0) {
+ bytes_skipped = client_skip_proxy(f, request);
if (bytes_skipped < 0) { /* error */
- filter->fatal = 1;
+ f->fatal = 1;
return (bytes_skipped);
}
- filter->position += bytes_skipped;
+ f->position += bytes_skipped;
total_bytes_skipped += bytes_skipped;
request -= bytes_skipped;
if (request == 0)
@@ -1604,37 +1638,37 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request)
/* Use ordinary reads as necessary to complete the request. */
for (;;) {
- bytes_read = (filter->vtable->read)(filter, &filter->client_buff);
+ bytes_read = (f->vtable->read)(f, &f->client_buff);
if (bytes_read < 0) {
- filter->client_buff = NULL;
- filter->fatal = 1;
+ f->client_buff = NULL;
+ f->fatal = 1;
return (bytes_read);
}
if (bytes_read == 0) {
- if (filter->archive->client.cursor !=
- filter->archive->client.nodes - 1) {
- if (client_switch_proxy(filter,
- filter->archive->client.cursor + 1)
+ if (f->archive->client.cursor !=
+ f->archive->client.nodes - 1) {
+ if (client_switch_proxy(f,
+ f->archive->client.cursor + 1)
== ARCHIVE_OK)
continue;
}
- filter->client_buff = NULL;
- filter->end_of_file = 1;
+ f->client_buff = NULL;
+ f->end_of_file = 1;
return (total_bytes_skipped);
}
if (bytes_read >= request) {
- filter->client_next =
- ((const char *)filter->client_buff) + request;
- filter->client_avail = (size_t)(bytes_read - request);
- filter->client_total = bytes_read;
+ f->client_next =
+ ((const char *)f->client_buff) + request;
+ f->client_avail = (size_t)(bytes_read - request);
+ f->client_total = bytes_read;
total_bytes_skipped += request;
- filter->position += request;
+ f->position += request;
return (total_bytes_skipped);
}
- filter->position += bytes_read;
+ f->position += bytes_read;
total_bytes_skipped += bytes_read;
request -= bytes_read;
}
@@ -1650,23 +1684,23 @@ __archive_read_seek(struct archive_read *a, int64_t offset, int whence)
}
int64_t
-__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
+__archive_read_filter_seek(struct archive_read_filter *f, int64_t offset,
int whence)
{
struct archive_read_client *client;
int64_t r;
unsigned int cursor;
- if (filter->closed || filter->fatal)
+ if (f->closed || f->fatal)
return (ARCHIVE_FATAL);
- if (filter->can_seek == 0)
+ if (f->can_seek == 0)
return (ARCHIVE_FAILED);
- client = &(filter->archive->client);
+ client = &(f->archive->client);
switch (whence) {
case SEEK_CUR:
/* Adjust the offset and use SEEK_SET instead */
- offset += filter->position;
+ offset += f->position;
__LA_FALLTHROUGH;
case SEEK_SET:
cursor = 0;
@@ -1683,10 +1717,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
client->dataset[++cursor].begin_position = r;
}
while (1) {
- r = client_switch_proxy(filter, cursor);
+ r = client_switch_proxy(f, cursor);
if (r != ARCHIVE_OK)
return r;
- if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0)
return r;
client->dataset[cursor].total_size = r;
if (client->dataset[cursor].begin_position +
@@ -1701,7 +1735,7 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
if (offset < 0
|| offset > client->dataset[cursor].total_size)
return ARCHIVE_FATAL;
- if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)
+ if ((r = client_seek_proxy(f, offset, SEEK_SET)) < 0)
return r;
break;
@@ -1717,10 +1751,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
client->dataset[++cursor].begin_position = r;
}
while (1) {
- r = client_switch_proxy(filter, cursor);
+ r = client_switch_proxy(f, cursor);
if (r != ARCHIVE_OK)
return r;
- if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ if ((r = client_seek_proxy(f, 0, SEEK_END)) < 0)
return r;
client->dataset[cursor].total_size = r;
r = client->dataset[cursor].begin_position +
@@ -1741,9 +1775,9 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
client->dataset[cursor].total_size;
}
offset = (r + offset) - client->dataset[cursor].begin_position;
- if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)
+ if ((r = client_switch_proxy(f, cursor)) != ARCHIVE_OK)
return r;
- r = client_seek_proxy(filter, offset, SEEK_SET);
+ r = client_seek_proxy(f, offset, SEEK_SET);
if (r < ARCHIVE_OK)
return r;
break;
@@ -1771,10 +1805,10 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
* size is (r - offset). Can we use that to simplify
* the TODO items above?
*/
- filter->avail = filter->client_avail = 0;
- filter->next = filter->buffer;
- filter->position = r;
- filter->end_of_file = 0;
+ f->avail = f->client_avail = 0;
+ f->next = f->buffer;
+ f->position = r;
+ f->end_of_file = 0;
}
return r;
}
diff --git a/libarchive/archive_read_append_filter.c b/libarchive/archive_read_append_filter.c
index 04c362741360..8de1b2ea8fba 100644
--- a/libarchive/archive_read_append_filter.c
+++ b/libarchive/archive_read_append_filter.c
@@ -38,8 +38,8 @@ archive_read_append_filter(struct archive *_a, int code)
{
int r1, r2, number_bidders, i;
const char *str;
- struct archive_read_filter_bidder *bidder;
- struct archive_read_filter *filter;
+ struct archive_read_filter_bidder *b;
+ struct archive_read_filter *f;
struct archive_read *a = (struct archive_read *)_a;
r2 = (ARCHIVE_OK);
@@ -118,30 +118,30 @@ archive_read_append_filter(struct archive *_a, int code)
{
number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
- bidder = a->bidders;
- for (i = 1; i < number_bidders; i++, bidder++)
+ b = a->bidders;
+ for (i = 1; i < number_bidders; i++, b++)
{
- if (!bidder->name || !strcmp(bidder->name, str))
+ if (!b->name || !strcmp(b->name, str))
break;
}
- if (!bidder->name || strcmp(bidder->name, str))
+ if (!b->name || strcmp(b->name, str))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Internal error: Unable to append filter");
return (ARCHIVE_FATAL);
}
- filter = calloc(1, sizeof(*filter));
- if (filter == NULL)
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
{
archive_set_error(&a->archive, ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
}
- filter->bidder = bidder;
- filter->archive = a;
- filter->upstream = a->filter;
- a->filter = filter;
- r2 = (bidder->vtable->init)(a->filter);
+ f->bidder = b;
+ f->archive = a;
+ f->upstream = a->filter;
+ a->filter = f;
+ r2 = (b->vtable->init)(a->filter);
if (r2 != ARCHIVE_OK) {
__archive_read_free_filters(a);
return (ARCHIVE_FATAL);
@@ -163,8 +163,8 @@ archive_read_append_filter_program_signature(struct archive *_a,
const char *cmd, const void *signature, size_t signature_len)
{
int r, number_bidders, i;
- struct archive_read_filter_bidder *bidder;
- struct archive_read_filter *filter;
+ struct archive_read_filter_bidder *b;
+ struct archive_read_filter *f;
struct archive_read *a = (struct archive_read *)_a;
if (archive_read_support_filter_program_signature(_a, cmd, signature,
@@ -173,36 +173,36 @@ archive_read_append_filter_program_signature(struct archive *_a,
number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
- bidder = a->bidders;
- for (i = 0; i < number_bidders; i++, bidder++)
+ b = a->bidders;
+ for (i = 0; i < number_bidders; i++, b++)
{
/* Program bidder name set to filter name after initialization */
- if (bidder->data && !bidder->name)
+ if (b->data && !b->name)
break;
}
- if (!bidder->data)
+ if (!b->data)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Internal error: Unable to append program filter");
return (ARCHIVE_FATAL);
}
- filter = calloc(1, sizeof(*filter));
- if (filter == NULL)
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
{
archive_set_error(&a->archive, ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
}
- filter->bidder = bidder;
- filter->archive = a;
- filter->upstream = a->filter;
- a->filter = filter;
- r = (bidder->vtable->init)(a->filter);
+ f->bidder = b;
+ f->archive = a;
+ f->upstream = a->filter;
+ a->filter = f;
+ r = (b->vtable->init)(a->filter);
if (r != ARCHIVE_OK) {
__archive_read_free_filters(a);
return (ARCHIVE_FATAL);
}
- bidder->name = a->filter->name;
+ b->name = a->filter->name;
a->bypass_filter_bidding = 1;
return r;
diff --git a/libarchive/archive_read_data_into_fd.c b/libarchive/archive_read_data_into_fd.c
index 38de5b4b4dff..d92ab9a51253 100644
--- a/libarchive/archive_read_data_into_fd.c
+++ b/libarchive/archive_read_data_into_fd.c
@@ -37,6 +37,7 @@
#include "archive.h"
#include "archive_private.h"
+#include "archive_read_private.h"
/* Maximum amount of data to write at one time. */
#define MAX_WRITE (1024 * 1024)
@@ -97,12 +98,15 @@ archive_read_data_into_fd(struct archive *a, int fd)
int64_t fd_offset;
int64_t target_offset;
int64_t actual_offset = 0;
+ int64_t declared_size;
int can_lseek;
char *nulls = NULL;
archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
"archive_read_data_into_fd");
+ declared_size = ((struct archive_read *)a)->entry_bytes_declared;
+
can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
if (can_lseek) {
fd_offset = lseek(fd, 0, SEEK_CUR);
@@ -113,6 +117,24 @@ archive_read_data_into_fd(struct archive *a, int fd)
while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
ARCHIVE_OK) {
const char *p = buff;
+ int overran = 0;
+
+ if (declared_size >= 0) {
+ /* We know the expected size, let's enforce that we don't overrun. */
+ if (target_offset > declared_size) {
+ /* We're already beyond the end? Don't write any more. */
+ target_offset = declared_size;
+ size = 0;
+ overran = 1;
+ } else if (size > (uint64_t)(declared_size - target_offset)) {
+ /* Above is safe because target_offset <= declared_size */
+ /* This block is bigger than the expected remainder */
+ size = (size_t)(declared_size - target_offset);
+ overran = 1;
+ }
+ /* Else size <= expected remainder of data and we're OK. */
+ }
+
if (target_offset > actual_offset) {
r = pad_to(a, fd, can_lseek, &nulls,
target_offset, actual_offset);
@@ -134,13 +156,37 @@ archive_read_data_into_fd(struct archive *a, int fd)
p += bytes_written;
size -= bytes_written;
}
+ if (overran) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Actual entry size exceeds the declared size "
+ "(%jd); truncated output at the declared size",
+ (intmax_t)declared_size);
+ r = ARCHIVE_FAILED;
+ goto cleanup;
+ }
}
if (r == ARCHIVE_EOF && target_offset > actual_offset) {
- r2 = pad_to(a, fd, can_lseek, &nulls,
- target_offset, actual_offset);
- if (r2 != ARCHIVE_OK)
- r = r2;
+ int trailing_hole_overran =
+ declared_size >= 0 && target_offset > declared_size;
+ if (trailing_hole_overran)
+ target_offset = declared_size;
+ if (target_offset > actual_offset) {
+ r2 = pad_to(a, fd, can_lseek, &nulls,
+ target_offset, actual_offset);
+ if (r2 != ARCHIVE_OK)
+ r = r2;
+ else
+ actual_offset = target_offset;
+ }
+ if (trailing_hole_overran) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Actual entry size exceeds the declared size "
+ "(%jd); truncated output at the declared size",
+ (intmax_t)declared_size);
+ r = ARCHIVE_FAILED;
+ goto cleanup;
+ }
}
cleanup:
diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c
index 47c1d3343640..a3c9e5833a94 100644
--- a/libarchive/archive_read_disk_posix.c
+++ b/libarchive/archive_read_disk_posix.c
@@ -453,6 +453,10 @@ archive_read_disk_new(void)
a->archive.state = ARCHIVE_STATE_NEW;
a->archive.vtable = &archive_read_disk_vtable;
a->entry = archive_entry_new2(&a->archive);
+ if (a->entry == NULL) {
+ free(a);
+ return (NULL);
+ }
a->lookup_uname = trivial_lookup_uname;
a->lookup_gname = trivial_lookup_gname;
a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
diff --git a/libarchive/archive_read_disk_set_standard_lookup.c b/libarchive/archive_read_disk_set_standard_lookup.c
index 3512d343f65b..01285d91bb56 100644
--- a/libarchive/archive_read_disk_set_standard_lookup.c
+++ b/libarchive/archive_read_disk_set_standard_lookup.c
@@ -45,6 +45,7 @@
#endif
#include "archive.h"
+#include "archive_integer.h"
#if defined(_WIN32) && !defined(__CYGWIN__)
int
@@ -209,7 +210,8 @@ lookup_uname_helper(struct name_cache *cache, id_t id)
* we just double it and try again. Because the buffer
* is kept around in the cache object, we shouldn't
* have to do this very often. */
- nbuff_size = cache->buff_size * 2;
+ if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2))
+ break;
nbuff = realloc(cache->buff, nbuff_size);
if (nbuff == NULL)
break;
@@ -276,7 +278,8 @@ lookup_gname_helper(struct name_cache *cache, id_t id)
/* ERANGE means our buffer was too small, but POSIX
* doesn't tell us how big the buffer should be, so
* we just double it and try again. */
- nbuff_size = cache->buff_size * 2;
+ if (archive_ckd_mul_size(&nbuff_size, cache->buff_size, 2))
+ break;
nbuff = realloc(cache->buff, nbuff_size);
if (nbuff == NULL)
break;
diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c
index d5f7ce77cbc9..eb4699a3f850 100644
--- a/libarchive/archive_read_disk_windows.c
+++ b/libarchive/archive_read_disk_windows.c
@@ -37,10 +37,11 @@
#include <winioctl.h>
#include "archive.h"
-#include "archive_string.h"
#include "archive_entry.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_read_disk_private.h"
+#include "archive_string.h"
#include "archive_time_private.h"
#ifndef O_BINARY
@@ -151,6 +152,8 @@ struct tree {
size_t dirname_length;
int depth;
+ /* Whether this tree began as a wildcard search */
+ int rootwild;
BY_HANDLE_FILE_INFORMATION lst;
BY_HANDLE_FILE_INFORMATION st;
@@ -555,6 +558,10 @@ archive_read_disk_new(void)
a->archive.state = ARCHIVE_STATE_NEW;
a->archive.vtable = &archive_read_disk_vtable;
a->entry = archive_entry_new2(&a->archive);
+ if (a->entry == NULL) {
+ free(a);
+ return (NULL);
+ }
a->lookup_uname = trivial_lookup_uname;
a->lookup_gname = trivial_lookup_gname;
a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
@@ -926,7 +933,27 @@ next_entry(struct archive_read_disk *a, struct tree *t,
case 0:
return (ARCHIVE_EOF);
case TREE_POSTDESCENT:
+ if (t->depth == 1 && t->rootwild && t->symlink_mode == 'H') {
+ /*
+ * We have descended from a directory at depth 0 specified by a
+ * wildcard search in 'H'ybrid link mode. All items under this
+ * directory should be 'P'hysical.
+ */
+ t->symlink_mode = 'P';
+ a->follow_symlinks = 0;
+ }
+ break;
case TREE_POSTASCENT:
+ if (t->depth == 0 && t->rootwild && t->initial_symlink_mode == 'H') {
+ /*
+ * We have returned to the root of a wildcard search, in 'H'ybrid
+ * mode, so restore the symlink mode we overwrote in
+ * TREE_POSTDESCENT.
+ */
+ t->symlink_mode = t->initial_symlink_mode;
+ /* Mimics setup done in setup_symlink_mode */
+ a->follow_symlinks = 1;
+ }
break;
case TREE_REGULAR:
lst = tree_current_lstat(t);
@@ -966,7 +993,14 @@ next_entry(struct archive_read_disk *a, struct tree *t,
switch(t->symlink_mode) {
case 'H':
/* 'H': After the first item, rest like 'P'. */
- t->symlink_mode = 'P';
+ if (!t->rootwild) {
+ /*
+ * 'H': When we started with a wildcard, entries at the starting
+ * depth should be kept in `H` mode; switching into 'P' mode
+ * is handled in TREE_POSTDESCENT when we enter depth = 1.
+ */
+ t->symlink_mode = 'P';
+ }
/* 'H': First item (from command line) like 'L'. */
/* FALLTHROUGH */
case 'L':
@@ -1733,6 +1767,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
t->full_path_dir_length = 0;
t->dirname_length = 0;
t->depth = 0;
+ t->rootwild = 0;
t->descend = 0;
t->current = NULL;
t->d = INVALID_HANDLE_VALUE;
@@ -1786,6 +1821,7 @@ tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
t->full_path.length = wcslen(t->full_path.s);
t->full_path_dir_length = archive_strlen(&t->full_path);
}
+ t->rootwild = 1;
}
tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);
archive_wstring_free(&ws);
@@ -2505,7 +2541,12 @@ setup_sparse_from_disk(struct archive_read_disk *a,
(DWORD)outranges_size, &retbytes, NULL);
if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {
free(outranges);
- outranges_size *= 2;
+ if (archive_ckd_mul_size(&outranges_size, outranges_size, 2)) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ exit_sts = ARCHIVE_FATAL;
+ goto exit_setup_sparse;
+ }
outranges = (FILE_ALLOCATED_RANGE_BUFFER *)
malloc(outranges_size);
if (outranges == NULL) {
diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c
index c85a62a3e2d7..358e516faf18 100644
--- a/libarchive/archive_read_open_fd.c
+++ b/libarchive/archive_read_open_fd.c
@@ -69,6 +69,7 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size)
la_seek_stat_t st;
struct read_fd_data *mine;
void *b;
+ int r;
archive_clear_error(a);
if (la_seek_fstat(fd, &st) != 0) {
@@ -107,7 +108,9 @@ archive_read_open_fd(struct archive *a, int fd, size_t block_size)
archive_read_set_skip_callback(a, file_skip);
archive_read_set_seek_callback(a, file_seek);
archive_read_set_close_callback(a, file_close);
- archive_read_set_callback_data(a, mine);
+ r = archive_read_set_callback_data(a, mine);
+ if (r < 0)
+ return (r);
return (archive_read_open1(a));
}
diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c
index 6ca2ff191aa8..e82069e502bf 100644
--- a/libarchive/archive_read_open_file.c
+++ b/libarchive/archive_read_open_file.c
@@ -70,6 +70,7 @@ archive_read_open_FILE(struct archive *a, FILE *f)
struct read_FILE_data *mine;
size_t block_size = 128 * 1024;
void *b;
+ int r;
archive_clear_error(a);
mine = calloc(1, sizeof(*mine));
@@ -104,7 +105,9 @@ archive_read_open_FILE(struct archive *a, FILE *f)
archive_read_set_skip_callback(a, FILE_skip);
archive_read_set_seek_callback(a, FILE_seek);
archive_read_set_close_callback(a, FILE_close);
- archive_read_set_callback_data(a, mine);
+ r = archive_read_set_callback_data(a, mine);
+ if (r < 0)
+ return (r);
return (archive_read_open1(a));
}
diff --git a/libarchive/archive_read_open_memory.c b/libarchive/archive_read_open_memory.c
index 460bb5ae6a4d..441662d464d7 100644
--- a/libarchive/archive_read_open_memory.c
+++ b/libarchive/archive_read_open_memory.c
@@ -68,6 +68,7 @@ archive_read_open_memory2(struct archive *a, const void *buff,
size_t size, size_t read_size)
{
struct read_memory_data *mine;
+ int r;
mine = calloc(1, sizeof(*mine));
if (mine == NULL) {
@@ -82,7 +83,9 @@ archive_read_open_memory2(struct archive *a, const void *buff,
archive_read_set_seek_callback(a, memory_read_seek);
archive_read_set_skip_callback(a, memory_read_skip);
archive_read_set_close_callback(a, memory_read_close);
- archive_read_set_callback_data(a, mine);
+ r = archive_read_set_callback_data(a, mine);
+ if (r < 0)
+ return (r);
return (archive_read_open1(a));
}
diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h
index 0c374f487eba..3bd8de2de663 100644
--- a/libarchive/archive_read_private.h
+++ b/libarchive/archive_read_private.h
@@ -76,10 +76,10 @@ struct archive_read_filter_bidder {
struct archive_read_filter_vtable {
/* Return next block. */
ssize_t (*read)(struct archive_read_filter *, const void **);
- /* Close (just this filter) and free(self). */
- int (*close)(struct archive_read_filter *self);
+ /* Close (just this filter) and free(filter). */
+ int (*close)(struct archive_read_filter *);
/* Read any header metadata if available. */
- int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+ int (*read_header)(struct archive_read_filter *, struct archive_entry *);
};
/*
@@ -180,6 +180,15 @@ struct archive_read {
/* File offset of beginning of most recently-read header. */
int64_t header_position;
+ /*
+ * Declared uncompressed size of the entry whose header was most
+ * recently read, or -1 if no size was declared. Lets format-
+ * agnostic consumers like archive_read_data_into_fd() cap their
+ * output at the size an entry actually promised, regardless of
+ * whether the format reader enforces that itself.
+ */
+ int64_t entry_bytes_declared;
+
/* Nodes and offsets of compressed data block */
unsigned int data_start_node;
unsigned int data_end_node;
diff --git a/libarchive/archive_read_support_filter_bzip2.c b/libarchive/archive_read_support_filter_bzip2.c
index 26d550e2c45a..588953f2aa94 100644
--- a/libarchive/archive_read_support_filter_bzip2.c
+++ b/libarchive/archive_read_support_filter_bzip2.c
@@ -50,7 +50,7 @@
#include "archive_read_private.h"
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
-struct private_data {
+struct bzip2 {
bz_stream stream;
char *out_block;
size_t out_block_size;
@@ -112,15 +112,15 @@ archive_read_support_filter_bzip2(struct archive *_a)
* from verifying as much as we would like.
*/
static int
-bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
+bzip2_reader_bid(struct archive_read_filter_bidder *b, struct archive_read_filter *f)
{
const unsigned char *buffer;
int bits_checked;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
/* Minimal bzip2 archive is 14 bytes. */
- buffer = __archive_read_filter_ahead(filter, 14, NULL);
+ buffer = __archive_read_filter_ahead(f, 14, NULL);
if (buffer == NULL)
return (0);
@@ -156,16 +156,16 @@ bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_fi
* in case that's available.
*/
static int
-bzip2_reader_init(struct archive_read_filter *self)
+bzip2_reader_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "bzip2 -d");
+ r = __archive_read_program(f, "bzip2 -d");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_BZIP2;
- self->name = "bzip2";
+ f->code = ARCHIVE_FILTER_BZIP2;
+ f->name = "bzip2";
return (r);
}
@@ -182,29 +182,29 @@ bzip2_reader_vtable = {
* Setup the callbacks.
*/
static int
-bzip2_reader_init(struct archive_read_filter *self)
+bzip2_reader_init(struct archive_read_filter *f)
{
static const size_t out_block_size = 64 * 1024;
void *out_block;
- struct private_data *state;
+ struct bzip2 *bzip2;
- self->code = ARCHIVE_FILTER_BZIP2;
- self->name = "bzip2";
+ f->code = ARCHIVE_FILTER_BZIP2;
+ f->name = "bzip2";
- state = calloc(1, sizeof(*state));
+ bzip2 = calloc(1, sizeof(*bzip2));
out_block = malloc(out_block_size);
- if (state == NULL || out_block == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ if (bzip2 == NULL || out_block == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for bzip2 decompression");
free(out_block);
- free(state);
+ free(bzip2);
return (ARCHIVE_FATAL);
}
- self->data = state;
- state->out_block_size = out_block_size;
- state->out_block = out_block;
- self->vtable = &bzip2_reader_vtable;
+ f->data = bzip2;
+ bzip2->out_block_size = out_block_size;
+ bzip2->out_block = out_block;
+ f->vtable = &bzip2_reader_vtable;
return (ARCHIVE_OK);
}
@@ -213,44 +213,42 @@ bzip2_reader_init(struct archive_read_filter *self)
* Return the next block of decompressed data.
*/
static ssize_t
-bzip2_filter_read(struct archive_read_filter *self, const void **p)
+bzip2_filter_read(struct archive_read_filter *f, const void **p)
{
- struct private_data *state;
+ struct bzip2 *bzip2 = f->data;
size_t decompressed;
const char *read_buf;
ssize_t ret;
- state = (struct private_data *)self->data;
-
- if (state->eof) {
+ if (bzip2->eof) {
*p = NULL;
return (0);
}
/* Empty our output buffer. */
- state->stream.next_out = state->out_block;
- state->stream.avail_out = (uint32_t)state->out_block_size;
+ bzip2->stream.next_out = bzip2->out_block;
+ bzip2->stream.avail_out = (uint32_t)bzip2->out_block_size;
/* Try to fill the output buffer. */
for (;;) {
ssize_t max_in;
- if (!state->valid) {
- if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
- state->eof = 1;
- *p = state->out_block;
- decompressed = state->stream.next_out
- - state->out_block;
+ if (!bzip2->valid) {
+ if (bzip2_reader_bid(f->bidder, f->upstream) == 0) {
+ bzip2->eof = 1;
+ *p = bzip2->out_block;
+ decompressed = bzip2->stream.next_out
+ - bzip2->out_block;
return (decompressed);
}
/* Initialize compression library. */
- ret = BZ2_bzDecompressInit(&(state->stream),
+ ret = BZ2_bzDecompressInit(&(bzip2->stream),
0 /* library verbosity */,
0 /* don't use low-mem algorithm */);
/* If init fails, try low-memory algorithm instead. */
if (ret == BZ_MEM_ERROR)
- ret = BZ2_bzDecompressInit(&(state->stream),
+ ret = BZ2_bzDecompressInit(&(bzip2->stream),
0 /* library verbosity */,
1 /* do use low-mem algo */);
@@ -269,63 +267,63 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p)
detail = "mis-compiled library";
break;
}
- archive_set_error(&self->archive->archive, err,
+ archive_set_error(&f->archive->archive, err,
"Internal error initializing decompressor%s%s",
detail == NULL ? "" : ": ",
detail);
return (ARCHIVE_FATAL);
}
- state->valid = 1;
+ bzip2->valid = 1;
}
/* stream.next_in is really const, but bzlib
* doesn't declare it so. <sigh> */
read_buf =
- __archive_read_filter_ahead(self->upstream, 1, &ret);
+ __archive_read_filter_ahead(f->upstream, 1, &ret);
if (read_buf == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated bzip2 input");
return (ARCHIVE_FATAL);
}
- state->stream.next_in = (char *)(uintptr_t)read_buf;
+ bzip2->stream.next_in = (char *)(uintptr_t)read_buf;
if (UINT_MAX >= SSIZE_MAX)
max_in = SSIZE_MAX;
else
max_in = UINT_MAX;
if (ret > max_in)
ret = max_in;
- state->stream.avail_in = (uint32_t)ret;
+ bzip2->stream.avail_in = (uint32_t)ret;
/* Decompress as much as we can in one pass. */
- ret = BZ2_bzDecompress(&(state->stream));
- __archive_read_filter_consume(self->upstream,
- state->stream.next_in - read_buf);
+ ret = BZ2_bzDecompress(&(bzip2->stream));
+ __archive_read_filter_consume(f->upstream,
+ bzip2->stream.next_in - read_buf);
switch (ret) {
case BZ_STREAM_END: /* Found end of stream. */
- switch (BZ2_bzDecompressEnd(&(state->stream))) {
+ switch (BZ2_bzDecompressEnd(&(bzip2->stream))) {
case BZ_OK:
break;
default:
- archive_set_error(&(self->archive->archive),
+ archive_set_error(&(f->archive->archive),
ARCHIVE_ERRNO_MISC,
"Failed to clean up decompressor");
return (ARCHIVE_FATAL);
}
- state->valid = 0;
+ bzip2->valid = 0;
/* FALLTHROUGH */
case BZ_OK: /* Decompressor made some progress. */
/* If we filled our buffer, update stats and return. */
- if (state->stream.avail_out == 0) {
- *p = state->out_block;
- decompressed = state->stream.next_out
- - state->out_block;
+ if (bzip2->stream.avail_out == 0) {
+ *p = bzip2->out_block;
+ decompressed = bzip2->stream.next_out
+ - bzip2->out_block;
return (decompressed);
}
break;
default: /* Return an error. */
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "bzip decompression failed");
return (ARCHIVE_FATAL);
}
@@ -336,28 +334,26 @@ bzip2_filter_read(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-bzip2_filter_close(struct archive_read_filter *self)
+bzip2_filter_close(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct bzip2 *bzip2 = f->data;
int ret = ARCHIVE_OK;
- state = (struct private_data *)self->data;
-
- if (state->valid) {
- switch (BZ2_bzDecompressEnd(&state->stream)) {
+ if (bzip2->valid) {
+ switch (BZ2_bzDecompressEnd(&bzip2->stream)) {
case BZ_OK:
break;
default:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Failed to clean up decompressor");
ret = ARCHIVE_FATAL;
}
- state->valid = 0;
+ bzip2->valid = 0;
}
- free(state->out_block);
- free(state);
+ free(bzip2->out_block);
+ free(bzip2);
return (ret);
}
diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c
index 54a99818655d..4e1f1dc52be8 100644
--- a/libarchive/archive_read_support_filter_compress.c
+++ b/libarchive/archive_read_support_filter_compress.c
@@ -90,7 +90,7 @@
* names and structure here don't exactly match those used by compress.
*/
-struct private_data {
+struct compress {
/* Input variables. */
const unsigned char *next_in;
size_t avail_in;
@@ -169,16 +169,16 @@ archive_read_support_filter_compress(struct archive *_a)
* This logic returns zero if any part of the signature fails.
*/
static int
-compress_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+compress_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *buffer;
int bits_checked;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
/* Shortest valid compress file is 3 bytes. */
- buffer = __archive_read_filter_ahead(filter, 3, NULL);
+ buffer = __archive_read_filter_ahead(f, 3, NULL);
if (buffer == NULL)
return (0);
@@ -207,69 +207,69 @@ compress_reader_vtable = {
* Setup the callbacks.
*/
static int
-compress_bidder_init(struct archive_read_filter *self)
+compress_bidder_init(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct compress *compress;
static const size_t out_block_size = 64 * 1024;
void *out_block;
- self->code = ARCHIVE_FILTER_COMPRESS;
- self->name = "compress (.Z)";
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->name = "compress (.Z)";
- state = calloc(1, sizeof(*state));
+ compress = calloc(1, sizeof(*compress));
out_block = malloc(out_block_size);
- if (state == NULL || out_block == NULL) {
+ if (compress == NULL || out_block == NULL) {
free(out_block);
- free(state);
- archive_set_error(&self->archive->archive, ENOMEM,
+ free(compress);
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for %s decompression",
- self->name);
+ f->name);
return (ARCHIVE_FATAL);
}
- self->data = state;
- state->out_block_size = out_block_size;
- state->out_block = out_block;
- self->vtable = &compress_reader_vtable;
+ f->data = compress;
+ compress->out_block_size = out_block_size;
+ compress->out_block = out_block;
+ f->vtable = &compress_reader_vtable;
return (ARCHIVE_OK);
}
static int
-compress_filter_init(struct archive_read_filter *self)
+compress_filter_init(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct compress *compress = f->data;
int code;
- state->initialized = 1;
+ compress->initialized = 1;
- (void)getbits(self, 8); /* Skip first signature byte. */
- (void)getbits(self, 8); /* Skip second signature byte. */
+ (void)getbits(f, 8); /* Skip first signature byte. */
+ (void)getbits(f, 8); /* Skip second signature byte. */
/* Get compression parameters. */
- code = getbits(self, 8);
+ code = getbits(f, 8);
if (code < 0 || (code & 0x1f) > 16) {
- archive_set_error(&self->archive->archive, -1,
+ archive_set_error(&f->archive->archive, -1,
"Invalid compressed data");
return (ARCHIVE_FATAL);
}
- state->maxcode_bits = code & 0x1f;
- state->maxcode = (1 << state->maxcode_bits);
- state->use_reset_code = code & 0x80;
+ compress->maxcode_bits = code & 0x1f;
+ compress->maxcode = (1 << compress->maxcode_bits);
+ compress->use_reset_code = code & 0x80;
/* Initialize decompressor. */
- state->free_ent = 256;
- state->stackp = state->stack;
- if (state->use_reset_code)
- state->free_ent++;
- state->bits = 9;
- state->section_end_code = (1<<state->bits) - 1;
- state->oldcode = -1;
+ compress->free_ent = 256;
+ compress->stackp = compress->stack;
+ if (compress->use_reset_code)
+ compress->free_ent++;
+ compress->bits = 9;
+ compress->section_end_code = (1<<compress->bits) - 1;
+ compress->oldcode = -1;
for (code = 255; code >= 0; code--) {
- state->prefix[code] = 0;
- state->suffix[code] = code;
+ compress->prefix[code] = 0;
+ compress->suffix[code] = code;
}
- next_code(self);
+ next_code(f);
return (ARCHIVE_OK);
}
@@ -279,32 +279,31 @@ compress_filter_init(struct archive_read_filter *self)
* as necessary.
*/
static ssize_t
-compress_filter_read(struct archive_read_filter *self, const void **pblock)
+compress_filter_read(struct archive_read_filter *f, const void **pblock)
{
- struct private_data *state;
+ struct compress *compress = f->data;
unsigned char *p, *start, *end;
int ret;
- state = (struct private_data *)self->data;
- if (!state->initialized) {
- ret = compress_filter_init(self);
+ if (!compress->initialized) {
+ ret = compress_filter_init(f);
if (ret != ARCHIVE_OK)
return (ret);
}
- if (state->end_of_stream) {
+ if (compress->end_of_stream) {
*pblock = NULL;
return (0);
}
- p = start = (unsigned char *)state->out_block;
- end = start + state->out_block_size;
+ p = start = (unsigned char *)compress->out_block;
+ end = start + compress->out_block_size;
- while (p < end && !state->end_of_stream) {
- if (state->stackp > state->stack) {
- *p++ = *--state->stackp;
+ while (p < end && !compress->end_of_stream) {
+ if (compress->stackp > compress->stack) {
+ *p++ = *--compress->stackp;
} else {
- ret = next_code(self);
+ ret = next_code(f);
if (ret == -1)
- state->end_of_stream = ret;
+ compress->end_of_stream = ret;
else if (ret != ARCHIVE_OK)
return (ret);
}
@@ -318,12 +317,12 @@ compress_filter_read(struct archive_read_filter *self, const void **pblock)
* Close and release the filter.
*/
static int
-compress_filter_close(struct archive_read_filter *self)
+compress_filter_close(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct compress *compress = f->data;
- free(state->out_block);
- free(state);
+ free(compress->out_block);
+ free(compress);
return (ARCHIVE_OK);
}
@@ -333,18 +332,18 @@ compress_filter_close(struct archive_read_filter *self)
* format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
*/
static int
-next_code(struct archive_read_filter *self)
+next_code(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct compress *compress = f->data;
int code, newcode;
again:
- code = newcode = getbits(self, state->bits);
+ code = newcode = getbits(f, compress->bits);
if (code < 0)
return (code);
/* If it's a reset code, reset the dictionary. */
- if ((code == 256) && state->use_reset_code) {
+ if ((code == 256) && compress->use_reset_code) {
/*
* The original 'compress' implementation blocked its
* I/O in a manner that resulted in junk bytes being
@@ -352,63 +351,63 @@ again:
* this junk. (Yes, the number of *bytes* to skip is
* a function of the current *bit* length.)
*/
- int skip_bytes = state->bits -
- (state->bytes_in_section % state->bits);
- skip_bytes %= state->bits;
- state->bits_avail = 0; /* Discard rest of this byte. */
+ int skip_bytes = compress->bits -
+ (compress->bytes_in_section % compress->bits);
+ skip_bytes %= compress->bits;
+ compress->bits_avail = 0; /* Discard rest of this byte. */
while (skip_bytes-- > 0) {
- code = getbits(self, 8);
+ code = getbits(f, 8);
if (code < 0)
return (code);
}
/* Now, actually do the reset. */
- state->bytes_in_section = 0;
- state->bits = 9;
- state->section_end_code = (1 << state->bits) - 1;
- state->free_ent = 257;
- state->oldcode = -1;
+ compress->bytes_in_section = 0;
+ compress->bits = 9;
+ compress->section_end_code = (1 << compress->bits) - 1;
+ compress->free_ent = 257;
+ compress->oldcode = -1;
goto again;
}
- if (code > state->free_ent
- || (code == state->free_ent && state->oldcode < 0)) {
+ if (code > compress->free_ent
+ || (code == compress->free_ent && compress->oldcode < 0)) {
/* An invalid code is a fatal error. */
- archive_set_error(&(self->archive->archive), -1,
+ archive_set_error(&(f->archive->archive), -1,
"Invalid compressed data");
return (ARCHIVE_FATAL);
}
/* Special case for KwKwK string. */
- if (code >= state->free_ent) {
- *state->stackp++ = state->finbyte;
- code = state->oldcode;
+ if (code >= compress->free_ent) {
+ *compress->stackp++ = compress->finbyte;
+ code = compress->oldcode;
}
/* Generate output characters in reverse order. */
while (code >= 256) {
- *state->stackp++ = state->suffix[code];
- code = state->prefix[code];
+ *compress->stackp++ = compress->suffix[code];
+ code = compress->prefix[code];
}
- *state->stackp++ = state->finbyte = code;
+ *compress->stackp++ = compress->finbyte = code;
/* Generate the new entry. */
- code = state->free_ent;
- if (code < state->maxcode && state->oldcode >= 0) {
- state->prefix[code] = state->oldcode;
- state->suffix[code] = state->finbyte;
- ++state->free_ent;
+ code = compress->free_ent;
+ if (code < compress->maxcode && compress->oldcode >= 0) {
+ compress->prefix[code] = compress->oldcode;
+ compress->suffix[code] = compress->finbyte;
+ ++compress->free_ent;
}
- if (state->free_ent > state->section_end_code) {
- state->bits++;
- state->bytes_in_section = 0;
- if (state->bits == state->maxcode_bits)
- state->section_end_code = state->maxcode;
+ if (compress->free_ent > compress->section_end_code) {
+ compress->bits++;
+ compress->bytes_in_section = 0;
+ if (compress->bits == compress->maxcode_bits)
+ compress->section_end_code = compress->maxcode;
else
- state->section_end_code = (1 << state->bits) - 1;
+ compress->section_end_code = (1 << compress->bits) - 1;
}
/* Remember previous code. */
- state->oldcode = newcode;
+ compress->oldcode = newcode;
return (ARCHIVE_OK);
}
@@ -418,9 +417,9 @@ again:
* -1 indicates end of available data.
*/
static int
-getbits(struct archive_read_filter *self, int n)
+getbits(struct archive_read_filter *f, int n)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct compress *compress = f->data;
int code;
ssize_t ret;
static const int mask[] = {
@@ -428,31 +427,31 @@ getbits(struct archive_read_filter *self, int n)
0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
- while (state->bits_avail < n) {
- if (state->avail_in <= 0) {
- if (state->consume_unnotified) {
- __archive_read_filter_consume(self->upstream,
- state->consume_unnotified);
- state->consume_unnotified = 0;
+ while (compress->bits_avail < n) {
+ if (compress->avail_in <= 0) {
+ if (compress->consume_unnotified) {
+ __archive_read_filter_consume(f->upstream,
+ compress->consume_unnotified);
+ compress->consume_unnotified = 0;
}
- state->next_in
- = __archive_read_filter_ahead(self->upstream,
+ compress->next_in
+ = __archive_read_filter_ahead(f->upstream,
1, &ret);
if (ret == 0)
return (-1);
- if (state->next_in == NULL)
+ if (compress->next_in == NULL)
return (ARCHIVE_FATAL);
- state->consume_unnotified = state->avail_in = ret;
+ compress->consume_unnotified = compress->avail_in = ret;
}
- state->bit_buffer |= *state->next_in++ << state->bits_avail;
- state->avail_in--;
- state->bits_avail += 8;
- state->bytes_in_section++;
+ compress->bit_buffer |= *compress->next_in++ << compress->bits_avail;
+ compress->avail_in--;
+ compress->bits_avail += 8;
+ compress->bytes_in_section++;
}
- code = state->bit_buffer;
- state->bit_buffer >>= n;
- state->bits_avail -= n;
+ code = compress->bit_buffer;
+ compress->bit_buffer >>= n;
+ compress->bits_avail -= n;
return (code & mask[n]);
}
diff --git a/libarchive/archive_read_support_filter_grzip.c b/libarchive/archive_read_support_filter_grzip.c
index 2bbb2e016e71..9d9ad46eaef4 100644
--- a/libarchive/archive_read_support_filter_grzip.c
+++ b/libarchive/archive_read_support_filter_grzip.c
@@ -76,14 +76,14 @@ archive_read_support_filter_grzip(struct archive *_a)
* Bidder just verifies the header and returns the number of verified bits.
*/
static int
-grzip_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+grzip_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *p;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), NULL);
+ p = __archive_read_filter_ahead(f, sizeof(grzip_magic), NULL);
if (p == NULL)
return (0);
@@ -94,15 +94,15 @@ grzip_bidder_bid(struct archive_read_filter_bidder *self,
}
static int
-grzip_bidder_init(struct archive_read_filter *self)
+grzip_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "grzip -d");
+ r = __archive_read_program(f, "grzip -d");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_GRZIP;
- self->name = "grzip";
+ f->code = ARCHIVE_FILTER_GRZIP;
+ f->name = "grzip";
return (r);
}
diff --git a/libarchive/archive_read_support_filter_gzip.c b/libarchive/archive_read_support_filter_gzip.c
index a312065c63e6..89ae0f371493 100644
--- a/libarchive/archive_read_support_filter_gzip.c
+++ b/libarchive/archive_read_support_filter_gzip.c
@@ -51,7 +51,7 @@
#include "archive_read_private.h"
#ifdef HAVE_ZLIB_H
-struct private_data {
+struct gzip {
z_stream stream;
char in_stream;
unsigned char *out_block;
@@ -120,9 +120,9 @@ archive_read_support_filter_gzip(struct archive *_a)
#define MAX_FILENAME_LENGTH (1024 * 1024L)
#define MAX_COMMENT_LENGTH (1024 * 1024L)
static ssize_t
-peek_at_header(struct archive_read_filter *filter, int *pbits,
+peek_at_header(struct archive_read_filter *f, int *pbits,
#ifdef HAVE_ZLIB_H
- struct private_data *state
+ struct gzip *gzip
#else
void *state
#endif
@@ -139,7 +139,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
/* Start by looking at the first ten bytes of the header, which
* is all fixed layout. */
len = 10;
- p = __archive_read_filter_ahead(filter, len, &avail);
+ p = __archive_read_filter_ahead(f, len, &avail);
if (p == NULL)
return (0);
/* We only support deflation- third byte must be 0x08. */
@@ -152,8 +152,8 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
header_flags = p[3];
/* Bytes 4-7 are mod time in little endian. */
#ifdef HAVE_ZLIB_H
- if (state)
- state->mtime = archive_le32dec(p + 4);
+ if (gzip)
+ gzip->mtime = archive_le32dec(p + 4);
#endif
/* Byte 8 is deflate flags. */
/* XXXX TODO: return deflate flags back to consume_header for use
@@ -162,7 +162,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
/* Optional extra data: 2 byte length plus variable body. */
if (header_flags & 4) {
- p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ p = __archive_read_filter_ahead(f, len + 2, &avail);
if (p == NULL)
return (0);
len += archive_le16dec(p + len);
@@ -180,7 +180,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
if (avail > MAX_FILENAME_LENGTH) {
return (0);
}
- p = __archive_read_filter_ahead(filter,
+ p = __archive_read_filter_ahead(f,
len, &avail);
}
if (p == NULL)
@@ -188,10 +188,10 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
} while (p[len - 1] != 0);
#ifdef HAVE_ZLIB_H
- if (state) {
+ if (gzip) {
/* Reset the name in case of repeat header reads. */
- free(state->name);
- state->name = strdup((const char *)&p[file_start]);
+ free(gzip->name);
+ gzip->name = strdup((const char *)&p[file_start]);
}
#endif
}
@@ -204,7 +204,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
if (avail > MAX_COMMENT_LENGTH) {
return (0);
}
- p = __archive_read_filter_ahead(filter,
+ p = __archive_read_filter_ahead(f,
len, &avail);
}
if (p == NULL)
@@ -214,7 +214,7 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
/* Optional header CRC */
if ((header_flags & 2)) {
- p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ p = __archive_read_filter_ahead(f, len + 2, &avail);
if (p == NULL)
return (0);
#if 0
@@ -236,14 +236,14 @@ peek_at_header(struct archive_read_filter *filter, int *pbits,
* Bidder just verifies the header and returns the number of verified bits.
*/
static int
-gzip_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+gzip_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
int bits_checked;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- if (peek_at_header(filter, &bits_checked, NULL))
+ if (peek_at_header(f, &bits_checked, NULL))
return (bits_checked);
return (0);
}
@@ -256,35 +256,33 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self,
* in case that's available.
*/
static int
-gzip_bidder_init(struct archive_read_filter *self)
+gzip_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "gzip -d");
+ r = __archive_read_program(f, "gzip -d");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_GZIP;
- self->name = "gzip";
+ f->code = ARCHIVE_FILTER_GZIP;
+ f->name = "gzip";
return (r);
}
#else
static int
-gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+gzip_read_header(struct archive_read_filter *f, struct archive_entry *entry)
{
- struct private_data *state;
-
- state = (struct private_data *)self->data;
+ struct gzip *gzip = f->data;
/* An mtime of 0 is considered invalid/missing. */
- if (state->mtime != 0)
- archive_entry_set_mtime(entry, state->mtime, 0);
+ if (gzip->mtime != 0)
+ archive_entry_set_mtime(entry, gzip->mtime, 0);
/* If the name is available, extract it. */
- if (state->name)
- archive_entry_set_pathname(entry, state->name);
+ if (gzip->name)
+ archive_entry_set_pathname(entry, gzip->name);
return (ARCHIVE_OK);
}
@@ -302,59 +300,57 @@ gzip_reader_vtable = {
* Initialize the filter object.
*/
static int
-gzip_bidder_init(struct archive_read_filter *self)
+gzip_bidder_init(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct gzip *gzip;
static const size_t out_block_size = 64 * 1024;
void *out_block;
- self->code = ARCHIVE_FILTER_GZIP;
- self->name = "gzip";
+ f->code = ARCHIVE_FILTER_GZIP;
+ f->name = "gzip";
- state = calloc(1, sizeof(*state));
+ gzip = calloc(1, sizeof(*gzip));
out_block = malloc(out_block_size);
- if (state == NULL || out_block == NULL) {
+ if (gzip == NULL || out_block == NULL) {
free(out_block);
- free(state);
- archive_set_error(&self->archive->archive, ENOMEM,
+ free(gzip);
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for gzip decompression");
return (ARCHIVE_FATAL);
}
- self->data = state;
- state->out_block_size = out_block_size;
- state->out_block = out_block;
- self->vtable = &gzip_reader_vtable;
+ f->data = gzip;
+ gzip->out_block_size = out_block_size;
+ gzip->out_block = out_block;
+ f->vtable = &gzip_reader_vtable;
- state->in_stream = 0; /* We're not actually within a stream yet. */
+ gzip->in_stream = 0; /* We're not actually within a stream yet. */
return (ARCHIVE_OK);
}
static int
-consume_header(struct archive_read_filter *self)
+consume_header(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct gzip *gzip = f->data;
ssize_t avail, max_in;
size_t len;
int ret;
- state = (struct private_data *)self->data;
-
/* If this is a real header, consume it. */
- len = peek_at_header(self->upstream, NULL, state);
+ len = peek_at_header(f->upstream, NULL, gzip);
if (len == 0)
return (ARCHIVE_EOF);
- __archive_read_filter_consume(self->upstream, len);
+ __archive_read_filter_consume(f->upstream, len);
/* Initialize CRC accumulator. */
- state->crc = crc32(0L, NULL, 0);
+ gzip->crc = crc32(0L, NULL, 0);
/* Initialize compression library. */
- state->stream.next_in = (unsigned char *)(uintptr_t)
- __archive_read_filter_ahead(self->upstream, 1, &avail);
+ gzip->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(f->upstream, 1, &avail);
if (avail < 0) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Failed to read gzip input");
return (ARCHIVE_FATAL);
@@ -365,34 +361,34 @@ consume_header(struct archive_read_filter *self)
max_in = UINT_MAX;
if (avail > max_in)
avail = max_in;
- state->stream.avail_in = (uInt)avail;
- ret = inflateInit2(&(state->stream),
+ gzip->stream.avail_in = (uInt)avail;
+ ret = inflateInit2(&(gzip->stream),
-15 /* Don't check for zlib header */);
/* Decipher the error code. */
switch (ret) {
case Z_OK:
- state->in_stream = 1;
+ gzip->in_stream = 1;
return (ARCHIVE_OK);
case Z_STREAM_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library: "
"invalid setup parameter");
break;
case Z_MEM_ERROR:
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Internal error initializing compression library: "
"out of memory");
break;
case Z_VERSION_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library: "
"invalid library version");
break;
default:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library: "
" Zlib error %d", ret);
@@ -402,59 +398,55 @@ consume_header(struct archive_read_filter *self)
}
static int
-consume_trailer(struct archive_read_filter *self)
+consume_trailer(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct gzip *gzip = f->data;
const unsigned char *p;
- state = (struct private_data *)self->data;
-
- state->in_stream = 0;
- switch (inflateEnd(&(state->stream))) {
+ gzip->in_stream = 0;
+ switch (inflateEnd(&(gzip->stream))) {
case Z_OK:
break;
default:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Failed to clean up gzip decompressor");
return (ARCHIVE_FATAL);
}
/* GZip trailer is a fixed 8 byte structure. */
- p = __archive_read_filter_ahead(self->upstream, 8, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 8, NULL);
if (p == NULL)
return (ARCHIVE_FATAL);
/* XXX TODO: Verify the length and CRC. */
/* We've verified the trailer, so consume it now. */
- __archive_read_filter_consume(self->upstream, 8);
+ __archive_read_filter_consume(f->upstream, 8);
return (ARCHIVE_OK);
}
static ssize_t
-gzip_filter_read(struct archive_read_filter *self, const void **p)
+gzip_filter_read(struct archive_read_filter *f, const void **p)
{
- struct private_data *state;
+ struct gzip *gzip = f->data;
size_t decompressed;
ssize_t avail_in, max_in;
int ret;
- state = (struct private_data *)self->data;
-
/* Empty our output buffer. */
- state->stream.next_out = state->out_block;
- state->stream.avail_out = (uInt)state->out_block_size;
+ gzip->stream.next_out = gzip->out_block;
+ gzip->stream.avail_out = (uInt)gzip->out_block_size;
/* Try to fill the output buffer. */
- while (state->stream.avail_out > 0 && !state->eof) {
+ while (gzip->stream.avail_out > 0 && !gzip->eof) {
/* If we're not in a stream, read a header
* and initialize the decompression library. */
- if (!state->in_stream) {
- ret = consume_header(self);
+ if (!gzip->in_stream) {
+ ret = consume_header(f);
if (ret == ARCHIVE_EOF) {
- state->eof = 1;
+ gzip->eof = 1;
break;
}
if (ret < ARCHIVE_OK)
@@ -464,10 +456,10 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
/* Peek at the next available data. */
/* ZLib treats stream.next_in as const but doesn't declare
* it so, hence this ugly cast. */
- state->stream.next_in = (unsigned char *)(uintptr_t)
- __archive_read_filter_ahead(self->upstream, 1, &avail_in);
- if (state->stream.next_in == NULL) {
- archive_set_error(&self->archive->archive,
+ gzip->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(f->upstream, 1, &avail_in);
+ if (gzip->stream.next_in == NULL) {
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated gzip input");
return (ARCHIVE_FATAL);
@@ -478,27 +470,27 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
max_in = UINT_MAX;
if (avail_in > max_in)
avail_in = max_in;
- state->stream.avail_in = (uInt)avail_in;
+ gzip->stream.avail_in = (uInt)avail_in;
/* Decompress and consume some of that data. */
- ret = inflate(&(state->stream), 0);
+ ret = inflate(&(gzip->stream), 0);
switch (ret) {
case Z_OK: /* Decompressor made some progress. */
- __archive_read_filter_consume(self->upstream,
- avail_in - state->stream.avail_in);
+ __archive_read_filter_consume(f->upstream,
+ avail_in - gzip->stream.avail_in);
break;
case Z_STREAM_END: /* Found end of stream. */
- __archive_read_filter_consume(self->upstream,
- avail_in - state->stream.avail_in);
+ __archive_read_filter_consume(f->upstream,
+ avail_in - gzip->stream.avail_in);
/* Consume the stream trailer; release the
* decompression library. */
- ret = consume_trailer(self);
+ ret = consume_trailer(f);
if (ret < ARCHIVE_OK)
return (ret);
break;
default:
/* Return an error. */
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"gzip decompression failed");
return (ARCHIVE_FATAL);
@@ -506,11 +498,11 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
}
/* We've read as much as we can. */
- decompressed = state->stream.next_out - state->out_block;
+ decompressed = gzip->stream.next_out - gzip->out_block;
if (decompressed == 0)
*p = NULL;
else
- *p = state->out_block;
+ *p = gzip->out_block;
return (decompressed);
}
@@ -518,29 +510,28 @@ gzip_filter_read(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-gzip_filter_close(struct archive_read_filter *self)
+gzip_filter_close(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct gzip *gzip = f->data;
int ret;
- state = (struct private_data *)self->data;
ret = ARCHIVE_OK;
- if (state->in_stream) {
- switch (inflateEnd(&(state->stream))) {
+ if (gzip->in_stream) {
+ switch (inflateEnd(&(gzip->stream))) {
case Z_OK:
break;
default:
- archive_set_error(&(self->archive->archive),
+ archive_set_error(&(f->archive->archive),
ARCHIVE_ERRNO_MISC,
"Failed to clean up gzip compressor");
ret = ARCHIVE_FATAL;
}
}
- free(state->name);
- free(state->out_block);
- free(state);
+ free(gzip->name);
+ free(gzip->out_block);
+ free(gzip);
return (ret);
}
diff --git a/libarchive/archive_read_support_filter_lrzip.c b/libarchive/archive_read_support_filter_lrzip.c
index 9d51837753e3..33910fe10a09 100644
--- a/libarchive/archive_read_support_filter_lrzip.c
+++ b/libarchive/archive_read_support_filter_lrzip.c
@@ -75,18 +75,18 @@ archive_read_support_filter_lrzip(struct archive *_a)
* Bidder just verifies the header and returns the number of verified bits.
*/
static int
-lrzip_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+lrzip_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *p;
ssize_t len;
int i;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
/* Start by looking at the first six bytes of the header, which
* is all fixed layout. */
len = 6;
- p = __archive_read_filter_ahead(filter, len, NULL);
+ p = __archive_read_filter_ahead(f, len, NULL);
if (p == NULL)
return (0);
@@ -105,15 +105,15 @@ lrzip_bidder_bid(struct archive_read_filter_bidder *self,
}
static int
-lrzip_bidder_init(struct archive_read_filter *self)
+lrzip_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "lrzip -d -q");
+ r = __archive_read_program(f, "lrzip -d -q");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_LRZIP;
- self->name = "lrzip";
+ f->code = ARCHIVE_FILTER_LRZIP;
+ f->name = "lrzip";
return (r);
}
diff --git a/libarchive/archive_read_support_filter_lz4.c b/libarchive/archive_read_support_filter_lz4.c
index 2e5f634b1aa3..19086a1fefb2 100644
--- a/libarchive/archive_read_support_filter_lz4.c
+++ b/libarchive/archive_read_support_filter_lz4.c
@@ -57,7 +57,7 @@
#define LZ4_SKIPPABLE_MASK 0xFFFFFFF0
#if defined(HAVE_LIBLZ4)
-struct private_data {
+struct lz4 {
enum { SELECT_STREAM,
READ_DEFAULT_STREAM,
READ_DEFAULT_BLOCK,
@@ -139,8 +139,8 @@ archive_read_support_filter_lz4(struct archive *_a)
* from verifying as much as we would like.
*/
static int
-lz4_reader_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+lz4_reader_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *buffer;
ssize_t avail;
@@ -159,7 +159,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *self,
const size_t max_lookahead = 64 * 1024;
uint32_t magic_number;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
/*
* Zstd and LZ4 skippable frame magic numbers are identical. To
@@ -168,7 +168,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *self,
*/
/* Minimal lz4 archive is 11 bytes. */
- buffer = __archive_read_filter_ahead(filter, min_lz4_archive_size,
+ buffer = __archive_read_filter_ahead(f, min_lz4_archive_size,
&avail);
if (buffer == NULL)
return (0);
@@ -184,7 +184,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *self,
/* Ensure that we can read another 4 bytes. */
if (offset_in_buffer + 4 > (size_t)avail) {
- buffer = __archive_read_filter_ahead(filter,
+ buffer = __archive_read_filter_ahead(f,
offset_in_buffer + 4, &avail);
if (buffer == NULL)
return (0);
@@ -212,7 +212,7 @@ lz4_reader_bid(struct archive_read_filter_bidder *self,
if (min > max_lookahead)
return (0);
- buffer = __archive_read_filter_ahead(filter,
+ buffer = __archive_read_filter_ahead(f,
min, &avail);
if (buffer == NULL)
return (0);
@@ -271,16 +271,16 @@ lz4_reader_bid(struct archive_read_filter_bidder *self,
* in case that's available.
*/
static int
-lz4_reader_init(struct archive_read_filter *self)
+lz4_reader_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "lz4 -d -q");
+ r = __archive_read_program(f, "lz4 -d -q");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_LZ4;
- self->name = "lz4";
+ f->code = ARCHIVE_FILTER_LZ4;
+ f->name = "lz4";
return (r);
}
@@ -297,73 +297,73 @@ lz4_reader_vtable = {
* Setup the callbacks.
*/
static int
-lz4_reader_init(struct archive_read_filter *self)
+lz4_reader_init(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct lz4 *lz4;
- self->code = ARCHIVE_FILTER_LZ4;
- self->name = "lz4";
+ f->code = ARCHIVE_FILTER_LZ4;
+ f->name = "lz4";
- state = calloc(1, sizeof(*state));
- if (state == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ lz4 = calloc(1, sizeof(*lz4));
+ if (lz4 == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for lz4 decompression");
return (ARCHIVE_FATAL);
}
- self->data = state;
- state->stage = SELECT_STREAM;
- self->vtable = &lz4_reader_vtable;
+ f->data = lz4;
+ lz4->stage = SELECT_STREAM;
+ f->vtable = &lz4_reader_vtable;
return (ARCHIVE_OK);
}
static int
-lz4_allocate_out_block(struct archive_read_filter *self)
+lz4_allocate_out_block(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
- size_t out_block_size = state->flags.block_maximum_size;
+ struct lz4 *lz4 = f->data;
+ size_t out_block_size = lz4->flags.block_maximum_size;
void *out_block;
- if (!state->flags.block_independence)
+ if (!lz4->flags.block_independence)
out_block_size += 64 * 1024;
- if (state->out_block_size < out_block_size) {
- free(state->out_block);
- state->out_block = NULL;
+ if (lz4->out_block_size < out_block_size) {
+ free(lz4->out_block);
+ lz4->out_block = NULL;
out_block = malloc(out_block_size);
if (out_block == NULL) {
- state->out_block_size = 0;
- archive_set_error(&self->archive->archive, ENOMEM,
+ lz4->out_block_size = 0;
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for lz4 decompression");
return (ARCHIVE_FATAL);
}
- state->out_block_size = out_block_size;
- state->out_block = out_block;
+ lz4->out_block_size = out_block_size;
+ lz4->out_block = out_block;
}
- if (!state->flags.block_independence)
- memset(state->out_block, 0, 64 * 1024);
+ if (!lz4->flags.block_independence)
+ memset(lz4->out_block, 0, 64 * 1024);
return (ARCHIVE_OK);
}
static int
-lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
+lz4_allocate_out_block_for_legacy(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
size_t out_block_size = LEGACY_BLOCK_SIZE;
void *out_block;
- if (state->out_block_size < out_block_size) {
- free(state->out_block);
- state->out_block = NULL;
+ if (lz4->out_block_size < out_block_size) {
+ free(lz4->out_block);
+ lz4->out_block = NULL;
out_block = malloc(out_block_size);
if (out_block == NULL) {
- state->out_block_size = 0;
- archive_set_error(&self->archive->archive, ENOMEM,
+ lz4->out_block_size = 0;
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for lz4 decompression");
return (ARCHIVE_FATAL);
}
- state->out_block_size = out_block_size;
- state->out_block = out_block;
+ lz4->out_block_size = out_block_size;
+ lz4->out_block = out_block;
}
return (ARCHIVE_OK);
}
@@ -372,96 +372,96 @@ lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
* Return the next block of decompressed data.
*/
static ssize_t
-lz4_filter_read(struct archive_read_filter *self, const void **p)
+lz4_filter_read(struct archive_read_filter *f, const void **p)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
ssize_t ret;
- if (state->eof) {
+ if (lz4->eof) {
*p = NULL;
return (0);
}
- __archive_read_filter_consume(self->upstream, state->unconsumed);
- state->unconsumed = 0;
+ __archive_read_filter_consume(f->upstream, lz4->unconsumed);
+ lz4->unconsumed = 0;
- switch (state->stage) {
+ switch (lz4->stage) {
case SELECT_STREAM:
break;
case READ_DEFAULT_STREAM:
case READ_LEGACY_STREAM:
/* Reading an lz4 stream already failed. */
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Invalid sequence");
return (ARCHIVE_FATAL);
case READ_DEFAULT_BLOCK:
- ret = lz4_filter_read_default_stream(self, p);
- if (ret != 0 || state->stage != SELECT_STREAM)
+ ret = lz4_filter_read_default_stream(f, p);
+ if (ret != 0 || lz4->stage != SELECT_STREAM)
return ret;
break;
case READ_LEGACY_BLOCK:
- ret = lz4_filter_read_legacy_stream(self, p);
- if (ret != 0 || state->stage != SELECT_STREAM)
+ ret = lz4_filter_read_legacy_stream(f, p);
+ if (ret != 0 || lz4->stage != SELECT_STREAM)
return ret;
break;
default:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Program error");
return (ARCHIVE_FATAL);
}
- while (state->stage == SELECT_STREAM) {
+ while (lz4->stage == SELECT_STREAM) {
const char *read_buf;
/* Read a magic number. */
- read_buf = __archive_read_filter_ahead(self->upstream, 4,
+ read_buf = __archive_read_filter_ahead(f->upstream, 4,
NULL);
if (read_buf == NULL) {
- state->eof = 1;
+ lz4->eof = 1;
*p = NULL;
return (0);
}
uint32_t number = archive_le32dec(read_buf);
- __archive_read_filter_consume(self->upstream, 4);
+ __archive_read_filter_consume(f->upstream, 4);
if (number == LZ4_MAGICNUMBER)
- return lz4_filter_read_default_stream(self, p);
+ return lz4_filter_read_default_stream(f, p);
else if (number == LZ4_LEGACY)
- return lz4_filter_read_legacy_stream(self, p);
+ return lz4_filter_read_legacy_stream(f, p);
else if ((number & LZ4_SKIPPABLE_MASK) == LZ4_SKIPPABLE_START) {
read_buf = __archive_read_filter_ahead(
- self->upstream, 4, NULL);
+ f->upstream, 4, NULL);
if (read_buf == NULL) {
archive_set_error(
- &self->archive->archive,
+ &f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Malformed lz4 data");
return (ARCHIVE_FATAL);
}
int64_t skip_bytes = archive_le32dec(read_buf);
- if (__archive_read_filter_consume(self->upstream,
+ if (__archive_read_filter_consume(f->upstream,
4 + skip_bytes) < 0) {
archive_set_error(
- &self->archive->archive,
+ &f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Malformed lz4 data");
return (ARCHIVE_FATAL);
}
} else {
/* Ignore following unrecognized data. */
- state->eof = 1;
+ lz4->eof = 1;
*p = NULL;
return (0);
}
}
- state->eof = 1;
+ lz4->eof = 1;
*p = NULL;
return (0);
}
static int
-lz4_filter_read_descriptor(struct archive_read_filter *self)
+lz4_filter_read_descriptor(struct archive_read_filter *f)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
const char *read_buf;
ssize_t bytes_remaining;
ssize_t descriptor_bytes;
@@ -469,10 +469,10 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
unsigned int chsum, chsum_verifier;
/* Make sure we have 2 bytes for flags. */
- read_buf = __archive_read_filter_ahead(self->upstream, 2,
+ read_buf = __archive_read_filter_ahead(f->upstream, 2,
&bytes_remaining);
if (read_buf == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated lz4 input");
return (ARCHIVE_FATAL);
@@ -488,11 +488,11 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
/* A reserved bit must be zero. */
if (flag & 0x02)
goto malformed_error;
- state->flags.block_independence = (flag & 0x20) != 0;
- state->flags.block_checksum = (flag & 0x10)?4:0;
- state->flags.stream_size = (flag & 0x08) != 0;
- state->flags.stream_checksum = (flag & 0x04) != 0;
- state->flags.preset_dictionary = (flag & 0x01) != 0;
+ lz4->flags.block_independence = (flag & 0x20) != 0;
+ lz4->flags.block_checksum = (flag & 0x10)?4:0;
+ lz4->flags.stream_size = (flag & 0x08) != 0;
+ lz4->flags.stream_checksum = (flag & 0x04) != 0;
+ lz4->flags.preset_dictionary = (flag & 0x01) != 0;
/* BD */
bd = (unsigned char)read_buf[1];
@@ -502,16 +502,16 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
/* Get a maximum block size. */
switch (read_buf[1] >> 4) {
case 4: /* 64 KB */
- state->flags.block_maximum_size = 64 * 1024;
+ lz4->flags.block_maximum_size = 64 * 1024;
break;
case 5: /* 256 KB */
- state->flags.block_maximum_size = 256 * 1024;
+ lz4->flags.block_maximum_size = 256 * 1024;
break;
case 6: /* 1 MB */
- state->flags.block_maximum_size = 1024 * 1024;
+ lz4->flags.block_maximum_size = 1024 * 1024;
break;
case 7: /* 4 MB */
- state->flags.block_maximum_size = 4 * 1024 * 1024;
+ lz4->flags.block_maximum_size = 4 * 1024 * 1024;
break;
default:
goto malformed_error;
@@ -519,15 +519,15 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
/* Read the whole descriptor in a stream block. */
descriptor_bytes = 3;
- if (state->flags.stream_size)
+ if (lz4->flags.stream_size)
descriptor_bytes += 8;
- if (state->flags.preset_dictionary)
+ if (lz4->flags.preset_dictionary)
descriptor_bytes += 4;
if (bytes_remaining < descriptor_bytes) {
- read_buf = __archive_read_filter_ahead(self->upstream,
+ read_buf = __archive_read_filter_ahead(f->upstream,
descriptor_bytes, &bytes_remaining);
if (read_buf == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated lz4 input");
return (ARCHIVE_FATAL);
@@ -542,30 +542,30 @@ lz4_filter_read_descriptor(struct archive_read_filter *self)
goto malformed_error;
#endif
- __archive_read_filter_consume(self->upstream, descriptor_bytes);
+ __archive_read_filter_consume(f->upstream, descriptor_bytes);
/* Make sure we have a large enough buffer for uncompressed data. */
- if (lz4_allocate_out_block(self) != ARCHIVE_OK)
+ if (lz4_allocate_out_block(f) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
- if (state->flags.stream_checksum) {
- state->xxh32_state = __archive_xxhash.XXH32_init(0);
- if (state->xxh32_state == NULL)
+ if (lz4->flags.stream_checksum) {
+ lz4->xxh32_state = __archive_xxhash.XXH32_init(0);
+ if (lz4->xxh32_state == NULL)
return (ARCHIVE_FATAL);
}
- state->decoded_size = 0;
+ lz4->decoded_size = 0;
/* Success */
return (ARCHIVE_OK);
malformed_error:
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"malformed lz4 data");
return (ARCHIVE_FATAL);
}
static ssize_t
-lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
+lz4_filter_read_data_block(struct archive_read_filter *f, const void **p)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
ssize_t compressed_size;
const char *read_buf;
int checksum_size;
@@ -575,19 +575,19 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
*p = NULL;
/* Make sure we have 4 bytes for a block size. */
- read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (read_buf == NULL)
goto truncated_error;
compressed_size = archive_le32dec(read_buf);
- if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)
+ if ((compressed_size & 0x7fffffff) > lz4->flags.block_maximum_size)
goto malformed_error;
/* A compressed size == 0 means the end of stream blocks. */
if (compressed_size == 0) {
- __archive_read_filter_consume(self->upstream, 4);
+ __archive_read_filter_consume(f->upstream, 4);
return 0;
}
- checksum_size = state->flags.block_checksum;
+ checksum_size = lz4->flags.block_checksum;
/* Check if the block is uncompressed. */
if (compressed_size & 0x80000000U) {
compressed_size &= 0x7fffffff;
@@ -600,7 +600,7 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
for its decompression speed, so we read a whole block and allocate
a huge buffer used for decoded data.
*/
- read_buf = __archive_read_filter_ahead(self->upstream,
+ read_buf = __archive_read_filter_ahead(f->upstream,
4 + compressed_size + checksum_size, NULL);
if (read_buf == NULL)
goto truncated_error;
@@ -621,24 +621,24 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
/* If the block is uncompressed, there is nothing to do. */
if (uncompressed_size) {
/* Prepare a prefix 64k block for next block. */
- if (!state->flags.block_independence) {
+ if (!lz4->flags.block_independence) {
prefix64k = 64 * 1024;
if (uncompressed_size < (ssize_t)prefix64k) {
- memcpy(state->out_block
+ memcpy(lz4->out_block
+ prefix64k - uncompressed_size,
read_buf + 4,
uncompressed_size);
- memset(state->out_block, 0,
+ memset(lz4->out_block, 0,
prefix64k - uncompressed_size);
} else {
- memcpy(state->out_block,
+ memcpy(lz4->out_block,
read_buf + 4
+ uncompressed_size - prefix64k,
prefix64k);
}
- state->decoded_size = 0;
+ lz4->decoded_size = 0;
}
- state->unconsumed = 4 + uncompressed_size + checksum_size;
+ lz4->unconsumed = 4 + uncompressed_size + checksum_size;
*p = read_buf + 4;
return uncompressed_size;
}
@@ -646,168 +646,168 @@ lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
/*
Decompress a block data.
*/
- if (state->flags.block_independence) {
+ if (lz4->flags.block_independence) {
prefix64k = 0;
uncompressed_size = LZ4_decompress_safe(read_buf + 4,
- state->out_block, (int)compressed_size,
- state->flags.block_maximum_size);
+ lz4->out_block, (int)compressed_size,
+ lz4->flags.block_maximum_size);
} else {
prefix64k = 64 * 1024;
- if (state->decoded_size) {
- if (state->decoded_size < prefix64k) {
- memmove(state->out_block
- + prefix64k - state->decoded_size,
- state->out_block + prefix64k,
- state->decoded_size);
- memset(state->out_block, 0,
- prefix64k - state->decoded_size);
+ if (lz4->decoded_size) {
+ if (lz4->decoded_size < prefix64k) {
+ memmove(lz4->out_block
+ + prefix64k - lz4->decoded_size,
+ lz4->out_block + prefix64k,
+ lz4->decoded_size);
+ memset(lz4->out_block, 0,
+ prefix64k - lz4->decoded_size);
} else {
- memmove(state->out_block,
- state->out_block + state->decoded_size,
+ memmove(lz4->out_block,
+ lz4->out_block + lz4->decoded_size,
prefix64k);
}
}
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
uncompressed_size = LZ4_decompress_safe_usingDict(
read_buf + 4,
- state->out_block + prefix64k, (int)compressed_size,
- state->flags.block_maximum_size,
- state->out_block,
+ lz4->out_block + prefix64k, (int)compressed_size,
+ lz4->flags.block_maximum_size,
+ lz4->out_block,
(int)prefix64k);
#else
uncompressed_size = LZ4_decompress_safe_withPrefix64k(
read_buf + 4,
- state->out_block + prefix64k, (int)compressed_size,
- state->flags.block_maximum_size);
+ lz4->out_block + prefix64k, (int)compressed_size,
+ lz4->flags.block_maximum_size);
#endif
}
/* Check if an error occurred in the decompression process. */
if (uncompressed_size < 0) {
- archive_set_error(&(self->archive->archive),
+ archive_set_error(&(f->archive->archive),
ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
return (ARCHIVE_FATAL);
}
- state->unconsumed = 4 + compressed_size + checksum_size;
- *p = state->out_block + prefix64k;
- state->decoded_size = uncompressed_size;
+ lz4->unconsumed = 4 + compressed_size + checksum_size;
+ *p = lz4->out_block + prefix64k;
+ lz4->decoded_size = uncompressed_size;
return uncompressed_size;
malformed_error:
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"malformed lz4 data");
return (ARCHIVE_FATAL);
truncated_error:
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"truncated lz4 input");
return (ARCHIVE_FATAL);
}
static ssize_t
-lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
+lz4_filter_read_default_stream(struct archive_read_filter *f, const void **p)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
const char *read_buf;
ssize_t ret;
- if (state->stage == SELECT_STREAM) {
- state->stage = READ_DEFAULT_STREAM;
+ if (lz4->stage == SELECT_STREAM) {
+ lz4->stage = READ_DEFAULT_STREAM;
/* First, read a descriptor. */
- if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
+ if((ret = lz4_filter_read_descriptor(f)) != ARCHIVE_OK)
return (ret);
- state->stage = READ_DEFAULT_BLOCK;
+ lz4->stage = READ_DEFAULT_BLOCK;
}
/* Decompress a block. */
- ret = lz4_filter_read_data_block(self, p);
+ ret = lz4_filter_read_data_block(f, p);
/* If the end of block is detected, change the filter status
to read next stream. */
if (ret == 0 && *p == NULL)
- state->stage = SELECT_STREAM;
+ lz4->stage = SELECT_STREAM;
/* Optional processing, checking a stream sum. */
- if (state->flags.stream_checksum) {
- if (state->stage == SELECT_STREAM) {
+ if (lz4->flags.stream_checksum) {
+ if (lz4->stage == SELECT_STREAM) {
unsigned int checksum;
unsigned int checksum_stream;
- read_buf = __archive_read_filter_ahead(self->upstream,
+ read_buf = __archive_read_filter_ahead(f->upstream,
4, NULL);
if (read_buf == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "truncated lz4 input");
return (ARCHIVE_FATAL);
}
checksum = archive_le32dec(read_buf);
- __archive_read_filter_consume(self->upstream, 4);
+ __archive_read_filter_consume(f->upstream, 4);
checksum_stream = __archive_xxhash.XXH32_digest(
- state->xxh32_state);
- state->xxh32_state = NULL;
+ lz4->xxh32_state);
+ lz4->xxh32_state = NULL;
if (checksum != checksum_stream) {
#ifndef DONT_FAIL_ON_CRC_ERROR
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"lz4 stream checksum error");
return (ARCHIVE_FATAL);
#endif
}
} else if (ret > 0)
- __archive_xxhash.XXH32_update(state->xxh32_state,
+ __archive_xxhash.XXH32_update(lz4->xxh32_state,
*p, (int)ret);
}
return (ret);
}
static ssize_t
-lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
+lz4_filter_read_legacy_stream(struct archive_read_filter *f, const void **p)
{
- struct private_data *state = (struct private_data *)self->data;
+ struct lz4 *lz4 = f->data;
uint32_t compressed;
const char *read_buf;
ssize_t ret;
*p = NULL;
- ret = lz4_allocate_out_block_for_legacy(self);
+ ret = lz4_allocate_out_block_for_legacy(f);
if (ret != ARCHIVE_OK)
return ret;
/* Make sure we have 4 bytes for a block size. */
- read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ read_buf = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (read_buf == NULL) {
- if (state->stage == SELECT_STREAM) {
- state->stage = READ_LEGACY_STREAM;
- archive_set_error(&self->archive->archive,
+ if (lz4->stage == SELECT_STREAM) {
+ lz4->stage = READ_LEGACY_STREAM;
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated lz4 input");
return (ARCHIVE_FATAL);
}
- state->stage = SELECT_STREAM;
+ lz4->stage = SELECT_STREAM;
return 0;
}
- state->stage = READ_LEGACY_BLOCK;
+ lz4->stage = READ_LEGACY_BLOCK;
compressed = archive_le32dec(read_buf);
if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {
- state->stage = SELECT_STREAM;
+ lz4->stage = SELECT_STREAM;
return 0;
}
/* Make sure we have a whole block. */
- read_buf = __archive_read_filter_ahead(self->upstream,
+ read_buf = __archive_read_filter_ahead(f->upstream,
4 + compressed, NULL);
if (read_buf == NULL) {
- archive_set_error(&(self->archive->archive),
+ archive_set_error(&(f->archive->archive),
ARCHIVE_ERRNO_MISC, "truncated lz4 input");
return (ARCHIVE_FATAL);
}
- ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
- compressed, (int)state->out_block_size);
+ ret = LZ4_decompress_safe(read_buf + 4, lz4->out_block,
+ compressed, (int)lz4->out_block_size);
if (ret < 0) {
- archive_set_error(&(self->archive->archive),
+ archive_set_error(&(f->archive->archive),
ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
return (ARCHIVE_FATAL);
}
- *p = state->out_block;
- state->unconsumed = 4 + compressed;
+ *p = lz4->out_block;
+ lz4->unconsumed = 4 + compressed;
return ret;
}
@@ -815,15 +815,14 @@ lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-lz4_filter_close(struct archive_read_filter *self)
+lz4_filter_close(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct lz4 *lz4 = f->data;
int ret = ARCHIVE_OK;
- state = (struct private_data *)self->data;
- free(state->xxh32_state);
- free(state->out_block);
- free(state);
+ free(lz4->xxh32_state);
+ free(lz4->out_block);
+ free(lz4);
return (ret);
}
diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c
index 40c4575882e0..ebfaf01e8533 100644
--- a/libarchive/archive_read_support_filter_lzop.c
+++ b/libarchive/archive_read_support_filter_lzop.c
@@ -68,7 +68,7 @@
#define LZOP_HEADER_MAGIC_LEN 9
#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
-struct read_lzop {
+struct lzop {
unsigned char *out_block;
size_t out_block_size;
int flags;
@@ -127,14 +127,14 @@ archive_read_support_filter_lzop(struct archive *_a)
* Bidder just verifies the header and returns the number of verified bits.
*/
static int
-lzop_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+lzop_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *p;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, NULL);
+ p = __archive_read_filter_ahead(f, LZOP_HEADER_MAGIC_LEN, NULL);
if (p == NULL)
return (0);
@@ -151,16 +151,16 @@ lzop_bidder_bid(struct archive_read_filter_bidder *self,
* in case that's available.
*/
static int
-lzop_bidder_init(struct archive_read_filter *self)
+lzop_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "lzop -d");
+ r = __archive_read_program(f, "lzop -d");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_LZOP;
- self->name = "lzop";
+ f->code = ARCHIVE_FILTER_LZOP;
+ f->name = "lzop";
return (r);
}
#else
@@ -175,47 +175,47 @@ lzop_reader_vtable = {
* Initialize the filter object.
*/
static int
-lzop_bidder_init(struct archive_read_filter *self)
+lzop_bidder_init(struct archive_read_filter *f)
{
- struct read_lzop *state;
+ struct lzop *lzop;
- self->code = ARCHIVE_FILTER_LZOP;
- self->name = "lzop";
+ f->code = ARCHIVE_FILTER_LZOP;
+ f->name = "lzop";
- state = calloc(1, sizeof(*state));
- if (state == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ lzop = calloc(1, sizeof(*lzop));
+ if (lzop == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for lzop decompression");
return (ARCHIVE_FATAL);
}
- self->data = state;
- self->vtable = &lzop_reader_vtable;
+ f->data = lzop;
+ f->vtable = &lzop_reader_vtable;
return (ARCHIVE_OK);
}
static int
-consume_header(struct archive_read_filter *self)
+consume_header(struct archive_read_filter *f)
{
- struct read_lzop *state = (struct read_lzop *)self->data;
+ struct lzop *lzop = f->data;
const unsigned char *p, *_p;
unsigned checksum, flags, len, method, version;
/*
* Check LZOP magic code.
*/
- p = __archive_read_filter_ahead(self->upstream,
+ p = __archive_read_filter_ahead(f->upstream,
LZOP_HEADER_MAGIC_LEN, NULL);
if (p == NULL)
return (ARCHIVE_EOF);
if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
return (ARCHIVE_EOF);
- __archive_read_filter_consume(self->upstream,
+ __archive_read_filter_consume(f->upstream,
LZOP_HEADER_MAGIC_LEN);
- p = __archive_read_filter_ahead(self->upstream, 29, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 29, NULL);
if (p == NULL)
goto truncated;
_p = p;
@@ -225,7 +225,7 @@ consume_header(struct archive_read_filter *self)
if (version >= 0x940) {
unsigned reqversion = archive_be16dec(p); p += 2;
if (reqversion < 0x900) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Invalid required version");
return (ARCHIVE_FAILED);
}
@@ -233,7 +233,7 @@ consume_header(struct archive_read_filter *self)
method = *p++;
if (method < 1 || method > 3) {
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Unsupported method");
return (ARCHIVE_FAILED);
}
@@ -252,7 +252,7 @@ consume_header(struct archive_read_filter *self)
;/* NOP */
#endif
else if (level > 9) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Invalid level");
return (ARCHIVE_FAILED);
}
@@ -270,7 +270,7 @@ consume_header(struct archive_read_filter *self)
len = *p++; /* Read filename length */
len += p - _p;
/* Make sure we have all bytes we need to calculate checksum. */
- p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);
+ p = __archive_read_filter_ahead(f->upstream, len + 4, NULL);
if (p == NULL)
goto truncated;
if (flags & CRC32_HEADER)
@@ -281,146 +281,146 @@ consume_header(struct archive_read_filter *self)
if (archive_be32dec(p + len) != checksum)
goto corrupted;
#endif
- __archive_read_filter_consume(self->upstream, len + 4);
+ __archive_read_filter_consume(f->upstream, len + 4);
if (flags & EXTRA_FIELD) {
/* Skip extra field */
- p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (p == NULL)
goto truncated;
len = archive_be32dec(p);
- __archive_read_filter_consume(self->upstream,
+ __archive_read_filter_consume(f->upstream,
(int64_t)len + 4 + 4);
}
- state->flags = flags;
- state->in_stream = 1;
+ lzop->flags = flags;
+ lzop->in_stream = 1;
return (ARCHIVE_OK);
truncated:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
return (ARCHIVE_FAILED);
corrupted:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
return (ARCHIVE_FAILED);
}
static int
-consume_block_info(struct archive_read_filter *self)
+consume_block_info(struct archive_read_filter *f)
{
- struct read_lzop *state = (struct read_lzop *)self->data;
+ struct lzop *lzop = f->data;
const unsigned char *p;
- unsigned flags = state->flags;
+ unsigned flags = lzop->flags;
- p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (p == NULL)
goto truncated;
- state->uncompressed_size = archive_be32dec(p);
- __archive_read_filter_consume(self->upstream, 4);
- if (state->uncompressed_size == 0)
+ lzop->uncompressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(f->upstream, 4);
+ if (lzop->uncompressed_size == 0)
return (ARCHIVE_EOF);
- if (state->uncompressed_size > MAX_BLOCK_SIZE)
+ if (lzop->uncompressed_size > MAX_BLOCK_SIZE)
goto corrupted;
- p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (p == NULL)
goto truncated;
- state->compressed_size = archive_be32dec(p);
- __archive_read_filter_consume(self->upstream, 4);
- if (state->compressed_size > state->uncompressed_size)
+ lzop->compressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(f->upstream, 4);
+ if (lzop->compressed_size > lzop->uncompressed_size)
goto corrupted;
if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {
- p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ p = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (p == NULL)
goto truncated;
- state->compressed_cksum = state->uncompressed_cksum =
+ lzop->compressed_cksum = lzop->uncompressed_cksum =
archive_be32dec(p);
- __archive_read_filter_consume(self->upstream, 4);
+ __archive_read_filter_consume(f->upstream, 4);
}
if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&
- state->compressed_size < state->uncompressed_size) {
- p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ lzop->compressed_size < lzop->uncompressed_size) {
+ p = __archive_read_filter_ahead(f->upstream, 4, NULL);
if (p == NULL)
goto truncated;
- state->compressed_cksum = archive_be32dec(p);
- __archive_read_filter_consume(self->upstream, 4);
+ lzop->compressed_cksum = archive_be32dec(p);
+ __archive_read_filter_consume(f->upstream, 4);
}
return (ARCHIVE_OK);
truncated:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
return (ARCHIVE_FAILED);
corrupted:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
return (ARCHIVE_FAILED);
}
static ssize_t
-lzop_filter_read(struct archive_read_filter *self, const void **p)
+lzop_filter_read(struct archive_read_filter *f, const void **p)
{
- struct read_lzop *state = (struct read_lzop *)self->data;
+ struct lzop *lzop = f->data;
const void *b;
lzo_uint out_size;
uint32_t cksum;
int ret, r;
- if (state->unconsumed_bytes) {
- __archive_read_filter_consume(self->upstream,
- state->unconsumed_bytes);
- state->unconsumed_bytes = 0;
+ if (lzop->unconsumed_bytes) {
+ __archive_read_filter_consume(f->upstream,
+ lzop->unconsumed_bytes);
+ lzop->unconsumed_bytes = 0;
}
- if (state->eof)
+ if (lzop->eof)
return (0);
for (;;) {
- if (!state->in_stream) {
- ret = consume_header(self);
+ if (!lzop->in_stream) {
+ ret = consume_header(f);
if (ret < ARCHIVE_OK)
return (ret);
if (ret == ARCHIVE_EOF) {
- state->eof = 1;
+ lzop->eof = 1;
return (0);
}
}
- ret = consume_block_info(self);
+ ret = consume_block_info(f);
if (ret < ARCHIVE_OK)
return (ret);
if (ret == ARCHIVE_EOF)
- state->in_stream = 0;
+ lzop->in_stream = 0;
else
break;
}
- if (state->out_block == NULL ||
- state->out_block_size < state->uncompressed_size) {
+ if (lzop->out_block == NULL ||
+ lzop->out_block_size < lzop->uncompressed_size) {
void *new_block;
- new_block = realloc(state->out_block, state->uncompressed_size);
+ new_block = realloc(lzop->out_block, lzop->uncompressed_size);
if (new_block == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for lzop decompression");
return (ARCHIVE_FATAL);
}
- state->out_block = new_block;
- state->out_block_size = state->uncompressed_size;
+ lzop->out_block = new_block;
+ lzop->out_block_size = lzop->uncompressed_size;
}
- b = __archive_read_filter_ahead(self->upstream,
- state->compressed_size, NULL);
+ b = __archive_read_filter_ahead(f->upstream,
+ lzop->compressed_size, NULL);
if (b == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
return (ARCHIVE_FATAL);
}
- if (state->flags & CRC32_COMPRESSED)
- cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);
- else if (state->flags & ADLER32_COMPRESSED)
- cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);
+ if (lzop->flags & CRC32_COMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), b, lzop->compressed_size);
+ else if (lzop->flags & ADLER32_COMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), b, lzop->compressed_size);
else
- cksum = state->compressed_cksum;
- if (cksum != state->compressed_cksum) {
- archive_set_error(&self->archive->archive,
+ cksum = lzop->compressed_cksum;
+ if (cksum != lzop->compressed_cksum) {
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Corrupted data");
return (ARCHIVE_FATAL);
}
@@ -429,51 +429,51 @@ lzop_filter_read(struct archive_read_filter *self, const void **p)
* If both uncompressed size and compressed size are the same,
* we do not decompress this block.
*/
- if (state->uncompressed_size == state->compressed_size) {
+ if (lzop->uncompressed_size == lzop->compressed_size) {
*p = b;
- state->unconsumed_bytes = state->compressed_size;
- return ((ssize_t)state->uncompressed_size);
+ lzop->unconsumed_bytes = lzop->compressed_size;
+ return ((ssize_t)lzop->uncompressed_size);
}
/*
* Drive lzo uncompression.
*/
- out_size = (lzo_uint)state->uncompressed_size;
- r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
- state->out_block, &out_size, NULL);
+ out_size = (lzo_uint)lzop->uncompressed_size;
+ r = lzo1x_decompress_safe(b, (lzo_uint)lzop->compressed_size,
+ lzop->out_block, &out_size, NULL);
switch (r) {
case LZO_E_OK:
- if (out_size == state->uncompressed_size)
+ if (out_size == lzop->uncompressed_size)
break;
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Corrupted data");
return (ARCHIVE_FATAL);
case LZO_E_OUT_OF_MEMORY:
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"lzop decompression failed: out of memory");
return (ARCHIVE_FATAL);
default:
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"lzop decompression failed: %d", r);
return (ARCHIVE_FATAL);
}
- if (state->flags & CRC32_UNCOMPRESSED)
- cksum = crc32(crc32(0, NULL, 0), state->out_block,
- state->uncompressed_size);
- else if (state->flags & ADLER32_UNCOMPRESSED)
- cksum = adler32(adler32(0, NULL, 0), state->out_block,
- state->uncompressed_size);
+ if (lzop->flags & CRC32_UNCOMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), lzop->out_block,
+ lzop->uncompressed_size);
+ else if (lzop->flags & ADLER32_UNCOMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), lzop->out_block,
+ lzop->uncompressed_size);
else
- cksum = state->uncompressed_cksum;
- if (cksum != state->uncompressed_cksum) {
- archive_set_error(&self->archive->archive,
+ cksum = lzop->uncompressed_cksum;
+ if (cksum != lzop->uncompressed_cksum) {
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC, "Corrupted data");
return (ARCHIVE_FATAL);
}
- __archive_read_filter_consume(self->upstream, state->compressed_size);
- *p = state->out_block;
+ __archive_read_filter_consume(f->upstream, lzop->compressed_size);
+ *p = lzop->out_block;
return ((ssize_t)out_size);
}
@@ -481,12 +481,12 @@ lzop_filter_read(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-lzop_filter_close(struct archive_read_filter *self)
+lzop_filter_close(struct archive_read_filter *f)
{
- struct read_lzop *state = (struct read_lzop *)self->data;
+ struct lzop *lzop = f->data;
- free(state->out_block);
- free(state);
+ free(lzop->out_block);
+ free(lzop);
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c
index 9f187f852df3..cf7c36137e1b 100644
--- a/libarchive/archive_read_support_filter_program.c
+++ b/libarchive/archive_read_support_filter_program.c
@@ -87,7 +87,7 @@ archive_read_support_filter_program(struct archive *a, const char *cmd)
* bid twice in the same pipeline.
*/
struct program_bidder {
- char *description;
+ struct archive_string description;
char *cmd;
void *signature;
size_t signature_len;
@@ -102,7 +102,7 @@ static void program_bidder_free(struct archive_read_filter_bidder *);
/*
* The actual filter needs to track input and output data.
*/
-struct program_filter {
+struct program {
struct archive_string description;
#if defined(_WIN32) && !defined(__CYGWIN__)
HANDLE child;
@@ -145,6 +145,8 @@ archive_read_support_filter_program_signature(struct archive *_a,
state->cmd = strdup(cmd);
if (state->cmd == NULL)
goto memerr;
+ archive_strcpy(&state->description, "Program: ");
+ archive_strcat(&state->description, cmd);
if (signature != NULL && signature_len > 0) {
state->signature_len = signature_len;
@@ -168,9 +170,9 @@ memerr:
}
static void
-program_bidder_free(struct archive_read_filter_bidder *self)
+program_bidder_free(struct archive_read_filter_bidder *b)
{
- struct program_bidder *state = (struct program_bidder *)self->data;
+ struct program_bidder *state = (struct program_bidder *)b->data;
free_state(state);
}
@@ -180,6 +182,7 @@ free_state(struct program_bidder *state)
{
if (state) {
+ archive_string_free(&state->description);
free(state->cmd);
free(state->signature);
free(state);
@@ -193,15 +196,15 @@ free_state(struct program_bidder *state)
* we're called, then never bid again.
*/
static int
-program_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *upstream)
+program_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
- struct program_bidder *state = self->data;
+ struct program_bidder *state = b->data;
const char *p;
/* If we have a signature, use that to match. */
if (state->signature_len > 0) {
- p = __archive_read_filter_ahead(upstream,
+ p = __archive_read_filter_ahead(f,
state->signature_len, NULL);
if (p == NULL)
return (0);
@@ -226,36 +229,36 @@ program_bidder_bid(struct archive_read_filter_bidder *self,
* (including error message if the child came to a bad end).
*/
static int
-child_stop(struct archive_read_filter *self, struct program_filter *state)
+child_stop(struct archive_read_filter *f, struct program *program)
{
/* Close our side of the I/O with the child. */
- if (state->child_stdin != -1) {
- close(state->child_stdin);
- state->child_stdin = -1;
+ if (program->child_stdin != -1) {
+ close(program->child_stdin);
+ program->child_stdin = -1;
}
- if (state->child_stdout != -1) {
- close(state->child_stdout);
- state->child_stdout = -1;
+ if (program->child_stdout != -1) {
+ close(program->child_stdout);
+ program->child_stdout = -1;
}
- if (state->child != 0) {
+ if (program->child != 0) {
/* Reap the child. */
do {
- state->waitpid_return
- = waitpid(state->child, &state->exit_status, 0);
- } while (state->waitpid_return == -1 && errno == EINTR);
- state->child = 0;
+ program->waitpid_return
+ = waitpid(program->child, &program->exit_status, 0);
+ } while (program->waitpid_return == -1 && errno == EINTR);
+ program->child = 0;
}
- if (state->waitpid_return < 0) {
+ if (program->waitpid_return < 0) {
/* waitpid() failed? This is ugly. */
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Error closing child process");
return (ARCHIVE_WARN);
}
#if !defined(_WIN32) || defined(__CYGWIN__)
- if (WIFSIGNALED(state->exit_status)) {
+ if (WIFSIGNALED(program->exit_status)) {
#ifdef SIGPIPE
/* If the child died because we stopped reading before
* it was done, that's okay. Some archive formats
@@ -263,24 +266,24 @@ child_stop(struct archive_read_filter *self, struct program_filter *state)
/* The alternative to this would be to add a step
* before close(child_stdout) above to read from the
* child until the child has no more to write. */
- if (WTERMSIG(state->exit_status) == SIGPIPE)
+ if (WTERMSIG(program->exit_status) == SIGPIPE)
return (ARCHIVE_OK);
#endif
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Child process exited with signal %d",
- WTERMSIG(state->exit_status));
+ WTERMSIG(program->exit_status));
return (ARCHIVE_WARN);
}
#endif /* !_WIN32 || __CYGWIN__ */
- if (WIFEXITED(state->exit_status)) {
- if (WEXITSTATUS(state->exit_status) == 0)
+ if (WIFEXITED(program->exit_status)) {
+ if (WEXITSTATUS(program->exit_status) == 0)
return (ARCHIVE_OK);
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Child process exited with status %d",
- WEXITSTATUS(state->exit_status));
+ WEXITSTATUS(program->exit_status));
return (ARCHIVE_WARN);
}
@@ -291,13 +294,13 @@ child_stop(struct archive_read_filter *self, struct program_filter *state)
* Use select() to decide whether the child is ready for read or write.
*/
static ssize_t
-child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
+child_read(struct archive_read_filter *f, char *buf, size_t buf_len)
{
- struct program_filter *state = self->data;
+ struct program *program = f->data;
ssize_t ret, requested, avail;
const char *p;
#if defined(_WIN32) && !defined(__CYGWIN__)
- HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);
+ HANDLE handle = (HANDLE)_get_osfhandle(program->child_stdout);
#endif
requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
@@ -325,7 +328,7 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
break;
}
#endif
- ret = read(state->child_stdout, buf, requested);
+ ret = read(program->child_stdout, buf, requested);
} while (ret == -1 && errno == EINTR);
if (ret > 0)
@@ -333,44 +336,44 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
if (ret == 0 || (ret == -1 && errno == EPIPE))
/* Child has closed its output; reap the child
* and return the status. */
- return (child_stop(self, state));
+ return (child_stop(f, program));
if (ret == -1 && errno != EAGAIN)
return (-1);
- if (state->child_stdin == -1) {
+ if (program->child_stdin == -1) {
/* Block until child has some I/O ready. */
- __archive_check_child(state->child_stdin,
- state->child_stdout);
+ __archive_check_child(program->child_stdin,
+ program->child_stdout);
continue;
}
/* Get some more data from upstream. */
- p = __archive_read_filter_ahead(self->upstream, 1, &avail);
+ p = __archive_read_filter_ahead(f->upstream, 1, &avail);
if (p == NULL) {
- close(state->child_stdin);
- state->child_stdin = -1;
- fcntl(state->child_stdout, F_SETFL, 0);
+ close(program->child_stdin);
+ program->child_stdin = -1;
+ fcntl(program->child_stdout, F_SETFL, 0);
if (avail < 0)
return (avail);
continue;
}
do {
- ret = write(state->child_stdin, p, avail);
+ ret = write(program->child_stdin, p, avail);
} while (ret == -1 && errno == EINTR);
if (ret > 0) {
/* Consume whatever we managed to write. */
- __archive_read_filter_consume(self->upstream, ret);
+ __archive_read_filter_consume(f->upstream, ret);
} else if (ret == -1 && errno == EAGAIN) {
/* Block until child has some I/O ready. */
- __archive_check_child(state->child_stdin,
- state->child_stdout);
+ __archive_check_child(program->child_stdin,
+ program->child_stdout);
} else {
/* Write failed. */
- close(state->child_stdin);
- state->child_stdin = -1;
- fcntl(state->child_stdout, F_SETFL, 0);
+ close(program->child_stdin);
+ program->child_stdin = -1;
+ fcntl(program->child_stdout, F_SETFL, 0);
/* If it was a bad error, we're done; otherwise
* it was EPIPE or EOF, and we can still read
* from the child. */
@@ -387,9 +390,9 @@ program_reader_vtable = {
};
int
-__archive_read_program(struct archive_read_filter *self, const char *cmd)
+__archive_read_program(struct archive_read_filter *f, const char *cmd)
{
- struct program_filter *state;
+ struct program *program;
static const size_t out_buf_len = 65536;
char *out_buf;
const char *prefix = "Program: ";
@@ -397,70 +400,71 @@ __archive_read_program(struct archive_read_filter *self, const char *cmd)
size_t l;
l = strlen(prefix) + strlen(cmd) + 1;
- state = calloc(1, sizeof(*state));
+ program = calloc(1, sizeof(*program));
out_buf = malloc(out_buf_len);
- if (state == NULL || out_buf == NULL ||
- archive_string_ensure(&state->description, l) == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ if (program == NULL || out_buf == NULL ||
+ archive_string_ensure(&program->description, l) == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate input data");
- if (state != NULL) {
- archive_string_free(&state->description);
- free(state);
+ if (program != NULL) {
+ archive_string_free(&program->description);
+ free(program);
}
free(out_buf);
return (ARCHIVE_FATAL);
}
- archive_strcpy(&state->description, prefix);
- archive_strcat(&state->description, cmd);
+ archive_strcpy(&program->description, prefix);
+ archive_strcat(&program->description, cmd);
- self->code = ARCHIVE_FILTER_PROGRAM;
- self->name = state->description.s;
+ f->code = ARCHIVE_FILTER_PROGRAM;
+ f->name = program->description.s;
- state->out_buf = out_buf;
- state->out_buf_len = out_buf_len;
+ program->out_buf = out_buf;
+ program->out_buf_len = out_buf_len;
- ret = __archive_create_child(cmd, &state->child_stdin,
- &state->child_stdout, &state->child);
+ ret = __archive_create_child(cmd, &program->child_stdin,
+ &program->child_stdout, &program->child);
if (ret != ARCHIVE_OK) {
- free(state->out_buf);
- archive_string_free(&state->description);
- free(state);
- archive_set_error(&self->archive->archive, EINVAL,
+ free(program->out_buf);
+ archive_string_free(&program->description);
+ free(program);
+ archive_set_error(&f->archive->archive, EINVAL,
"Can't initialize filter; unable to run program \"%s\"",
cmd);
return (ARCHIVE_FATAL);
}
- self->data = state;
- self->vtable = &program_reader_vtable;
+ f->data = program;
+ f->vtable = &program_reader_vtable;
/* XXX Check that we can read at least one byte? */
return (ARCHIVE_OK);
}
static int
-program_bidder_init(struct archive_read_filter *self)
+program_bidder_init(struct archive_read_filter *f)
{
struct program_bidder *bidder_state;
+ int r;
- bidder_state = (struct program_bidder *)self->bidder->data;
- return (__archive_read_program(self, bidder_state->cmd));
+ bidder_state = (struct program_bidder *)f->bidder->data;
+ r = __archive_read_program(f, bidder_state->cmd);
+ f->name = bidder_state->description.s;
+ return (r);
}
static ssize_t
-program_filter_read(struct archive_read_filter *self, const void **buff)
+program_filter_read(struct archive_read_filter *f, const void **buff)
{
- struct program_filter *state;
+ struct program *program = f->data;
ssize_t bytes;
size_t total;
char *p;
- state = (struct program_filter *)self->data;
-
total = 0;
- p = state->out_buf;
- while (state->child_stdout != -1 && total < state->out_buf_len) {
- bytes = child_read(self, p, state->out_buf_len - total);
+ p = program->out_buf;
+ while (program->child_stdout != -1 && total < program->out_buf_len) {
+ bytes = child_read(f, p, program->out_buf_len - total);
if (bytes < 0)
/* No recovery is possible if we can no longer
* read from the child. */
@@ -472,23 +476,22 @@ program_filter_read(struct archive_read_filter *self, const void **buff)
p += bytes;
}
- *buff = state->out_buf;
+ *buff = program->out_buf;
return (total);
}
static int
-program_filter_close(struct archive_read_filter *self)
+program_filter_close(struct archive_read_filter *f)
{
- struct program_filter *state;
+ struct program *program = f->data;
int e;
- state = (struct program_filter *)self->data;
- e = child_stop(self, state);
+ e = child_stop(f, program);
/* Release our private data. */
- free(state->out_buf);
- archive_string_free(&state->description);
- free(state);
+ free(program->out_buf);
+ archive_string_free(&program->description);
+ free(program);
return (e);
}
diff --git a/libarchive/archive_read_support_filter_rpm.c b/libarchive/archive_read_support_filter_rpm.c
index 1213df6c9b17..01bd1bf7af5b 100644
--- a/libarchive/archive_read_support_filter_rpm.c
+++ b/libarchive/archive_read_support_filter_rpm.c
@@ -76,38 +76,38 @@ archive_read_support_filter_rpm(struct archive *_a)
}
static int
-rpm_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+rpm_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
- const unsigned char *b;
+ const unsigned char *p;
int bits_checked;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- b = __archive_read_filter_ahead(filter, 8, NULL);
- if (b == NULL)
+ p = __archive_read_filter_ahead(f, 8, NULL);
+ if (p == NULL)
return (0);
bits_checked = 0;
/*
* Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
*/
- if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
+ if (memcmp(p, "\xED\xAB\xEE\xDB", 4) != 0)
return (0);
bits_checked += 32;
/*
* Check major version.
*/
- if (b[4] != 3 && b[4] != 4)
+ if (p[4] != 3 && p[4] != 4)
return (0);
bits_checked += 8;
/*
* Check package type; binary or source.
*/
- if (b[6] != 0)
+ if (p[6] != 0)
return (0);
bits_checked += 8;
- if (b[7] != 0 && b[7] != 1)
+ if (p[7] != 0 && p[7] != 1)
return (0);
bits_checked += 8;
@@ -121,40 +121,40 @@ rpm_reader_vtable = {
};
static int
-rpm_bidder_init(struct archive_read_filter *self)
+rpm_bidder_init(struct archive_read_filter *f)
{
struct rpm *rpm;
- self->code = ARCHIVE_FILTER_RPM;
- self->name = "rpm";
+ f->code = ARCHIVE_FILTER_RPM;
+ f->name = "rpm";
rpm = calloc(1, sizeof(*rpm));
if (rpm == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for rpm");
return (ARCHIVE_FATAL);
}
- self->data = rpm;
+ f->data = rpm;
rpm->data_reached = 0;
- self->vtable = &rpm_reader_vtable;
+ f->vtable = &rpm_reader_vtable;
return (ARCHIVE_OK);
}
static ssize_t
-skip_padding(struct archive_read_filter *self)
+skip_padding(struct archive_read_filter *f)
{
const unsigned char *h;
ssize_t avail, count, r;
do {
- h = __archive_read_filter_ahead(self->upstream, 1, &avail);
+ h = __archive_read_filter_ahead(f->upstream, 1, &avail);
if (h == NULL)
return (ARCHIVE_FATAL);
for (count = 0; count < avail && *h++ == '\0'; count++)
;
- r = __archive_read_filter_consume(self->upstream, count);
+ r = __archive_read_filter_consume(f->upstream, count);
if (r < 0)
return (r);
} while (count == avail);
@@ -163,20 +163,20 @@ skip_padding(struct archive_read_filter *self)
}
static ssize_t
-skip_prologue(struct archive_read_filter *self)
+skip_prologue(struct archive_read_filter *f)
{
const unsigned char *h;
ssize_t r;
int header, seen_header = 0;
/* Skip lead size. */
- r = __archive_read_filter_consume(self->upstream, RPM_LEAD_SIZE);
+ r = __archive_read_filter_consume(f->upstream, RPM_LEAD_SIZE);
if (r < 0)
return (r);
do {
/* Read header intro. */
- h = __archive_read_filter_ahead(self->upstream,
+ h = __archive_read_filter_ahead(f->upstream,
RPM_MIN_HEAD_SIZE, NULL);
if (h == NULL)
return (ARCHIVE_FATAL);
@@ -193,13 +193,13 @@ skip_prologue(struct archive_read_filter *self)
length = RPM_MIN_HEAD_SIZE + section * 16 + bytes;
/* Skip header. */
- r = __archive_read_filter_consume(self->upstream,
+ r = __archive_read_filter_consume(f->upstream,
length);
if (r < 0)
return (r);
/* Skip padding. */
- r = skip_padding(self);
+ r = skip_padding(f);
if (r != ARCHIVE_OK)
return (r);
}
@@ -208,7 +208,7 @@ skip_prologue(struct archive_read_filter *self)
/* At least one header must have been encountered. */
if (!seen_header) {
archive_set_error(
- &self->archive->archive,
+ &f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Unrecognized rpm header");
return (ARCHIVE_FATAL);
@@ -218,33 +218,30 @@ skip_prologue(struct archive_read_filter *self)
}
static ssize_t
-rpm_filter_read(struct archive_read_filter *self, const void **buff)
+rpm_filter_read(struct archive_read_filter *f, const void **buff)
{
- struct rpm *rpm;
+ struct rpm *rpm = f->data;
ssize_t r;
- rpm = (struct rpm *)self->data;
-
if (!rpm->data_reached) {
- r = skip_prologue(self);
+ r = skip_prologue(f);
if (r != ARCHIVE_OK)
return (r);
rpm->data_reached = 1;
}
- *buff = __archive_read_filter_ahead(self->upstream, 1, &r);
+ *buff = __archive_read_filter_ahead(f->upstream, 1, &r);
if (r > 0)
- __archive_read_filter_consume(self->upstream, r);
+ __archive_read_filter_consume(f->upstream, r);
return r;
}
static int
-rpm_filter_close(struct archive_read_filter *self)
+rpm_filter_close(struct archive_read_filter *f)
{
- struct rpm *rpm;
+ struct rpm *rpm = f->data;
- rpm = (struct rpm *)self->data;
free(rpm);
return (ARCHIVE_OK);
diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c
index 714c7e836170..c0f6f48ed413 100644
--- a/libarchive/archive_read_support_filter_uu.c
+++ b/libarchive/archive_read_support_filter_uu.c
@@ -43,7 +43,7 @@
/* Maximum lookahead during bid phase */
#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
-struct uudecode {
+struct uu {
int64_t total;
unsigned char *in_buff;
#define IN_BUFF_SIZE (1024)
@@ -63,7 +63,7 @@ struct uudecode {
};
static int uudecode_bidder_bid(struct archive_read_filter_bidder *,
- struct archive_read_filter *filter);
+ struct archive_read_filter *f);
static int uudecode_bidder_init(struct archive_read_filter *);
static int uudecode_read_header(struct archive_read_filter *,
@@ -207,7 +207,7 @@ get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
}
static ssize_t
-bid_get_line(struct archive_read_filter *filter,
+bid_get_line(struct archive_read_filter *f,
const unsigned char **b, ssize_t *avail, ssize_t *ravail,
ssize_t *nl, size_t* nbytes_read)
{
@@ -235,12 +235,12 @@ bid_get_line(struct archive_read_filter *filter,
if (nbytes_req < (size_t)*ravail + 160)
nbytes_req <<= 1;
- *b = __archive_read_filter_ahead(filter, nbytes_req, avail);
+ *b = __archive_read_filter_ahead(f, nbytes_req, avail);
if (*b == NULL) {
if (*ravail >= *avail)
return (0);
/* Reading bytes reaches the end of a stream. */
- *b = __archive_read_filter_ahead(filter, *avail, avail);
+ *b = __archive_read_filter_ahead(f, *avail, avail);
quit = 1;
}
*nbytes_read = *avail;
@@ -258,42 +258,42 @@ bid_get_line(struct archive_read_filter *filter,
#define UUDECODE(c) (((c) - 0x20) & 0x3f)
static int
-uudecode_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+uudecode_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
- const unsigned char *b;
+ const unsigned char *p;
ssize_t avail, ravail;
ssize_t len, nl;
int l;
int firstline;
size_t nbytes_read;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- b = __archive_read_filter_ahead(filter, 1, &avail);
- if (b == NULL)
+ p = __archive_read_filter_ahead(f, 1, &avail);
+ if (p == NULL)
return (0);
firstline = 20;
ravail = avail;
nbytes_read = avail;
for (;;) {
- len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read);
if (len < 0 || nl == 0)
return (0); /* No match found. */
- if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+ if (len - nl >= 11 && memcmp(p, "begin ", 6) == 0)
l = 6;
- else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
+ else if (len -nl >= 18 && memcmp(p, "begin-base64 ", 13) == 0)
l = 13;
else
l = 0;
- if (l > 0 && (b[l] < '0' || b[l] > '7' ||
- b[l+1] < '0' || b[l+1] > '7' ||
- b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
+ if (l > 0 && (p[l] < '0' || p[l] > '7' ||
+ p[l+1] < '0' || p[l+1] > '7' ||
+ p[l+2] < '0' || p[l+2] > '7' || p[l+3] != ' '))
l = 0;
- b += len;
+ p += len;
avail -= len;
if (l)
break;
@@ -305,51 +305,51 @@ uudecode_bidder_bid(struct archive_read_filter_bidder *self,
}
if (!avail)
return (0);
- len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ len = bid_get_line(f, &p, &avail, &ravail, &nl, &nbytes_read);
if (len < 0 || nl == 0)
return (0);/* There are non-ascii characters. */
avail -= len;
if (l == 6) {
/* "begin " */
- if (!uuchar[*b])
+ if (!uuchar[*p])
return (0);
/* Get a length of decoded bytes. */
- l = UUDECODE(*b++); len--;
+ l = UUDECODE(*p++); len--;
if (l > 45)
/* Normally, maximum length is 45(character 'M'). */
return (0);
if (l > len - nl)
return (0); /* Line too short. */
while (l) {
- if (!uuchar[*b++])
+ if (!uuchar[*p++])
return (0);
--len;
--l;
}
if (len-nl == 1 &&
- (uuchar[*b] || /* Check sum. */
- (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
- ++b;
+ (uuchar[*p] || /* Check sum. */
+ (*p >= 'a' && *p <= 'z'))) {/* Padding data(MINIX). */
+ ++p;
--len;
}
- b += nl;
- if (avail && uuchar[*b])
+ p += nl;
+ if (avail && uuchar[*p])
return (firstline+30);
} else if (l == 13) {
/* "begin-base64 " */
while (len-nl > 0) {
- if (!base64[*b++])
+ if (!base64[*p++])
return (0);
--len;
}
- b += nl;
+ p += nl;
- if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
+ if (avail >= 5 && memcmp(p, "====\n", 5) == 0)
return (firstline+40);
- if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
+ if (avail >= 6 && memcmp(p, "====\r\n", 6) == 0)
return (firstline+40);
- if (avail > 0 && base64[*b])
+ if (avail > 0 && base64[*p])
return (firstline+30);
}
@@ -364,46 +364,46 @@ uudecode_reader_vtable = {
};
static int
-uudecode_bidder_init(struct archive_read_filter *self)
+uudecode_bidder_init(struct archive_read_filter *f)
{
- struct uudecode *uudecode;
+ struct uu *uu;
void *out_buff;
void *in_buff;
- self->code = ARCHIVE_FILTER_UU;
- self->name = "uu";
+ f->code = ARCHIVE_FILTER_UU;
+ f->name = "uu";
- uudecode = calloc(1, sizeof(*uudecode));
+ uu = calloc(1, sizeof(*uu));
out_buff = malloc(OUT_BUFF_SIZE);
in_buff = malloc(IN_BUFF_SIZE);
- if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ if (uu == NULL || out_buff == NULL || in_buff == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for uudecode");
- free(uudecode);
+ free(uu);
free(out_buff);
free(in_buff);
return (ARCHIVE_FATAL);
}
- self->data = uudecode;
- uudecode->in_buff = in_buff;
- uudecode->in_cnt = 0;
- uudecode->in_allocated = IN_BUFF_SIZE;
- uudecode->out_buff = out_buff;
- uudecode->state = ST_FIND_HEAD;
- uudecode->mode_set = 0;
- uudecode->name = NULL;
- self->vtable = &uudecode_reader_vtable;
+ f->data = uu;
+ uu->in_buff = in_buff;
+ uu->in_cnt = 0;
+ uu->in_allocated = IN_BUFF_SIZE;
+ uu->out_buff = out_buff;
+ uu->state = ST_FIND_HEAD;
+ uu->mode_set = 0;
+ uu->name = NULL;
+ f->vtable = &uudecode_reader_vtable;
return (ARCHIVE_OK);
}
static int
-ensure_in_buff_size(struct archive_read_filter *self,
- struct uudecode *uudecode, size_t size)
+ensure_in_buff_size(struct archive_read_filter *f,
+ struct uu *uu, size_t size)
{
- if (size > uudecode->in_allocated) {
+ if (size > uu->in_allocated) {
unsigned char *ptr;
size_t newsize;
@@ -411,7 +411,7 @@ ensure_in_buff_size(struct archive_read_filter *self,
* Calculate a new buffer size for in_buff.
* Increase its value until it is enough for our needs.
*/
- newsize = uudecode->in_allocated;
+ newsize = uu->in_allocated;
do {
if (newsize < IN_BUFF_SIZE*32)
newsize <<= 1;
@@ -421,42 +421,40 @@ ensure_in_buff_size(struct archive_read_filter *self,
/* Allocate the new buffer. */
ptr = malloc(newsize);
if (ptr == NULL) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ENOMEM,
"Can't allocate data for uudecode");
return (ARCHIVE_FATAL);
}
/* Move the remaining data in in_buff into the new buffer. */
- if (uudecode->in_cnt)
- memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
+ if (uu->in_cnt)
+ memmove(ptr, uu->in_buff, uu->in_cnt);
/* Replace in_buff with the new buffer. */
- free(uudecode->in_buff);
- uudecode->in_buff = ptr;
- uudecode->in_allocated = newsize;
+ free(uu->in_buff);
+ uu->in_buff = ptr;
+ uu->in_allocated = newsize;
}
return (ARCHIVE_OK);
}
static int
-uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+uudecode_read_header(struct archive_read_filter *f, struct archive_entry *entry)
{
+ struct uu *uu = f->data;
- struct uudecode *uudecode;
- uudecode = (struct uudecode *)self->data;
+ if (uu->mode_set != 0)
+ archive_entry_set_mode(entry, S_IFREG | uu->mode);
- if (uudecode->mode_set != 0)
- archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
-
- if (uudecode->name != NULL)
- archive_entry_set_pathname(entry, uudecode->name);
+ if (uu->name != NULL)
+ archive_entry_set_pathname(entry, uu->name);
return (ARCHIVE_OK);
}
static ssize_t
-uudecode_filter_read(struct archive_read_filter *self, const void **buff)
+uudecode_filter_read(struct archive_read_filter *f, const void **buff)
{
- struct uudecode *uudecode;
+ struct uu *uu = f->data;
const unsigned char *b, *d;
unsigned char *out;
ssize_t avail_in, ravail;
@@ -464,10 +462,8 @@ uudecode_filter_read(struct archive_read_filter *self, const void **buff)
ssize_t total;
ssize_t len, llen, nl, namelen;
- uudecode = (struct uudecode *)self->data;
-
read_more:
- d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ d = __archive_read_filter_ahead(f->upstream, 1, &avail_in);
if (d == NULL && avail_in < 0)
return (ARCHIVE_FATAL);
/* Quiet a code analyzer; make sure avail_in must be zero
@@ -476,17 +472,17 @@ read_more:
avail_in = 0;
used = 0;
total = 0;
- out = uudecode->out_buff;
+ out = uu->out_buff;
if (avail_in > 2 * UUENCODE_BID_MAX_READ)
avail_in = 2 * UUENCODE_BID_MAX_READ;
ravail = avail_in;
- if (uudecode->state == ST_IGNORE) {
+ if (uu->state == ST_IGNORE) {
used = avail_in;
goto finish;
}
- if (uudecode->in_cnt) {
- if (uudecode->in_cnt > UUENCODE_BID_MAX_READ) {
- archive_set_error(&self->archive->archive,
+ if (uu->in_cnt) {
+ if (uu->in_cnt > UUENCODE_BID_MAX_READ) {
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid format data");
return (ARCHIVE_FATAL);
@@ -495,14 +491,15 @@ read_more:
* If there is remaining data which is saved by
* a previous call, use it first.
*/
- if (ensure_in_buff_size(self, uudecode,
- avail_in + uudecode->in_cnt) != ARCHIVE_OK)
+ if (ensure_in_buff_size(f, uu,
+ avail_in + uu->in_cnt) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
- memcpy(uudecode->in_buff + uudecode->in_cnt,
- d, avail_in);
- d = uudecode->in_buff;
- avail_in += uudecode->in_cnt;
- uudecode->in_cnt = 0;
+ if (avail_in > 0)
+ memcpy(uu->in_buff + uu->in_cnt,
+ d, avail_in);
+ d = uu->in_buff;
+ avail_in += uu->in_cnt;
+ uu->in_cnt = 0;
}
for (;used < avail_in; d += llen, used += llen) {
ssize_t l, body;
@@ -511,28 +508,28 @@ read_more:
len = get_line(b, avail_in - used, &nl);
if (len < 0) {
/* Non-ascii character is found. */
- if (uudecode->state == ST_FIND_HEAD &&
- (uudecode->total > 0 || total > 0)) {
- uudecode->state = ST_IGNORE;
+ if (uu->state == ST_FIND_HEAD &&
+ (uu->total > 0 || total > 0)) {
+ uu->state = ST_IGNORE;
used = avail_in;
goto finish;
}
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
}
if (len > UUENCODE_BID_MAX_READ) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid format data");
return (ARCHIVE_FATAL);
}
llen = len;
- if ((nl == 0) && (uudecode->state != ST_UUEND)) {
+ if ((nl == 0) && (uu->state != ST_UUEND)) {
if (total == 0 && ravail <= 0) {
/* There is nothing more to read, fail */
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Missing format data");
return (ARCHIVE_FATAL);
@@ -541,28 +538,28 @@ read_more:
* Save remaining data which does not contain
* NL('\n','\r').
*/
- if (ensure_in_buff_size(self, uudecode, len)
+ if (ensure_in_buff_size(f, uu, len)
!= ARCHIVE_OK)
return (ARCHIVE_FATAL);
- if (uudecode->in_buff != b)
- memmove(uudecode->in_buff, b, len);
- uudecode->in_cnt = len;
+ if (uu->in_buff != b)
+ memmove(uu->in_buff, b, len);
+ uu->in_cnt = len;
if (total == 0) {
/* Do not return 0; it means end-of-file.
* We should try to read more bytes. */
__archive_read_filter_consume(
- self->upstream, ravail);
+ f->upstream, ravail);
goto read_more;
}
used += len;
break;
}
- switch (uudecode->state) {
+ switch (uu->state) {
default:
case ST_FIND_HEAD:
/* Do not read more than UUENCODE_BID_MAX_READ bytes */
if (total + len >= UUENCODE_BID_MAX_READ) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid format data");
return (ARCHIVE_FATAL);
@@ -578,30 +575,30 @@ read_more:
b[l+1] >= '0' && b[l+1] <= '7' &&
b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
if (l == 6)
- uudecode->state = ST_READ_UU;
+ uu->state = ST_READ_UU;
else
- uudecode->state = ST_READ_BASE64;
- uudecode->mode = (mode_t)(
+ uu->state = ST_READ_BASE64;
+ uu->mode = (mode_t)(
((int)(b[l] - '0') * 64) +
((int)(b[l+1] - '0') * 8) +
(int)(b[l+2] - '0'));
- uudecode->mode_set = 1;
+ uu->mode_set = 1;
namelen = len - nl - 4 - l;
if (namelen > 1) {
- if (uudecode->name != NULL)
- free(uudecode->name);
- uudecode->name = malloc(namelen + 1);
- if (uudecode->name == NULL) {
+ if (uu->name != NULL)
+ free(uu->name);
+ uu->name = malloc(namelen + 1);
+ if (uu->name == NULL) {
archive_set_error(
- &self->archive->archive,
+ &f->archive->archive,
ENOMEM,
"Can't allocate data for uudecode");
return (ARCHIVE_FATAL);
}
- strncpy(uudecode->name,
+ strncpy(uu->name,
(const char *)(b + l + 4),
namelen);
- uudecode->name[namelen] = '\0';
+ uu->name[namelen] = '\0';
}
}
break;
@@ -610,7 +607,7 @@ read_more:
goto finish;
body = len - nl;
if (!uuchar[*b] || body <= 0) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
@@ -619,13 +616,13 @@ read_more:
l = UUDECODE(*b++);
body--;
if (l > body) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
}
if (l == 0) {
- uudecode->state = ST_UUEND;
+ uu->state = ST_UUEND;
break;
}
while (l > 0) {
@@ -654,7 +651,7 @@ read_more:
}
}
if (l) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
@@ -662,9 +659,9 @@ read_more:
break;
case ST_UUEND:
if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
- uudecode->state = ST_FIND_HEAD;
+ uu->state = ST_FIND_HEAD;
else {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
@@ -676,7 +673,7 @@ read_more:
l = len - nl;
if (l >= 3 && b[0] == '=' && b[1] == '=' &&
b[2] == '=') {
- uudecode->state = ST_FIND_HEAD;
+ uu->state = ST_FIND_HEAD;
break;
}
while (l > 0) {
@@ -709,7 +706,7 @@ read_more:
}
}
if (l && *b != '=') {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Insufficient compressed data");
return (ARCHIVE_FATAL);
@@ -720,23 +717,22 @@ read_more:
finish:
if (ravail < avail_in)
used -= avail_in - ravail;
- __archive_read_filter_consume(self->upstream, used);
+ __archive_read_filter_consume(f->upstream, used);
- *buff = uudecode->out_buff;
- uudecode->total += total;
+ *buff = uu->out_buff;
+ uu->total += total;
return (total);
}
static int
-uudecode_filter_close(struct archive_read_filter *self)
+uudecode_filter_close(struct archive_read_filter *f)
{
- struct uudecode *uudecode;
+ struct uu *uu = f->data;
- uudecode = (struct uudecode *)self->data;
- free(uudecode->in_buff);
- free(uudecode->out_buff);
- free(uudecode->name);
- free(uudecode);
+ free(uu->in_buff);
+ free(uu->out_buff);
+ free(uu->name);
+ free(uu);
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c
index 22cbb26cd091..7c8e0cb52e36 100644
--- a/libarchive/archive_read_support_filter_xz.c
+++ b/libarchive/archive_read_support_filter_xz.c
@@ -50,7 +50,7 @@
#if HAVE_LZMA_H && HAVE_LIBLZMA
-struct private_data {
+struct xz {
lzma_stream stream;
unsigned char *out_block;
size_t out_block_size;
@@ -197,14 +197,14 @@ archive_read_support_filter_lzip(struct archive *_a)
* Test whether we can handle this data.
*/
static int
-xz_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+xz_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *buffer;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- buffer = __archive_read_filter_ahead(filter, 6, NULL);
+ buffer = __archive_read_filter_ahead(f, 6, NULL);
if (buffer == NULL)
return (0);
@@ -230,17 +230,17 @@ xz_bidder_bid(struct archive_read_filter_bidder *self,
* they have other evidence (file name, command-line option) to go on.
*/
static int
-lzma_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+lzma_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *buffer;
uint32_t dicsize;
uint64_t uncompressed_size;
int bits_checked;
- (void)self; /* UNUSED */
+ (void)b; /* UNUSED */
- buffer = __archive_read_filter_ahead(filter, 14, NULL);
+ buffer = __archive_read_filter_ahead(f, 14, NULL);
if (buffer == NULL)
return (0);
@@ -336,13 +336,13 @@ lzma_bidder_bid(struct archive_read_filter_bidder *self,
}
static int
-lzip_has_member(struct archive_read_filter *filter)
+lzip_has_member(struct archive_read_filter *f)
{
const unsigned char *buffer;
int bits_checked;
int log2dic;
- buffer = __archive_read_filter_ahead(filter, 6, NULL);
+ buffer = __archive_read_filter_ahead(f, 6, NULL);
if (buffer == NULL)
return (0);
@@ -369,12 +369,12 @@ lzip_has_member(struct archive_read_filter *filter)
}
static int
-lzip_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+lzip_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
- (void)self; /* UNUSED */
- return (lzip_has_member(filter));
+ (void)b; /* UNUSED */
+ return (lzip_has_member(f));
}
#if HAVE_LZMA_H && HAVE_LIBLZMA
@@ -383,34 +383,34 @@ lzip_bidder_bid(struct archive_read_filter_bidder *self,
* liblzma 4.999.7 and later support both lzma and xz streams.
*/
static int
-xz_bidder_init(struct archive_read_filter *self)
+xz_bidder_init(struct archive_read_filter *f)
{
- self->code = ARCHIVE_FILTER_XZ;
- self->name = "xz";
- return (xz_lzma_bidder_init(self));
+ f->code = ARCHIVE_FILTER_XZ;
+ f->name = "xz";
+ return (xz_lzma_bidder_init(f));
}
static int
-lzma_bidder_init(struct archive_read_filter *self)
+lzma_bidder_init(struct archive_read_filter *f)
{
- self->code = ARCHIVE_FILTER_LZMA;
- self->name = "lzma";
- return (xz_lzma_bidder_init(self));
+ f->code = ARCHIVE_FILTER_LZMA;
+ f->name = "lzma";
+ return (xz_lzma_bidder_init(f));
}
static int
-lzip_bidder_init(struct archive_read_filter *self)
+lzip_bidder_init(struct archive_read_filter *f)
{
- self->code = ARCHIVE_FILTER_LZIP;
- self->name = "lzip";
- return (xz_lzma_bidder_init(self));
+ f->code = ARCHIVE_FILTER_LZIP;
+ f->name = "lzip";
+ return (xz_lzma_bidder_init(f));
}
/*
* Set an error code and choose an error message
*/
static void
-set_error(struct archive_read_filter *self, int ret)
+set_error(struct archive_read_filter *f, int ret)
{
switch (ret) {
@@ -418,36 +418,36 @@ set_error(struct archive_read_filter *self, int ret)
case LZMA_OK: /* Decompressor made some progress. */
break;
case LZMA_MEM_ERROR:
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Lzma library error: Cannot allocate memory");
break;
case LZMA_MEMLIMIT_ERROR:
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Lzma library error: Out of memory");
break;
case LZMA_FORMAT_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Lzma library error: format not recognized");
break;
case LZMA_OPTIONS_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Lzma library error: Invalid options");
break;
case LZMA_DATA_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Lzma library error: Corrupted input data");
break;
case LZMA_BUF_ERROR:
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Lzma library error: No progress is possible");
break;
default:
/* Return an error. */
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Lzma decompression failed: Unknown error");
break;
@@ -464,84 +464,83 @@ xz_lzma_reader_vtable = {
* Setup the callbacks.
*/
static int
-xz_lzma_bidder_init(struct archive_read_filter *self)
+xz_lzma_bidder_init(struct archive_read_filter *f)
{
static const size_t out_block_size = 64 * 1024;
void *out_block;
- struct private_data *state;
+ struct xz *xz;
int ret;
- state = calloc(1, sizeof(*state));
+ xz = calloc(1, sizeof(*xz));
out_block = malloc(out_block_size);
- if (state == NULL || out_block == NULL) {
- archive_set_error(&self->archive->archive, ENOMEM,
+ if (xz == NULL || out_block == NULL) {
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for xz decompression");
free(out_block);
- free(state);
+ free(xz);
return (ARCHIVE_FATAL);
}
- self->data = state;
- state->out_block_size = out_block_size;
- state->out_block = out_block;
- self->vtable = &xz_lzma_reader_vtable;
+ f->data = xz;
+ xz->out_block_size = out_block_size;
+ xz->out_block = out_block;
+ f->vtable = &xz_lzma_reader_vtable;
- state->stream.avail_in = 0;
+ xz->stream.avail_in = 0;
- state->stream.next_out = state->out_block;
- state->stream.avail_out = state->out_block_size;
+ xz->stream.next_out = xz->out_block;
+ xz->stream.avail_out = xz->out_block_size;
- state->crc32 = 0;
- if (self->code == ARCHIVE_FILTER_LZIP) {
+ xz->crc32 = 0;
+ if (f->code == ARCHIVE_FILTER_LZIP) {
/*
* We have to read a lzip header and use it to initialize
* compression library, thus we cannot initialize the
* library for lzip here.
*/
- state->in_stream = 0;
+ xz->in_stream = 0;
return (ARCHIVE_OK);
} else
- state->in_stream = 1;
+ xz->in_stream = 1;
/* Initialize compression library. */
- if (self->code == ARCHIVE_FILTER_XZ)
- ret = lzma_stream_decoder(&(state->stream),
+ if (f->code == ARCHIVE_FILTER_XZ)
+ ret = lzma_stream_decoder(&(xz->stream),
LZMA_MEMLIMIT,/* memlimit */
LZMA_CONCATENATED);
else
- ret = lzma_alone_decoder(&(state->stream),
+ ret = lzma_alone_decoder(&(xz->stream),
LZMA_MEMLIMIT);/* memlimit */
if (ret == LZMA_OK)
return (ARCHIVE_OK);
/* Library setup failed: Choose an error message and clean up. */
- set_error(self, ret);
+ set_error(f, ret);
- free(state->out_block);
- free(state);
- self->data = NULL;
- self->vtable = NULL;
+ free(xz->out_block);
+ free(xz);
+ f->data = NULL;
+ f->vtable = NULL;
return (ARCHIVE_FATAL);
}
static int
-lzip_init(struct archive_read_filter *self)
+lzip_init(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct xz *xz = f->data;
const unsigned char *h;
lzma_filter filters[2];
unsigned char props[5];
uint32_t dicsize;
int log2dic, ret;
- state = (struct private_data *)self->data;
- h = __archive_read_filter_ahead(self->upstream, 6, NULL);
+ h = __archive_read_filter_ahead(f->upstream, 6, NULL);
if (h == NULL)
return (ARCHIVE_FATAL);
/* Get a version number. */
- state->lzip_ver = h[4];
+ xz->lzip_ver = h[4];
/*
* Setup lzma property.
@@ -558,8 +557,8 @@ lzip_init(struct archive_read_filter *self)
archive_le32enc(props+1, dicsize);
/* Consume lzip header. */
- __archive_read_filter_consume(self->upstream, 6);
- state->member_in = 6;
+ __archive_read_filter_consume(f->upstream, 6);
+ xz->member_in = 6;
filters[0].id = LZMA_FILTER_LZMA1;
filters[0].options = NULL;
@@ -568,74 +567,73 @@ lzip_init(struct archive_read_filter *self)
ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));
if (ret != LZMA_OK) {
- set_error(self, ret);
+ set_error(f, ret);
return (ARCHIVE_FATAL);
}
- ret = lzma_raw_decoder(&(state->stream), filters);
+ ret = lzma_raw_decoder(&(xz->stream), filters);
free(filters[0].options);
if (ret != LZMA_OK) {
- set_error(self, ret);
+ set_error(f, ret);
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
static int
-lzip_tail(struct archive_read_filter *self)
+lzip_tail(struct archive_read_filter *f)
{
- struct private_data *state;
- const unsigned char *f;
+ struct xz *xz = f->data;
+ const unsigned char *p;
ssize_t avail_in;
int tail;
- state = (struct private_data *)self->data;
- if (state->lzip_ver == 0)
+ if (xz->lzip_ver == 0)
tail = 12;
else
tail = 20;
- f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);
- if (f == NULL && avail_in < 0)
+ p = __archive_read_filter_ahead(f->upstream, tail, &avail_in);
+ if (p == NULL && avail_in < 0)
return (ARCHIVE_FATAL);
- if (f == NULL || avail_in < tail) {
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ if (p == NULL || avail_in < tail) {
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: Remaining data is less bytes");
return (ARCHIVE_FAILED);
}
/* Check the crc32 value of the uncompressed data of the current
* member */
- if (state->crc32 != archive_le32dec(f)) {
+ if (xz->crc32 != archive_le32dec(p)) {
#ifndef DONT_FAIL_ON_CRC_ERROR
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: CRC32 error");
return (ARCHIVE_FAILED);
#endif
}
/* Check the uncompressed size of the current member */
- if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ if ((uint64_t)xz->member_out != archive_le64dec(p + 4)) {
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: Uncompressed size error");
return (ARCHIVE_FAILED);
}
/* Check the total size of the current member */
- if (state->lzip_ver == 1 &&
- (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {
- archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ if (xz->lzip_ver == 1 &&
+ (uint64_t)xz->member_in + tail != archive_le64dec(p + 12)) {
+ archive_set_error(&f->archive->archive, ARCHIVE_ERRNO_MISC,
"Lzip: Member size error");
return (ARCHIVE_FAILED);
}
- __archive_read_filter_consume(self->upstream, tail);
+ __archive_read_filter_consume(f->upstream, tail);
/* If current lzip data consists of multi member, try decompressing
* a next member. */
- if (lzip_has_member(self->upstream) != 0) {
- state->in_stream = 0;
- state->crc32 = 0;
- state->member_out = 0;
- state->member_in = 0;
- state->eof = 0;
+ if (lzip_has_member(f->upstream) != 0) {
+ xz->in_stream = 0;
+ xz->crc32 = 0;
+ xz->member_out = 0;
+ xz->member_in = 0;
+ xz->eof = 0;
}
return (ARCHIVE_OK);
}
@@ -644,82 +642,80 @@ lzip_tail(struct archive_read_filter *self)
* Return the next block of decompressed data.
*/
static ssize_t
-xz_filter_read(struct archive_read_filter *self, const void **p)
+xz_filter_read(struct archive_read_filter *f, const void **p)
{
- struct private_data *state;
+ struct xz *xz = f->data;
size_t decompressed;
ssize_t avail_in;
int64_t member_in;
int ret;
- state = (struct private_data *)self->data;
-
redo:
/* Empty our output buffer. */
- state->stream.next_out = state->out_block;
- state->stream.avail_out = state->out_block_size;
- member_in = state->member_in;
+ xz->stream.next_out = xz->out_block;
+ xz->stream.avail_out = xz->out_block_size;
+ member_in = xz->member_in;
/* Try to fill the output buffer. */
- while (state->stream.avail_out > 0 && !state->eof) {
- if (!state->in_stream) {
+ while (xz->stream.avail_out > 0 && !xz->eof) {
+ if (!xz->in_stream) {
/*
* Initialize liblzma for lzip
*/
- ret = lzip_init(self);
+ ret = lzip_init(f);
if (ret != ARCHIVE_OK)
return (ret);
- state->in_stream = 1;
+ xz->in_stream = 1;
}
- state->stream.next_in =
- __archive_read_filter_ahead(self->upstream, 1, &avail_in);
- if (state->stream.next_in == NULL && avail_in < 0) {
- archive_set_error(&self->archive->archive,
+ xz->stream.next_in =
+ __archive_read_filter_ahead(f->upstream, 1, &avail_in);
+ if (xz->stream.next_in == NULL && avail_in < 0) {
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"truncated input");
return (ARCHIVE_FATAL);
}
- state->stream.avail_in = avail_in;
+ xz->stream.avail_in = avail_in;
/* Decompress as much as we can in one pass. */
- ret = lzma_code(&(state->stream),
- (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
+ ret = lzma_code(&(xz->stream),
+ (xz->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
switch (ret) {
case LZMA_STREAM_END: /* Found end of stream. */
- state->eof = 1;
+ xz->eof = 1;
/* FALL THROUGH */
case LZMA_OK: /* Decompressor made some progress. */
- __archive_read_filter_consume(self->upstream,
- avail_in - state->stream.avail_in);
- state->member_in +=
- avail_in - state->stream.avail_in;
+ __archive_read_filter_consume(f->upstream,
+ avail_in - xz->stream.avail_in);
+ xz->member_in +=
+ avail_in - xz->stream.avail_in;
break;
default:
- set_error(self, ret);
+ set_error(f, ret);
return (ARCHIVE_FATAL);
}
}
- decompressed = state->stream.next_out - state->out_block;
- state->member_out += decompressed;
+ decompressed = xz->stream.next_out - xz->out_block;
+ xz->member_out += decompressed;
if (decompressed == 0) {
- if (member_in != state->member_in &&
- self->code == ARCHIVE_FILTER_LZIP &&
- state->eof) {
- ret = lzip_tail(self);
+ if (member_in != xz->member_in &&
+ f->code == ARCHIVE_FILTER_LZIP &&
+ xz->eof) {
+ ret = lzip_tail(f);
if (ret != ARCHIVE_OK)
return (ret);
- if (!state->eof)
+ if (!xz->eof)
goto redo;
}
*p = NULL;
} else {
- *p = state->out_block;
- if (self->code == ARCHIVE_FILTER_LZIP) {
- state->crc32 = lzma_crc32(state->out_block,
- decompressed, state->crc32);
- if (state->eof) {
- ret = lzip_tail(self);
+ *p = xz->out_block;
+ if (f->code == ARCHIVE_FILTER_LZIP) {
+ xz->crc32 = lzma_crc32(xz->out_block,
+ decompressed, xz->crc32);
+ if (xz->eof) {
+ ret = lzip_tail(f);
if (ret != ARCHIVE_OK)
return (ret);
}
@@ -732,14 +728,13 @@ xz_filter_read(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-xz_filter_close(struct archive_read_filter *self)
+xz_filter_close(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct xz *xz = f->data;
- state = (struct private_data *)self->data;
- lzma_end(&(state->stream));
- free(state->out_block);
- free(state);
+ lzma_end(&(xz->stream));
+ free(xz->out_block);
+ free(xz);
return (ARCHIVE_OK);
}
@@ -753,44 +748,44 @@ xz_filter_close(struct archive_read_filter *self)
*
*/
static int
-lzma_bidder_init(struct archive_read_filter *self)
+lzma_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "lzma -d -qq");
+ r = __archive_read_program(f, "lzma -d -qq");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_LZMA;
- self->name = "lzma";
+ f->code = ARCHIVE_FILTER_LZMA;
+ f->name = "lzma";
return (r);
}
static int
-xz_bidder_init(struct archive_read_filter *self)
+xz_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "xz -d -qq");
+ r = __archive_read_program(f, "xz -d -qq");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_XZ;
- self->name = "xz";
+ f->code = ARCHIVE_FILTER_XZ;
+ f->name = "xz";
return (r);
}
static int
-lzip_bidder_init(struct archive_read_filter *self)
+lzip_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "lzip -d -q");
+ r = __archive_read_program(f, "lzip -d -q");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_LZIP;
- self->name = "lzip";
+ f->code = ARCHIVE_FILTER_LZIP;
+ f->name = "lzip";
return (r);
}
diff --git a/libarchive/archive_read_support_filter_zstd.c b/libarchive/archive_read_support_filter_zstd.c
index 100c2c10ddb1..12eac04ca3aa 100644
--- a/libarchive/archive_read_support_filter_zstd.c
+++ b/libarchive/archive_read_support_filter_zstd.c
@@ -54,7 +54,7 @@
#if HAVE_ZSTD_H && HAVE_LIBZSTD
-struct private_data {
+struct zstd {
ZSTD_DStream *dstream;
unsigned char *out_block;
size_t out_block_size;
@@ -104,8 +104,8 @@ archive_read_support_filter_zstd(struct archive *_a)
* Test whether we can handle this data.
*/
static int
-zstd_bidder_bid(struct archive_read_filter_bidder *self,
- struct archive_read_filter *filter)
+zstd_bidder_bid(struct archive_read_filter_bidder *b,
+ struct archive_read_filter *f)
{
const unsigned char *buffer;
ssize_t avail;
@@ -132,9 +132,9 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self,
const uint32_t zstd_magic_skippable_start = 0x184D2A50;
const uint32_t zstd_magic_skippable_mask = 0xFFFFFFF0;
- (void) self; /* UNUSED */
+ (void) b; /* UNUSED */
- buffer = __archive_read_filter_ahead(filter, min_zstd_frame_size,
+ buffer = __archive_read_filter_ahead(f, min_zstd_frame_size,
&avail);
if (buffer == NULL)
return (0);
@@ -151,7 +151,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self,
/* Ensure that we can read another 4 bytes. */
if (offset_in_buffer + 4 > (size_t)avail) {
- buffer = __archive_read_filter_ahead(filter,
+ buffer = __archive_read_filter_ahead(f,
offset_in_buffer + 4, &avail);
if (buffer == NULL)
return (0);
@@ -178,7 +178,7 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self,
if (min > max_lookahead)
return (0);
- buffer = __archive_read_filter_ahead(filter,
+ buffer = __archive_read_filter_ahead(f,
min, &avail);
if (buffer == NULL)
return (0);
@@ -206,16 +206,16 @@ zstd_bidder_bid(struct archive_read_filter_bidder *self,
* in case that's available.
*/
static int
-zstd_bidder_init(struct archive_read_filter *self)
+zstd_bidder_init(struct archive_read_filter *f)
{
int r;
- r = __archive_read_program(self, "zstd -d -qq");
+ r = __archive_read_program(f, "zstd -d -qq");
/* Note: We set the format here even if __archive_read_program()
* above fails. We do, after all, know what the format is
* even if we weren't able to read it. */
- self->code = ARCHIVE_FILTER_ZSTD;
- self->name = "zstd";
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->name = "zstd";
return (r);
}
@@ -231,80 +231,78 @@ zstd_reader_vtable = {
* Initialize the filter object
*/
static int
-zstd_bidder_init(struct archive_read_filter *self)
+zstd_bidder_init(struct archive_read_filter *f)
{
- struct private_data *state;
+ struct zstd *zstd;
size_t out_block_size = ZSTD_DStreamOutSize();
void *out_block;
ZSTD_DStream *dstream;
- self->code = ARCHIVE_FILTER_ZSTD;
- self->name = "zstd";
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->name = "zstd";
- state = calloc(1, sizeof(*state));
+ zstd = calloc(1, sizeof(*zstd));
out_block = malloc(out_block_size);
dstream = ZSTD_createDStream();
- if (state == NULL || out_block == NULL || dstream == NULL) {
+ if (zstd == NULL || out_block == NULL || dstream == NULL) {
free(out_block);
- free(state);
+ free(zstd);
ZSTD_freeDStream(dstream); /* supports free on NULL */
- archive_set_error(&self->archive->archive, ENOMEM,
+ archive_set_error(&f->archive->archive, ENOMEM,
"Can't allocate data for zstd decompression");
return (ARCHIVE_FATAL);
}
- self->data = state;
+ f->data = zstd;
- state->out_block_size = out_block_size;
- state->out_block = out_block;
- state->dstream = dstream;
- self->vtable = &zstd_reader_vtable;
+ zstd->out_block_size = out_block_size;
+ zstd->out_block = out_block;
+ zstd->dstream = dstream;
+ f->vtable = &zstd_reader_vtable;
- state->eof = 0;
- state->in_frame = 0;
+ zstd->eof = 0;
+ zstd->in_frame = 0;
return (ARCHIVE_OK);
}
static ssize_t
-zstd_filter_read(struct archive_read_filter *self, const void **p)
+zstd_filter_read(struct archive_read_filter *f, const void **p)
{
- struct private_data *state;
+ struct zstd *zstd = f->data;
size_t decompressed;
ssize_t avail_in;
ZSTD_outBuffer out;
ZSTD_inBuffer in;
size_t ret;
- state = (struct private_data *)self->data;
-
- out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
+ out = (ZSTD_outBuffer) { zstd->out_block, zstd->out_block_size, 0 };
/* Try to fill the output buffer. */
- while (out.pos < out.size && !state->eof) {
- if (!state->in_frame) {
- ret = ZSTD_initDStream(state->dstream);
+ while (out.pos < out.size && !zstd->eof) {
+ if (!zstd->in_frame) {
+ ret = ZSTD_initDStream(zstd->dstream);
if (ZSTD_isError(ret)) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Error initializing zstd decompressor: %s",
ZSTD_getErrorName(ret));
return (ARCHIVE_FATAL);
}
}
- in.src = __archive_read_filter_ahead(self->upstream, 1,
+ in.src = __archive_read_filter_ahead(f->upstream, 1,
&avail_in);
if (avail_in < 0) {
return avail_in;
}
if (in.src == NULL && avail_in == 0) {
- if (!state->in_frame) {
+ if (!zstd->in_frame) {
/* end of stream */
- state->eof = 1;
+ zstd->eof = 1;
break;
} else {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Truncated zstd input");
return (ARCHIVE_FATAL);
@@ -314,10 +312,10 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
in.pos = 0;
{
- ret = ZSTD_decompressStream(state->dstream, &out, &in);
+ ret = ZSTD_decompressStream(zstd->dstream, &out, &in);
if (ZSTD_isError(ret)) {
- archive_set_error(&self->archive->archive,
+ archive_set_error(&f->archive->archive,
ARCHIVE_ERRNO_MISC,
"Zstd decompression failed: %s",
ZSTD_getErrorName(ret));
@@ -325,10 +323,10 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
}
/* Decompressor made some progress */
- __archive_read_filter_consume(self->upstream, in.pos);
+ __archive_read_filter_consume(f->upstream, in.pos);
/* ret guaranteed to be > 0 if frame isn't done yet */
- state->in_frame = (ret != 0);
+ zstd->in_frame = (ret != 0);
}
}
@@ -336,7 +334,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
if (decompressed == 0)
*p = NULL;
else
- *p = state->out_block;
+ *p = zstd->out_block;
return (decompressed);
}
@@ -344,15 +342,13 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
* Clean up the decompressor.
*/
static int
-zstd_filter_close(struct archive_read_filter *self)
+zstd_filter_close(struct archive_read_filter *f)
{
- struct private_data *state;
-
- state = (struct private_data *)self->data;
+ struct zstd *zstd = f->data;
- ZSTD_freeDStream(state->dstream);
- free(state->out_block);
- free(state);
+ ZSTD_freeDStream(zstd->dstream);
+ free(zstd->out_block);
+ free(zstd);
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
index 0b476edd6691..9c9f9a0eefdf 100644
--- a/libarchive/archive_read_support_format_7zip.c
+++ b/libarchive/archive_read_support_format_7zip.c
@@ -53,6 +53,7 @@
#include "archive.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_ppmd7_private.h"
#include "archive_private.h"
#include "archive_read_private.h"
@@ -72,12 +73,12 @@
/*
* PE format
*/
-#define PE_DOS_HDR_LEN 0x40
+#define PE_DOS_HDR_LEN 0x40
#define PE_DOS_HDR_ELFANEW_OFFSET 0x3c
-#define PE_COFF_HDR_LEN 0x18
+#define PE_COFF_HDR_LEN 0x18
#define PE_COFF_HDR_SEC_CNT_OFFSET 0x6
#define PE_COFF_HDR_OPT_SZ_OFFSET 0x14
-#define PE_SEC_HDR_LEN 0x28
+#define PE_SEC_HDR_LEN 0x28
#define PE_SEC_HDR_RAW_ADDR_OFFSET 0x14
#define PE_SEC_HDR_RAW_SZ_OFFSET 0x10
@@ -98,8 +99,8 @@
#define _7Z_BZ2 0x040202
#define _7Z_PPMD 0x030401
#define _7Z_DELTA 0x03
-#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */
-#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
+#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */
+#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
#define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */
@@ -173,50 +174,48 @@ struct _7z_digests {
};
struct _7z_folder {
- uint64_t numCoders;
+ size_t numCoders;
struct _7z_coder {
- uint64_t codec;
- uint64_t numInStreams;
- uint64_t numOutStreams;
- uint64_t propertiesSize;
+ int64_t codec;
+ size_t numInStreams;
+ size_t numOutStreams;
+ size_t propertiesSize;
unsigned char *properties;
} *coders;
- uint64_t numBindPairs;
+ size_t numBindPairs;
struct {
- uint64_t inIndex;
- uint64_t outIndex;
+ size_t inIndex;
+ size_t outIndex;
} *bindPairs;
- uint64_t numPackedStreams;
- uint64_t *packedStreams;
- uint64_t numInStreams;
- uint64_t numOutStreams;
- uint64_t *unPackSize;
+ size_t numPackedStreams;
+ size_t numInStreams;
+ size_t numOutStreams;
+ int64_t *unPackSize;
unsigned char digest_defined;
uint32_t digest;
- uint64_t numUnpackStreams;
- uint32_t packIndex;
+ size_t numUnpackStreams;
+ size_t packIndex;
/* Unoperated bytes. */
- uint64_t skipped_bytes;
+ int64_t skipped_bytes;
};
struct _7z_coders_info {
- uint64_t numFolders;
+ size_t numFolders;
struct _7z_folder *folders;
- uint64_t dataStreamIndex;
};
struct _7z_pack_info {
- uint64_t pos;
- uint64_t numPackStreams;
- uint64_t *sizes;
+ int64_t pos;
+ size_t numPackStreams;
+ int64_t *sizes;
struct _7z_digests digest;
/* Calculated from pos and numPackStreams. */
- uint64_t *positions;
+ int64_t *positions;
};
struct _7z_substream_info {
size_t unpack_streams;
- uint64_t *unpackSizes;
+ int64_t *unpackSizes;
unsigned char *digestsDefined;
uint32_t *digests;
};
@@ -228,8 +227,6 @@ struct _7z_stream_info {
};
struct _7z_header_info {
- uint64_t dataIndex;
-
unsigned char *emptyStreamBools;
unsigned char *emptyFileBools;
unsigned char *antiBools;
@@ -242,8 +239,8 @@ struct _7zip_entry {
#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
const wchar_t *wname;
#endif
- uint32_t folderIndex;
- uint32_t ssIndex;
+ size_t folderIndex;
+ size_t ssIndex;
unsigned flg;
#define MTIME_IS_SET (1<<0)
#define ATIME_IS_SET (1<<1)
@@ -267,24 +264,24 @@ struct _7zip {
int header_is_being_read;
int header_is_encoded;
- uint64_t header_bytes_remaining;
+ int64_t header_bytes_remaining;
unsigned long header_crc32;
/* Header offset to check that reading points of the file contents
* will not exceed the header. */
- uint64_t header_offset;
+ int64_t header_offset;
/* Base offset of the archive file for a seek in case reading SFX. */
- uint64_t seek_base;
+ int64_t seek_base;
/* List of entries */
size_t entries_remaining;
- uint64_t numFiles;
+ size_t numFiles;
struct _7zip_entry *entries;
struct _7zip_entry *entry;
unsigned char *entry_names;
/* entry_bytes_remaining is the number of bytes we expect. */
int64_t entry_offset;
- uint64_t entry_bytes_remaining;
+ int64_t entry_bytes_remaining;
/* Running CRC32 of the decompressed data */
unsigned long entry_crc32;
@@ -305,16 +302,16 @@ struct _7zip {
/*
* Decompressing control data.
*/
- unsigned folder_index;
- uint64_t folder_outbytes_remaining;
- unsigned pack_stream_index;
- unsigned pack_stream_remaining;
- uint64_t pack_stream_inbytes_remaining;
- size_t pack_stream_bytes_unconsumed;
+ size_t folder_index;
+ int64_t folder_outbytes_remaining;
+ size_t pack_stream_index;
+ size_t pack_stream_remaining;
+ int64_t pack_stream_inbytes_remaining;
+ int64_t pack_stream_bytes_unconsumed;
/* The codec information of a folder. */
- unsigned long codec;
- unsigned long codec2;
+ int64_t codec;
+ int64_t codec2;
/*
* Decompressor controllers.
@@ -364,7 +361,7 @@ struct _7zip {
uint32_t bcj_ip;
/* Decoding BCJ2 data. */
- size_t main_stream_bytes_remaining;
+ int64_t main_stream_bytes_remaining;
unsigned char *sub_stream_buff[3];
size_t sub_stream_size[3];
size_t sub_stream_bytes_remaining[3];
@@ -381,13 +378,11 @@ struct _7zip {
uint8_t bcj2_prevByte;
uint32_t bcj2_range;
uint32_t bcj2_code;
- uint64_t bcj2_outPos;
+ size_t bcj2_outPos;
/* Filename character-set conversion data. */
struct archive_string_conv *sconv;
- char format_name[64];
-
/* Custom value that is non-zero if this archive contains encrypted entries. */
int has_encrypted_entries;
};
@@ -395,31 +390,14 @@ struct _7zip {
/* Maximum entry size. This limitation prevents reading intentional
* corrupted 7-zip files on assuming there are not so many entries in
* the files. */
-#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000)
+#define UMAX_ENTRY ARCHIVE_LITERAL_LL(100000000)
-/*
- * Files without unpack streams must be described by the EmptyStream bitmap,
- * which consumes one bit for every file entry in FilesInfo.
- */
-static int
-files_info_numfiles_is_sane(const struct _7zip *zip)
-{
- uint64_t empty_stream_map_bytes;
-
- if (zip->numFiles > UMAX_ENTRY)
- return (0);
- if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries))
- return (0);
-
- if (zip->numFiles <= zip->si.ss.unpack_streams)
- return (1);
-
- empty_stream_map_bytes = (zip->numFiles + 7) / 8;
- return (empty_stream_map_bytes <= zip->header_bytes_remaining);
-}
+/* Don't try to read more than 16 MB at a time */
+#define MAX_READ 16 * 1024 * 1024
+static size_t align_size(size_t);
static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
-static int archive_read_support_format_7zip_capabilities(struct archive_read *a);
+static int archive_read_support_format_7zip_capabilities(struct archive_read *);
static int archive_read_format_7zip_bid(struct archive_read *, int);
static int archive_read_format_7zip_cleanup(struct archive_read *);
static int archive_read_format_7zip_read_data(struct archive_read *,
@@ -427,14 +405,15 @@ static int archive_read_format_7zip_read_data(struct archive_read *,
static int archive_read_format_7zip_read_data_skip(struct archive_read *);
static int archive_read_format_7zip_read_header(struct archive_read *,
struct archive_entry *);
-static int check_7zip_header_in_sfx(const unsigned char *);
-static int decode_codec_id(const unsigned char *, size_t, uint64_t *);
+static size_t check_7zip_header_in_sfx(const unsigned char *);
+static int decode_codec_id(const unsigned char *, size_t, int64_t *);
static int decode_encoded_header_info(struct archive_read *,
struct _7z_stream_info *);
static int decompress(struct archive_read *, struct _7zip *,
void *, size_t *, const void *, size_t *);
static ssize_t extract_pack_stream(struct archive_read *, size_t);
-static uint64_t folder_uncompressed_size(struct _7z_folder *);
+static int files_info_numfiles_is_sane(const struct _7zip *);
+static int64_t folder_uncompressed_size(struct _7z_folder *);
static void free_CodersInfo(struct _7z_coders_info *);
static void free_Digest(struct _7z_digests *);
static void free_Folder(struct _7z_folder *);
@@ -445,10 +424,11 @@ static void free_SubStreamsInfo(struct _7z_substream_info *);
static int free_decompression(struct archive_read *, struct _7zip *);
static ssize_t get_uncompressed_data(struct archive_read *, const void **,
size_t, size_t);
-static const unsigned char * header_bytes(struct archive_read *, size_t);
+static const unsigned char *header_bytes(struct archive_read *, size_t);
static int init_decompression(struct archive_read *, struct _7zip *,
const struct _7z_coder *, const struct _7z_coder *);
-static int parse_7zip_uint64(struct archive_read *, uint64_t *);
+static int parse_7zip_size(struct archive_read *, size_t *);
+static int parse_7zip_int64(struct archive_read *, int64_t *);
static int read_Bools(struct archive_read *, unsigned char *, size_t);
static int read_CodersInfo(struct archive_read *,
struct _7z_coders_info *);
@@ -462,13 +442,12 @@ static int read_StreamsInfo(struct archive_read *,
struct _7z_stream_info *);
static int read_SubStreamsInfo(struct archive_read *,
struct _7z_substream_info *, struct _7z_folder *, size_t);
-static int read_Times(struct archive_read *, struct _7z_header_info *,
- int);
-static void read_consume(struct archive_read *);
+static int read_Times(struct archive_read *, int);
+static int read_consume(struct archive_read *);
static ssize_t read_stream(struct archive_read *, const void **, size_t,
size_t);
static int seek_pack(struct archive_read *);
-static int64_t skip_stream(struct archive_read *, size_t);
+static int skip_stream(struct archive_read *, int64_t);
static int get_data_offset(struct archive_read *, int64_t *, int);
static int get_pe_sfx_offset(struct archive_read *, int64_t *);
static int get_elf_sfx_offset(struct archive_read *, int64_t *, int);
@@ -481,7 +460,7 @@ static size_t x86_Convert(struct _7zip *, uint8_t *, size_t);
static void arm_Init(struct _7zip *);
static size_t arm_Convert(struct _7zip *, uint8_t *, size_t);
static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t);
-static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
static size_t sparc_Convert(struct _7zip *, uint8_t *, size_t);
static size_t powerpc_Convert(struct _7zip *, uint8_t *, size_t);
static int64_t seek_compat(struct archive_read *, int64_t, int, int);
@@ -530,19 +509,18 @@ archive_read_support_format_7zip(struct archive *_a)
}
static int
-archive_read_support_format_7zip_capabilities(struct archive_read * a)
+archive_read_support_format_7zip_capabilities(struct archive_read *a)
{
(void)a; /* UNUSED */
return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
}
-
static int
-archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
+archive_read_format_7zip_has_encrypted_entries(struct archive_read *a)
{
- if (_a && _a->format) {
- struct _7zip * zip = (struct _7zip *)_a->format->data;
+ if (a && a->format) {
+ struct _7zip *zip = a->format->data;
if (zip) {
return zip->has_encrypted_entries;
}
@@ -601,8 +579,8 @@ get_data_offset(struct archive_read *a, int64_t *data_offset, int compat)
continue;
}
p = buff + offset;
- while (p + 32 < buff + bytes_avail) {
- int step = check_7zip_header_in_sfx(p);
+ while (buff + bytes_avail - p >= 32) {
+ size_t step = check_7zip_header_in_sfx(p);
if (step == 0) {
*data_offset = p - buff;
return (ARCHIVE_OK);
@@ -633,7 +611,7 @@ archive_read_format_7zip_bid(struct archive_read *a, int best_bid)
return (48);
}
-static int
+static size_t
check_7zip_header_in_sfx(const unsigned char *p)
{
switch (p[5]) {
@@ -798,7 +776,7 @@ get_elf_sfx_offset(struct archive_read *a, int64_t *sfx_offset, int compat)
e_shentsize = (*dec16)(h + 0x3A);
e_shnum = (*dec16)(h + 0x3C);
e_shstrndx = (*dec16)(h + 0x3E);
- if (e_shnum < e_shstrndx || e_shentsize < 0x28)
+ if (e_shnum <= e_shstrndx || e_shentsize < 0x28)
break;
} else {
@@ -806,7 +784,7 @@ get_elf_sfx_offset(struct archive_read *a, int64_t *sfx_offset, int compat)
e_shentsize = (*dec16)(h + 0x2E);
e_shnum = (*dec16)(h + 0x30);
e_shstrndx = (*dec16)(h + 0x32);
- if (e_shnum < e_shstrndx || e_shentsize < 0x18)
+ if (e_shnum <= e_shstrndx || e_shentsize < 0x18)
break;
}
@@ -860,7 +838,7 @@ get_elf_sfx_offset(struct archive_read *a, int64_t *sfx_offset, int compat)
return (ARCHIVE_FATAL);
}
size_t data_sym_offset = strtab_size;
- for (size_t offset = 0; offset < strtab_size - 6; offset++) {
+ for (size_t offset = 0; offset + 6 <= strtab_size; offset++) {
if (memcmp(h + offset, ".data\00", 6) == 0) {
data_sym_offset = offset;
break;
@@ -916,11 +894,10 @@ static int
archive_read_format_7zip_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
struct _7zip_entry *zip_entry;
int r, ret = ARCHIVE_OK;
struct _7z_folder *folder = 0;
- uint64_t fidx = 0;
/*
* It should be sufficient to call archive_read_next_header() for
@@ -934,8 +911,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
}
a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
- if (a->archive.archive_format_name == NULL)
- a->archive.archive_format_name = "7-Zip";
+ a->archive.archive_format_name = "7-Zip";
if (zip->entries == NULL) {
struct _7z_header_info header;
@@ -945,20 +921,20 @@ archive_read_format_7zip_read_header(struct archive_read *a,
free_Header(&header);
if (r != ARCHIVE_OK)
return (r);
- zip->entries_remaining = (size_t)zip->numFiles;
+ zip->entries_remaining = zip->numFiles;
zip->entry = zip->entries;
} else {
++zip->entry;
}
zip_entry = zip->entry;
- if (zip->entries_remaining <= 0 || zip_entry == NULL)
+ if (zip->entries_remaining == 0 || zip_entry == NULL)
return ARCHIVE_EOF;
--zip->entries_remaining;
zip->entry_offset = 0;
zip->end_of_entry = 0;
- zip->entry_crc32 = crc32(0, NULL, 0);
+ zip->entry_crc32 = 0;
/* Setup a string conversion for a filename. */
if (zip->sconv == NULL) {
@@ -972,9 +948,11 @@ archive_read_format_7zip_read_header(struct archive_read *a,
that is associated to the current 7zip entry. If the folder
has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
Hence the entry must also be encrypted. */
- if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
+ if (zip_entry->folderIndex < zip->si.ci.numFolders) {
+ size_t fidx = 0;
+
folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
- for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
+ for (fidx = 0; fidx < folder->numCoders; fidx++) {
switch(folder->coders[fidx].codec) {
case _7Z_CRYPTO_MAIN_ZIP:
case _7Z_CRYPTO_RAR_29:
@@ -987,13 +965,6 @@ archive_read_format_7zip_read_header(struct archive_read *a,
}
}
- /* Now that we've checked for encryption, if there were still no
- * encrypted entries found we can say for sure that there are none.
- */
- if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
- zip->has_encrypted_entries = 0;
- }
-
if (archive_entry_copy_pathname_l(entry,
(const char *)zip_entry->utf16name,
zip_entry->name_len, zip->sconv) != 0) {
@@ -1021,7 +992,7 @@ archive_read_format_7zip_read_header(struct archive_read *a,
if (zip_entry->flg & ATIME_IS_SET)
archive_entry_set_atime(entry, zip_entry->atime,
zip_entry->atime_ns);
- if (zip_entry->ssIndex != (uint32_t)-1) {
+ if (zip_entry->ssIndex != (size_t)-1) {
zip->entry_bytes_remaining =
zip->si.ss.unpackSizes[zip_entry->ssIndex];
archive_entry_set_size(entry, zip->entry_bytes_remaining);
@@ -1035,8 +1006,8 @@ archive_read_format_7zip_read_header(struct archive_read *a,
if (zip_entry->attr & supported_attrs) {
char buf[sizeof(",rdonly,hidden,system")];
- char *fflags[3] = { "", "", "" };
- char **flag = fflags;
+ const char *fflags[3] = { "", "", "" };
+ const char **flag = fflags;
if (zip_entry->attr & FILE_ATTRIBUTE_READONLY)
*flag++ = ",rdonly";
@@ -1117,10 +1088,6 @@ archive_read_format_7zip_read_header(struct archive_read *a,
archive_entry_set_size(entry, 0);
}
- /* Set up a more descriptive format name. */
- snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
- a->archive.archive_format_name = zip->format_name;
-
return (ret);
}
@@ -1128,12 +1095,10 @@ static int
archive_read_format_7zip_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format->data;
ssize_t bytes;
int ret = ARCHIVE_OK;
- zip = (struct _7zip *)(a->format->data);
-
if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
zip->has_encrypted_entries = 0;
}
@@ -1151,8 +1116,8 @@ archive_read_format_7zip_read_data(struct archive_read *a,
if (zip->end_of_entry)
return (ARCHIVE_EOF);
- size_t bytes_to_read = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time
- if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) {
+ size_t bytes_to_read = MAX_READ;
+ if ((int64_t)bytes_to_read > zip->entry_bytes_remaining) {
bytes_to_read = (size_t)zip->entry_bytes_remaining;
}
bytes = read_stream(a, buff, bytes_to_read, 0);
@@ -1181,7 +1146,7 @@ archive_read_format_7zip_read_data(struct archive_read *a,
zip->entry_crc32) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"7-Zip bad CRC: 0x%lx should be 0x%lx",
- (unsigned long)zip->entry_crc32,
+ zip->entry_crc32,
(unsigned long)zip->si.ss.digests[
zip->entry->ssIndex]);
ret = ARCHIVE_WARN;
@@ -1198,10 +1163,8 @@ archive_read_format_7zip_read_data(struct archive_read *a,
static int
archive_read_format_7zip_read_data_skip(struct archive_read *a)
{
- struct _7zip *zip;
- int64_t bytes_skipped;
-
- zip = (struct _7zip *)(a->format->data);
+ struct _7zip *zip = a->format->data;
+ int r;
if (zip->pack_stream_bytes_unconsumed)
read_consume(a);
@@ -1214,9 +1177,9 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a)
* If the length is at the beginning, we can skip the
* compressed data much more quickly.
*/
- bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
- if (bytes_skipped < 0)
- return ((int)bytes_skipped);
+ r = skip_stream(a, zip->entry_bytes_remaining);
+ if (r < 0)
+ return (r);
zip->entry_bytes_remaining = 0;
/* This entry is finished and done. */
@@ -1227,9 +1190,8 @@ archive_read_format_7zip_read_data_skip(struct archive_read *a)
static int
archive_read_format_7zip_cleanup(struct archive_read *a)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format->data;
- zip = (struct _7zip *)(a->format->data);
free_StreamsInfo(&(zip->si));
free(zip->entries);
free(zip->entry_names);
@@ -1240,24 +1202,30 @@ archive_read_format_7zip_cleanup(struct archive_read *a)
free(zip->sub_stream_buff[2]);
free(zip->tmp_stream_buff);
free(zip);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
-static void
+static int
read_consume(struct archive_read *a)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
if (zip->pack_stream_bytes_unconsumed) {
- __archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
+ int64_t r;
+
+ if ((r = __archive_read_consume(a,
+ zip->pack_stream_bytes_unconsumed)) < 0)
+ return ((int)r);
+
zip->stream_offset += zip->pack_stream_bytes_unconsumed;
zip->pack_stream_bytes_unconsumed = 0;
}
+
+ return (ARCHIVE_OK);
}
#ifdef HAVE_LZMA_H
-
/*
* Set an error code and choose an error message for liblzma.
*/
@@ -1305,22 +1273,19 @@ set_error(struct archive_read *a, int ret)
break;
}
}
-
#endif
static int
-decode_codec_id(const unsigned char *codecId, size_t id_size, uint64_t *id)
+decode_codec_id(const unsigned char *codecId, size_t id_size, int64_t *id)
{
- unsigned i;
- uint64_t v = 0;
+ size_t i;
+ *id = 0;
for (i = 0; i < id_size; i++) {
- if (v > (uint64_t)INT64_MAX / 256)
+ if (archive_ckd_mul_i64(id, *id, 256) ||
+ archive_ckd_add_i64(id, *id, codecId[i]))
return (-1);
- v <<= 8;
- v += codecId[i];
}
- *id = v;
return (0);
}
@@ -1328,7 +1293,7 @@ static Byte
ppmd_read(void *p)
{
struct archive_read *a = ((IByteIn*)p)->a;
- struct _7zip *zip = (struct _7zip *)(a->format->data);
+ struct _7zip *zip = a->format->data;
Byte b;
if (zip->ppstream.avail_in == 0) {
@@ -1337,7 +1302,7 @@ ppmd_read(void *p)
* and we are on boundary;
* last resort to read using __archive_read_ahead.
*/
- const uint8_t* data = __archive_read_ahead(a,
+ const uint8_t *data = __archive_read_ahead(a,
zip->ppstream.stream_in + 1, NULL);
if (data == NULL) {
archive_set_error(&a->archive,
@@ -1466,7 +1431,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
memset(&delta_opt, 0, sizeof(delta_opt));
delta_opt.type = LZMA_DELTA_TYPE_BYTE;
delta_opt.dist =
- (uint32_t)coder2->properties[0] + 1;
+ coder2->properties[0] + 1;
filters[fi].options = &delta_opt;
fi++;
break;
@@ -1506,7 +1471,8 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
- "Unexpected codec ID: %lX", zip->codec2);
+ "Unexpected codec ID: %jX",
+ (uintmax_t)zip->codec2);
return (ARCHIVE_FAILED);
}
}
@@ -1518,7 +1484,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
filters[fi].options = NULL;
ff = &filters[fi];
r = lzma_properties_decode(&filters[fi], NULL,
- coder1->properties, (size_t)coder1->propertiesSize);
+ coder1->properties, coder1->propertiesSize);
if (r != LZMA_OK) {
set_error(a, r);
return (ARCHIVE_FAILED);
@@ -1673,7 +1639,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
case _7Z_SPARC:
case _7Z_DELTA:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Unexpected codec ID: %lX", zip->codec);
+ "Unexpected codec ID: %jX", (uintmax_t)zip->codec);
return (ARCHIVE_FAILED);
case _7Z_CRYPTO_MAIN_ZIP:
case _7Z_CRYPTO_RAR_29:
@@ -1684,11 +1650,12 @@ init_decompression(struct archive_read *a, struct _7zip *zip,
zip->has_encrypted_entries = 1;
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
+ "Crypto codec not supported yet (ID: 0x%jX)",
+ (uintmax_t)zip->codec);
return (ARCHIVE_FAILED);
default:
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Unknown codec ID: %lX", zip->codec);
+ "Unknown codec ID: %jX", (uintmax_t)zip->codec);
return (ARCHIVE_FAILED);
}
@@ -1740,7 +1707,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
bcj2_avail_out = t_avail_out;
if (zip->codec2 == _7Z_X86_BCJ2) {
/*
- * Decord a remaining decompressed main stream for BCJ2.
+ * Decode a remaining decompressed main stream for BCJ2.
*/
if (zip->tmp_stream_bytes_remaining) {
ssize_t bytes;
@@ -1774,8 +1741,8 @@ decompress(struct archive_read *a, struct _7zip *zip,
switch (zip->codec) {
case _7Z_COPY:
{
- size_t bytes =
- (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
+ size_t bytes = (t_avail_in > t_avail_out) ?
+ t_avail_out : t_avail_in;
memcpy(t_next_out, t_next_in, bytes);
t_avail_in -= bytes;
@@ -1813,10 +1780,14 @@ decompress(struct archive_read *a, struct _7zip *zip,
#endif
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
case _7Z_BZ2:
+ if (t_avail_in > UINT_MAX)
+ t_avail_in = UINT_MAX;
+ if (t_avail_out > UINT_MAX)
+ t_avail_out = UINT_MAX;
zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
- zip->bzstream.avail_in = (uint32_t)t_avail_in;
+ zip->bzstream.avail_in = (unsigned int)t_avail_in;
zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
- zip->bzstream.avail_out = (uint32_t)t_avail_out;
+ zip->bzstream.avail_out = (unsigned int)t_avail_out;
r = BZ2_bzDecompress(&(zip->bzstream));
switch (r) {
case BZ_STREAM_END: /* Found end of stream. */
@@ -1846,6 +1817,10 @@ decompress(struct archive_read *a, struct _7zip *zip,
#endif
#ifdef HAVE_ZLIB_H
case _7Z_DEFLATE:
+ if (t_avail_in > UINT_MAX)
+ t_avail_in = UINT_MAX;
+ if (t_avail_out > UINT_MAX)
+ t_avail_out = UINT_MAX;
zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
zip->stream.avail_in = (uInt)t_avail_in;
zip->stream.next_out = t_next_out;
@@ -1869,12 +1844,17 @@ decompress(struct archive_read *a, struct _7zip *zip,
#if HAVE_ZSTD_H && HAVE_LIBZSTD
case _7Z_ZSTD:
{
- ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
- ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+ /* src, size, pos */
+ ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 };
+ /* dst, size, pos */
+ ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 };
- size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+ size_t const zret = ZSTD_decompressStream(zip->zstd_dstream,
+ &output, &input);
if (ZSTD_isError(zret)) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Zstd decompression failed: %s",
+ ZSTD_getErrorName(zret));
return ARCHIVE_FAILED;
}
t_avail_in -= input.pos;
@@ -1884,7 +1864,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
#endif
case _7Z_PPMD:
{
- uint64_t flush_bytes;
+ int64_t flush_bytes;
if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
t_avail_out <= 0) {
@@ -1942,7 +1922,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
}
*zip->ppstream.next_out++ = (unsigned char)sym;
zip->ppstream.avail_out--;
- if (flush_bytes)
+ if (flush_bytes > 0)
flush_bytes--;
} while (zip->ppstream.avail_out &&
(zip->ppstream.avail_in || flush_bytes));
@@ -1963,7 +1943,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
*outbytes = o_avail_out - t_avail_out;
/*
- * Decord BCJ.
+ * Decode BCJ.
*/
if (zip->codec != _7Z_LZMA2) {
if (zip->codec2 == _7Z_X86) {
@@ -1971,9 +1951,10 @@ decompress(struct archive_read *a, struct _7zip *zip,
zip->odd_bcj_size = *outbytes - l;
if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
- o_avail_in && ret != ARCHIVE_EOF) {
- memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
- zip->odd_bcj_size);
+ o_avail_in && ret != ARCHIVE_EOF) {
+ memcpy(zip->odd_bcj,
+ ((unsigned char *)buff) + l,
+ zip->odd_bcj_size);
*outbytes = l;
} else
zip->odd_bcj_size = 0;
@@ -1989,7 +1970,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
}
/*
- * Decord BCJ2 with a decompressed main stream.
+ * Decode BCJ2 with a decompressed main stream.
*/
if (zip->codec2 == _7Z_X86_BCJ2) {
ssize_t bytes;
@@ -1997,7 +1978,7 @@ decompress(struct archive_read *a, struct _7zip *zip,
zip->tmp_stream_bytes_avail =
zip->tmp_stream_buff_size - t_avail_out;
if (zip->tmp_stream_bytes_avail >
- zip->main_stream_bytes_remaining)
+ (uint64_t)zip->main_stream_bytes_remaining)
zip->tmp_stream_bytes_avail =
zip->main_stream_bytes_remaining;
zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
@@ -2065,8 +2046,21 @@ free_decompression(struct archive_read *a, struct _7zip *zip)
}
static int
-parse_7zip_uint64(struct archive_read *a, uint64_t *val)
+parse_7zip_size(struct archive_read *a, size_t *val)
{
+ int64_t v;
+
+ if (parse_7zip_int64(a, &v) < 0 || v > UMAX_ENTRY)
+ return (-1);
+
+ *val = (size_t)v;
+ return (0);
+}
+
+static int
+parse_7zip_int64(struct archive_read *a, int64_t *val)
+{
+ uint64_t v;
const unsigned char *p;
unsigned char avail, mask;
int i;
@@ -2075,18 +2069,21 @@ parse_7zip_uint64(struct archive_read *a, uint64_t *val)
return (-1);
avail = *p;
mask = 0x80;
- *val = 0;
+ v = 0;
for (i = 0; i < 8; i++) {
if (avail & mask) {
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
- *val |= ((uint64_t)*p) << (8 * i);
+ v |= ((uint64_t)*p) << (8 * i);
mask >>= 1;
continue;
}
- *val += ((uint64_t)(avail & (mask -1))) << (8 * i);
+ v += ((uint64_t)(avail & (mask - 1))) << (8 * i);
break;
}
+ if (v > (uint64_t)INT64_MAX)
+ return (-1);
+ *val = (int64_t)v;
return (0);
}
@@ -2094,7 +2091,8 @@ static int
read_Bools(struct archive_read *a, unsigned char *data, size_t num)
{
const unsigned char *p;
- unsigned i, mask = 0, avail = 0;
+ size_t i;
+ unsigned mask = 0, avail = 0;
for (i = 0; i < num; i++) {
if (mask == 0) {
@@ -2103,7 +2101,7 @@ read_Bools(struct archive_read *a, unsigned char *data, size_t num)
avail = *p;
mask = 0x80;
}
- data[i] = (avail & mask)?1:0;
+ data[i] = (avail & mask) ? 1 : 0;
mask >>= 1;
}
return (0);
@@ -2120,7 +2118,7 @@ static int
read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
{
const unsigned char *p;
- unsigned i;
+ size_t i;
if (num == 0)
return (-1);
@@ -2167,25 +2165,23 @@ static int
read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
{
const unsigned char *p;
- unsigned i;
+ size_t i;
memset(pi, 0, sizeof(*pi));
/*
* Read PackPos.
*/
- if (parse_7zip_uint64(a, &(pi->pos)) < 0)
+ if (parse_7zip_int64(a, &(pi->pos)) < 0)
return (-1);
/*
* Read NumPackStreams.
*/
- if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
+ if (parse_7zip_size(a, &(pi->numPackStreams)) < 0)
return (-1);
if (pi->numPackStreams == 0)
return (-1);
- if (UMAX_ENTRY < pi->numPackStreams)
- return (-1);
/*
* Read PackSizes[num]
@@ -2197,13 +2193,13 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
return (0);
if (*p != kSize)
return (-1);
- pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
- pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+ pi->sizes = calloc(pi->numPackStreams, sizeof(int64_t));
+ pi->positions = calloc(pi->numPackStreams, sizeof(int64_t));
if (pi->sizes == NULL || pi->positions == NULL)
return (-1);
for (i = 0; i < pi->numPackStreams; i++) {
- if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
+ if (parse_7zip_int64(a, &(pi->sizes[i])) < 0)
return (-1);
}
@@ -2215,9 +2211,9 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
if (*p == kEnd) {
/* PackStreamDigests[num] are not present. */
pi->digest.defineds =
- calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
+ calloc(pi->numPackStreams, sizeof(*pi->digest.defineds));
pi->digest.digests =
- calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
+ calloc(pi->numPackStreams, sizeof(*pi->digest.digests));
if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
return (-1);
return (0);
@@ -2226,7 +2222,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
if (*p != kCRC)
return (-1);
- if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
+ if (read_Digests(a, &(pi->digest), pi->numPackStreams) < 0)
return (-1);
/*
@@ -2242,40 +2238,39 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
static void
free_Folder(struct _7z_folder *f)
{
- unsigned i;
-
if (f->coders) {
+ size_t i;
+
for (i = 0; i< f->numCoders; i++) {
free(f->coders[i].properties);
}
free(f->coders);
}
free(f->bindPairs);
- free(f->packedStreams);
free(f->unPackSize);
}
static int
read_Folder(struct archive_read *a, struct _7z_folder *f)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
- uint64_t numInStreamsTotal = 0;
- uint64_t numOutStreamsTotal = 0;
- unsigned i;
+ size_t numInStreamsTotal = 0;
+ size_t numOutStreamsTotal = 0;
+ size_t i;
memset(f, 0, sizeof(*f));
/*
* Read NumCoders.
*/
- if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
+ if (parse_7zip_size(a, &(f->numCoders)) < 0)
return (-1);
if (f->numCoders > 4)
/* Too many coders. */
return (-1);
- f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
+ f->coders = calloc(f->numCoders, sizeof(*f->coders));
if (f->coders == NULL)
return (-1);
for (i = 0; i< f->numCoders; i++) {
@@ -2293,7 +2288,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
* 7: Must be zero.
*/
codec_size = *p & 0xf;
- simple = (*p & 0x10)?0:1;
+ simple = (*p & 0x10) ? 0 : 1;
attr = *p & 0x20;
if (*p & 0x80)
return (-1);/* Not supported. */
@@ -2311,37 +2306,34 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
f->coders[i].numInStreams = 1;
f->coders[i].numOutStreams = 1;
} else {
- if (parse_7zip_uint64(
+ if (parse_7zip_size(
a, &(f->coders[i].numInStreams)) < 0)
return (-1);
- if (UMAX_ENTRY < f->coders[i].numInStreams)
- return (-1);
- if (parse_7zip_uint64(
+ if (parse_7zip_size(
a, &(f->coders[i].numOutStreams)) < 0)
return (-1);
- if (UMAX_ENTRY < f->coders[i].numOutStreams)
- return (-1);
}
if (attr) {
- if (parse_7zip_uint64(
+ if (parse_7zip_size(
a, &(f->coders[i].propertiesSize)) < 0)
return (-1);
- if (UMAX_ENTRY < f->coders[i].propertiesSize)
- return (-1);
if ((p = header_bytes(
- a, (size_t)f->coders[i].propertiesSize)) == NULL)
+ a, f->coders[i].propertiesSize)) == NULL)
return (-1);
f->coders[i].properties =
- malloc((size_t)f->coders[i].propertiesSize);
+ malloc(f->coders[i].propertiesSize);
if (f->coders[i].properties == NULL)
return (-1);
memcpy(f->coders[i].properties, p,
- (size_t)f->coders[i].propertiesSize);
+ f->coders[i].propertiesSize);
}
- numInStreamsTotal += f->coders[i].numInStreams;
- numOutStreamsTotal += f->coders[i].numOutStreams;
+ if (archive_ckd_add_size(&numInStreamsTotal,
+ numInStreamsTotal, f->coders[i].numInStreams) ||
+ archive_ckd_add_size(&numOutStreamsTotal,
+ numOutStreamsTotal, f->coders[i].numOutStreams))
+ return (-1);
}
if (numOutStreamsTotal == 0 ||
@@ -2349,34 +2341,27 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
return (-1);
f->numBindPairs = numOutStreamsTotal - 1;
- if (zip->header_bytes_remaining < f->numBindPairs)
+ if ((uint64_t)zip->header_bytes_remaining < f->numBindPairs)
return (-1);
if (f->numBindPairs > 0) {
f->bindPairs =
- calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+ calloc(f->numBindPairs, sizeof(*f->bindPairs));
if (f->bindPairs == NULL)
return (-1);
} else
f->bindPairs = NULL;
for (i = 0; i < f->numBindPairs; i++) {
- if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
+ if (parse_7zip_size(a, &(f->bindPairs[i].inIndex)) < 0)
return (-1);
- if (UMAX_ENTRY < f->bindPairs[i].inIndex)
- return (-1);
- if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
- return (-1);
- if (UMAX_ENTRY < f->bindPairs[i].outIndex)
+ if (parse_7zip_size(a, &(f->bindPairs[i].outIndex)) < 0)
return (-1);
}
f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
- f->packedStreams =
- calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
- if (f->packedStreams == NULL)
- return (-1);
+ /* packedStreams are not needed; parse/verify nonetheless */
if (f->numPackedStreams == 1) {
for (i = 0; i < numInStreamsTotal; i++) {
- unsigned j;
+ size_t j;
for (j = 0; j < f->numBindPairs; j++) {
if (f->bindPairs[j].inIndex == i)
break;
@@ -2386,12 +2371,10 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
}
if (i == numInStreamsTotal)
return (-1);
- f->packedStreams[0] = i;
} else {
for (i = 0; i < f->numPackedStreams; i++) {
- if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
- return (-1);
- if (UMAX_ENTRY < f->packedStreams[i])
+ size_t packedStream;
+ if (parse_7zip_size(a, &packedStream) < 0)
return (-1);
}
}
@@ -2404,7 +2387,7 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
static void
free_CodersInfo(struct _7z_coders_info *ci)
{
- unsigned i;
+ size_t i;
if (ci->folders) {
for (i = 0; i < ci->numFolders; i++)
@@ -2416,9 +2399,10 @@ free_CodersInfo(struct _7z_coders_info *ci)
static int
read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
{
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
struct _7z_digests digest;
- unsigned i;
+ size_t dataStreamIndex, i;
memset(ci, 0, sizeof(*ci));
memset(&digest, 0, sizeof(digest));
@@ -2431,10 +2415,16 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
/*
* Read NumFolders.
*/
- if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
+ if (parse_7zip_size(a, &(ci->numFolders)) < 0)
+ goto failed;
+ /*
+ * Each folder is encoded by at least one byte in the coders
+ * list that follows, so a folder count larger than the bytes
+ * left in the header cannot be honored and is rejected here
+ * before it is used to size the folders allocation.
+ */
+ if (ci->numFolders > (uint64_t)zip->header_bytes_remaining)
goto failed;
- if (UMAX_ENTRY < ci->numFolders)
- return (-1);
/*
* Read External.
@@ -2444,7 +2434,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
switch (*p) {
case 0:
ci->folders =
- calloc((size_t)ci->numFolders, sizeof(*ci->folders));
+ calloc(ci->numFolders, sizeof(*ci->folders));
if (ci->folders == NULL)
return (-1);
for (i = 0; i < ci->numFolders; i++) {
@@ -2453,9 +2443,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
}
break;
case 1:
- if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
- return (-1);
- if (UMAX_ENTRY < ci->dataStreamIndex)
+ if (parse_7zip_size(a, &dataStreamIndex) < 0)
return (-1);
if (ci->numFolders > 0) {
archive_set_error(&a->archive, -1,
@@ -2476,14 +2464,14 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
for (i = 0; i < ci->numFolders; i++) {
struct _7z_folder *folder = &(ci->folders[i]);
- unsigned j;
+ size_t j;
folder->unPackSize =
- calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
+ calloc(folder->numOutStreams, sizeof(*folder->unPackSize));
if (folder->unPackSize == NULL)
goto failed;
for (j = 0; j < folder->numOutStreams; j++) {
- if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
+ if (parse_7zip_int64(a, &(folder->unPackSize[j])) < 0)
goto failed;
}
}
@@ -2497,7 +2485,7 @@ read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
return (0);
if (*p != kCRC)
goto failed;
- if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
+ if (read_Digests(a, &digest, ci->numFolders) < 0)
goto failed;
for (i = 0; i < ci->numFolders; i++) {
ci->folders[i].digest_defined = digest.defineds[i];
@@ -2518,16 +2506,16 @@ failed:
return (-1);
}
-static uint64_t
+static int64_t
folder_uncompressed_size(struct _7z_folder *f)
{
- int n = (int)f->numOutStreams;
- unsigned pairs = (unsigned)f->numBindPairs;
+ size_t n = f->numOutStreams;
+ size_t pairs = f->numBindPairs;
- while (--n >= 0) {
- unsigned i;
+ while (n-- > 0) {
+ size_t i;
for (i = 0; i < pairs; i++) {
- if (f->bindPairs[i].outIndex == (uint64_t)n)
+ if (f->bindPairs[i].outIndex == n)
break;
}
if (i >= pairs)
@@ -2549,11 +2537,11 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
struct _7z_folder *f, size_t numFolders)
{
const unsigned char *p;
- uint64_t *usizes;
+ int64_t *usizes;
+ size_t numDigests;
size_t unpack_streams;
+ size_t i;
int type;
- unsigned i;
- uint32_t numDigests;
memset(ss, 0, sizeof(*ss));
@@ -2567,13 +2555,11 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
if (type == kNumUnPackStream) {
unpack_streams = 0;
for (i = 0; i < numFolders; i++) {
- if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
+ if (parse_7zip_size(a, &(f[i].numUnpackStreams)) < 0)
return (-1);
- if (f[i].numUnpackStreams >
- UMAX_ENTRY - unpack_streams) {
+ if (archive_ckd_add_size(&unpack_streams,
+ unpack_streams, f[i].numUnpackStreams))
return (-1);
- }
- unpack_streams += (size_t)f[i].numUnpackStreams;
}
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
@@ -2581,6 +2567,9 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
} else
unpack_streams = numFolders;
+ if (unpack_streams > UMAX_ENTRY)
+ return (-1);
+
if (type != kSize) {
for (i = 0; i < numFolders; i++) {
if (f[i].numUnpackStreams > 1)
@@ -2603,8 +2592,8 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
usizes = ss->unpackSizes;
for (i = 0; i < numFolders; i++) {
- unsigned pack;
- uint64_t size, sum;
+ int64_t size, sum;
+ size_t pack;
if (f[i].numUnpackStreams == 0)
continue;
@@ -2612,11 +2601,10 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
sum = 0;
if (type == kSize) {
for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
- if (parse_7zip_uint64(a, usizes) < 0)
+ if (parse_7zip_int64(a, usizes) < 0)
return (-1);
- if (*usizes > UINT64_MAX - sum)
+ if (archive_ckd_add_i64(&sum, sum, *usizes++))
return (-1);
- sum += *usizes++;
}
}
size = folder_uncompressed_size(&f[i]);
@@ -2634,14 +2622,18 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
numDigests = 0;
for (i = 0; i < numFolders; i++) {
if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
- numDigests += (uint32_t)f[i].numUnpackStreams;
+ if (archive_ckd_add_size(&numDigests,
+ numDigests, f[i].numUnpackStreams)) {
+ errno = ENOMEM;
+ return (-1);
+ }
}
if (type == kCRC) {
struct _7z_digests tmpDigests;
unsigned char *digestsDefined = ss->digestsDefined;
- uint32_t * digests = ss->digests;
- int di = 0;
+ uint32_t *digests = ss->digests;
+ size_t di = 0;
memset(&tmpDigests, 0, sizeof(tmpDigests));
if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
@@ -2653,7 +2645,7 @@ read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
*digestsDefined++ = 1;
*digests++ = f[i].digest;
} else {
- unsigned j;
+ size_t j;
for (j = 0; j < f[i].numUnpackStreams;
j++, di++) {
@@ -2689,16 +2681,16 @@ free_StreamsInfo(struct _7z_stream_info *si)
static int
read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
- unsigned i;
+ size_t i;
memset(si, 0, sizeof(*si));
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
if (*p == kPackInfo) {
- uint64_t packPos;
+ int64_t packPos;
if (read_PackInfo(a, &(si->pi)) < 0)
return (-1);
@@ -2711,9 +2703,9 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
packPos = si->pi.pos;
for (i = 0; i < si->pi.numPackStreams; i++) {
si->pi.positions[i] = packPos;
- if (packPos > UINT64_MAX - si->pi.sizes[i])
+ if (archive_ckd_add_i64(&packPos,
+ packPos, si->pi.sizes[i]))
return (-1);
- packPos += si->pi.sizes[i];
if (packPos > zip->header_offset)
return (-1);
}
@@ -2721,7 +2713,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
return (-1);
}
if (*p == kUnPackInfo) {
- uint32_t packIndex;
+ size_t packIndex;
struct _7z_folder *f;
if (read_CodersInfo(a, &(si->ci)) < 0)
@@ -2734,12 +2726,9 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
f = si->ci.folders;
for (i = 0; i < si->ci.numFolders; i++) {
f[i].packIndex = packIndex;
- if (f[i].numPackedStreams > UINT32_MAX)
- return (-1);
- if (packIndex > UINT32_MAX - (uint32_t)f[i].numPackedStreams)
- return (-1);
- packIndex += (uint32_t)f[i].numPackedStreams;
- if (packIndex > si->pi.numPackStreams)
+ if (archive_ckd_add_size(&packIndex,
+ packIndex, f[i].numPackedStreams) ||
+ packIndex > si->pi.numPackStreams)
return (-1);
}
if ((p = header_bytes(a, 1)) == NULL)
@@ -2748,7 +2737,7 @@ read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
if (*p == kSubStreamsInfo) {
if (read_SubStreamsInfo(a, &(si->ss),
- si->ci.folders, (size_t)si->ci.numFolders) < 0)
+ si->ci.folders, si->ci.numFolders) < 0)
return (-1);
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
@@ -2771,18 +2760,38 @@ free_Header(struct _7z_header_info *h)
free(h->attrBools);
}
+/*
+ * Files without unpack streams must be described by the EmptyStream bitmap,
+ * which consumes one bit for every file entry in FilesInfo.
+ */
+static int
+files_info_numfiles_is_sane(const struct _7zip *zip)
+{
+ int64_t empty_stream_map_bytes;
+
+ if (zip->numFiles > SIZE_MAX / sizeof(*zip->entries))
+ return (0);
+
+ if (zip->numFiles <= zip->si.ss.unpack_streams)
+ return (1);
+
+ empty_stream_map_bytes = (zip->numFiles + 7) / 8;
+ return (empty_stream_map_bytes <= zip->header_bytes_remaining);
+}
+
static int
read_Header(struct archive_read *a, struct _7z_header_info *h,
int check_header_id)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
struct _7z_folder *folders;
struct _7z_stream_info *si = &(zip->si);
struct _7zip_entry *entries;
- uint32_t folderIndex, indexInFolder;
- unsigned i;
- int eindex, empty_streams, sindex;
+ size_t folderIndex, indexInFolder;
+ size_t i;
+ size_t eindex, empty_streams, sindex;
+ int attr_seen = 0;
if (check_header_id) {
/*
@@ -2801,13 +2810,26 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
return (-1);
if (*p == kArchiveProperties) {
for (;;) {
- uint64_t size;
+ int64_t size;
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
- if (*p == 0)
+ if (*p == kEnd)
break;
- if (parse_7zip_uint64(a, &size) < 0)
+ if (parse_7zip_int64(a, &size) < 0)
+ return (-1);
+ if (size < 0 || zip->header_bytes_remaining < size)
return (-1);
+
+ /* Skip the property data to keep header parsing aligned. */
+ while (size > 0) {
+ int64_t skip = size;
+
+ if (skip > UBUFF_SIZE)
+ skip = UBUFF_SIZE;
+ if (header_bytes(a, (size_t)skip) == NULL)
+ return (-1);
+ size -= skip;
+ }
}
if ((p = header_bytes(a, 1)) == NULL)
return (-1);
@@ -2831,12 +2853,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
if (*p != kFilesInfo)
return (-1);
- if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
+ if (parse_7zip_size(a, &(zip->numFiles)) < 0)
return (-1);
if (!files_info_numfiles_is_sane(zip))
return (-1);
- zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
+ zip->entries = calloc(zip->numFiles, sizeof(*zip->entries));
if (zip->entries == NULL)
return (-1);
entries = zip->entries;
@@ -2844,7 +2866,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
empty_streams = 0;
for (;;) {
int type;
- uint64_t size;
+ int64_t size;
size_t ll;
if ((p = header_bytes(a, 1)) == NULL)
@@ -2853,9 +2875,10 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
if (type == kEnd)
break;
- if (parse_7zip_uint64(a, &size) < 0)
+ if (parse_7zip_int64(a, &size) < 0)
return (-1);
- if (zip->header_bytes_remaining < size || size > SIZE_MAX / 4)
+ if (zip->header_bytes_remaining < size ||
+ size > (int64_t)(SIZE_MAX / 4))
return (-1);
ll = (size_t)size;
@@ -2863,12 +2886,12 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
case kEmptyStream:
if (h->emptyStreamBools != NULL)
return (-1);
- h->emptyStreamBools = calloc((size_t)zip->numFiles,
+ h->emptyStreamBools = calloc(zip->numFiles,
sizeof(*h->emptyStreamBools));
if (h->emptyStreamBools == NULL)
return (-1);
if (read_Bools(
- a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
+ a, h->emptyStreamBools, zip->numFiles) < 0)
return (-1);
empty_streams = 0;
for (i = 0; i < zip->numFiles; i++) {
@@ -2911,7 +2934,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
case kCTime:
case kATime:
case kMTime:
- if (read_Times(a, h, type) < 0)
+ if (read_Times(a, type) < 0)
return (-1);
break;
case kName:
@@ -2981,21 +3004,20 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
if ((p = header_bytes(a, 2)) == NULL)
return (-1);
allAreDefined = *p;
- if (h->attrBools != NULL)
- return (-1);
- h->attrBools = calloc((size_t)zip->numFiles,
- sizeof(*h->attrBools));
- if (h->attrBools == NULL)
+ if (attr_seen)
return (-1);
- if (allAreDefined)
- memset(h->attrBools, 1, (size_t)zip->numFiles);
- else {
+ attr_seen = 1;
+ if (!allAreDefined) {
+ h->attrBools = calloc(zip->numFiles,
+ sizeof(*h->attrBools));
+ if (h->attrBools == NULL)
+ return (-1);
if (read_Bools(a, h->attrBools,
- (size_t)zip->numFiles) < 0)
+ zip->numFiles) < 0)
return (-1);
}
for (i = 0; i < zip->numFiles; i++) {
- if (h->attrBools[i]) {
+ if (allAreDefined || h->attrBools[i]) {
if ((p = header_bytes(a, 4)) == NULL)
return (-1);
entries[i].attr = archive_le32dec(p);
@@ -3048,7 +3070,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
}
if (entries[i].flg & HAS_STREAM) {
- if ((size_t)sindex >= si->ss.unpack_streams)
+ if (sindex >= si->ss.unpack_streams)
return (-1);
if (entries[i].mode == 0)
entries[i].mode = AE_IFREG | 0666;
@@ -3057,14 +3079,10 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
entries[i].ssIndex = sindex;
sindex++;
} else {
- int dir;
- if (h->emptyFileBools == NULL)
- dir = 1;
- else {
- if (h->emptyFileBools[eindex])
- dir = 0;
- else
- dir = 1;
+ int dir = 1;
+
+ if (h->emptyFileBools != NULL) {
+ dir = !h->emptyFileBools[eindex];
eindex++;
}
if (entries[i].mode == 0) {
@@ -3121,27 +3139,25 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
}
static int
-read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
+read_Times(struct archive_read *a, int type)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
struct _7zip_entry *entries = zip->entries;
- unsigned char *timeBools;
+ unsigned char *timeBools = NULL;
int allAreDefined;
- unsigned i;
-
- timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
- if (timeBools == NULL)
- return (-1);
+ size_t dataIndex, i;
/* Read allAreDefined. */
if ((p = header_bytes(a, 1)) == NULL)
goto failed;
allAreDefined = *p;
- if (allAreDefined)
- memset(timeBools, 1, (size_t)zip->numFiles);
- else {
- if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
+
+ if (!allAreDefined) {
+ timeBools = calloc(zip->numFiles, sizeof(*timeBools));
+ if (timeBools == NULL)
+ goto failed;
+ if (read_Bools(a, timeBools, zip->numFiles) < 0)
goto failed;
}
@@ -3149,14 +3165,12 @@ read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
if ((p = header_bytes(a, 1)) == NULL)
goto failed;
if (*p) {
- if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
- goto failed;
- if (UMAX_ENTRY < h->dataIndex)
+ if (parse_7zip_size(a, &dataIndex) < 0)
goto failed;
}
for (i = 0; i < zip->numFiles; i++) {
- if (!timeBools[i])
+ if (!allAreDefined && !timeBools[i])
continue;
if ((p = header_bytes(a, 8)) == NULL)
goto failed;
@@ -3192,7 +3206,8 @@ failed:
static int
decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
+ int64_t pi_end;
errno = 0;
if (read_StreamsInfo(a, si) < 0) {
@@ -3210,9 +3225,12 @@ decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
return (ARCHIVE_FATAL);
}
- if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
- (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
- si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
+ if (archive_ckd_add_i64(&pi_end, si->pi.pos, si->pi.sizes[0])) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (zip->header_offset < pi_end) {
archive_set_error(&a->archive, -1, "Malformed Header offset");
return (ARCHIVE_FATAL);
}
@@ -3223,10 +3241,10 @@ decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
static const unsigned char *
header_bytes(struct archive_read *a, size_t rbytes)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const unsigned char *p;
- if (zip->header_bytes_remaining < rbytes)
+ if ((uint64_t)zip->header_bytes_remaining < rbytes)
return (NULL);
if (zip->pack_stream_bytes_unconsumed)
read_consume(a);
@@ -3258,8 +3276,8 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
struct _7z_header_info *header)
{
const unsigned char *p;
- uint64_t next_header_offset;
- uint64_t next_header_size;
+ int64_t next_header_offset;
+ int64_t next_header_size;
uint32_t next_header_crc;
ssize_t bytes_avail;
int64_t data_offset;
@@ -3277,7 +3295,10 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
return (ARCHIVE_FATAL);
}
- zip->seek_base = (uint64_t)data_offset + 32;
+ if (archive_ckd_add_i64(&zip->seek_base, data_offset, 32)) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
@@ -3294,25 +3315,35 @@ slurp_central_directory(struct archive_read *a, struct _7zip *zip,
}
next_header_offset = archive_le64dec(p + 12);
+ if (next_header_offset < 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
next_header_size = archive_le64dec(p + 20);
+ if (next_header_size < 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
next_header_crc = archive_le32dec(p + 28);
if (next_header_size == 0)
- /* There is no entry in an archive file. */
+ /* There is no entry in the archive file. */
return (ARCHIVE_EOF);
- if (((int64_t)next_header_offset) < 0) {
- archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
- return (ARCHIVE_FATAL);
- }
__archive_read_consume(a, 32);
if (next_header_offset != 0) {
- if (bytes_avail >= (ssize_t)next_header_offset)
+ if (bytes_avail >= next_header_offset)
__archive_read_consume(a, next_header_offset);
- else if (seek_compat(a,
- next_header_offset + zip->seek_base, SEEK_SET, 1) < 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error");
- return (ARCHIVE_FATAL);
+ else {
+ int64_t target;
+
+ if (archive_ckd_add_i64(&target,
+ zip->seek_base, next_header_offset) ||
+ seek_compat(a, target, SEEK_SET, 1) < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Seek error");
+ return (ARCHIVE_FATAL);
+ }
}
}
zip->stream_offset = next_header_offset;
@@ -3424,10 +3455,10 @@ static ssize_t
get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
size_t minimum)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
ssize_t bytes_avail;
- if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
/* Copy mode. */
*buff = __archive_read_ahead(a, minimum, &bytes_avail);
@@ -3473,14 +3504,25 @@ get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
return (bytes_avail);
}
+static size_t
+align_size(size_t s)
+{
+ size_t r;
+
+ if (archive_ckd_add_size(&r, s, 1023))
+ return (s);
+ r &= ~0x3ff;
+ return (r);
+}
+
static ssize_t
extract_pack_stream(struct archive_read *a, size_t minimum)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
ssize_t bytes_avail;
int r;
- if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ if (zip->codec == _7Z_COPY && zip->codec2 == -1) {
if (minimum == 0)
minimum = 1;
if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
@@ -3490,10 +3532,10 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
"Truncated 7-Zip file body");
return (ARCHIVE_FATAL);
}
- if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining)
+ if (bytes_avail > zip->pack_stream_inbytes_remaining)
bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
zip->pack_stream_inbytes_remaining -= bytes_avail;
- if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining)
+ if (bytes_avail > zip->folder_outbytes_remaining)
bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
zip->folder_outbytes_remaining -= bytes_avail;
zip->uncompressed_buffer_bytes_remaining = bytes_avail;
@@ -3502,18 +3544,17 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
/* If the buffer hasn't been allocated, allocate it now. */
if (zip->uncompressed_buffer == NULL) {
- zip->uncompressed_buffer_size = UBUFF_SIZE;
- if (zip->uncompressed_buffer_size < minimum) {
- zip->uncompressed_buffer_size = minimum + 1023;
- zip->uncompressed_buffer_size &= ~0x3ff;
- }
- zip->uncompressed_buffer =
- malloc(zip->uncompressed_buffer_size);
+ size_t new_size = UBUFF_SIZE;
+
+ if (new_size < minimum)
+ new_size = align_size(minimum);
+ zip->uncompressed_buffer = malloc(new_size);
if (zip->uncompressed_buffer == NULL) {
archive_set_error(&a->archive, ENOMEM,
"No memory for 7-Zip decompression");
return (ARCHIVE_FATAL);
}
+ zip->uncompressed_buffer_size = new_size;
zip->uncompressed_buffer_bytes_remaining = 0;
} else if (zip->uncompressed_buffer_size < minimum ||
zip->uncompressed_buffer_bytes_remaining < minimum) {
@@ -3534,10 +3575,8 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
* the minimum size.
*/
void *p;
- size_t new_size;
+ size_t new_size = align_size(minimum);
- new_size = minimum + 1023;
- new_size &= ~0x3ff;
p = realloc(zip->uncompressed_buffer, new_size);
if (p == NULL) {
archive_set_error(&a->archive, ENOMEM,
@@ -3583,7 +3622,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
bytes_out = zip->uncompressed_buffer_size
- zip->uncompressed_buffer_bytes_remaining;
bytes_in = bytes_avail;
- if (bytes_in > zip->pack_stream_inbytes_remaining)
+ if (bytes_in > (uint64_t)zip->pack_stream_inbytes_remaining)
bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
/* Drive decompression. */
r = decompress(a, zip, buff_out, &bytes_out,
@@ -3599,7 +3638,7 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
return (ARCHIVE_FATAL);
}
zip->pack_stream_inbytes_remaining -= bytes_in;
- if (bytes_out > zip->folder_outbytes_remaining)
+ if (bytes_out > (uint64_t)zip->folder_outbytes_remaining)
bytes_out = (size_t)zip->folder_outbytes_remaining;
zip->folder_outbytes_remaining -= bytes_out;
zip->uncompressed_buffer_bytes_remaining += bytes_out;
@@ -3637,10 +3676,10 @@ extract_pack_stream(struct archive_read *a, size_t minimum)
static int
seek_pack(struct archive_read *a)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
int64_t pack_offset;
- if (zip->pack_stream_remaining <= 0) {
+ if (zip->pack_stream_remaining == 0) {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
return (ARCHIVE_FATAL);
@@ -3649,8 +3688,11 @@ seek_pack(struct archive_read *a)
zip->si.pi.sizes[zip->pack_stream_index];
pack_offset = zip->si.pi.positions[zip->pack_stream_index];
if (zip->stream_offset != pack_offset) {
- if (0 > seek_compat(a, pack_offset + zip->seek_base,
- SEEK_SET, 1)) {
+ int64_t target;
+
+ if (archive_ckd_add_i64(&target,
+ zip->seek_base, pack_offset) ||
+ 0 > seek_compat(a, target, SEEK_SET, 1)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek error");
return (ARCHIVE_FATAL);
}
@@ -3665,8 +3707,8 @@ static ssize_t
read_stream(struct archive_read *a, const void **buff, size_t size,
size_t minimum)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
- uint64_t skip_bytes = 0;
+ struct _7zip *zip = a->format->data;
+ int64_t skip_bytes = 0;
ssize_t r;
if (zip->uncompressed_buffer_bytes_remaining == 0) {
@@ -3737,9 +3779,10 @@ read_stream(struct archive_read *a, const void **buff, size_t size,
return (r);
/*
- * Skip the bytes we already has skipped in skip_stream().
+ * Skip the bytes we already have skipped in skip_stream().
*/
while (1) {
+ size_t request;
ssize_t skipped;
if (zip->uncompressed_buffer_bytes_remaining == 0) {
@@ -3763,8 +3806,12 @@ read_stream(struct archive_read *a, const void **buff, size_t size,
if (!skip_bytes)
break;
- skipped = get_uncompressed_data(
- a, buff, (size_t)skip_bytes, 0);
+ if (skip_bytes > MAX_READ)
+ request = MAX_READ;
+ else
+ request = (size_t)skip_bytes;
+
+ skipped = get_uncompressed_data(a, buff, request, 0);
if (skipped < 0)
return (skipped);
skip_bytes -= skipped;
@@ -3779,10 +3826,10 @@ static int
setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
int header)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const struct _7z_coder *coder1, *coder2;
const char *cname = (header)?"archive header":"file content";
- unsigned i;
+ size_t i;
int r, found_bcj2 = 0;
/*
@@ -3840,8 +3887,8 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
/*
* Initialize a stream reader.
*/
- zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
- zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->pack_stream_remaining = folder->numPackedStreams;
+ zip->pack_stream_index = folder->packIndex;
zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
zip->uncompressed_buffer_bytes_remaining = 0;
coder1 = &(folder->coders[0]);
@@ -3863,8 +3910,8 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
const void *buff;
ssize_t bytes;
unsigned char *b[3] = {NULL, NULL, NULL};
- uint64_t sunpack[3] ={-1, -1, -1};
- uint64_t remaining;
+ int64_t sunpack[3] ={-1, -1, -1};
+ int64_t remaining;
size_t s[3] = {0, 0, 0};
int idx[3] = {0, 1, 2};
@@ -3917,15 +3964,11 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
}
}
coder2 = &(fc[3]);
- zip->main_stream_bytes_remaining =
- (size_t)folder->unPackSize[2];
remaining = folder->unPackSize[2];
} else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
zip->pack_stream_remaining == 4 &&
folder->numInStreams == 5 && folder->numOutStreams == 2) {
/* Source type 0 made by 7z */
- zip->main_stream_bytes_remaining =
- (size_t)folder->unPackSize[0];
remaining = folder->unPackSize[0];
} else {
/* We got an unexpected form. */
@@ -3934,13 +3977,6 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
"Unsupported form of BCJ2 streams");
return (ARCHIVE_FATAL);
}
- if (remaining > SIZE_MAX) {
- archive_set_error(&(a->archive),
- ARCHIVE_ERRNO_MISC,
- "7-Zip sub-stream size exceeds "
- "platform maximum");
- return (ARCHIVE_FATAL);
- }
zip->main_stream_bytes_remaining = remaining;
@@ -3948,8 +3984,9 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
if ((r = seek_pack(a)) < 0)
return (r);
zip->pack_stream_bytes_unconsumed =
- (size_t)zip->pack_stream_inbytes_remaining;
- read_consume(a);
+ zip->pack_stream_inbytes_remaining;
+ if ((r = read_consume(a)) < 0)
+ return (r);
/* Read following three sub streams. */
for (i = 0; i < 3; i++) {
@@ -3960,7 +3997,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
return (r);
}
- if (sunpack[i] == (uint64_t)-1)
+ if (sunpack[i] == -1)
zip->folder_outbytes_remaining =
zip->pack_stream_inbytes_remaining;
else
@@ -3974,7 +4011,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
/* Allocate memory for the decoded data of a sub
* stream. */
- if (zip->folder_outbytes_remaining > SIZE_MAX) {
+ if ((uint64_t)zip->folder_outbytes_remaining > SIZE_MAX) {
free(b[0]); free(b[1]); free(b[2]);
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
@@ -4039,7 +4076,7 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
* of BCJ2.
*/
zip->pack_stream_remaining = 1;
- zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->pack_stream_index = folder->packIndex;
zip->folder_outbytes_remaining =
folder_uncompressed_size(folder);
zip->uncompressed_buffer_bytes_remaining = 0;
@@ -4054,26 +4091,40 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
return (ARCHIVE_OK);
}
-static int64_t
-skip_stream(struct archive_read *a, size_t skip_bytes)
+static int
+skip_stream(struct archive_read *a, int64_t skip_bytes)
{
- struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip *zip = a->format->data;
const void *p;
int64_t skipped_bytes;
- size_t bytes = skip_bytes;
+ int64_t bytes = skip_bytes;
if (zip->folder_index == 0) {
+ int64_t *v;
+
/*
* Optimization for a list mode.
* Avoid unnecessary decoding operations.
*/
- zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
- += skip_bytes;
- return (skip_bytes);
+ v = &zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes;
+ if (archive_ckd_add_i64(v, *v, skip_bytes)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too many bytes to skip");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
}
while (bytes) {
- skipped_bytes = read_stream(a, &p, bytes, 0);
+ size_t request;
+
+ if (bytes > MAX_READ)
+ request = MAX_READ;
+ else
+ request = (size_t)bytes;
+
+ skipped_bytes = read_stream(a, &p, request, 0);
if (skipped_bytes < 0)
return (skipped_bytes);
if (skipped_bytes == 0) {
@@ -4082,11 +4133,11 @@ skip_stream(struct archive_read *a, size_t skip_bytes)
"Truncated 7-Zip file body");
return (ARCHIVE_FATAL);
}
- bytes -= (size_t)skipped_bytes;
+ bytes -= skipped_bytes;
if (zip->pack_stream_bytes_unconsumed)
read_consume(a);
}
- return (skip_bytes);
+ return (ARCHIVE_OK);
}
/*
@@ -4135,7 +4186,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
if (p >= limit)
break;
prevPosT = bufferPos - prevPosT;
- if (prevPosT > 3)
+ if (prevPosT == 0 || prevPosT > 3)
prevMask = 0;
else {
prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
@@ -4439,7 +4490,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
const uint8_t *buf0, *buf1, *buf2, *buf3;
size_t size0, size1, size2, size3;
const uint8_t *buffer, *bufferLim;
- unsigned int i, j;
+ size_t i, j;
size0 = zip->tmp_stream_bytes_remaining;
buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
@@ -4538,7 +4589,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
size2 -= 4;
}
dest = archive_be32dec(v) -
- ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
+ (zip->bcj2_outPos + outPos + 4);
archive_le32enc(out, dest);
zip->bcj2_prevByte = out[3];
@@ -4549,7 +4600,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
* Save odd bytes which we could not add into
* the output buffer because of out of space.
*/
- zip->odd_bcj_size = 4 -i;
+ zip->odd_bcj_size = 4 - i;
for (; i < 4; i++) {
j = i - 4 + (unsigned)zip->odd_bcj_size;
zip->odd_bcj[j] = out[i];
diff --git a/libarchive/archive_read_support_format_all.c b/libarchive/archive_read_support_format_all.c
index 3b53c9ad5f57..a2c5a4c6f540 100644
--- a/libarchive/archive_read_support_format_all.c
+++ b/libarchive/archive_read_support_format_all.c
@@ -80,7 +80,7 @@ archive_read_support_format_all(struct archive *a)
/* Note: We always return ARCHIVE_OK here, even if some of the
* above return ARCHIVE_WARN. The intent here is to enable
* "as much as possible." Clients who need specific
- * compression should enable those individually so they can
+ * formats should enable those individually so they can
* verify the level of support. */
/* Clear any warning messages set by the above functions. */
archive_clear_error(a);
diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c
index 5633594405fa..8585de28fd14 100644
--- a/libarchive/archive_read_support_format_ar.c
+++ b/libarchive/archive_read_support_format_ar.c
@@ -55,7 +55,7 @@ struct ar {
* but haven't yet marked as consumed. Must be paired with
* entry_bytes_remaining usage/modification.
*/
- size_t entry_bytes_unconsumed;
+ int64_t entry_bytes_unconsumed;
int64_t entry_offset;
int64_t entry_padding;
char *strtab;
@@ -81,18 +81,18 @@ struct ar {
#define AR_fmag_offset 58
#define AR_fmag_size 2
-static int archive_read_format_ar_bid(struct archive_read *a, int);
-static int archive_read_format_ar_cleanup(struct archive_read *a);
-static int archive_read_format_ar_read_data(struct archive_read *a,
- const void **buff, size_t *size, int64_t *offset);
-static int archive_read_format_ar_skip(struct archive_read *a);
-static int archive_read_format_ar_read_header(struct archive_read *a,
- struct archive_entry *e);
-static uint64_t ar_atol8(const char *p, unsigned char_cnt);
-static uint64_t ar_atol10(const char *p, unsigned char_cnt);
-static int ar_parse_gnu_filename_table(struct archive_read *a);
-static int ar_parse_common_header(struct ar *ar, struct archive_entry *,
- const char *h);
+static int archive_read_format_ar_bid(struct archive_read *, int);
+static int archive_read_format_ar_cleanup(struct archive_read *);
+static int archive_read_format_ar_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_ar_skip(struct archive_read *);
+static int archive_read_format_ar_read_header(struct archive_read *,
+ struct archive_entry *);
+static uint64_t ar_atol8(const char *, size_t);
+static uint64_t ar_atol10(const char *, size_t);
+static int ar_parse_gnu_filename_table(struct archive_read *);
+static void ar_parse_common_header(struct ar *, struct archive_entry *,
+ const char *);
int
archive_read_support_format_ar(struct archive *_a)
@@ -135,12 +135,12 @@ archive_read_support_format_ar(struct archive *_a)
static int
archive_read_format_ar_cleanup(struct archive_read *a)
{
- struct ar *ar;
+ struct ar *ar = a->format->data;
- ar = (struct ar *)(a->format->data);
free(ar->strtab);
free(ar);
- (a->format->data) = NULL;
+ a->format->data = NULL;
+
return (ARCHIVE_OK);
}
@@ -165,14 +165,12 @@ archive_read_format_ar_bid(struct archive_read *a, int best_bid)
static int
_ar_read_header(struct archive_read *a, struct archive_entry *entry,
- struct ar *ar, const char *h, size_t *unconsumed)
+ struct ar *ar, const char *h, int64_t *unconsumed)
{
char filename[AR_name_size + 1];
uint64_t number; /* Used to hold parsed numbers before validation. */
- size_t bsd_name_length, entry_size;
- char *p, *st;
+ char *p;
const void *b;
- int r;
/* Verify the magic signature on the file header. */
if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
@@ -248,14 +246,20 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
}
/*
+ * Parse the time, owner, mode, size fields.
+ * This must come before any call to _read_ahead.
+ */
+ ar_parse_common_header(ar, entry, h);
+
+ /*
* '//' is the GNU filename table.
* Later entries can refer to names in this table.
*/
if (strcmp(filename, "//") == 0) {
- /* This must come before any call to _read_ahead. */
- ar_parse_common_header(ar, entry, h);
+ char *st;
+ size_t entry_size;
+
archive_entry_copy_pathname(entry, filename);
- archive_entry_set_filetype(entry, AE_IFREG);
/* Get the size of the filename table. */
number = ar_atol10(h + AR_size_offset, AR_size_size);
if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {
@@ -310,22 +314,15 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
*/
if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
- /*
- * If we can't look up the real name, warn and return
- * the entry with the wrong name.
- */
+ /* Fail if we can't look up the real name. */
if (ar->strtab == NULL || number >= ar->strtab_size) {
archive_set_error(&a->archive, EINVAL,
"Can't find long filename for GNU/SVR4 archive entry");
- archive_entry_copy_pathname(entry, filename);
- /* Parse the time, owner, mode, size fields. */
- ar_parse_common_header(ar, entry, h);
return (ARCHIVE_FATAL);
}
archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
- /* Parse the time, owner, mode, size fields. */
- return (ar_parse_common_header(ar, entry, h));
+ return (ARCHIVE_OK);
}
/*
@@ -334,9 +331,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
* the filename to the file contents.
*/
if (strncmp(filename, "#1/", 3) == 0) {
- /* Parse the time, owner, mode, size fields. */
- /* This must occur before _read_ahead is called again. */
- ar_parse_common_header(ar, entry, h);
+ size_t bsd_name_length;
/* Parse the size of the name, adjust the file size. */
number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
@@ -347,7 +342,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
*/
if (number > SIZE_MAX - 1
|| number > 1024 * 1024
- || (int64_t)number > ar->entry_bytes_remaining) {
+ || number > (uint64_t)ar->entry_bytes_remaining) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Bad input file size");
return (ARCHIVE_FATAL);
@@ -386,49 +381,33 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
}
/*
- * "/" is the SVR4/GNU archive symbol table.
- * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
- */
- if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {
- archive_entry_copy_pathname(entry, filename);
- /* Parse the time, owner, mode, size fields. */
- r = ar_parse_common_header(ar, entry, h);
- /* Force the file type to a regular file. */
- archive_entry_set_filetype(entry, AE_IFREG);
- return (r);
- }
-
- /*
- * "__.SYMDEF" is a BSD archive symbol table.
- */
- if (strcmp(filename, "__.SYMDEF") == 0) {
- archive_entry_copy_pathname(entry, filename);
- /* Parse the time, owner, mode, size fields. */
- return (ar_parse_common_header(ar, entry, h));
- }
-
- /*
* Otherwise, this is a standard entry. The filename
* has already been trimmed as much as possible, based
* on our current knowledge of the format.
+ *
+ * Note for future implementations:
+ *
+ * - "/" is the SVR4/GNU archive symbol table.
+ * - "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
+ * - "__.SYMDEF" is a BSD archive symbol table.
*/
archive_entry_copy_pathname(entry, filename);
- return (ar_parse_common_header(ar, entry, h));
+ return (ARCHIVE_OK);
}
static int
archive_read_format_ar_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct ar *ar = (struct ar*)(a->format->data);
- size_t unconsumed;
+ struct ar *ar = a->format->data;
+ int64_t unconsumed;
const void *header_data;
int ret;
if (!ar->read_global_header) {
/*
- * We are now at the beginning of the archive,
- * so we need first consume the ar global header.
+ * We are at the beginning of the archive now,
+ * so we have to consume the ar global header first.
*/
__archive_read_consume(a, 8);
ar->read_global_header = 1;
@@ -452,11 +431,11 @@ archive_read_format_ar_read_header(struct archive_read *a,
}
-static int
+static void
ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
const char *h)
{
- uint64_t n;
+ int64_t n;
/* Copy remaining header */
archive_entry_set_mtime(entry,
@@ -468,23 +447,20 @@ ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
archive_entry_set_mode(entry,
(mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
archive_entry_set_filetype(entry, AE_IFREG);
- n = ar_atol10(h + AR_size_offset, AR_size_size);
+ n = (int64_t)ar_atol10(h + AR_size_offset, AR_size_size);
ar->entry_offset = 0;
ar->entry_padding = n % 2;
archive_entry_set_size(entry, n);
ar->entry_bytes_remaining = n;
- return (ARCHIVE_OK);
}
static int
archive_read_format_ar_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
+ struct ar *ar = a->format->data;
ssize_t bytes_read;
- struct ar *ar;
-
- ar = (struct ar *)(a->format->data);
if (ar->entry_bytes_unconsumed) {
__archive_read_consume(a, ar->entry_bytes_unconsumed);
@@ -509,17 +485,9 @@ archive_read_format_ar_read_data(struct archive_read *a,
ar->entry_bytes_remaining -= bytes_read;
return (ARCHIVE_OK);
} else {
- int64_t skipped = __archive_read_consume(a, ar->entry_padding);
- if (skipped >= 0) {
- ar->entry_padding -= skipped;
- }
- if (ar->entry_padding) {
- if (skipped >= 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
- "Truncated ar archive - failed consuming padding");
- }
+ if (__archive_read_consume(a, ar->entry_padding) < 0)
return (ARCHIVE_FATAL);
- }
+ ar->entry_padding = 0;
*buff = NULL;
*size = 0;
*offset = ar->entry_offset;
@@ -530,15 +498,11 @@ archive_read_format_ar_read_data(struct archive_read *a,
static int
archive_read_format_ar_skip(struct archive_read *a)
{
- int64_t bytes_skipped;
- struct ar* ar;
-
- ar = (struct ar *)(a->format->data);
+ struct ar *ar = a->format->data;
- bytes_skipped = __archive_read_consume(a,
- ar->entry_bytes_remaining + ar->entry_padding
- + ar->entry_bytes_unconsumed);
- if (bytes_skipped < 0)
+ if (__archive_read_consume(a,
+ ar->entry_bytes_remaining + ar->entry_padding +
+ ar->entry_bytes_unconsumed) < 0)
return (ARCHIVE_FATAL);
ar->entry_bytes_remaining = 0;
@@ -551,11 +515,10 @@ archive_read_format_ar_skip(struct archive_read *a)
static int
ar_parse_gnu_filename_table(struct archive_read *a)
{
- struct ar *ar;
+ struct ar *ar = a->format->data;
char *p;
size_t size;
- ar = (struct ar*)(a->format->data);
size = ar->strtab_size;
for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
@@ -587,7 +550,7 @@ bad_string_table:
}
static uint64_t
-ar_atol8(const char *p, unsigned char_cnt)
+ar_atol8(const char *p, size_t char_cnt)
{
uint64_t l;
unsigned int digit, base;
@@ -611,7 +574,7 @@ ar_atol8(const char *p, unsigned char_cnt)
}
static uint64_t
-ar_atol10(const char *p, unsigned char_cnt)
+ar_atol10(const char *p, size_t char_cnt)
{
uint64_t l;
unsigned int base, digit;
diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c
index bd82b44accc5..f90250e1952e 100644
--- a/libarchive/archive_read_support_format_cab.c
+++ b/libarchive/archive_read_support_format_cab.c
@@ -49,85 +49,186 @@
#include "archive_endian.h"
+/*
+ * Huffman coding.
+ *
+ * Array representation of a Huffman tree for codes of up to 16 bit lengths.
+ * Lookups are performed through a direct, expanded lookup table.
+ *
+ * An expanded table has as many elements as needed to cover all possible
+ * indices formable with bit patterns of given lookup_bits length.
+ *
+ * If less codes exist, these span multiple entries for all possible
+ * combinations of following bits.
+ *
+ * Example of a Huffman tree with len_size = 3, lookup_bits = 2:
+ *
+ * Symbol | Code
+ * -------+-----
+ * A | 0b0
+ * B | 0b10
+ * C | 0b11
+ *
+ * The bit sequences 0b00 and 0b01 are rightfully not covered by a code,
+ * since code 0b0 already maps to symbol A. The table will contain two
+ * entries for symbol A instead:
+ *
+ * idx | tbl[idx]
+ * -----+---------
+ * 0b00 | A
+ * 0b01 | A
+ * 0b10 | B
+ * 0b11 | C
+ *
+ * By using lookup_bits bits as a lookup, it becomes apparent that 0b00 and
+ * 0b01 point to a symbol which actually has the code 0b0. A user of this
+ * data structure must check the code bit length of a retrieved symbol after
+ * the lookup to properly advance the bit stream:
+ *
+ * idx | bitlen[idx]
+ * ----+------------
+ * A | 1
+ * B | 2
+ * C | 2
+ *
+ * Thus, a proper code sequence would be:
+ *
+ * symbol = tbl[read_bits(lookup_bits)]
+ * consume_bits(bitlen[symbol])
+ */
+struct huffman {
+ /*
+ * Amount of symbols.
+ *
+ * This implementation keeps track of unused symbols as well,
+ * thus symbols start with 0x0000 up to given symbol amount:
+ * [0..symbol_count)
+ *
+ * Used to construct tbl.
+ */
+ uint16_t symbol_count;
+ /*
+ * Frequency of code bit lengths.
+ *
+ * Represents the amount of occurrences of given bit lengths.
+ * Index 0 is used for "empty" codes (aka unused symbols),
+ * otherwise index represents the bit length
+ * (index 1 is bit length 1 and so on).
+ *
+ * Used to construct tbl.
+ */
+ uint16_t freq[17];
+ /* Map of symbols to their code bit lengths. */
+ uint8_t *bitlen;
+ /* Amount of bits to use for lookup (<= tbl_bits). */
+ uint8_t lookup_bits;
+ /*
+ * Code bit length used for allocation (<= 16).
+ *
+ * Used to construct tbl.
+ */
+ uint8_t tbl_bits;
+ /* Direct, expanded lookup table. */
+ uint16_t *tbl;
+};
+
+/*
+ * Bit stream reader.
+ */
+struct lzx_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ size_t cache_avail;
+ uint8_t odd;
+ int have_odd;
+};
+
+struct lzx_pos_tbl {
+ uint32_t base;
+ uint8_t footer_bits;
+};
+
struct lzx_dec {
/* Decoding status. */
int state;
+#define ST_RD_TRANSLATION 0
+#define ST_RD_TRANSLATION_SIZE 1
+#define ST_RD_BLOCK_TYPE 2
+#define ST_RD_BLOCK_SIZE 3
+#define ST_RD_ALIGNMENT 4
+#define ST_RD_R0 5
+#define ST_RD_R1 6
+#define ST_RD_R2 7
+#define ST_COPY_UNCOMP1 8
+#define ST_COPY_UNCOMP2 9
+#define ST_RD_ALIGNED_OFFSET 10
+#define ST_RD_VERBATIM 11
+#define ST_RD_PRE_MAIN_TREE_256 12
+#define ST_MAIN_TREE_256 13
+#define ST_RD_PRE_MAIN_TREE_REM 14
+#define ST_MAIN_TREE_REM 15
+#define ST_RD_PRE_LENGTH_TREE 16
+#define ST_LENGTH_TREE 17
+#define ST_MAIN 18
+#define ST_LENGTH 19
+#define ST_OFFSET 20
+#define ST_REAL_POS 21
+#define ST_COPY 22
/*
- * Window to see last decoded data, from 32KBi to 2MBi.
+ * Window to see last decoded data, from 32 KiB to 2 MiB.
*/
- int w_size;
- int w_mask;
+ size_t w_size;
+ size_t w_mask;
/* Window buffer, which is a loop buffer. */
- unsigned char *w_buff;
+ uint8_t *w_buff;
/* The insert position to the window. */
- int w_pos;
+ size_t w_pos;
/* The position where we can copy decoded code from the window. */
- int copy_pos;
+ size_t copy_pos;
/* The length how many bytes we can copy decoded code from
* the window. */
- int copy_len;
+ size_t copy_len;
/* Translation reversal for x86 processor CALL byte sequence(E8).
* This is used for LZX only. */
- uint32_t translation_size;
- char translation;
- char block_type;
+ int32_t translation_size;
+ int translation;
+ uint8_t block_type;
#define VERBATIM_BLOCK 1
#define ALIGNED_OFFSET_BLOCK 2
#define UNCOMPRESSED_BLOCK 3
size_t block_size;
size_t block_bytes_avail;
/* Repeated offset. */
- int r0, r1, r2;
- unsigned char rbytes[4];
- int rbytes_avail;
- int length_header;
- int position_slot;
- int offset_bits;
+ size_t r0, r1, r2;
+ uint8_t rbytes[4];
+ size_t rbytes_avail;
+ uint8_t length_header;
+ uint16_t position_slot;
+ uint8_t offset_bits;
- struct lzx_pos_tbl {
- int base;
- int footer_bits;
- } *pos_tbl;
+ struct lzx_pos_tbl *pos_tbl;
/*
* Bit stream reader.
*/
- struct lzx_br {
-#define CACHE_TYPE uint64_t
-#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
- /* Cache buffer. */
- CACHE_TYPE cache_buffer;
- /* Indicates how many bits avail in cache_buffer. */
- int cache_avail;
- unsigned char odd;
- char have_odd;
- } br;
+ struct lzx_br br;
/*
* Huffman coding.
*/
- struct huffman {
- int len_size;
- int freq[17];
- unsigned char *bitlen;
+ struct huffman at;
+ struct huffman lt;
+ struct huffman mt;
+ struct huffman pt;
- /*
- * Use an index table. It's faster than searching a huffman
- * coding tree, which is a binary tree. But usage of a large
- * index table causes L1 cache read miss many times.
- */
- int max_bits;
- int tbl_bits;
- int tree_used;
- /* Direct access table. */
- uint16_t *tbl;
- } at, lt, mt, pt;
-
- int loop;
+ uint16_t loop;
int error;
};
-static const int slots[] = {
+static const size_t slots[] = {
30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
};
#define SLOT_BASE 15
@@ -135,11 +236,11 @@ static const int slots[] = {
struct lzx_stream {
const unsigned char *next_in;
- int64_t avail_in;
- int64_t total_in;
+ size_t avail_in;
+ size_t total_in;
unsigned char *next_out;
- int64_t avail_out;
- int64_t total_out;
+ size_t avail_out;
+ size_t total_out;
struct lzx_dec *ds;
};
@@ -179,6 +280,11 @@ struct lzx_stream {
#define CFDATA_cbData 4
#define CFDATA_cbUncomp 6
+/* Limits */
+#define MAX_UNCOMPRESS_SIZE 0x8000
+#define MAX_FILE_SIZE (UINT16_MAX * MAX_UNCOMPRESS_SIZE)
+#define MAX_E8_TRANSLATION (0x8000 * MAX_UNCOMPRESS_SIZE)
+
static const char * const compression_name[] = {
"NONE",
"MSZIP",
@@ -240,8 +346,6 @@ struct cffile {
};
struct cfheader {
- /* Total bytes of all file size in a Cabinet. */
- uint32_t total_bytes;
uint32_t files_offset;
uint16_t folder_count;
uint16_t file_count;
@@ -249,7 +353,6 @@ struct cfheader {
#define PREV_CABINET 0x0001
#define NEXT_CABINET 0x0002
#define RESERVE_PRESENT 0x0004
- uint16_t setid;
uint16_t cabinet;
/* Version number. */
unsigned char major;
@@ -268,8 +371,6 @@ struct cab {
int64_t entry_offset;
int64_t entry_bytes_remaining;
int64_t entry_unconsumed;
- int64_t entry_compressed_bytes_read;
- int64_t entry_uncompressed_bytes_read;
struct cffolder *entry_cffolder;
struct cffile *entry_cffile;
struct cfdata *entry_cfdata;
@@ -314,7 +415,7 @@ static int archive_read_format_cab_read_data_skip(struct archive_read *);
static int archive_read_format_cab_cleanup(struct archive_read *);
static int cab_skip_sfx(struct archive_read *);
-static time_t cab_dos_time(const unsigned char *);
+static time_t cab_dos_time(const char *);
static int cab_read_data(struct archive_read *, const void **,
size_t *, int64_t *);
static int cab_read_header(struct archive_read *);
@@ -335,15 +436,15 @@ static int lzx_decode_init(struct lzx_stream *, int);
static int lzx_read_blocks(struct lzx_stream *, int);
static int lzx_decode_blocks(struct lzx_stream *, int);
static void lzx_decode_free(struct lzx_stream *);
-static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void lzx_translation(struct lzx_stream *, unsigned char *, size_t, int32_t);
static void lzx_cleanup_bitstream(struct lzx_stream *);
static int lzx_decode(struct lzx_stream *, int);
static int lzx_read_pre_tree(struct lzx_stream *);
-static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
-static int lzx_huffman_init(struct huffman *, size_t, int);
+static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, uint16_t);
+static int lzx_huffman_init(struct huffman *, uint16_t, uint8_t);
static void lzx_huffman_free(struct huffman *);
static int lzx_make_huffman_table(struct huffman *);
-static inline int lzx_decode_huffman(struct huffman *, unsigned);
+static uint16_t lzx_decode_huffman(struct huffman *, uint16_t);
int
@@ -465,10 +566,9 @@ static int
archive_read_format_cab_options(struct archive_read *a,
const char *key, const char *val)
{
- struct cab *cab;
+ struct cab *cab = a->format->data;
int ret = ARCHIVE_FAILED;
- cab = (struct cab *)(a->format->data);
if (strcmp(key, "hdrcharset") == 0) {
if (val == NULL || val[0] == 0)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -540,19 +640,21 @@ truncated_error(struct archive_read *a)
return (ARCHIVE_FATAL);
}
-static ssize_t
-cab_strnlen(const unsigned char *p, size_t maxlen)
+#ifdef HAVE_STRNLEN
+#define cab_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+cab_strnlen(const char *p, size_t maxlen)
{
size_t i;
- for (i = 0; i <= maxlen; i++) {
+ for (i = 0; i < maxlen; i++) {
if (p[i] == 0)
break;
}
- if (i > maxlen)
- return (-1);/* invalid */
- return ((ssize_t)i);
+ return (i);
}
+#endif
/* Read up to max remaining bytes. */
static const void *
@@ -622,11 +724,11 @@ cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)
static int
cab_read_header(struct archive_read *a)
{
- const unsigned char *p;
- struct cab *cab;
+ struct cab *cab = a->format->data;
+ const char *p;
struct cfheader *hd;
- size_t bytes, used;
- ssize_t avail, len;
+ size_t bytes, len, maxlen, used;
+ ssize_t avail;
int64_t skip;
int err, i;
int cur_folder, prev_folder;
@@ -639,7 +741,6 @@ cab_read_header(struct archive_read *a)
if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
return (truncated_error(a));
- cab = (struct cab *)(a->format->data);
if (cab->found_header == 0 &&
p[0] == 'M' && p[1] == 'Z') {
/* This is an executable? Must be self-extracting... */
@@ -663,7 +764,6 @@ cab_read_header(struct archive_read *a)
"Couldn't find out CAB header");
return (ARCHIVE_FATAL);
}
- hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
hd->minor = p[CFHEADER_versionMinor];
hd->major = p[CFHEADER_versionMajor];
@@ -674,7 +774,6 @@ cab_read_header(struct archive_read *a)
if (hd->file_count == 0)
goto invalid;
hd->flags = archive_le16dec(p + CFHEADER_flags);
- hd->setid = archive_le16dec(p + CFHEADER_setID);
hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
used = CFHEADER_iCabinet + 2;
if (hd->flags & RESERVE_PRESENT) {
@@ -693,7 +792,9 @@ cab_read_header(struct archive_read *a)
if ((p = cab_read_ahead_remaining(a, used + 256,
&avail)) == NULL || (size_t)avail <= used)
return (truncated_error(a));
- if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0) {
+ maxlen = avail - used;
+ len = cab_strnlen(p + used, maxlen);
+ if (len == 0 || len == maxlen) {
goto invalid;
}
used += len + 1;
@@ -701,7 +802,9 @@ cab_read_header(struct archive_read *a)
if ((p = cab_read_ahead_remaining(a, used + 256,
&avail)) == NULL || (size_t)avail <= used)
return (truncated_error(a));
- if ((len = cab_strnlen(p + used, avail - used - 1)) < 0)
+ maxlen = avail - used;
+ len = cab_strnlen(p + used, maxlen);
+ if (len == maxlen)
goto invalid;
used += len + 1;
}
@@ -710,14 +813,18 @@ cab_read_header(struct archive_read *a)
if ((p = cab_read_ahead_remaining(a, used + 256,
&avail)) == NULL || (size_t)avail <= used)
return (truncated_error(a));
- if ((len = cab_strnlen(p + used, avail - used - 1)) <= 0)
+ maxlen = avail - used;
+ len = cab_strnlen(p + used, maxlen);
+ if (len == 0 || len == maxlen)
goto invalid;
used += len + 1;
/* How many bytes are used for szDiskNext. */
if ((p = cab_read_ahead_remaining(a, used + 256,
&avail)) == NULL || (size_t)avail <= used)
return (truncated_error(a));
- if ((len = cab_strnlen(p + used, avail - used - 1)) < 0)
+ maxlen = avail - used;
+ len = cab_strnlen(p + used, maxlen);
+ if (len == maxlen)
goto invalid;
used += len + 1;
}
@@ -787,7 +894,8 @@ cab_read_header(struct archive_read *a)
return (ARCHIVE_FATAL);
}
if (skip) {
- __archive_read_consume(a, skip);
+ if (__archive_read_consume(a, skip) < 0)
+ return (truncated_error(a));
cab->cab_offset += skip;
}
/* Allocate memory for CFDATA */
@@ -812,7 +920,9 @@ cab_read_header(struct archive_read *a)
cab->cab_offset += 16;
if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
return (truncated_error(a));
- if ((len = cab_strnlen(p, avail-1)) <= 0)
+ maxlen = avail;
+ len = cab_strnlen(p, maxlen);
+ if (len == 0 || len == maxlen)
goto invalid;
/* Copy a pathname. */
@@ -824,10 +934,10 @@ cab_read_header(struct archive_read *a)
/*
* Sanity check if each data is acceptable.
*/
- if (file->uncompressed_size > 0x7FFF8000)
+ if (file->uncompressed_size > MAX_FILE_SIZE)
goto invalid;/* Too large */
if ((int64_t)file->offset + (int64_t)file->uncompressed_size
- > ARCHIVE_LITERAL_LL(0x7FFF8000))
+ > (int64_t)MAX_FILE_SIZE)
goto invalid;/* Too large */
switch (file->folder) {
case iFoldCONTINUED_TO_NEXT:
@@ -893,14 +1003,13 @@ static int
archive_read_format_cab_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct cab *cab;
+ struct cab *cab = a->format->data;
struct cfheader *hd;
struct cffolder *prev_folder;
struct cffile *file;
struct archive_string_conv *sconv;
int err = ARCHIVE_OK, r;
-
- cab = (struct cab *)(a->format->data);
+
if (cab->found_header == 0) {
err = cab_read_header(a);
if (err < ARCHIVE_WARN)
@@ -918,8 +1027,6 @@ archive_read_format_cab_read_header(struct archive_read *a,
cab->end_of_entry = 0;
cab->end_of_entry_cleanup = 0;
- cab->entry_compressed_bytes_read = 0;
- cab->entry_uncompressed_bytes_read = 0;
cab->entry_unconsumed = 0;
cab->entry_cffile = file;
@@ -1017,7 +1124,7 @@ static int
archive_read_format_cab_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
int r;
switch (cab->entry_cffile->folder) {
@@ -1117,7 +1224,7 @@ cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)
static void
cab_checksum_update(struct archive_read *a, size_t bytes)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata = cab->entry_cfdata;
const unsigned char *p;
size_t sumbytes;
@@ -1157,7 +1264,7 @@ cab_checksum_update(struct archive_read *a, size_t bytes)
static int
cab_checksum_finish(struct archive_read *a)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata = cab->entry_cfdata;
int l;
@@ -1202,7 +1309,7 @@ cab_checksum_finish(struct archive_read *a)
static int
cab_next_cfdata(struct archive_read *a)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata = cab->entry_cfdata;
/* There are remaining bytes in current CFDATA, use it first. */
@@ -1280,9 +1387,9 @@ cab_next_cfdata(struct archive_read *a)
* Sanity check if data size is acceptable.
*/
if (cfdata->compressed_size == 0 ||
- cfdata->compressed_size > (0x8000+6144))
+ cfdata->compressed_size > (MAX_UNCOMPRESS_SIZE + 6144))
goto invalid;
- if (cfdata->uncompressed_size > 0x8000)
+ if (cfdata->uncompressed_size > MAX_UNCOMPRESS_SIZE)
goto invalid;
if (cfdata->uncompressed_size == 0) {
switch (cab->entry_cffile->folder) {
@@ -1295,10 +1402,10 @@ cab_next_cfdata(struct archive_read *a)
}
}
/* If CFDATA is not last in a folder, an uncompressed
- * size must be 0x8000(32KBi) */
+ * size must be 0x8000 (32 KiB) */
if ((cab->entry_cffolder->cfdata_index <
cab->entry_cffolder->cfdata_count) &&
- cfdata->uncompressed_size != 0x8000)
+ cfdata->uncompressed_size != MAX_UNCOMPRESS_SIZE)
goto invalid;
/* A compressed data size and an uncompressed data size must
@@ -1339,10 +1446,6 @@ cab_next_cfdata(struct archive_read *a)
}
return (ARCHIVE_OK);
invalid:
- cfdata->compressed_size = 0;
- cfdata->compressed_bytes_remaining = 0;
- cfdata->uncompressed_size = 0;
- cfdata->uncompressed_bytes_remaining = 0;
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid CFDATA");
return (ARCHIVE_FATAL);
@@ -1354,7 +1457,7 @@ invalid:
static const void *
cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
int err;
err = cab_next_cfdata(a);
@@ -1385,7 +1488,7 @@ cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
static const void *
cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata;
const void *d;
@@ -1417,7 +1520,7 @@ cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
static const void *
cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata;
const void *d;
int r, mszip;
@@ -1427,7 +1530,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
cfdata = cab->entry_cfdata;
/* If the buffer hasn't been allocated, allocate it now. */
if (cab->uncompressed_buffer == NULL) {
- cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE;
cab->uncompressed_buffer
= malloc(cab->uncompressed_buffer_size);
if (cab->uncompressed_buffer == NULL) {
@@ -1485,14 +1588,6 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
cab->uncompressed_buffer + cab->stream.total_out;
cab->stream.avail_out =
cfdata->uncompressed_size - cab->stream.total_out;
- if ((size_t)cfdata->uncompressed_size >
- cab->uncompressed_buffer_size) {
- archive_set_error(&a->archive,
- ARCHIVE_ERRNO_FILE_FORMAT,
- "Invalid CFDATA uncompressed size");
- *avail = ARCHIVE_FATAL;
- return (NULL);
- }
d = __archive_read_ahead(a, 1, &bytes_avail);
if (bytes_avail <= 0) {
@@ -1653,7 +1748,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
static const void *
cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata;
const void *d;
int r;
@@ -1662,7 +1757,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
cfdata = cab->entry_cfdata;
/* If the buffer hasn't been allocated, allocate it now. */
if (cab->uncompressed_buffer == NULL) {
- cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer_size = MAX_UNCOMPRESS_SIZE;
cab->uncompressed_buffer
= malloc(cab->uncompressed_buffer_size);
if (cab->uncompressed_buffer == NULL) {
@@ -1703,13 +1798,6 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
cab->uncompressed_buffer + cab->xstrm.total_out;
cab->xstrm.avail_out =
cfdata->uncompressed_size - cab->xstrm.total_out;
-
- if ((size_t)cfdata->uncompressed_size > cab->uncompressed_buffer_size) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Invalid CFDATA uncompressed size");
- *avail = ARCHIVE_FATAL;
- return (NULL);
- }
d = __archive_read_ahead(a, 1, &bytes_avail);
if (d == NULL) {
@@ -1769,7 +1857,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
*/
lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
cfdata->uncompressed_size,
- (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+ (cab->entry_cffolder->cfdata_index - 1) * MAX_UNCOMPRESS_SIZE);
d = cab->uncompressed_buffer + cfdata->read_offset;
*avail = uavail - cfdata->read_offset;
@@ -1790,7 +1878,7 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
static int64_t
cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata;
int64_t cbytes, rbytes;
int err;
@@ -1883,7 +1971,7 @@ cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
static int64_t
cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfdata *cfdata;
int64_t cbytes, rbytes;
int err;
@@ -1940,7 +2028,7 @@ static int
cab_read_data(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
ssize_t bytes_avail;
if (cab->entry_bytes_remaining == 0) {
@@ -1986,12 +2074,10 @@ cab_read_data(struct archive_read *a, const void **buff,
static int
archive_read_format_cab_read_data_skip(struct archive_read *a)
{
- struct cab *cab;
+ struct cab *cab = a->format->data;
int64_t bytes_skipped;
int r;
- cab = (struct cab *)(a->format->data);
-
if (cab->end_of_archive)
return (ARCHIVE_EOF);
@@ -2041,9 +2127,9 @@ archive_read_format_cab_read_data_skip(struct archive_read *a)
static int
archive_read_format_cab_cleanup(struct archive_read *a)
{
- struct cab *cab = (struct cab *)(a->format->data);
+ struct cab *cab = a->format->data;
struct cfheader *hd = &cab->cfheader;
- int i;
+ uint16_t i;
if (hd->folder_array != NULL) {
for (i = 0; i < hd->folder_count; i++)
@@ -2063,13 +2149,13 @@ archive_read_format_cab_cleanup(struct archive_read *a)
archive_wstring_free(&cab->ws);
free(cab->uncompressed_buffer);
free(cab);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
/* Convert an MSDOS-style date/time into Unix-style time. */
static time_t
-cab_dos_time(const unsigned char *p)
+cab_dos_time(const char *p)
{
int msTime, msDate;
struct tm ts;
@@ -2105,10 +2191,11 @@ cab_dos_time(const unsigned char *p)
static int
lzx_decode_init(struct lzx_stream *strm, int w_bits)
{
- struct lzx_dec *ds;
- int slot, w_size, w_slot;
- int base, footer;
int base_inc[18];
+ struct lzx_dec *ds;
+ uint32_t base;
+ uint16_t slot, w_size, w_slot;
+ uint8_t footer;
if (strm->ds == NULL) {
strm->ds = calloc(1, sizeof(*strm->ds));
@@ -2118,7 +2205,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits)
ds = strm->ds;
ds->error = ARCHIVE_FAILED;
- /* Allow bits from 15(32KBi) up to 21(2MBi) */
+ /* Allow bits from 15 (32 KiB) up to 21 (2 MiB) */
if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
return (ARCHIVE_FAILED);
@@ -2130,7 +2217,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits)
w_size = ds->w_size;
w_slot = slots[w_bits - SLOT_BASE];
ds->w_size = 1U << w_bits;
- ds->w_mask = ds->w_size -1;
+ ds->w_mask = ds->w_size - 1;
if (ds->w_buff == NULL || w_size != ds->w_size) {
free(ds->w_buff);
ds->w_buff = malloc(ds->w_size);
@@ -2152,18 +2239,20 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits)
else
base += base_inc[footer];
if (footer < 17) {
- footer = -2;
+ footer = 0;
for (n = base; n; n >>= 1)
footer++;
- if (footer <= 0)
+ if (footer <= 2)
footer = 0;
+ else
+ footer -= 2;
}
ds->pos_tbl[slot].base = base;
ds->pos_tbl[slot].footer_bits = footer;
}
ds->w_pos = 0;
- ds->state = 0;
+ ds->state = ST_RD_TRANSLATION;
ds->br.cache_buffer = 0;
ds->br.cache_avail = 0;
ds->r0 = ds->r1 = ds->r2 = 1;
@@ -2177,7 +2266,7 @@ lzx_decode_init(struct lzx_stream *strm, int w_bits)
return (ARCHIVE_FATAL);
/* Initialize Main tree. */
- if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+ if (lzx_huffman_init(&(ds->mt), 256 + (w_slot << 3), 16)
!= ARCHIVE_OK)
return (ARCHIVE_FATAL);
@@ -2213,29 +2302,36 @@ lzx_decode_free(struct lzx_stream *strm)
* E8 Call Translation reversal.
*/
static void
-lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+lzx_translation(struct lzx_stream *strm, unsigned char *buffer, size_t size,
+ int32_t offset)
{
struct lzx_dec *ds = strm->ds;
- unsigned char *b, *end;
+ unsigned char *p, *end;
- if (!ds->translation || size <= 10)
+ if (!ds->translation || offset >= MAX_E8_TRANSLATION || size <= 10)
return;
- b = p;
- end = b + size - 10;
- while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
- size_t i = b - (unsigned char *)p;
- int32_t cp, displacement, value;
- cp = (int32_t)(offset + (uint32_t)i);
- value = archive_le32dec(&b[1]);
- if (value >= -cp && value < (int32_t)ds->translation_size) {
- if (value >= 0)
- displacement = value - cp;
+ p = buffer;
+ end = buffer + size - 10;
+
+ while (p < end && (p = memchr(p, 0xE8, end - p)) != NULL) {
+ int32_t address, position;
+
+ address = archive_le32dec(p + 1);
+ position = offset + (p - buffer);
+
+ if (address >= -position && address < ds->translation_size) {
+ uint32_t relative;
+
+ if (address >= 0)
+ relative = address - position;
else
- displacement = value + ds->translation_size;
- archive_le32enc(&b[1], (uint32_t)displacement);
+ relative = address + ds->translation_size;
+
+ archive_le32enc(p + 1, relative);
}
- b += 5;
+
+ p += 5;
}
}
@@ -2257,7 +2353,7 @@ lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
* False : we met that strm->next_in is empty, we have to get following
* bytes. */
#define lzx_br_read_ahead_0(strm, br, n) \
- (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
+ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br) == ARCHIVE_OK)
/* True : the cache buffer has some bits as much as we need.
* False : there are no enough bits in the cache buffer to be used,
* we have to get following bytes if we could. */
@@ -2286,8 +2382,8 @@ static const uint32_t cache_masks[] = {
* Shift away used bits in the cache data and fill it up with following bits.
* Call this when cache buffer does not have enough bits you need.
*
- * Returns 1 if the cache buffer is full.
- * Returns 0 if the cache buffer is not full; input buffer is empty.
+ * Returns ARCHIVE_OK if the cache buffer is full.
+ * Returns ARCHIVE_EOF if the cache buffer is not full; input buffer is empty.
*/
static int
lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
@@ -2295,7 +2391,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
/*
* x86 processor family can read misaligned data without an access error.
*/
- int n = CACHE_BITS - br->cache_avail;
+ ssize_t n = CACHE_BITS - br->cache_avail;
for (;;) {
switch (n >> 4) {
@@ -2313,7 +2409,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
strm->next_in += 8;
strm->avail_in -= 8;
br->cache_avail += 8 * 8;
- return (1);
+ return (ARCHIVE_OK);
}
break;
case 3:
@@ -2329,13 +2425,13 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
strm->next_in += 6;
strm->avail_in -= 6;
br->cache_avail += 6 * 8;
- return (1);
+ return (ARCHIVE_OK);
}
break;
case 0:
/* We have enough compressed data in
* the cache buffer.*/
- return (1);
+ return (ARCHIVE_EOF);
default:
break;
}
@@ -2347,7 +2443,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
strm->avail_in--;
br->have_odd = 1;
}
- return (0);
+ return (ARCHIVE_EOF);
}
br->cache_buffer =
(br->cache_buffer << 16) |
@@ -2362,7 +2458,7 @@ lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
static void
lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
{
- int n = CACHE_BITS - br->cache_avail;
+ ssize_t n = CACHE_BITS - br->cache_avail;
if (br->have_odd && n >= 16 && strm->avail_in > 0) {
br->cache_buffer =
@@ -2391,35 +2487,11 @@ lzx_cleanup_bitstream(struct lzx_stream *strm)
* 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
* is broken or you do not set 'last' flag properly.
*/
-#define ST_RD_TRANSLATION 0
-#define ST_RD_TRANSLATION_SIZE 1
-#define ST_RD_BLOCK_TYPE 2
-#define ST_RD_BLOCK_SIZE 3
-#define ST_RD_ALIGNMENT 4
-#define ST_RD_R0 5
-#define ST_RD_R1 6
-#define ST_RD_R2 7
-#define ST_COPY_UNCOMP1 8
-#define ST_COPY_UNCOMP2 9
-#define ST_RD_ALIGNED_OFFSET 10
-#define ST_RD_VERBATIM 11
-#define ST_RD_PRE_MAIN_TREE_256 12
-#define ST_MAIN_TREE_256 13
-#define ST_RD_PRE_MAIN_TREE_REM 14
-#define ST_MAIN_TREE_REM 15
-#define ST_RD_PRE_LENGTH_TREE 16
-#define ST_LENGTH_TREE 17
-#define ST_MAIN 18
-#define ST_LENGTH 19
-#define ST_OFFSET 20
-#define ST_REAL_POS 21
-#define ST_COPY 22
-
static int
lzx_decode(struct lzx_stream *strm, int last)
{
struct lzx_dec *ds = strm->ds;
- int64_t avail_in;
+ size_t avail_in;
int r;
if (ds->error)
@@ -2431,7 +2503,8 @@ lzx_decode(struct lzx_stream *strm, int last)
if (ds->state < ST_MAIN)
r = lzx_read_blocks(strm, last);
else {
- int64_t bytes_written = strm->avail_out;
+ size_t bytes_written = strm->avail_out;
+
r = lzx_decode_blocks(strm, last);
bytes_written -= strm->avail_out;
strm->next_out += bytes_written;
@@ -2447,7 +2520,8 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
{
struct lzx_dec *ds = strm->ds;
struct lzx_br *br = &(ds->br);
- int i, r;
+ int r;
+ uint16_t i;
for (;;) {
switch (ds->state) {
@@ -2463,16 +2537,21 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/* FALL THROUGH */
case ST_RD_TRANSLATION_SIZE:
if (ds->translation) {
+ uint32_t v;
+
if (!lzx_br_read_ahead(strm, br, 32)) {
ds->state = ST_RD_TRANSLATION_SIZE;
if (last)
goto failed;
return (ARCHIVE_OK);
}
- ds->translation_size = lzx_br_bits(br, 16);
+ v = lzx_br_bits(br, 16);
lzx_br_consume(br, 16);
- ds->translation_size <<= 16;
- ds->translation_size |= lzx_br_bits(br, 16);
+ v <<= 16;
+ v |= lzx_br_bits(br, 16);
+ if (v > MAX_FILE_SIZE)
+ goto failed;
+ ds->translation_size = (int32_t)v;
lzx_br_consume(br, 16);
}
/* FALL THROUGH */
@@ -2579,17 +2658,17 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
ds->rbytes_avail = 0;
if (ds->state == ST_RD_R0) {
ds->r0 = archive_le32dec(ds->rbytes);
- if (ds->r0 < 0)
+ if (ds->r0 > (size_t)INT32_MAX)
goto failed;
ds->state = ST_RD_R1;
} else if (ds->state == ST_RD_R1) {
ds->r1 = archive_le32dec(ds->rbytes);
- if (ds->r1 < 0)
+ if (ds->r1 > (size_t)INT32_MAX)
goto failed;
ds->state = ST_RD_R2;
} else if (ds->state == ST_RD_R2) {
ds->r2 = archive_le32dec(ds->rbytes);
- if (ds->r2 < 0)
+ if (ds->r2 > (size_t)INT32_MAX)
goto failed;
/* We've gotten all repeated offsets. */
ds->state = ST_COPY_UNCOMP1;
@@ -2598,10 +2677,10 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/* FALL THROUGH */
case ST_COPY_UNCOMP1:
/*
- * Copy bytes form next_in to next_out directly.
+ * Copy bytes from next_in to next_out directly.
*/
while (ds->block_bytes_avail) {
- int l;
+ size_t l;
if (strm->avail_out <= 0)
/* Output buffer is empty. */
@@ -2612,13 +2691,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
goto failed;
return (ARCHIVE_OK);
}
- l = (int)ds->block_bytes_avail;
+ l = ds->block_bytes_avail;
if (l > ds->w_size - ds->w_pos)
l = ds->w_size - ds->w_pos;
if (l > strm->avail_out)
- l = (int)strm->avail_out;
+ l = strm->avail_out;
if (l > strm->avail_in)
- l = (int)strm->avail_in;
+ l = strm->avail_in;
memcpy(strm->next_out, strm->next_in, l);
memcpy(&(ds->w_buff[ds->w_pos]),
strm->next_in, l);
@@ -2652,19 +2731,19 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/*
* Read Aligned offset tree.
*/
- if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
+ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.symbol_count)) {
ds->state = ST_RD_ALIGNED_OFFSET;
if (last)
goto failed;
return (ARCHIVE_OK);
}
memset(ds->at.freq, 0, sizeof(ds->at.freq));
- for (i = 0; i < ds->at.len_size; i++) {
+ for (i = 0; i < ds->at.symbol_count; i++) {
ds->at.bitlen[i] = lzx_br_bits(br, 3);
ds->at.freq[ds->at.bitlen[i]]++;
lzx_br_consume(br, 3);
}
- if (!lzx_make_huffman_table(&ds->at))
+ if (lzx_make_huffman_table(&ds->at) < 0)
goto failed;
/* FALL THROUGH */
case ST_RD_VERBATIM:
@@ -2674,13 +2753,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/*
* Read Pre-tree for first 256 elements of main tree.
*/
- if (!lzx_read_pre_tree(strm)) {
+ if (lzx_read_pre_tree(strm) < 0) {
ds->state = ST_RD_PRE_MAIN_TREE_256;
if (last)
goto failed;
return (ARCHIVE_OK);
}
- if (!lzx_make_huffman_table(&ds->pt))
+ if (lzx_make_huffman_table(&ds->pt) < 0)
goto failed;
ds->loop = 0;
/* FALL THROUGH */
@@ -2689,27 +2768,26 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
* Get path lengths of first 256 elements of main tree.
*/
r = lzx_read_bitlen(strm, &ds->mt, 256);
- if (r < 0)
- goto failed;
- else if (!r) {
+ if (r == ARCHIVE_EOF) {
ds->state = ST_MAIN_TREE_256;
if (last)
goto failed;
return (ARCHIVE_OK);
- }
+ } else if (r < 0)
+ goto failed;
ds->loop = 0;
/* FALL THROUGH */
case ST_RD_PRE_MAIN_TREE_REM:
/*
* Read Pre-tree for remaining elements of main tree.
*/
- if (!lzx_read_pre_tree(strm)) {
+ if (lzx_read_pre_tree(strm) < 0) {
ds->state = ST_RD_PRE_MAIN_TREE_REM;
if (last)
goto failed;
return (ARCHIVE_OK);
}
- if (!lzx_make_huffman_table(&ds->pt))
+ if (lzx_make_huffman_table(&ds->pt) < 0)
goto failed;
ds->loop = 256;
/* FALL THROUGH */
@@ -2717,16 +2795,15 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/*
* Get path lengths of remaining elements of main tree.
*/
- r = lzx_read_bitlen(strm, &ds->mt, -1);
- if (r < 0)
- goto failed;
- else if (!r) {
+ r = lzx_read_bitlen(strm, &ds->mt, 0);
+ if (r == ARCHIVE_EOF) {
ds->state = ST_MAIN_TREE_REM;
if (last)
goto failed;
return (ARCHIVE_OK);
- }
- if (!lzx_make_huffman_table(&ds->mt))
+ } else if (r < 0)
+ goto failed;
+ if (lzx_make_huffman_table(&ds->mt) < 0)
goto failed;
ds->loop = 0;
/* FALL THROUGH */
@@ -2734,13 +2811,13 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/*
* Read Pre-tree for remaining elements of main tree.
*/
- if (!lzx_read_pre_tree(strm)) {
+ if (lzx_read_pre_tree(strm) < 0) {
ds->state = ST_RD_PRE_LENGTH_TREE;
if (last)
goto failed;
return (ARCHIVE_OK);
}
- if (!lzx_make_huffman_table(&ds->pt))
+ if (lzx_make_huffman_table(&ds->pt) < 0)
goto failed;
ds->loop = 0;
/* FALL THROUGH */
@@ -2748,16 +2825,15 @@ lzx_read_blocks(struct lzx_stream *strm, int last)
/*
* Get path lengths of remaining elements of main tree.
*/
- r = lzx_read_bitlen(strm, &ds->lt, -1);
- if (r < 0)
- goto failed;
- else if (!r) {
+ r = lzx_read_bitlen(strm, &ds->lt, 0);
+ if (r == ARCHIVE_EOF) {
ds->state = ST_LENGTH_TREE;
if (last)
goto failed;
return (ARCHIVE_OK);
- }
- if (!lzx_make_huffman_table(&ds->lt))
+ } else if (r < 0)
+ goto failed;
+ if (lzx_make_huffman_table(&ds->lt) < 0)
goto failed;
ds->state = ST_MAIN;
return (100);
@@ -2776,22 +2852,23 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
unsigned char *noutp = strm->next_out;
unsigned char *endp = noutp + strm->avail_out;
- unsigned char *w_buff = ds->w_buff;
- unsigned char *at_bitlen = at->bitlen;
- unsigned char *lt_bitlen = lt->bitlen;
- unsigned char *mt_bitlen = mt->bitlen;
+ uint8_t *w_buff = ds->w_buff;
+ uint8_t *at_bitlen = at->bitlen;
+ uint8_t *lt_bitlen = lt->bitlen;
+ uint8_t *mt_bitlen = mt->bitlen;
size_t block_bytes_avail = ds->block_bytes_avail;
- int at_max_bits = at->max_bits;
- int lt_max_bits = lt->max_bits;
- int mt_max_bits = mt->max_bits;
- int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
- int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
- int length_header = ds->length_header;
- int offset_bits = ds->offset_bits;
- int position_slot = ds->position_slot;
- int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+ uint8_t at_lookup_bits = at->lookup_bits;
+ uint8_t lt_lookup_bits = lt->lookup_bits;
+ uint8_t mt_lookup_bits = mt->lookup_bits;
+ size_t copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+ size_t w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+ uint8_t length_header = ds->length_header;
+ uint8_t offset_bits = ds->offset_bits;
+ uint16_t position_slot = ds->position_slot;
+ size_t r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
int state = ds->state;
- char block_type = ds->block_type;
+ uint16_t c;
+ uint8_t block_type = ds->block_type;
for (;;) {
switch (state) {
@@ -2817,26 +2894,26 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
goto next_data;
if (!lzx_br_read_ahead(strm, &bre,
- mt_max_bits)) {
+ mt_lookup_bits)) {
if (!last)
goto next_data;
/* Remaining bits are less than
- * maximum bits(mt.max_bits) but maybe
- * it still remains as much as we need,
- * so we should try to use it with
- * dummy bits. */
+ * maximum bits (mt.lookup_bits) but
+ * maybe it still remains as much as we
+ * need, so we should try to use it
+ * with dummy bits. */
c = lzx_decode_huffman(mt,
lzx_br_bits_forced(
- &bre, mt_max_bits));
- lzx_br_consume(&bre, mt_bitlen[c]);
- if (!lzx_br_has(&bre, 0))
+ &bre, mt_lookup_bits));
+ if (!lzx_br_has(&bre, mt_bitlen[c]))
goto failed;/* Over read. */
+ lzx_br_consume(&bre, mt_bitlen[c]);
} else {
c = lzx_decode_huffman(mt,
- lzx_br_bits(&bre, mt_max_bits));
+ lzx_br_bits(&bre, mt_lookup_bits));
lzx_br_consume(&bre, mt_bitlen[c]);
}
- if ((unsigned int)c > UCHAR_MAX)
+ if (c > UCHAR_MAX)
break;
/*
* 'c' is exactly literal code.
@@ -2862,26 +2939,26 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
*/
if (length_header == 7) {
if (!lzx_br_read_ahead(strm, &bre,
- lt_max_bits)) {
+ lt_lookup_bits)) {
if (!last) {
state = ST_LENGTH;
goto next_data;
}
c = lzx_decode_huffman(lt,
lzx_br_bits_forced(
- &bre, lt_max_bits));
- lzx_br_consume(&bre, lt_bitlen[c]);
- if (!lzx_br_has(&bre, 0))
+ &bre, lt_lookup_bits));
+ if (!lzx_br_has(&bre, lt_bitlen[c]))
goto failed;/* Over read. */
+ lzx_br_consume(&bre, lt_bitlen[c]);
} else {
c = lzx_decode_huffman(lt,
- lzx_br_bits(&bre, lt_max_bits));
+ lzx_br_bits(&bre, lt_lookup_bits));
lzx_br_consume(&bre, lt_bitlen[c]);
}
copy_len = c + 7 + 2;
} else
copy_len = length_header + 2;
- if ((size_t)copy_len > block_bytes_avail)
+ if (copy_len > block_bytes_avail)
goto failed;
/*
* Get an offset.
@@ -2918,7 +2995,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
*/
if (block_type == ALIGNED_OFFSET_BLOCK &&
offset_bits >= 3) {
- int offbits = offset_bits - 3;
+ unsigned offbits = offset_bits - 3;
if (!lzx_br_read_ahead(strm, &bre, offbits)) {
state = ST_OFFSET;
@@ -2930,7 +3007,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
/* Get an aligned number. */
if (!lzx_br_read_ahead(strm, &bre,
- offbits + at_max_bits)) {
+ offbits + at_lookup_bits)) {
if (!last) {
state = ST_OFFSET;
goto next_data;
@@ -2938,14 +3015,14 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
lzx_br_consume(&bre, offbits);
c = lzx_decode_huffman(at,
lzx_br_bits_forced(&bre,
- at_max_bits));
- lzx_br_consume(&bre, at_bitlen[c]);
- if (!lzx_br_has(&bre, 0))
+ at_lookup_bits));
+ if (!lzx_br_has(&bre, at_bitlen[c]))
goto failed;/* Over read. */
+ lzx_br_consume(&bre, at_bitlen[c]);
} else {
lzx_br_consume(&bre, offbits);
c = lzx_decode_huffman(at,
- lzx_br_bits(&bre, at_max_bits));
+ lzx_br_bits(&bre, at_lookup_bits));
lzx_br_consume(&bre, at_bitlen[c]);
}
/* Add an aligned number. */
@@ -2961,7 +3038,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
copy_pos = lzx_br_bits(&bre, offset_bits);
lzx_br_consume(&bre, offset_bits);
}
- copy_pos += pos_tbl[position_slot].base -2;
+ copy_pos += pos_tbl[position_slot].base - 2;
/* Update repeated offset LRU queue. */
r2 = r1;
@@ -2980,8 +3057,8 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
* into the output buffer.
*/
for (;;) {
- const unsigned char *s;
- int l;
+ const uint8_t *s;
+ size_t l;
l = copy_len;
if (copy_pos > w_pos) {
@@ -2992,15 +3069,15 @@ lzx_decode_blocks(struct lzx_stream *strm, int last)
l = w_size - w_pos;
}
if (noutp + l >= endp)
- l = (int)(endp - noutp);
+ l = endp - noutp;
s = w_buff + copy_pos;
if (l >= 8 && ((copy_pos + l < w_pos)
|| (w_pos + l < copy_pos))) {
memcpy(w_buff + w_pos, s, l);
memcpy(noutp, s, l);
} else {
- unsigned char *d;
- int li;
+ uint8_t *d;
+ size_t li;
d = w_buff + w_pos;
for (li = 0; li < l; li++)
@@ -3046,122 +3123,124 @@ lzx_read_pre_tree(struct lzx_stream *strm)
{
struct lzx_dec *ds = strm->ds;
struct lzx_br *br = &(ds->br);
- int i;
+ uint16_t i;
if (ds->loop == 0)
memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
- for (i = ds->loop; i < ds->pt.len_size; i++) {
+ for (i = ds->loop; i < ds->pt.symbol_count; i++) {
if (!lzx_br_read_ahead(strm, br, 4)) {
ds->loop = i;
- return (0);
+ return (ARCHIVE_EOF);
}
ds->pt.bitlen[i] = lzx_br_bits(br, 4);
ds->pt.freq[ds->pt.bitlen[i]]++;
lzx_br_consume(br, 4);
}
ds->loop = i;
- return (1);
+ return (ARCHIVE_OK);
}
/*
* Read a bunch of bit-lengths from pre-tree.
*/
static int
-lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, uint16_t end)
{
struct lzx_dec *ds = strm->ds;
struct lzx_br *br = &(ds->br);
- int c, i, j, ret, same;
- unsigned rbits;
+ int ret;
+ uint16_t c, i, j, rbits, same;
i = ds->loop;
if (i == 0)
memset(d->freq, 0, sizeof(d->freq));
- ret = 0;
- if (end < 0)
- end = d->len_size;
+ ret = ARCHIVE_EOF;
+ if (end == 0)
+ end = d->symbol_count;
while (i < end) {
ds->loop = i;
- if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
+ if (!lzx_br_read_ahead(strm, br, (unsigned)ds->pt.lookup_bits))
goto getdata;
- rbits = lzx_br_bits(br, ds->pt.max_bits);
+ rbits = lzx_br_bits(br, ds->pt.lookup_bits);
c = lzx_decode_huffman(&(ds->pt), rbits);
switch (c) {
case 17:/* several zero lengths, from 4 to 19. */
- if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 4U))
goto getdata;
lzx_br_consume(br, ds->pt.bitlen[c]);
same = lzx_br_bits(br, 4) + 4;
- if (i + same > end)
- return (-1);/* Invalid */
+ if (same > end - i)
+ return (ARCHIVE_FATAL);
lzx_br_consume(br, 4);
for (j = 0; j < same; j++)
d->bitlen[i++] = 0;
break;
case 18:/* many zero lengths, from 20 to 51. */
- if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c] + 5U))
goto getdata;
lzx_br_consume(br, ds->pt.bitlen[c]);
same = lzx_br_bits(br, 5) + 20;
- if (i + same > end)
- return (-1);/* Invalid */
+ if (same > end - i)
+ return (ARCHIVE_FATAL);
lzx_br_consume(br, 5);
memset(d->bitlen + i, 0, same);
i += same;
break;
case 19:/* a few same lengths. */
if (!lzx_br_read_ahead(strm, br,
- ds->pt.bitlen[c]+1+ds->pt.max_bits))
+ ds->pt.bitlen[c] + 1U + ds->pt.lookup_bits))
goto getdata;
lzx_br_consume(br, ds->pt.bitlen[c]);
same = lzx_br_bits(br, 1) + 4;
- if (i + same > end)
- return (-1);
+ if (same > end - i)
+ return (ARCHIVE_FATAL);
lzx_br_consume(br, 1);
- rbits = lzx_br_bits(br, ds->pt.max_bits);
+ rbits = lzx_br_bits(br, ds->pt.lookup_bits);
c = lzx_decode_huffman(&(ds->pt), rbits);
lzx_br_consume(br, ds->pt.bitlen[c]);
- c = (d->bitlen[i] - c + 17) % 17;
- if (c < 0)
- return (-1);/* Invalid */
+ if (c > d->bitlen[i] + 17)
+ return (ARCHIVE_FATAL);
+ c = (d->bitlen[i] + 17 - c) % 17;
for (j = 0; j < same; j++)
d->bitlen[i++] = c;
d->freq[c] += same;
break;
default:
lzx_br_consume(br, ds->pt.bitlen[c]);
- c = (d->bitlen[i] - c + 17) % 17;
- if (c < 0)
- return (-1);/* Invalid */
+ if (c > d->bitlen[i] + 17)
+ return (ARCHIVE_FATAL);
+ c = (d->bitlen[i] + 17 - c) % 17;
d->freq[c]++;
d->bitlen[i++] = c;
break;
}
}
- ret = 1;
+ ret = ARCHIVE_OK;
getdata:
ds->loop = i;
return (ret);
}
static int
-lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+lzx_huffman_init(struct huffman *hf, uint16_t symbol_count, uint8_t tbl_bits)
{
+ size_t tbl_size = (size_t)1 << tbl_bits;
- if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
+ if (hf->bitlen == NULL || hf->symbol_count != symbol_count) {
free(hf->bitlen);
- hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0]));
+ hf->bitlen = calloc(symbol_count, sizeof(hf->bitlen[0]));
if (hf->bitlen == NULL)
return (ARCHIVE_FATAL);
- hf->len_size = (int)len_size;
+ hf->symbol_count = symbol_count;
} else
- memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0]));
+ memset(hf->bitlen, 0, symbol_count * sizeof(hf->bitlen[0]));
if (hf->tbl == NULL) {
- hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
+ hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0]));
if (hf->tbl == NULL)
return (ARCHIVE_FATAL);
hf->tbl_bits = tbl_bits;
- }
+ } else
+ memset(hf->tbl, 0, tbl_size * sizeof(hf->bitlen[0]));
return (ARCHIVE_OK);
}
@@ -3173,19 +3252,47 @@ lzx_huffman_free(struct huffman *hf)
}
/*
- * Make a huffman coding table.
+ * Create a direct, expanded Huffman lookup table based on
+ * canonical representation.
*/
static int
lzx_make_huffman_table(struct huffman *hf)
{
+ uint16_t bitptn[17], weight[17];
uint16_t *tbl;
- const unsigned char *bitlen;
- int bitptn[17], weight[17];
- int i, maxbits = 0, ptn, tbl_size, w;
- int len_avail;
+ const uint8_t *bitlen;
+ uint8_t maxbits = 0;
+ uint32_t ptn;
+ uint16_t i, symbol_count, w;
/*
* Initialize bit patterns.
+ *
+ * Each bitptn element represents the smallest possible
+ * code sequence allowed. The weight represents the amount
+ * of lookup bit patterns covered by a code of this length.
+ *
+ * Example of a Huffman tree:
+ *
+ * idx | freq[idx]
+ * ----+----------
+ * 1 | 1
+ * 2 | 2
+ *
+ * The result will be:
+ *
+ * idx | bitptn[idx] | weight[idx]
+ * ----+-------------+------------
+ * 1 | 0x0000 | 32768
+ * 2 | 0x8000 | 16384
+ *
+ * This means that a code with bit length 1 will take 32768
+ * possible combinations starting with 0b0. Since one such code exists
+ * (freq[1] = 1), codes with bit length 2 must start with 0b1. Since
+ * two codes with bit length 2 exist (freq[2] = 2), no more codes can
+ * be added.
+ *
+ * In this example, maxbits will be 2 (longest code length is 2).
*/
ptn = 0;
for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
@@ -3196,65 +3303,108 @@ lzx_make_huffman_table(struct huffman *hf)
maxbits = i;
}
}
- if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
- return (0);/* Invalid */
-
- hf->max_bits = maxbits;
+ /* Verify Kraft's inequality. */
+ if (ptn != 0 && ptn != 0x10000)
+ return (ARCHIVE_FATAL);
/*
- * Cut out extra bits which we won't house in the table.
- * This preparation reduces the same calculation in the for-loop
- * making the table.
+ * Shrink codes to smallest size by removing extra bits after
+ * the actual code sequences as good as possible.
+ *
+ * Continuing the example above:
+ *
+ * idx | bitptn[idx] | weight[idx]
+ * ----+-------------+-------------
+ * 1 | 0x0000 | 32768
+ * 2 | 0x8000 | 16384
+ *
+ * As can be seen, a total of 65536 entries must be created even
+ * though 4 would be sufficient (indices 0b00 to 0b11). Right shift
+ * the pattern and divide weight as much as possible:
+ *
+ * idx | bitptn[idx] | weight[idx]
+ * ----+-------------+-------------
+ * 1 | 0x0000 | 2
+ * 2 | 0x0002 | 1
+ *
+ * Thus, the direct, expanded lookup table only needs 4 entries.
*/
if (maxbits < 16) {
- int ebits = 16 - maxbits;
+ uint8_t ebits = 16 - maxbits;
for (i = 1; i <= maxbits; i++) {
bitptn[i] >>= ebits;
weight[i] >>= ebits;
}
}
+ /* Grow table if necessary. */
+ if (maxbits > hf->tbl_bits) {
+ size_t tbl_size;
+
+ hf->tbl_bits = 16;
+ tbl_size = (size_t)1 << hf->tbl_bits;
+
+ free(hf->tbl);
+ hf->tbl = calloc(tbl_size, sizeof(hf->tbl[0]));
+ if (hf->tbl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ hf->lookup_bits = maxbits;
+
/*
- * Make the table.
+ * Construct the direct, expanded lookup table.
+ *
+ * Store each symbol in table for every possible bit patterns starting
+ * with their code.
+ *
+ * Following the example with len_avail being 4:
+ *
+ * idx | bitlen[idx]
+ * ----+------------
+ * 0 | 0
+ * 1 | 1
+ * 2 | 0
+ * 3 | 2
+ * 4 | 2
+ *
+ * The resulting table contains all used symbols (1, 3, 4) for up to
+ * max_len bit patterns:
+ *
+ * idx | tbl[idx]
+ * ----------+---------
+ * 0 (0b00) | 1
+ * 1 (0b01) | 1
+ * 2 (0b10) | 3
+ * 3 (0b11) | 4
*/
- tbl_size = 1 << hf->tbl_bits;
tbl = hf->tbl;
bitlen = hf->bitlen;
- len_avail = hf->len_size;
- hf->tree_used = 0;
- /* Initialize table to invalid values */
- for (i = 0; i < tbl_size; i++) {
- tbl[i] = (uint16_t)hf->len_size;
- }
- for (i = 0; i < len_avail; i++) {
+ symbol_count = hf->symbol_count;
+ for (i = 0; i < symbol_count; i++) {
uint16_t *p;
- int len, cnt;
+ uint16_t cnt;
+ uint8_t len;
if (bitlen[i] == 0)
continue;
/* Get a bit pattern */
len = bitlen[i];
- if (len > tbl_size)
- return (0);
+ if (len > maxbits)
+ return (ARCHIVE_FATAL);
ptn = bitptn[len];
cnt = weight[len];
/* Calculate next bit pattern */
- if ((bitptn[len] = ptn + cnt) > tbl_size)
- return (0);/* Invalid */
+ bitptn[len] = ptn + cnt;
/* Update the table */
- p = &(tbl[ptn]);
- while (--cnt >= 0)
- p[cnt] = (uint16_t)i;
+ p = tbl + ptn;
+ while (cnt-- > 0)
+ *p++ = i;
}
- return (1);
+ return (ARCHIVE_OK);
}
-static inline int
-lzx_decode_huffman(struct huffman *hf, unsigned rbits)
+static uint16_t
+lzx_decode_huffman(struct huffman *hf, uint16_t rbits)
{
- int c;
- c = hf->tbl[rbits];
- if (c < hf->len_size)
- return (c);
- return (0);
+ return hf->tbl[rbits];
}
diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c
index 86d2bc95d53b..7902cdf84918 100644
--- a/libarchive/archive_read_support_format_cpio.c
+++ b/libarchive/archive_read_support_format_cpio.c
@@ -261,14 +261,12 @@ archive_read_support_format_cpio(struct archive *_a)
static int
archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
{
+ struct cpio *cpio = a->format->data;
const unsigned char *p;
- struct cpio *cpio;
int bid;
(void)best_bid; /* UNUSED */
- cpio = (struct cpio *)(a->format->data);
-
if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
return (-1);
@@ -326,10 +324,9 @@ static int
archive_read_format_cpio_options(struct archive_read *a,
const char *key, const char *val)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format->data;
int ret = ARCHIVE_FAILED;
- cpio = (struct cpio *)(a->format->data);
if (strcmp(key, "compat-2x") == 0) {
/* Handle filenames as libarchive 2.x */
cpio->init_default_conversion = (val != NULL)?1:0;
@@ -364,14 +361,14 @@ static int
archive_read_format_cpio_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format->data;
const void *h, *hl;
struct archive_string_conv *sconv;
size_t namelength;
size_t name_pad;
+ int is_trailer;
int r;
- cpio = (struct cpio *)(a->format->data);
sconv = cpio->opt_sconv;
if (sconv == NULL) {
if (!cpio->init_default_conversion) {
@@ -411,6 +408,9 @@ archive_read_format_cpio_read_header(struct archive_read *a,
archive_string_conversion_charset_name(sconv));
r = ARCHIVE_WARN;
}
+ /* Save this before consuming the name buffer below. */
+ is_trailer = (namelength == 11 &&
+ memcmp((const char *)h, "TRAILER!!!", 10) == 0);
cpio->entry_offset = 0;
__archive_read_consume(a, namelength);
@@ -451,8 +451,7 @@ archive_read_format_cpio_read_header(struct archive_read *a,
* header. XXX */
/* Compare name to "TRAILER!!!" to test for end-of-archive. */
- if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
- 10) == 0) {
+ if (is_trailer) {
/* TODO: Store file location of start of block. */
archive_clear_error(&a->archive);
return (ARCHIVE_EOF);
@@ -470,10 +469,8 @@ static int
archive_read_format_cpio_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
+ struct cpio *cpio = a->format->data;
ssize_t bytes_read;
- struct cpio *cpio;
-
- cpio = (struct cpio *)(a->format->data);
if (cpio->entry_bytes_unconsumed) {
__archive_read_consume(a, cpio->entry_bytes_unconsumed);
@@ -508,7 +505,7 @@ archive_read_format_cpio_read_data(struct archive_read *a,
static int
archive_read_format_cpio_skip(struct archive_read *a)
{
- struct cpio *cpio = (struct cpio *)(a->format->data);
+ struct cpio *cpio = a->format->data;
int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
cpio->entry_bytes_unconsumed;
@@ -545,7 +542,8 @@ find_newc_header(struct archive_read *a)
{
const void *h;
const char *p, *q;
- size_t skip, skipped = 0;
+ int64_t skip;
+ uintmax_t skipped = 0;
ssize_t bytes;
for (;;) {
@@ -577,9 +575,9 @@ find_newc_header(struct archive_read *a)
if (skipped > 0) {
archive_set_error(&a->archive,
0,
- "Skipped %d bytes before "
+ "Skipped %ju bytes before "
"finding valid header",
- (int)skipped);
+ skipped);
return (ARCHIVE_WARN);
}
return (ARCHIVE_OK);
@@ -1000,9 +998,8 @@ header_bin_be(struct archive_read *a, struct cpio *cpio,
static int
archive_read_format_cpio_cleanup(struct archive_read *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format->data;
- cpio = (struct cpio *)(a->format->data);
/* Free inode->name map */
while (cpio->links_head != NULL) {
struct links_entry *lp = cpio->links_head->next;
@@ -1012,7 +1009,7 @@ archive_read_format_cpio_cleanup(struct archive_read *a)
cpio->links_head = lp;
}
free(cpio);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c
index e1df6273fe63..c3da44c8266c 100644
--- a/libarchive/archive_read_support_format_iso9660.c
+++ b/libarchive/archive_read_support_format_iso9660.c
@@ -47,6 +47,7 @@
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "archive_string.h"
@@ -314,8 +315,8 @@ struct file_info {
struct heap_queue {
struct file_info **files;
- int allocated;
- int used;
+ size_t allocated;
+ size_t used;
};
struct iso9660 {
@@ -337,8 +338,8 @@ struct iso9660 {
uint64_t offset;/* Offset of CE on disk. */
struct file_info *file;
} *reqs;
- int cnt;
- int allocated;
+ size_t cnt;
+ size_t allocated;
} read_ce_req;
int64_t previous_number;
@@ -379,8 +380,6 @@ struct iso9660 {
size_t utf16be_path_len;
unsigned char *utf16be_previous_path;
size_t utf16be_previous_path_len;
- /* Null buffer used in bidder to improve its performance. */
- unsigned char null[2048];
};
static int archive_read_format_iso9660_bid(struct archive_read *, int);
@@ -403,12 +402,12 @@ static time_t isodate17(const unsigned char *);
static int isodate17_valid(const unsigned char *);
static time_t isodate7(const unsigned char *);
static int isodate7_valid(const unsigned char *);
-static int isBootRecord(struct iso9660 *, const unsigned char *);
+static int isBootRecord(const unsigned char *);
static int isVolumePartition(struct iso9660 *, const unsigned char *);
-static int isVDSetTerminator(struct iso9660 *, const unsigned char *);
+static int isVDSetTerminator(const unsigned char *);
static int isJolietSVD(struct iso9660 *, const unsigned char *);
-static int isSVD(struct iso9660 *, const unsigned char *);
-static int isEVD(struct iso9660 *, const unsigned char *);
+static int isSVD(const unsigned char *);
+static int isEVD(const unsigned char *);
static int isPVD(struct iso9660 *, const unsigned char *);
static int isRootDirectoryRecord(const unsigned char *);
static int isValid723Integer(const unsigned char *);
@@ -503,7 +502,7 @@ archive_read_support_format_iso9660(struct archive *_a)
static int
archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
ssize_t bytes_read;
const unsigned char *p;
int seenTerminator;
@@ -513,8 +512,6 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
if (best_bid > 48)
return (-1);
- iso9660 = (struct iso9660 *)(a->format->data);
-
/*
* Skip the first 32k (reserved area) and get the first
* 8 sectors of the volume descriptor table. Of course,
@@ -547,15 +544,15 @@ archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
if (isJolietSVD(iso9660, p))
continue;
}
- if (isBootRecord(iso9660, p))
+ if (isBootRecord(p))
continue;
- if (isEVD(iso9660, p))
+ if (isEVD(p))
continue;
- if (isSVD(iso9660, p))
+ if (isSVD(p))
continue;
if (isVolumePartition(iso9660, p))
continue;
- if (isVDSetTerminator(iso9660, p)) {
+ if (isVDSetTerminator(p)) {
seenTerminator = 1;
break;
}
@@ -576,9 +573,7 @@ static int
archive_read_format_iso9660_options(struct archive_read *a,
const char *key, const char *val)
{
- struct iso9660 *iso9660;
-
- iso9660 = (struct iso9660 *)(a->format->data);
+ struct iso9660 *iso9660 = a->format->data;
if (strcmp(key, "joliet") == 0) {
if (val == NULL || strcmp(val, "off") == 0 ||
@@ -603,26 +598,23 @@ archive_read_format_iso9660_options(struct archive_read *a,
}
static int
-isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
-unsigned bytes)
+isNull(const unsigned char *h, unsigned offset, unsigned bytes)
{
-
- while (bytes >= sizeof(iso9660->null)) {
- if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
- return (0);
- offset += sizeof(iso9660->null);
- bytes -= sizeof(iso9660->null);
- }
- if (bytes)
- return memcmp(iso9660->null, h + offset, bytes) == 0;
- else
+ if (bytes == 0)
return (1);
+
+ /*
+ * If the first byte is zero and every byte equals the following
+ * byte, the entire range is zero.
+ */
+ return (h[offset] == 0 &&
+ (bytes == 1 ||
+ memcmp(h + offset, h + offset + 1, bytes - 1) == 0));
}
static int
-isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
+isBootRecord(const unsigned char *h)
{
- (void)iso9660; /* UNUSED */
/* Type of the Volume Descriptor Boot Record must be 0. */
if (h[0] != 0)
@@ -662,9 +654,8 @@ isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
}
static int
-isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+isVDSetTerminator(const unsigned char *h)
{
- (void)iso9660; /* UNUSED */
/* Type of the Volume Descriptor Set Terminator must be 255. */
if (h[0] != 255)
@@ -675,7 +666,7 @@ isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, 7, 2048-7))
+ if (!isNull(h, 7, 2048-7))
return (0);
return (1);
@@ -690,7 +681,7 @@ isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
/* Check if current sector is a kind of Supplementary Volume
* Descriptor. */
- if (!isSVD(iso9660, h))
+ if (!isSVD(h))
return (0);
/* FIXME: do more validations according to joliet spec. */
@@ -731,25 +722,24 @@ isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
}
static int
-isSVD(struct iso9660 *iso9660, const unsigned char *h)
+isSVD(const unsigned char *h)
{
const unsigned char *p;
ssize_t logical_block_size;
int32_t volume_block;
int32_t location;
- (void)iso9660; /* UNUSED */
/* Type 2 means it's a SVD. */
if (h[SVD_type_offset] != 2)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
+ if (!isNull(h, SVD_reserved1_offset, SVD_reserved1_size))
return (0);
- if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
+ if (!isNull(h, SVD_reserved2_offset, SVD_reserved2_size))
return (0);
- if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
+ if (!isNull(h, SVD_reserved3_offset, SVD_reserved3_size))
return (0);
/* File structure version must be 1 for ISO9660/ECMA119. */
@@ -791,14 +781,13 @@ isSVD(struct iso9660 *iso9660, const unsigned char *h)
}
static int
-isEVD(struct iso9660 *iso9660, const unsigned char *h)
+isEVD(const unsigned char *h)
{
const unsigned char *p;
ssize_t logical_block_size;
int32_t volume_block;
int32_t location;
- (void)iso9660; /* UNUSED */
/* Type of the Enhanced Volume Descriptor must be 2. */
if (h[PVD_type_offset] != 2)
@@ -813,11 +802,11 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size))
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size))
return (0);
/* Logical block size must be > 0. */
@@ -853,11 +842,11 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
+ if (!isNull(h, PVD_reserved4_offset, PVD_reserved4_size))
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size))
return (0);
/* Read Root Directory Record in Volume Descriptor. */
@@ -891,7 +880,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ if (!isNull(h, PVD_reserved2_offset, PVD_reserved2_size))
return (0);
/* Volume space size must be encoded according to 7.3.3 */
@@ -903,7 +892,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ if (!isNull(h, PVD_reserved3_offset, PVD_reserved3_size))
return (0);
/* Volume set size must be encoded according to 7.2.3 */
@@ -961,7 +950,7 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h)
return (0);
/* Reserved field must be 0. */
- if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ if (!isNull(h, PVD_reserved5_offset, PVD_reserved5_size))
return (0);
/* XXX TODO: Check other values for sanity; reject more
@@ -1034,12 +1023,11 @@ isRootDirectoryRecord(const unsigned char *p) {
static int
read_children(struct archive_read *a, struct file_info *parent)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
const unsigned char *b, *p;
struct file_info *multi;
size_t step, skip_size;
- iso9660 = (struct iso9660 *)(a->format->data);
/* flush any remaining bytes from the last round to ensure
* we're positioned */
if (iso9660->entry_bytes_unconsumed) {
@@ -1086,7 +1074,7 @@ read_children(struct archive_read *a, struct file_info *parent)
p = b;
b += iso9660->logical_block_size;
step -= iso9660->logical_block_size;
- for (; *p != 0 && p + DR_name_offset < b && p + *p <= b;
+ for (; p < b && b - p > DR_name_offset && *p != 0 && *p <= b - p;
p += *p) {
struct file_info *child;
@@ -1247,12 +1235,10 @@ static int
archive_read_format_iso9660_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
struct file_info *file;
int r, rd_r = ARCHIVE_OK;
- iso9660 = (struct iso9660 *)(a->format->data);
-
if (!a->archive.archive_format) {
a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
a->archive.archive_format_name = "ISO9660";
@@ -1499,7 +1485,7 @@ static int
zisofs_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
struct zisofs *zisofs;
const unsigned char *p;
size_t avail;
@@ -1507,7 +1493,6 @@ zisofs_read_data(struct archive_read *a,
size_t uncompressed_size;
int r;
- iso9660 = (struct iso9660 *)(a->format->data);
zisofs = &iso9660->entry_zisofs;
p = __archive_read_ahead(a, 1, &bytes_read);
@@ -1738,10 +1723,8 @@ static int
archive_read_format_iso9660_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
+ struct iso9660 *iso9660 = a->format->data;
ssize_t bytes_read;
- struct iso9660 *iso9660;
-
- iso9660 = (struct iso9660 *)(a->format->data);
if (iso9660->entry_bytes_unconsumed) {
__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
@@ -1805,10 +1788,9 @@ archive_read_format_iso9660_read_data(struct archive_read *a,
static int
archive_read_format_iso9660_cleanup(struct archive_read *a)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
int r = ARCHIVE_OK;
- iso9660 = (struct iso9660 *)(a->format->data);
release_files(iso9660);
free(iso9660->read_ce_req.reqs);
archive_string_free(&iso9660->pathname);
@@ -1828,7 +1810,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a)
free(iso9660->utf16be_path);
free(iso9660->utf16be_previous_path);
free(iso9660);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (r);
}
@@ -1840,7 +1822,7 @@ static struct file_info *
parse_file_info(struct archive_read *a, struct file_info *parent,
const unsigned char *isodirrec, size_t reclen)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
struct file_info *file, *filep;
size_t name_len;
const unsigned char *rr_start, *rr_end;
@@ -1850,8 +1832,6 @@ parse_file_info(struct archive_read *a, struct file_info *parent,
int32_t location;
int flags;
- iso9660 = (struct iso9660 *)(a->format->data);
-
if (reclen != 0)
dr_len = (size_t)isodirrec[DR_length_offset];
/*
@@ -2192,11 +2172,9 @@ static int
parse_rockridge(struct archive_read *a, struct file_info *file,
const unsigned char *p, const unsigned char *end)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
int entry_seen = 0;
- iso9660 = (struct iso9660 *)(a->format->data);
-
while (p + 4 <= end /* Enough space for another entry. */
&& p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
&& p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
@@ -2366,13 +2344,11 @@ static int
register_CE(struct archive_read *a, int32_t location,
struct file_info *file)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format->data;
struct read_ce_queue *heap;
- struct read_ce_req *p;
uint64_t offset, parent_offset;
- int hole, parent;
+ size_t hole, parent;
- iso9660 = (struct iso9660 *)(a->format->data);
offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
if (((file->mode & AE_IFMT) == AE_IFREG &&
offset >= file->offset) ||
@@ -2389,14 +2365,13 @@ register_CE(struct archive_read *a, int32_t location,
/* Expand our CE list as necessary. */
heap = &(iso9660->read_ce_req);
if (heap->cnt >= heap->allocated) {
- int new_size;
+ struct read_ce_req *p;
+ size_t new_size;
if (heap->allocated < 16)
new_size = 16;
- else
- new_size = heap->allocated * 2;
- /* Overflow might keep us from growing the list. */
- if (new_size <= heap->allocated) {
+ else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+ /* Overflow keeps us from growing the list. */
archive_set_error(&a->archive, ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
}
@@ -2438,7 +2413,7 @@ static void
next_CE(struct read_ce_queue *heap)
{
uint64_t a_offset, b_offset, c_offset;
- int a, b, c;
+ size_t a, b, c;
struct read_ce_req tmp;
if (heap->cnt < 1)
@@ -2765,7 +2740,7 @@ parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
int data_length)
{
- if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
+ if (data_length == 12 && data[0] == 0x70 && data[1] == 0x7a) {
/* paged zlib */
file->pz = 1;
file->pz_log2_bs = data[3];
@@ -3144,7 +3119,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap,
struct file_info *file, uint64_t key)
{
uint64_t file_key, parent_key;
- int hole, parent;
+ size_t hole, parent;
/* Reserve 16 bits for possible key collisions (needed for linked items) */
/* For ISO files with more than 65535 entries, reordering will still occur */
@@ -3154,12 +3129,12 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap,
/* Expand our pending files list as necessary. */
if (heap->used >= heap->allocated) {
struct file_info **new_pending_files;
- int new_size = heap->allocated * 2;
+ size_t new_size;
if (heap->allocated < 1024)
new_size = 1024;
- /* Overflow might keep us from growing the list. */
- if (new_size <= heap->allocated) {
+ else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+ /* Overflow keeps us from growing the list. */
archive_set_error(&a->archive,
ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
@@ -3186,7 +3161,7 @@ heap_add_entry(struct archive_read *a, struct heap_queue *heap,
*/
hole = heap->used++;
while (hole > 0) {
- parent = (hole - 1)/2;
+ parent = (hole - 1) / 2;
parent_key = heap->files[parent]->key;
if (file_key >= parent_key) {
heap->files[hole] = file;
@@ -3205,7 +3180,7 @@ static struct file_info *
heap_get_entry(struct heap_queue *heap)
{
uint64_t a_key, b_key, c_key;
- int a, b, c;
+ size_t a, b, c;
struct file_info *r, *tmp;
if (heap->used < 1)
diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c
index b9e6a178934f..0a475461576d 100644
--- a/libarchive/archive_read_support_format_lha.c
+++ b/libarchive/archive_read_support_format_lha.c
@@ -390,10 +390,9 @@ static int
archive_read_format_lha_options(struct archive_read *a,
const char *key, const char *val)
{
- struct lha *lha;
+ struct lha *lha = a->format->data;
int ret = ARCHIVE_FAILED;
- lha = (struct lha *)(a->format->data);
if (strcmp(key, "hdrcharset") == 0) {
if (val == NULL || val[0] == 0)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -472,9 +471,9 @@ static int
archive_read_format_lha_read_header(struct archive_read *a,
struct archive_entry *entry)
{
+ struct lha *lha = a->format->data;
struct archive_wstring linkname;
struct archive_wstring pathname;
- struct lha *lha;
const unsigned char *p;
const char *signature;
int err;
@@ -485,7 +484,6 @@ archive_read_format_lha_read_header(struct archive_read *a,
if (a->archive.archive_format_name == NULL)
a->archive.archive_format_name = "lha";
- lha = (struct lha *)(a->format->data);
lha->decompress_init = 0;
lha->end_of_entry = 0;
lha->end_of_entry_cleanup = 0;
@@ -900,7 +898,7 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha)
{
const unsigned char *p;
size_t extdsize;
- int i, err, err2;
+ int err, err2;
int namelen, padding;
unsigned char headersum, sum_calculated;
@@ -925,10 +923,9 @@ lha_read_file_header_1(struct archive_read *a, struct lha *lha)
if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
return (truncated_error(a));
- for (i = 0; i < namelen; i++) {
- if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
- goto invalid;/* Invalid filename. */
- }
+ if (memchr(p + H1_FILE_NAME_OFFSET, 0xff,
+ (size_t)namelen) != NULL)
+ goto invalid; /* Invalid filename. */
archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
lha->setflag |= CRC_IS_SET;
@@ -1097,7 +1094,7 @@ lha_read_file_header_3(struct archive_read *a, struct lha *lha)
header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
__archive_read_consume(a, H3_FIXED_SIZE);
- /* Reject rediculously large header */
+ /* Reject ridiculously large header */
if (lha->header_size > 65536) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"LHa header size too large");
@@ -1452,7 +1449,7 @@ invalid:
static int
lha_end_of_entry(struct archive_read *a)
{
- struct lha *lha = (struct lha *)(a->format->data);
+ struct lha *lha = a->format->data;
int r = ARCHIVE_EOF;
if (!lha->end_of_entry_cleanup) {
@@ -1473,7 +1470,7 @@ static int
archive_read_format_lha_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct lha *lha = (struct lha *)(a->format->data);
+ struct lha *lha = a->format->data;
int r;
if (lha->entry_unconsumed) {
@@ -1506,7 +1503,7 @@ static int
lha_read_data_none(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct lha *lha = (struct lha *)(a->format->data);
+ struct lha *lha = a->format->data;
ssize_t bytes_avail;
if (lha->entry_bytes_remaining == 0) {
@@ -1553,7 +1550,7 @@ static int
lha_read_data_lzh(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct lha *lha = (struct lha *)(a->format->data);
+ struct lha *lha = a->format->data;
ssize_t bytes_avail;
int r;
@@ -1643,11 +1640,9 @@ lha_read_data_lzh(struct archive_read *a, const void **buff,
static int
archive_read_format_lha_read_data_skip(struct archive_read *a)
{
- struct lha *lha;
+ struct lha *lha = a->format->data;
int64_t bytes_skipped;
- lha = (struct lha *)(a->format->data);
-
if (lha->entry_unconsumed) {
/* Consume as much as the decompressor actually used. */
__archive_read_consume(a, lha->entry_unconsumed);
@@ -1674,7 +1669,7 @@ archive_read_format_lha_read_data_skip(struct archive_read *a)
static int
archive_read_format_lha_cleanup(struct archive_read *a)
{
- struct lha *lha = (struct lha *)(a->format->data);
+ struct lha *lha = a->format->data;
lzh_decode_free(&(lha->strm));
archive_string_free(&(lha->dirname));
@@ -1683,7 +1678,7 @@ archive_read_format_lha_cleanup(struct archive_read *a)
archive_string_free(&(lha->gname));
archive_wstring_free(&(lha->ws));
free(lha);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c
index 66e2cb160532..dab580a30376 100644
--- a/libarchive/archive_read_support_format_mtree.c
+++ b/libarchive/archive_read_support_format_mtree.c
@@ -141,56 +141,6 @@ static int64_t mtree_atol(char **, int base);
static size_t mtree_strnlen(const char *, size_t);
#endif
-/*
- * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them
- * here. TODO: Move this to configure time, but be careful
- * about cross-compile environments.
- */
-static int64_t
-get_time_t_max(void)
-{
-#if defined(TIME_T_MAX)
- return TIME_T_MAX;
-#else
- /* ISO C allows time_t to be a floating-point type,
- but POSIX requires an integer type. The following
- should work on any system that follows the POSIX
- conventions. */
- if (((time_t)0) < ((time_t)-1)) {
- /* Time_t is unsigned */
- return (~(time_t)0);
- } else {
- /* Time_t is signed. */
- /* Assume it's the same as int64_t or int32_t */
- if (sizeof(time_t) == sizeof(int64_t)) {
- return (time_t)INT64_MAX;
- } else {
- return (time_t)INT32_MAX;
- }
- }
-#endif
-}
-
-static int64_t
-get_time_t_min(void)
-{
-#if defined(TIME_T_MIN)
- return TIME_T_MIN;
-#else
- if (((time_t)0) < ((time_t)-1)) {
- /* Time_t is unsigned */
- return (time_t)0;
- } else {
- /* Time_t is signed. */
- if (sizeof(time_t) == sizeof(int64_t)) {
- return (time_t)INT64_MIN;
- } else {
- return (time_t)INT32_MIN;
- }
- }
-#endif
-}
-
#ifdef HAVE_STRNLEN
#define mtree_strnlen(a,b) strnlen(a,b)
#else
@@ -199,12 +149,10 @@ mtree_strnlen(const char *p, size_t maxlen)
{
size_t i;
- for (i = 0; i <= maxlen; i++) {
+ for (i = 0; i < maxlen; i++) {
if (p[i] == 0)
break;
}
- if (i > maxlen)
- return (-1);/* invalid */
return (i);
}
#endif
@@ -213,9 +161,8 @@ static int
archive_read_format_mtree_options(struct archive_read *a,
const char *key, const char *val)
{
- struct mtree *mtree;
+ struct mtree *mtree = a->format->data;
- mtree = (struct mtree *)(a->format->data);
if (strcmp(key, "checkfs") == 0) {
/* Allows to read information missing from the mtree from the file system */
if (val == NULL || val[0] == 0) {
@@ -297,11 +244,9 @@ archive_read_support_format_mtree(struct archive *_a)
static int
cleanup(struct archive_read *a)
{
- struct mtree *mtree;
+ struct mtree *mtree = a->format->data;
struct mtree_entry *p, *q;
- mtree = (struct mtree *)(a->format->data);
-
/* Close any dangling file descriptor before freeing */
if (mtree->fd >= 0) {
close(mtree->fd);
@@ -322,7 +267,7 @@ cleanup(struct archive_read *a)
free(mtree->buff);
free(mtree);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
@@ -387,7 +332,7 @@ next_line(struct archive_read *a,
*/
while (*nl == 0 && len == *avail && !quit) {
ssize_t diff = *ravail - *avail;
- size_t nbytes_req = (*ravail+1023) & ~1023U;
+ size_t nbytes_req = ((size_t)*ravail + 1023) & ~1023U;
ssize_t tested;
/*
@@ -876,7 +821,7 @@ process_global_set(struct archive_read *a,
line = next;
next = line + strcspn(line, " \t\r\n");
eq = strchr(line, '=');
- if (eq > next)
+ if (eq == NULL || eq > next)
len = next - line;
else
len = eq - line;
@@ -1121,12 +1066,10 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
static int
read_header(struct archive_read *a, struct archive_entry *entry)
{
- struct mtree *mtree;
+ struct mtree *mtree = a->format->data;
char *p;
int r, use_next;
- mtree = (struct mtree *)(a->format->data);
-
if (mtree->fd >= 0) {
close(mtree->fd);
mtree->fd = -1;
@@ -1781,8 +1724,6 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
}
if (strcmp(key, "time") == 0) {
int64_t m;
- int64_t my_time_t_max = get_time_t_max();
- int64_t my_time_t_min = get_time_t_min();
long ns = 0;
*parsed_kws |= MTREE_HAS_MTIME;
@@ -1802,10 +1743,10 @@ parse_keyword(struct archive_read *a, struct mtree *mtree,
else
ns = (long)v;
}
- if (m > my_time_t_max)
- m = my_time_t_max;
- else if (m < my_time_t_min)
- m = my_time_t_min;
+ if (m > TIME_MAX)
+ m = TIME_MAX;
+ else if (m < TIME_MIN)
+ m = TIME_MIN;
archive_entry_set_mtime(entry, (time_t)m, ns);
return (ARCHIVE_OK);
}
@@ -1892,11 +1833,10 @@ static int
read_data(struct archive_read *a, const void **buff, size_t *size,
int64_t *offset)
{
+ struct mtree *mtree = a->format->data;
size_t bytes_to_read;
ssize_t bytes_read;
- struct mtree *mtree;
- mtree = (struct mtree *)(a->format->data);
if (mtree->fd < 0) {
*buff = NULL;
*offset = 0;
@@ -1937,9 +1877,8 @@ read_data(struct archive_read *a, const void **buff, size_t *size,
static int
skip(struct archive_read *a)
{
- struct mtree *mtree;
+ struct mtree *mtree = a->format->data;
- mtree = (struct mtree *)(a->format->data);
if (mtree->fd >= 0) {
close(mtree->fd);
mtree->fd = -1;
diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c
index 803b2e281a99..ff764f503042 100644
--- a/libarchive/archive_read_support_format_rar.c
+++ b/libarchive/archive_read_support_format_rar.c
@@ -42,6 +42,7 @@
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_ppmd7_private.h"
#include "archive_private.h"
#include "archive_read_private.h"
@@ -201,8 +202,8 @@ struct huffman_table_entry
struct huffman_code
{
struct huffman_tree_node *tree;
- int numentries;
- int numallocatedentries;
+ size_t numentries;
+ size_t numallocatedentries;
int minlength;
int maxlength;
int tablesize;
@@ -227,8 +228,6 @@ struct rar_program_code
{
uint8_t *staticdata;
uint32_t staticdatalen;
- uint8_t *globalbackup;
- uint32_t globalbackuplen;
uint64_t fingerprint;
uint32_t usagecount;
uint32_t oldfilterlength;
@@ -499,7 +498,7 @@ static const uint32_t cache_masks[] = {
static int
rar_br_fillup(struct archive_read *a, struct rar_br *br)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
int n = CACHE_BITS - br->cache_avail;
for (;;) {
@@ -594,7 +593,7 @@ rar_br_fillup(struct archive_read *a, struct rar_br *br)
static int
rar_br_preparation(struct archive_read *a, struct rar_br *br)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
if (rar->bytes_remaining > 0) {
br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
@@ -709,7 +708,7 @@ static Byte
ppmd_read(void *p)
{
struct archive_read *a = ((IByteIn*)p)->a;
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
struct rar_br *br = &(rar->br);
Byte b;
if (!rar_br_read_ahead(a, br, 8))
@@ -777,7 +776,7 @@ static int
archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
{
if (_a && _a->format) {
- struct rar * rar = (struct rar *)_a->format->data;
+ struct rar *rar = _a->format->data;
if (rar) {
return rar->has_encrypted_entries;
}
@@ -877,10 +876,9 @@ static int
archive_read_format_rar_options(struct archive_read *a,
const char *key, const char *val)
{
- struct rar *rar;
+ struct rar *rar = a->format->data;
int ret = ARCHIVE_FAILED;
- rar = (struct rar *)(a->format->data);
if (strcmp(key, "hdrcharset") == 0) {
if (val == NULL || val[0] == 0)
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -907,9 +905,9 @@ static int
archive_read_format_rar_read_header(struct archive_read *a,
struct archive_entry *entry)
{
+ struct rar *rar = a->format->data;
const void *h;
const char *p;
- struct rar *rar;
int64_t skip;
char head_type;
int ret;
@@ -920,8 +918,6 @@ archive_read_format_rar_read_header(struct archive_read *a,
if (a->archive.archive_format_name == NULL)
a->archive.archive_format_name = "RAR";
- rar = (struct rar *)(a->format->data);
-
/*
* It should be sufficient to call archive_read_next_header() for
* a reader to determine if an entry is encrypted or not. If the
@@ -1100,7 +1096,7 @@ static int
archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
int ret;
if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
@@ -1151,12 +1147,10 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
static int
archive_read_format_rar_read_data_skip(struct archive_read *a)
{
- struct rar *rar;
+ struct rar *rar = a->format->data;
int64_t bytes_skipped;
int ret;
- rar = (struct rar *)(a->format->data);
-
if (rar->bytes_unconsumed > 0) {
/* Consume as much as the decompressor actually used. */
__archive_read_consume(a, rar->bytes_unconsumed);
@@ -1189,9 +1183,9 @@ static int64_t
archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
int whence)
{
+ struct rar *rar = a->format->data;
int64_t client_offset, ret;
size_t i;
- struct rar *rar = (struct rar *)(a->format->data);
if (rar->compression_method == COMPRESS_METHOD_STORE)
{
@@ -1294,6 +1288,12 @@ archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
"Error during seek of RAR file");
return (ARCHIVE_FAILED);
}
+ if (rar->cursor == 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid RAR seek cursor state");
+ return (ARCHIVE_FAILED);
+ }
client_offset += rar->dbo[rar->cursor].start_offset -
rar->dbo[rar->cursor-1].end_offset;
continue;
@@ -1342,9 +1342,8 @@ archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
static int
archive_read_format_rar_cleanup(struct archive_read *a)
{
- struct rar *rar;
+ struct rar *rar = a->format->data;
- rar = (struct rar *)(a->format->data);
free_codes(a);
clear_filters(&rar->filters);
free(rar->filename);
@@ -1354,7 +1353,7 @@ archive_read_format_rar_cleanup(struct archive_read *a)
free(rar->lzss.window);
__archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
free(rar);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
@@ -1362,9 +1361,9 @@ static int
read_header(struct archive_read *a, struct archive_entry *entry,
char head_type)
{
+ struct rar *rar = a->format->data;
const void *h;
const char *p, *endp;
- struct rar *rar;
struct rar_header rar_header;
struct rar_file_header file_header;
int64_t header_size;
@@ -1380,8 +1379,6 @@ read_header(struct archive_read *a, struct archive_entry *entry,
char *newptr;
size_t newsize;
- rar = (struct rar *)(a->format->data);
-
/* Setup a string conversion object for non-rar-unicode filenames. */
sconv = rar->opt_sconv;
if (sconv == NULL) {
@@ -1668,16 +1665,18 @@ read_header(struct archive_read *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
fn_sconv = rar->sconv_utf8;
- while ((strp = strchr(filename, '\\')) != NULL)
- *strp = '/';
+ strp = filename;
+ while ((strp = strchr(strp, '\\')) != NULL)
+ *strp++ = '/';
p += filename_size;
}
}
else
{
fn_sconv = sconv;
- while ((strp = strchr(filename, '\\')) != NULL)
- *strp = '/';
+ strp = filename;
+ while ((strp = strchr(strp, '\\')) != NULL)
+ *strp++ = '/';
p += filename_size;
}
@@ -1962,12 +1961,11 @@ static int
read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
struct archive_string_conv *sconv)
{
+ struct rar *rar = a->format->data;
const void *h;
const char *p;
- struct rar *rar;
int ret = (ARCHIVE_OK);
- rar = (struct rar *)(a->format->data);
if ((uintmax_t)rar->packed_size > SIZE_MAX)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2004,10 +2002,9 @@ static int
read_data_stored(struct archive_read *a, const void **buff, size_t *size,
int64_t *offset)
{
- struct rar *rar;
+ struct rar *rar = a->format->data;
ssize_t bytes_avail;
- rar = (struct rar *)(a->format->data);
if (rar->bytes_remaining == 0 &&
!(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
{
@@ -2052,13 +2049,11 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
if (looper++ > MAX_COMPRESS_DEPTH)
return (ARCHIVE_FAILED);
- struct rar *rar;
+ struct rar *rar = a->format->data;
int64_t start, end;
size_t bs;
int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
- rar = (struct rar *)(a->format->data);
-
do {
if (!rar->valid)
return (ARCHIVE_FAILED);
@@ -2303,11 +2298,11 @@ ending_block:
static int
parse_codes(struct archive_read *a)
{
+ struct rar *rar = a->format->data;
int i, j, val, n, r;
unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
unsigned int maxorder;
struct huffman_code precode;
- struct rar *rar = (struct rar *)(a->format->data);
struct rar_br *br = &(rar->br);
free_codes(a);
@@ -2589,7 +2584,7 @@ truncated_data:
static void
free_codes(struct archive_read *a)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
free(rar->maincode.tree);
free(rar->offsetcode.tree);
free(rar->lowoffsetcode.tree);
@@ -2608,10 +2603,10 @@ free_codes(struct archive_read *a)
static int
read_next_symbol(struct archive_read *a, struct huffman_code *code)
{
+ struct rar *rar = a->format->data;
unsigned char bit;
unsigned int bits;
int length, value, node;
- struct rar *rar;
struct rar_br *br;
if (!code->table)
@@ -2620,7 +2615,6 @@ read_next_symbol(struct archive_read *a, struct huffman_code *code)
return -1;
}
- rar = (struct rar *)(a->format->data);
br = &(rar->br);
/* Look ahead (peek) at bits */
@@ -2825,11 +2819,16 @@ new_node(struct huffman_code *code)
{
void *new_tree;
if (code->numallocatedentries == code->numentries) {
- int new_num_entries = 256;
- if (code->numentries > 0) {
- new_num_entries = code->numentries * 2;
- }
- new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
+ size_t size, new_num_entries;
+
+ if (code->numentries == 0)
+ new_num_entries = 256;
+ else if (archive_ckd_mul_size(&new_num_entries, code->numentries, 2)
+ || new_num_entries > INT_MAX)
+ return -1;
+ if (archive_ckd_mul_size(&size, new_num_entries, sizeof(*code->tree)))
+ return -1;
+ new_tree = realloc(code->tree, size);
if (new_tree == NULL)
return (-1);
code->tree = (struct huffman_tree_node *)new_tree;
@@ -2870,7 +2869,7 @@ make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
"Huffman tree was not created");
return (ARCHIVE_FAILED);
}
- if (node < 0 || node >= code->numentries)
+ if (node < 0 || (size_t)node >= code->numentries)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid location to Huffman tree specified");
@@ -2958,9 +2957,9 @@ expand(struct archive_read *a, int64_t *end)
static const unsigned char shortbits[] =
{ 2, 2, 3, 4, 5, 6, 6, 6 };
+ struct rar *rar = a->format->data;
int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
unsigned char newfile;
- struct rar *rar = (struct rar *)(a->format->data);
struct rar_br *br = &(rar->br);
if (rar->filters.filterstart < *end)
@@ -3148,8 +3147,8 @@ static int
copy_from_lzss_window(struct archive_read *a, uint8_t *buffer,
int64_t startpos, int length)
{
+ struct rar *rar = a->format->data;
int windowoffs, firstpart;
- struct rar *rar = (struct rar *)(a->format->data);
windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
firstpart = lzss_size(&rar->lzss) - windowoffs;
@@ -3176,8 +3175,8 @@ static int
copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
int64_t startpos, size_t length)
{
+ struct rar *rar = a->format->data;
int windowoffs, firstpart;
- struct rar *rar = (struct rar *)(a->format->data);
if (length > rar->unp_buffer_size)
{
@@ -3233,7 +3232,7 @@ fatal:
static const void *
rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
const void *h;
int ret;
@@ -3270,7 +3269,7 @@ again:
static int
parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
struct rar_filters *filters = &rar->filters;
struct memory_bit_reader br = { 0 };
@@ -3438,7 +3437,7 @@ create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t
static int
run_filters(struct archive_read *a)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
struct rar_filters *filters = &rar->filters;
struct rar_filter *filter = filters->stack;
struct rar_filter *f;
@@ -3606,7 +3605,6 @@ delete_program_code(struct rar_program_code *prog)
{
struct rar_program_code *next = prog->next;
free(prog->staticdata);
- free(prog->globalbackup);
free(prog);
prog = next;
}
@@ -3659,7 +3657,7 @@ membr_fill(struct memory_bit_reader *br, int bits)
static int
read_filter(struct archive_read *a, int64_t *end)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
uint8_t flags, val, *code;
uint16_t length, i;
@@ -3855,7 +3853,7 @@ execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm)
state.delta[0] = state.lastdelta;
predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
byte = (predbyte - delta) & 0xFF;
- prederror = delta << 3;
+ prederror = delta * 8;
state.error[0] += abs(prederror);
state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
@@ -3912,7 +3910,7 @@ execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_vir
static int
rar_decode_byte(struct archive_read *a, uint8_t *byte)
{
- struct rar *rar = (struct rar *)(a->format->data);
+ struct rar *rar = a->format->data;
struct rar_br *br = &(rar->br);
if (!rar_br_read_ahead(a, br, 8))
return 0;
diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c
index ae36a7562e28..faddad9ff497 100644
--- a/libarchive/archive_read_support_format_rar5.c
+++ b/libarchive/archive_read_support_format_rar5.c
@@ -44,6 +44,7 @@
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_ppmd7_private.h"
#include "archive_entry_private.h"
#include "archive_time_private.h"
@@ -167,11 +168,9 @@ enum FILTER_TYPE {
struct filter_info {
int type;
int channels;
- int pos_r;
int64_t block_start;
ssize_t block_length;
- uint16_t width;
};
struct data_ready {
@@ -374,10 +373,10 @@ struct rar5 {
static void rar5_signature(char *buf);
static int verify_global_checksums(struct archive_read* a);
static int rar5_read_data_skip(struct archive_read *a);
-static int push_data_ready(struct archive_read* a, struct rar5* rar,
+static int push_data_ready(struct archive_read* a, struct rar5 *rar5,
const uint8_t* buf, size_t size, int64_t offset);
-static void clear_data_ready_stack(struct rar5* rar);
-static void rar5_deinit(struct rar5* rar);
+static void clear_data_ready_stack(struct rar5 *rar5);
+static void rar5_deinit(struct rar5 *rar5);
/* CDE_xxx = Circular Double Ended (Queue) return values. */
enum CDE_RETURN_VALUES {
@@ -516,10 +515,6 @@ uint8_t bf_is_last_block(const struct compressed_block_header* hdr) {
return (hdr->block_flags_u8 >> 6) & 1;
}
-static inline struct rar5* get_context(struct archive_read* a) {
- return (struct rar5*) a->format->data;
-}
-
/* Convenience functions used by filter implementations. */
static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask,
int64_t start, int64_t end)
@@ -535,28 +530,28 @@ static void circular_memcpy(uint8_t* dst, uint8_t* window, const ssize_t mask,
}
}
-static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
+static uint32_t read_filter_data(struct rar5 *rar5, uint32_t offset) {
uint8_t linear_buf[4];
- circular_memcpy(linear_buf, rar->cstate.window_buf,
- rar->cstate.window_mask, offset, offset + 4);
+ circular_memcpy(linear_buf, rar5->cstate.window_buf,
+ rar5->cstate.window_mask, offset, offset + 4);
return archive_le32dec(linear_buf);
}
-static void write_filter_data(struct rar5* rar, uint32_t offset,
+static void write_filter_data(struct rar5 *rar5, uint32_t offset,
uint32_t value)
{
- archive_le32enc(&rar->cstate.filtered_buf[offset], value);
+ archive_le32enc(&rar5->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) {
+static struct filter_info* add_new_filter(struct rar5 *rar5) {
struct filter_info* f = calloc(1, sizeof(*f));
if(!f) {
return NULL;
}
- if (CDE_OK != cdeque_push_back(&rar->cstate.filters, cdeque_filter(f))) {
+ if (CDE_OK != cdeque_push_back(&rar5->cstate.filters, cdeque_filter(f))) {
free(f);
return NULL;
}
@@ -564,7 +559,7 @@ static struct filter_info* add_new_filter(struct rar5* rar) {
return f;
}
-static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
+static int run_delta_filter(struct rar5 *rar5, struct filter_info* flt) {
int i;
ssize_t dest_pos, src_pos = 0;
@@ -576,12 +571,12 @@ static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
{
uint8_t byte;
- byte = rar->cstate.window_buf[
- (rar->cstate.solid_offset + flt->block_start +
- src_pos) & rar->cstate.window_mask];
+ byte = rar5->cstate.window_buf[
+ (rar5->cstate.solid_offset + flt->block_start +
+ src_pos) & rar5->cstate.window_mask];
prev_byte -= byte;
- rar->cstate.filtered_buf[dest_pos] = prev_byte;
+ rar5->cstate.filtered_buf[dest_pos] = prev_byte;
src_pos++;
}
}
@@ -589,21 +584,21 @@ static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
return ARCHIVE_OK;
}
-static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
+static int run_e8e9_filter(struct rar5 *rar5, struct filter_info* flt,
int extended)
{
const uint32_t file_size = 0x1000000;
ssize_t i;
- circular_memcpy(rar->cstate.filtered_buf,
- rar->cstate.window_buf, rar->cstate.window_mask,
- rar->cstate.solid_offset + flt->block_start,
- rar->cstate.solid_offset + flt->block_start + flt->block_length);
+ circular_memcpy(rar5->cstate.filtered_buf,
+ rar5->cstate.window_buf, rar5->cstate.window_mask,
+ rar5->cstate.solid_offset + flt->block_start,
+ rar5->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];
+ uint8_t b = rar5->cstate.window_buf[
+ (rar5->cstate.solid_offset + flt->block_start +
+ i++) & rar5->cstate.window_mask];
/*
* 0xE8 = x86's call <relative_addr_uint32> (function call)
@@ -614,19 +609,19 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
uint32_t addr;
uint32_t offset = (i + flt->block_start) % file_size;
- addr = read_filter_data(rar,
- (uint32_t)(rar->cstate.solid_offset +
- flt->block_start + i) & rar->cstate.window_mask);
+ addr = read_filter_data(rar5,
+ (uint32_t)(rar5->cstate.solid_offset +
+ flt->block_start + i) & rar5->cstate.window_mask);
if(addr & 0x80000000) {
if(((addr + offset) & 0x80000000) == 0) {
- write_filter_data(rar, (uint32_t)i,
+ write_filter_data(rar5, (uint32_t)i,
addr + file_size);
}
} else {
if((addr - file_size) & 0x80000000) {
uint32_t naddr = addr - offset;
- write_filter_data(rar, (uint32_t)i,
+ write_filter_data(rar5, (uint32_t)i,
naddr);
}
}
@@ -638,29 +633,29 @@ static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
return ARCHIVE_OK;
}
-static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
+static int run_arm_filter(struct rar5 *rar5, struct filter_info* flt) {
ssize_t i = 0;
uint32_t offset;
- circular_memcpy(rar->cstate.filtered_buf,
- rar->cstate.window_buf, rar->cstate.window_mask,
- rar->cstate.solid_offset + flt->block_start,
- rar->cstate.solid_offset + flt->block_start + flt->block_length);
+ circular_memcpy(rar5->cstate.filtered_buf,
+ rar5->cstate.window_buf, rar5->cstate.window_mask,
+ rar5->cstate.solid_offset + flt->block_start,
+ rar5->cstate.solid_offset + flt->block_start + flt->block_length);
for(i = 0; i < flt->block_length - 3; i += 4) {
- uint8_t* b = &rar->cstate.window_buf[
- (rar->cstate.solid_offset +
- flt->block_start + i + 3) & rar->cstate.window_mask];
+ uint8_t* b = &rar5->cstate.window_buf[
+ (rar5->cstate.solid_offset +
+ flt->block_start + i + 3) & rar5->cstate.window_mask];
if(*b == 0xEB) {
/* 0xEB = ARM's BL (branch + link) instruction. */
- offset = read_filter_data(rar,
- (rar->cstate.solid_offset + flt->block_start + i) &
- (uint32_t)rar->cstate.window_mask) & 0x00ffffff;
+ offset = read_filter_data(rar5,
+ (rar5->cstate.solid_offset + flt->block_start + i) &
+ (uint32_t)rar5->cstate.window_mask) & 0x00ffffff;
offset -= (uint32_t) ((i + flt->block_start) / 4);
offset = (offset & 0x00ffffff) | 0xeb000000;
- write_filter_data(rar, (uint32_t)i, offset);
+ write_filter_data(rar5, (uint32_t)i, offset);
}
}
@@ -668,14 +663,14 @@ static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
}
static int run_filter(struct archive_read* a, struct filter_info* flt) {
+ struct rar5 *rar5 = a->format->data;
int ret;
- struct rar5* rar = get_context(a);
- clear_data_ready_stack(rar);
- free(rar->cstate.filtered_buf);
+ clear_data_ready_stack(rar5);
+ free(rar5->cstate.filtered_buf);
- rar->cstate.filtered_buf = malloc(flt->block_length);
- if(!rar->cstate.filtered_buf) {
+ rar5->cstate.filtered_buf = malloc(flt->block_length);
+ if(!rar5->cstate.filtered_buf) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for filter data");
return ARCHIVE_FATAL;
@@ -683,18 +678,18 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
switch(flt->type) {
case FILTER_DELTA:
- ret = run_delta_filter(rar, flt);
+ ret = run_delta_filter(rar5, flt);
break;
case FILTER_E8:
/* fallthrough */
case FILTER_E8E9:
- ret = run_e8e9_filter(rar, flt,
+ ret = run_e8e9_filter(rar5, flt,
flt->type == FILTER_E8E9);
break;
case FILTER_ARM:
- ret = run_arm_filter(rar, flt);
+ ret = run_arm_filter(rar5, flt);
break;
default:
@@ -710,8 +705,8 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
return ret;
}
- if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
- flt->block_length, rar->cstate.last_write_ptr))
+ if(ARCHIVE_OK != push_data_ready(a, rar5, rar5->cstate.filtered_buf,
+ flt->block_length, rar5->cstate.last_write_ptr))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Stack overflow when submitting unpacked data");
@@ -719,7 +714,7 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
return ARCHIVE_FATAL;
}
- rar->cstate.last_write_ptr += flt->block_length;
+ rar5->cstate.last_write_ptr += flt->block_length;
return ARCHIVE_OK;
}
@@ -727,15 +722,15 @@ static int run_filter(struct archive_read* a, struct filter_info* flt) {
* Next call of `use_data` will use the pointer, size and offset arguments
* that are specified here. These arguments are pushed to the FIFO stack here,
* and popped from the stack by the `use_data` function. */
-static void push_data(struct archive_read* a, struct rar5* rar,
+static void push_data(struct archive_read* a, struct rar5 *rar5,
const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
{
- const ssize_t wmask = rar->cstate.window_mask;
- const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
- rar->cstate.last_write_ptr) & wmask;
+ const ssize_t wmask = rar5->cstate.window_mask;
+ const ssize_t solid_write_ptr = (rar5->cstate.solid_offset +
+ rar5->cstate.last_write_ptr) & wmask;
- idx_begin += rar->cstate.solid_offset;
- idx_end += rar->cstate.solid_offset;
+ idx_begin += rar5->cstate.solid_offset;
+ idx_end += rar5->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
@@ -745,57 +740,57 @@ static void push_data(struct archive_read* a, struct rar5* rar,
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 -
+ const ssize_t frag1_size = rar5->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);
+ push_data_ready(a, rar5, buf + solid_write_ptr, frag1_size,
+ rar5->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);
+ push_data_ready(a, rar5, buf, frag2_size,
+ rar5->cstate.last_write_ptr + frag1_size);
- rar->cstate.last_write_ptr += frag1_size + frag2_size;
+ rar5->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,
+ push_data_ready(a, rar5,
buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
- rar->cstate.last_write_ptr);
+ rar5->cstate.last_write_ptr);
- rar->cstate.last_write_ptr += idx_end - idx_begin;
+ rar5->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,
+static void push_window_data(struct archive_read* a, struct rar5 *rar5,
int64_t idx_begin, int64_t idx_end)
{
- push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
+ push_data(a, rar5, rar5->cstate.window_buf, idx_begin, idx_end);
}
static int apply_filters(struct archive_read* a) {
+ struct rar5 *rar5 = a->format->data;
struct filter_info* flt;
- struct rar5* rar = get_context(a);
int ret;
- rar->cstate.all_filters_applied = 0;
+ rar5->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,
+ if(CDE_OK == cdeque_front(&rar5->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 +
+ if(rar5->cstate.write_ptr > flt->block_start &&
+ rar5->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) {
+ if(rar5->cstate.last_write_ptr == flt->block_start) {
/* Run the filter specified by descriptor
* `flt`. */
ret = run_filter(a, flt);
@@ -807,15 +802,15 @@ static int apply_filters(struct archive_read* a) {
/* 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,
+ (void) cdeque_pop_front(&rar5->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,
+ push_window_data(a, rar5,
+ rar5->cstate.last_write_ptr,
flt->block_start);
}
@@ -825,12 +820,12 @@ static int apply_filters(struct archive_read* a) {
}
}
- rar->cstate.all_filters_applied = 1;
+ rar5->cstate.all_filters_applied = 1;
return ARCHIVE_OK;
}
-static void dist_cache_push(struct rar5* rar, int value) {
- int* q = rar->cstate.dist_cache;
+static void dist_cache_push(struct rar5 *rar5, int value) {
+ int* q = rar5->cstate.dist_cache;
q[3] = q[2];
q[2] = q[1];
@@ -838,8 +833,8 @@ static void dist_cache_push(struct rar5* rar, int value) {
q[0] = value;
}
-static int dist_cache_touch(struct rar5* rar, int idx) {
- int* q = rar->cstate.dist_cache;
+static int dist_cache_touch(struct rar5 *rar5, int idx) {
+ int* q = rar5->cstate.dist_cache;
int i, dist = q[idx];
for(i = idx; i > 0; i--)
@@ -849,8 +844,8 @@ static int dist_cache_touch(struct rar5* rar, int idx) {
return dist;
}
-static void free_filters(struct rar5* rar) {
- struct cdeque* d = &rar->cstate.filters;
+static void free_filters(struct rar5 *rar5) {
+ struct cdeque* d = &rar5->cstate.filters;
/* Free any remaining filters. All filters should be naturally
* consumed by the unpacking function, so remaining filters after
@@ -870,28 +865,28 @@ static void free_filters(struct rar5* rar) {
cdeque_clear(d);
/* Also clear out the variables needed for sanity checking. */
- rar->cstate.last_block_start = 0;
- rar->cstate.last_block_length = 0;
+ rar5->cstate.last_block_start = 0;
+ rar5->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);
+static void reset_file_context(struct rar5 *rar5) {
+ memset(&rar5->file, 0, sizeof(rar5->file));
+ blake2sp_init(&rar5->file.b2state, 32);
- if(rar->main.solid) {
- rar->cstate.solid_offset += rar->cstate.write_ptr;
+ if(rar5->main.solid) {
+ rar5->cstate.solid_offset += rar5->cstate.write_ptr;
} else {
- rar->cstate.solid_offset = 0;
+ rar5->cstate.solid_offset = 0;
}
- rar->cstate.write_ptr = 0;
- rar->cstate.last_write_ptr = 0;
- rar->cstate.last_unstore_ptr = 0;
+ rar5->cstate.write_ptr = 0;
+ rar5->cstate.last_write_ptr = 0;
+ rar5->cstate.last_unstore_ptr = 0;
- rar->file.redir_type = REDIR_TYPE_NONE;
- rar->file.redir_flags = 0;
+ rar5->file.redir_type = REDIR_TYPE_NONE;
+ rar5->file.redir_flags = 0;
- free_filters(rar);
+ free_filters(rar5);
}
static inline int get_archive_read(struct archive* a,
@@ -948,23 +943,30 @@ 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 multiplier;
uint64_t result = 0;
- size_t shift, i;
+ size_t i;
const uint8_t* p;
- uint8_t b;
- /* We will read maximum of 8 bytes. We don't have to handle the
+ /* We will read maximum of 10 bytes. We don't have to handle the
* situation to read the RAR5 variable-sized value stored at the end of
* the file, because such situation will never happen. */
- if(!read_ahead(a, 8, &p))
+ if(!read_ahead(a, 10, &p))
return 0;
- for(shift = 0, i = 0; i < 8; i++, shift += 7) {
+ for(multiplier = 1, i = 0; i < 10; i++, multiplier *= 128) {
+ uint64_t val;
+ uint8_t b;
+
b = p[i];
/* Strip the MSB from the input byte and add the resulting
* number to the `result`. */
- result += (b & (uint64_t)0x7F) << shift;
+ if(archive_ckd_mul_u64(&val, b & 0x7F, multiplier) ||
+ archive_ckd_add_u64(&result, result, val)) {
+ /* Integer overflow occurred. */
+ return 0;
+ }
/* MSB set to 1 means we need to continue decoding process.
* MSB set to 0 means we're done.
@@ -998,22 +1000,8 @@ static int read_var(struct archive_read* a, uint64_t* pvalue,
}
}
- /* 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;
+ /* All continuation bits were set. This is an error. */
+ return 0;
}
static int read_var_sized(struct archive_read* a, size_t* pvalue,
@@ -1037,47 +1025,47 @@ static int read_var_sized(struct archive_read* a, size_t* pvalue,
return ret;
}
-static int read_bits_32(struct archive_read* a, struct rar5* rar,
+static int read_bits_32(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p, uint32_t* value)
{
- if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Premature end of stream during extraction of data (#1)");
return ARCHIVE_FATAL;
}
- uint32_t bits = archive_be32dec(p + rar->bits.in_addr);
- bits <<= rar->bits.bit_addr;
- bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
+ uint32_t bits = archive_be32dec(p + rar5->bits.in_addr);
+ bits <<= rar5->bits.bit_addr;
+ bits |= p[rar5->bits.in_addr + 4] >> (8 - rar5->bits.bit_addr);
*value = bits;
return ARCHIVE_OK;
}
-static int read_bits_16(struct archive_read* a, struct rar5* rar,
+static int read_bits_16(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p, uint16_t* value)
{
- if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ if(rar5->bits.in_addr >= rar5->cstate.cur_block_size) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Premature end of stream during extraction of data (#2)");
return ARCHIVE_FATAL;
}
- uint32_t bits = archive_be24dec(p + (unsigned)rar->bits.in_addr);
- bits >>= (8 - rar->bits.bit_addr);
+ uint32_t bits = archive_be24dec(p + (unsigned)rar5->bits.in_addr);
+ bits >>= (8 - rar5->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;
+static void skip_bits(struct rar5 *rar5, int bits) {
+ const int new_bits = rar5->bits.bit_addr + bits;
+ rar5->bits.in_addr += new_bits >> 3;
+ rar5->bits.bit_addr = new_bits & 7;
}
/* n = up to 16 */
-static int read_consume_bits(struct archive_read* a, struct rar5* rar,
+static int read_consume_bits(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p, int n, int* value)
{
uint16_t v;
@@ -1089,14 +1077,14 @@ static int read_consume_bits(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- ret = read_bits_16(a, rar, p, &v);
+ ret = read_bits_16(a, rar5, p, &v);
if(ret != ARCHIVE_OK)
return ret;
num = (int) v;
num >>= 16 - n;
- skip_bits(rar, n);
+ skip_bits(rar5, n);
if(value)
*value = num;
@@ -1211,11 +1199,11 @@ static void init_header(struct archive_read* a) {
a->archive.archive_format_name = "RAR5";
}
-static void init_window_mask(struct rar5* rar) {
- if (rar->cstate.window_size)
- rar->cstate.window_mask = rar->cstate.window_size - 1;
+static void init_window_mask(struct rar5 *rar5) {
+ if (rar5->cstate.window_size)
+ rar5->cstate.window_mask = rar5->cstate.window_size - 1;
else
- rar->cstate.window_mask = 0;
+ rar5->cstate.window_mask = 0;
}
enum HEADER_FLAGS {
@@ -1229,7 +1217,7 @@ enum HEADER_FLAGS {
};
static int process_main_locator_extra_block(struct archive_read* a,
- struct rar5* rar)
+ struct rar5 *rar5)
{
uint64_t locator_flags;
@@ -1242,7 +1230,7 @@ static int process_main_locator_extra_block(struct archive_read* a,
}
if(locator_flags & QLIST) {
- if(!read_var(a, &rar->qlist_offset, NULL)) {
+ if(!read_var(a, &rar5->qlist_offset, NULL)) {
return ARCHIVE_EOF;
}
@@ -1250,7 +1238,7 @@ static int process_main_locator_extra_block(struct archive_read* a,
}
if(locator_flags & RECOVERY) {
- if(!read_var(a, &rar->rr_offset, NULL)) {
+ if(!read_var(a, &rar5->rr_offset, NULL)) {
return ARCHIVE_EOF;
}
@@ -1260,7 +1248,7 @@ static int process_main_locator_extra_block(struct archive_read* a,
return ARCHIVE_OK;
}
-static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
+static int parse_file_extra_hash(struct archive_read* a, struct rar5 *rar5,
int64_t* extra_data_size)
{
size_t hash_type = 0;
@@ -1282,13 +1270,13 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
* CRC32. */
if(hash_type == BLAKE2sp) {
const uint8_t* p;
- const int hash_size = sizeof(rar->file.blake2sp);
+ const int hash_size = sizeof(rar5->file.blake2sp);
if(!read_ahead(a, hash_size, &p))
return ARCHIVE_EOF;
- rar->file.has_blake2 = 1;
- memcpy(&rar->file.blake2sp, p, hash_size);
+ rar5->file.has_blake2 = 1;
+ memcpy(&rar5->file.blake2sp, p, hash_size);
if(ARCHIVE_OK != consume(a, hash_size)) {
return ARCHIVE_EOF;
@@ -1381,7 +1369,7 @@ static int parse_file_extra_version(struct archive_read* a,
}
static int parse_file_extra_htime(struct archive_read* a,
- struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size)
+ struct archive_entry* e, struct rar5 *rar5, int64_t* extra_data_size)
{
char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime;
size_t flags = 0;
@@ -1408,39 +1396,39 @@ static int parse_file_extra_htime(struct archive_read* a,
has_mtime = flags & HAS_MTIME;
has_atime = flags & HAS_ATIME;
has_ctime = flags & HAS_CTIME;
- rar->file.e_atime_ns = rar->file.e_ctime_ns = rar->file.e_mtime_ns = 0;
+ rar5->file.e_atime_ns = rar5->file.e_ctime_ns = rar5->file.e_mtime_ns = 0;
if(has_mtime) {
- parse_htime_item(a, unix_time, &rar->file.e_mtime,
- &rar->file.e_mtime_ns, extra_data_size);
+ parse_htime_item(a, unix_time, &rar5->file.e_mtime,
+ &rar5->file.e_mtime_ns, extra_data_size);
}
if(has_ctime) {
- parse_htime_item(a, unix_time, &rar->file.e_ctime,
- &rar->file.e_ctime_ns, extra_data_size);
+ parse_htime_item(a, unix_time, &rar5->file.e_ctime,
+ &rar5->file.e_ctime_ns, extra_data_size);
}
if(has_atime) {
- parse_htime_item(a, unix_time, &rar->file.e_atime,
- &rar->file.e_atime_ns, extra_data_size);
+ parse_htime_item(a, unix_time, &rar5->file.e_atime,
+ &rar5->file.e_atime_ns, extra_data_size);
}
if(has_mtime && has_unix_ns) {
- if(!read_u32(a, &rar->file.e_mtime_ns))
+ if(!read_u32(a, &rar5->file.e_mtime_ns))
return ARCHIVE_EOF;
*extra_data_size -= 4;
}
if(has_ctime && has_unix_ns) {
- if(!read_u32(a, &rar->file.e_ctime_ns))
+ if(!read_u32(a, &rar5->file.e_ctime_ns))
return ARCHIVE_EOF;
*extra_data_size -= 4;
}
if(has_atime && has_unix_ns) {
- if(!read_u32(a, &rar->file.e_atime_ns))
+ if(!read_u32(a, &rar5->file.e_atime_ns))
return ARCHIVE_EOF;
*extra_data_size -= 4;
@@ -1449,35 +1437,35 @@ static int parse_file_extra_htime(struct archive_read* a,
/* The seconds and nanoseconds are either together, or separated in two
* fields so we parse them, then set the archive_entry's times. */
if(has_mtime) {
- archive_entry_set_mtime(e, rar->file.e_mtime, rar->file.e_mtime_ns);
+ archive_entry_set_mtime(e, rar5->file.e_mtime, rar5->file.e_mtime_ns);
}
if(has_ctime) {
- archive_entry_set_ctime(e, rar->file.e_ctime, rar->file.e_ctime_ns);
+ archive_entry_set_ctime(e, rar5->file.e_ctime, rar5->file.e_ctime_ns);
}
if(has_atime) {
- archive_entry_set_atime(e, rar->file.e_atime, rar->file.e_atime_ns);
+ archive_entry_set_atime(e, rar5->file.e_atime, rar5->file.e_atime_ns);
}
return ARCHIVE_OK;
}
static int parse_file_extra_redir(struct archive_read* a,
- struct archive_entry* e, struct rar5* rar, int64_t* extra_data_size)
+ struct archive_entry* e, struct rar5 *rar5, int64_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))
+ if(!read_var(a, &rar5->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))
+ if(!read_var(a, &rar5->file.redir_flags, &value_size))
return ARCHIVE_EOF;
if(ARCHIVE_OK != consume(a, (int64_t)value_size))
return ARCHIVE_EOF;
@@ -1508,12 +1496,12 @@ static int parse_file_extra_redir(struct archive_read* a,
if(ARCHIVE_OK != consume(a, (int64_t)target_size))
return ARCHIVE_EOF;
- switch(rar->file.redir_type) {
+ switch(rar5->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) {
+ if (rar5->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
archive_entry_set_symlink_type(e,
AE_SYMLINK_TYPE_DIRECTORY);
} else {
@@ -1615,7 +1603,7 @@ static int parse_file_extra_owner(struct archive_read* a,
}
static int process_head_file_extra(struct archive_read* a,
- struct archive_entry* e, struct rar5* rar, int64_t extra_data_size)
+ struct archive_entry* e, struct rar5 *rar5, int64_t extra_data_size)
{
uint64_t extra_field_size;
uint64_t extra_field_id = 0;
@@ -1645,15 +1633,15 @@ static int process_head_file_extra(struct archive_read* a,
switch(extra_field_id) {
case EX_HASH:
- ret = parse_file_extra_hash(a, rar,
+ ret = parse_file_extra_hash(a, rar5,
&extra_data_size);
break;
case EX_HTIME:
- ret = parse_file_extra_htime(a, e, rar,
+ ret = parse_file_extra_htime(a, e, rar5,
&extra_data_size);
break;
case EX_REDIR:
- ret = parse_file_extra_redir(a, e, rar,
+ ret = parse_file_extra_redir(a, e, rar5,
&extra_data_size);
break;
case EX_UOWNER:
@@ -1667,8 +1655,8 @@ static int process_head_file_extra(struct archive_read* a,
case EX_CRYPT:
/* Mark the entry as encrypted */
archive_entry_set_is_data_encrypted(e, 1);
- rar->has_encrypted_entries = 1;
- rar->cstate.data_encrypted = 1;
+ rar5->has_encrypted_entries = 1;
+ rar5->cstate.data_encrypted = 1;
/* fallthrough */
case EX_SUBDATA:
/* fallthrough */
@@ -1730,7 +1718,7 @@ static int file_entry_sanity_checks(struct archive_read* a,
return ARCHIVE_OK;
}
-static int process_head_file(struct archive_read* a, struct rar5* rar,
+static int process_head_file(struct archive_read* a, struct rar5 *rar5,
struct archive_entry* entry, size_t block_flags)
{
int64_t extra_data_size = 0;
@@ -1769,8 +1757,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
archive_entry_clear(entry);
/* Do not reset file context if we're switching archives. */
- if(!rar->cstate.switch_multivolume) {
- reset_file_context(rar);
+ if(!rar5->cstate.switch_multivolume) {
+ reset_file_context(rar5);
}
if(block_flags & HFL_EXTRA_DATA) {
@@ -1793,9 +1781,9 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- rar->file.bytes_remaining = data_size;
+ rar5->file.bytes_remaining = data_size;
} else {
- rar->file.bytes_remaining = 0;
+ rar5->file.bytes_remaining = 0;
}
if(!read_var_sized(a, &file_flags, NULL))
@@ -1810,9 +1798,9 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+ rar5->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
- sanity_ret = file_entry_sanity_checks(a, block_flags, rar->file.dir,
+ sanity_ret = file_entry_sanity_checks(a, block_flags, rar5->file.dir,
unpacked_size, data_size);
if (sanity_ret != ARCHIVE_OK) {
@@ -1839,19 +1827,19 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
c_version = (int) (compression_info & 0x3f);
/* RAR5 seems to limit the dictionary size to 64MB. */
- window_size = (rar->file.dir > 0) ?
+ window_size = (rar5->file.dir > 0) ?
0 :
g_unpack_window_size << ((compression_info >> 10) & 15);
- rar->cstate.method = c_method;
- rar->cstate.version = c_version + 50;
- rar->file.solid = (compression_info & SOLID) > 0;
+ rar5->cstate.method = c_method;
+ rar5->cstate.version = c_version + 50;
+ rar5->file.solid = (compression_info & SOLID) > 0;
/* Archives which declare solid files without initializing the window
* buffer first are invalid, unless previous data was encrypted, in
* which case we may never have had the chance */
- if(rar->file.solid > 0 && rar->cstate.data_encrypted == 0 &&
- rar->cstate.window_buf == NULL) {
+ if(rar5->file.solid > 0 && rar5->cstate.data_encrypted == 0 &&
+ rar5->cstate.window_buf == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Declared solid file, but no window buffer "
"initialized yet");
@@ -1861,18 +1849,18 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
/* 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))
+ (rar5->file.dir == 0 && window_size == 0))
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Declared dictionary size is not supported");
return ARCHIVE_FATAL;
}
- if(rar->file.solid > 0) {
+ if(rar5->file.solid > 0) {
/* Re-check if current window size is the same as previous
* window size (for solid files only). */
- if(rar->file.solid_window_size > 0 &&
- rar->file.solid_window_size != (ssize_t) window_size)
+ if(rar5->file.solid_window_size > 0 &&
+ rar5->file.solid_window_size != (ssize_t) window_size)
{
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Window size for this solid file doesn't match "
@@ -1881,23 +1869,23 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
}
}
else
- rar->cstate.data_encrypted = 0; /* Reset for new buffer */
+ rar5->cstate.data_encrypted = 0; /* Reset for new buffer */
- if(rar->cstate.window_size < (ssize_t) window_size &&
- rar->cstate.window_buf)
+ if(rar5->cstate.window_size < (ssize_t) window_size &&
+ rar5->cstate.window_buf)
{
/* The `data_ready` stack contains pointers to the `window_buf` or
* `filtered_buf` buffers. Since we're about to reallocate the first
* buffer, some of those pointers could become invalid. Therefore, we
* need to dispose of all entries from the stack before attempting the
* realloc. */
- clear_data_ready_stack(rar);
+ clear_data_ready_stack(rar5);
/* If window_buf has been allocated before, reallocate it, so
* that its size will match new window_size. */
uint8_t* new_window_buf =
- realloc(rar->cstate.window_buf, (size_t) window_size);
+ realloc(rar5->cstate.window_buf, (size_t) window_size);
if(!new_window_buf) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
@@ -1906,23 +1894,23 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- rar->cstate.window_buf = new_window_buf;
+ rar5->cstate.window_buf = new_window_buf;
}
/* Values up to 64M should fit into ssize_t on every
* architecture. */
- rar->cstate.window_size = (ssize_t) window_size;
+ rar5->cstate.window_size = (ssize_t) window_size;
- if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
+ if(rar5->file.solid > 0 && rar5->file.solid_window_size == 0) {
/* Solid files have to have the same window_size across
whole archive. Remember the window_size parameter
for first solid file found. */
- rar->file.solid_window_size = rar->cstate.window_size;
+ rar5->file.solid_window_size = rar5->cstate.window_size;
}
- init_window_mask(rar);
+ init_window_mask(rar5);
- rar->file.service = 0;
+ rar5->file.service = 0;
if(!read_var_sized(a, &host_os, NULL))
return ARCHIVE_EOF;
@@ -1950,8 +1938,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
char buf[sizeof(",rdonly,hidden,system")];
- char *fflags[3] = { "", "", "" };
- char **flag = fflags;
+ const char *fflags[3] = { "", "", "" };
+ const char **flag = fflags;
if (file_attr & ATTR_READONLY)
*flag++ = ",rdonly";
@@ -2005,7 +1993,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
archive_entry_update_pathname_utf8(entry, name_utf8_buf);
if(extra_data_size > 0) {
- int ret = process_head_file_extra(a, entry, rar,
+ int ret = process_head_file_extra(a, entry, rar5,
extra_data_size);
/*
@@ -2024,8 +2012,8 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
}
if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
- rar->file.unpacked_size = (ssize_t) unpacked_size;
- if(rar->file.redir_type == REDIR_TYPE_NONE)
+ rar5->file.unpacked_size = (ssize_t) unpacked_size;
+ if(rar5->file.redir_type == REDIR_TYPE_NONE)
archive_entry_set_size(entry, unpacked_size);
}
@@ -2034,18 +2022,18 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
}
if(file_flags & CRC32) {
- rar->file.stored_crc32 = crc;
+ rar5->file.stored_crc32 = crc;
}
- if(!rar->cstate.switch_multivolume) {
+ if(!rar5->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;
+ rar5->cstate.block_parsing_finished = 1;
+ rar5->cstate.all_filters_applied = 1;
+ rar5->cstate.initialized = 0;
}
- if(rar->generic.split_before > 0) {
+ if(rar5->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
@@ -2058,15 +2046,15 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
}
}
-static int process_head_service(struct archive_read* a, struct rar5* rar,
+static int process_head_service(struct archive_read* a, struct rar5 *rar5,
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);
+ int ret = process_head_file(a, rar5, entry, block_flags);
if(ret != ARCHIVE_OK)
return ret;
- rar->file.service = 1;
+ rar5->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
@@ -2079,7 +2067,7 @@ static int process_head_service(struct archive_read* a, struct rar5* rar,
return ARCHIVE_RETRY;
}
-static int process_head_main(struct archive_read* a, struct rar5* rar,
+static int process_head_main(struct archive_read* a, struct rar5 *rar5,
struct archive_entry* entry, size_t block_flags)
{
int ret;
@@ -2115,8 +2103,8 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
return ARCHIVE_EOF;
}
- rar->main.volume = (archive_flags & VOLUME) > 0;
- rar->main.solid = (archive_flags & SOLID) > 0;
+ rar5->main.volume = (archive_flags & VOLUME) > 0;
+ rar5->main.solid = (archive_flags & SOLID) > 0;
if(archive_flags & VOLUME_NUMBER) {
size_t v = 0;
@@ -2131,13 +2119,13 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- rar->main.vol_no = (unsigned int) v;
+ rar5->main.vol_no = (unsigned int) v;
} else {
- rar->main.vol_no = 0;
+ rar5->main.vol_no = 0;
}
- if(rar->vol.expected_vol_no > 0 &&
- rar->main.vol_no != rar->vol.expected_vol_no)
+ if(rar5->vol.expected_vol_no > 0 &&
+ rar5->main.vol_no != rar5->vol.expected_vol_no)
{
/* Returning EOF instead of FATAL because of strange
* libarchive behavior. When opening multiple files via
@@ -2168,7 +2156,7 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
switch(extra_field_id) {
case LOCATOR:
- ret = process_main_locator_extra_block(a, rar);
+ ret = process_main_locator_extra_block(a, rar5);
if(ret != ARCHIVE_OK) {
/* Error while parsing main locator extra
* block. */
@@ -2188,25 +2176,25 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
}
static int skip_unprocessed_bytes(struct archive_read* a) {
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
int ret;
- if(rar->file.bytes_remaining) {
+ if(rar5->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) {
+ if(rar5->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);
+ ret = consume(a, rar5->file.bytes_remaining);
if(ret != ARCHIVE_OK) {
return ret;
}
- rar->file.bytes_remaining = 0;
+ rar5->file.bytes_remaining = 0;
} else {
/* If we're not in merge mode, use safe skipping code.
* This will ensure we'll handle solid archives
@@ -2300,7 +2288,7 @@ static int process_base_block(struct archive_read* a,
{
const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
uint32_t hdr_crc, computed_crc;
size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
size_t header_id = 0;
@@ -2383,16 +2371,16 @@ static int process_base_block(struct archive_read* a,
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;
+ rar5->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+ rar5->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+ rar5->generic.size = (int)hdr_size;
+ rar5->generic.last_header_id = (int)header_id;
+ rar5->main.endarc = 0;
/* Those are possible header ids in RARv5. */
switch(header_id) {
case HEAD_MAIN:
- ret = process_head_main(a, rar, entry, header_flags);
+ ret = process_head_main(a, rar5, entry, header_flags);
/* Main header doesn't have any files in it, so it's
* pointless to return to the caller. Retry to next
@@ -2405,28 +2393,28 @@ static int process_base_block(struct archive_read* a,
return ret;
case HEAD_SERVICE:
- ret = process_head_service(a, rar, entry, header_flags);
+ ret = process_head_service(a, rar5, entry, header_flags);
return ret;
case HEAD_FILE:
- ret = process_head_file(a, rar, entry, header_flags);
+ ret = process_head_file(a, rar5, entry, header_flags);
return ret;
case HEAD_CRYPT:
archive_entry_set_is_metadata_encrypted(entry, 1);
archive_entry_set_is_data_encrypted(entry, 1);
- rar->has_encrypted_entries = 1;
- rar->headers_are_encrypted = 1;
+ rar5->has_encrypted_entries = 1;
+ rar5->headers_are_encrypted = 1;
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Encryption is not supported");
return ARCHIVE_FATAL;
case HEAD_ENDARC:
- rar->main.endarc = 1;
+ rar5->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) {
+ if(rar5->main.volume) {
/* In case there is part02.rar, position the
* read pointer in a proper place, so we can
* resume parsing. */
@@ -2434,7 +2422,7 @@ static int process_base_block(struct archive_read* a,
if(ret == ARCHIVE_FATAL) {
return ARCHIVE_EOF;
} else {
- if(rar->vol.expected_vol_no ==
+ if(rar5->vol.expected_vol_no ==
UINT_MAX) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2442,8 +2430,8 @@ static int process_base_block(struct archive_read* a,
return ARCHIVE_FATAL;
}
- rar->vol.expected_vol_no =
- rar->main.vol_no + 1;
+ rar5->vol.expected_vol_no =
+ rar5->main.vol_no + 1;
return ARCHIVE_OK;
}
} else {
@@ -2476,13 +2464,16 @@ static int process_base_block(struct archive_read* a,
}
static int skip_base_block(struct archive_read* a) {
+ struct rar5 *rar5 = a->format->data;
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();
+ if (entry == NULL)
+ return ARCHIVE_FATAL;
+
ret = process_base_block(a, entry);
/* Discard operations on this archive_entry structure. */
@@ -2490,7 +2481,7 @@ static int skip_base_block(struct archive_read* a) {
if(ret == ARCHIVE_FATAL)
return ret;
- if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
+ if(rar5->generic.last_header_id == 2 && rar5->generic.split_before > 0)
return ARCHIVE_OK;
if(ret == ARCHIVE_OK)
@@ -2558,76 +2549,77 @@ fatal:
static int rar5_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
int ret;
/*
* It should be sufficient to call archive_read_next_header() for
* a reader to determine if an entry is encrypted or not.
*/
- if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
- rar->has_encrypted_entries = 0;
+ if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar5->has_encrypted_entries = 0;
}
- if(rar->header_initialized == 0) {
+ if(rar5->header_initialized == 0) {
init_header(a);
if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
return ret;
- rar->header_initialized = 1;
+ rar5->header_initialized = 1;
}
- if(rar->skipped_magic == 0) {
+ if(rar5->skipped_magic == 0) {
if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
return ARCHIVE_EOF;
}
- rar->skipped_magic = 1;
+ rar5->skipped_magic = 1;
}
do {
ret = process_base_block(a, entry);
} while(ret == ARCHIVE_RETRY ||
- (rar->main.endarc > 0 && ret == ARCHIVE_OK));
+ (rar5->main.endarc > 0 && ret == ARCHIVE_OK));
return ret;
}
-static int init_unpack(struct rar5* rar) {
- rar->file.calculated_crc32 = 0;
- init_window_mask(rar);
+static int init_unpack(struct rar5 *rar5) {
+ rar5->file.calculated_crc32 = 0;
+ init_window_mask(rar5);
- free(rar->cstate.window_buf);
- free(rar->cstate.filtered_buf);
+ free(rar5->cstate.window_buf);
+ free(rar5->cstate.filtered_buf);
- rar->cstate.window_buf = NULL;
- rar->cstate.filtered_buf = NULL;
+ rar5->cstate.window_buf = NULL;
+ rar5->cstate.filtered_buf = NULL;
- if(rar->cstate.window_size > 0) {
- rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
- if(rar->cstate.window_buf == NULL)
+ if(rar5->cstate.window_size > 0) {
+ rar5->cstate.window_buf = calloc(1, rar5->cstate.window_size);
+ if(rar5->cstate.window_buf == NULL)
return ARCHIVE_FATAL;
- rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
- if(rar->cstate.filtered_buf == NULL)
+ rar5->cstate.filtered_buf = calloc(1,
+ rar5->cstate.window_size);
+ if(rar5->cstate.filtered_buf == NULL)
return ARCHIVE_FATAL;
}
- clear_data_ready_stack(rar);
+ clear_data_ready_stack(rar5);
- rar->cstate.write_ptr = 0;
- rar->cstate.last_write_ptr = 0;
+ rar5->cstate.write_ptr = 0;
+ rar5->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(&rar5->cstate.bd, 0, sizeof(rar5->cstate.bd));
+ memset(&rar5->cstate.ld, 0, sizeof(rar5->cstate.ld));
+ memset(&rar5->cstate.dd, 0, sizeof(rar5->cstate.dd));
+ memset(&rar5->cstate.ldd, 0, sizeof(rar5->cstate.ldd));
+ memset(&rar5->cstate.rd, 0, sizeof(rar5->cstate.rd));
return ARCHIVE_OK;
}
-static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
+static void update_crc(struct rar5 *rar5, const uint8_t* p, size_t to_read) {
int verify_crc;
- if(rar->skip_mode) {
+ if(rar5->skip_mode) {
#if defined CHECK_CRC_ON_SOLID_SKIP
verify_crc = 1;
#else
@@ -2639,17 +2631,19 @@ static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
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, (unsigned int)to_read);
+ if(rar5->file.stored_crc32 > 0) {
+ rar5->file.calculated_crc32 =
+ crc32(rar5->file.calculated_crc32, p,
+ (unsigned int)to_read);
}
/* Check if the file uses an optional BLAKE2sp checksum
* algorithm. */
- if(rar->file.has_blake2 > 0) {
+ if(rar5->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);
+ (void) blake2sp_update(&rar5->file.b2state, p,
+ to_read);
}
}
}
@@ -2733,12 +2727,12 @@ static int create_decode_tables(uint8_t* bit_length,
static int decode_number(struct archive_read* a, struct decode_table* table,
const uint8_t* p, uint16_t* num)
{
+ struct rar5 *rar5 = a->format->data;
int i, bits, dist, ret;
uint16_t bitfield;
uint32_t pos;
- struct rar5* rar = get_context(a);
- if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &bitfield))) {
return ret;
}
@@ -2746,7 +2740,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table,
if(bitfield < table->decode_len[table->quick_bits]) {
int code = bitfield >> (16 - table->quick_bits);
- skip_bits(rar, table->quick_len[code]);
+ skip_bits(rar5, table->quick_len[code]);
*num = table->quick_num[code];
return ARCHIVE_OK;
}
@@ -2760,7 +2754,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table,
}
}
- skip_bits(rar, bits);
+ skip_bits(rar5, bits);
dist = bitfield - table->decode_len[bits - 1];
dist >>= (16 - bits);
@@ -2774,7 +2768,7 @@ static int decode_number(struct archive_read* a, struct decode_table* table,
}
/* Reads and parses Huffman tables from the beginning of the block. */
-static int parse_tables(struct archive_read* a, struct rar5* rar,
+static int parse_tables(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p)
{
int ret, value, i, w, idx = 0;
@@ -2788,7 +2782,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
/* The data for table generation is compressed using a simple RLE-like
* algorithm when storing zeroes, so we need to unpack it first. */
for(w = 0, i = 0; w < HUFF_BC;) {
- if(i >= rar->cstate.cur_block_size) {
+ if(i >= rar5->cstate.cur_block_size) {
/* Truncated data, can't continue. */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2832,10 +2826,10 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
}
}
- rar->bits.in_addr = i;
- rar->bits.bit_addr = nibble_shift ^ 4;
+ rar5->bits.in_addr = i;
+ rar5->bits.bit_addr = nibble_shift ^ 4;
- ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
+ ret = create_decode_tables(bit_length, &rar5->cstate.bd, HUFF_BC);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Decoding huffman tables failed");
@@ -2845,7 +2839,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
for(i = 0; i < HUFF_TABLE_SIZE;) {
uint16_t num;
- ret = decode_number(a, &rar->cstate.bd, p, &num);
+ ret = decode_number(a, &rar5->cstate.bd, p, &num);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@@ -2861,17 +2855,17 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
/* 16..17: repeat previous code */
uint16_t n;
- if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n)))
return ret;
if(num == 16) {
n >>= 13;
n += 3;
- skip_bits(rar, 3);
+ skip_bits(rar5, 3);
} else {
n >>= 9;
n += 11;
- skip_bits(rar, 7);
+ skip_bits(rar5, 7);
}
if(i > 0) {
@@ -2890,17 +2884,17 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
/* other codes: fill with zeroes `n` times */
uint16_t n;
- if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &n)))
return ret;
if(num == 18) {
n >>= 13;
n += 3;
- skip_bits(rar, 3);
+ skip_bits(rar5, 3);
} else {
n >>= 9;
n += 11;
- skip_bits(rar, 7);
+ skip_bits(rar5, 7);
}
while(n-- > 0 && i < HUFF_TABLE_SIZE)
@@ -2908,7 +2902,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
}
}
- ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
+ ret = create_decode_tables(&table[idx], &rar5->cstate.ld, HUFF_NC);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Failed to create literal table");
@@ -2917,7 +2911,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
idx += HUFF_NC;
- ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
+ ret = create_decode_tables(&table[idx], &rar5->cstate.dd, HUFF_DC);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Failed to create distance table");
@@ -2926,7 +2920,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
idx += HUFF_DC;
- ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
+ ret = create_decode_tables(&table[idx], &rar5->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");
@@ -2935,7 +2929,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
idx += HUFF_LDC;
- ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
+ ret = create_decode_tables(&table[idx], &rar5->cstate.rd, HUFF_RC);
if(ret != ARCHIVE_OK) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Failed to create repeating distances table");
@@ -3009,13 +3003,13 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p,
}
/* Convenience function used during filter processing. */
-static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+static int parse_filter_data(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p, uint32_t* filter_data)
{
int i, bytes, ret;
uint32_t data = 0;
- if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5, p, 2, &bytes)))
return ret;
bytes++;
@@ -3023,14 +3017,14 @@ static int parse_filter_data(struct archive_read* a, struct rar5* rar,
for(i = 0; i < bytes; i++) {
uint16_t byte;
- if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar5, p, &byte))) {
return ret;
}
/* 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);
+ skip_bits(rar5, 8);
}
*filter_data = data;
@@ -3038,12 +3032,12 @@ static int parse_filter_data(struct archive_read* a, struct rar5* rar,
}
/* Function is used during sanity checking. */
-static int is_valid_filter_block_start(struct rar5* rar,
+static int is_valid_filter_block_start(struct rar5 *rar5,
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 + rar5->cstate.write_ptr;
+ const int64_t last_bs = rar5->cstate.last_block_start;
+ const ssize_t last_bl = rar5->cstate.last_block_length;
if(last_bs == 0 || last_bl == 0) {
/* We didn't have any filters yet, so accept this offset. */
@@ -3063,32 +3057,32 @@ static int is_valid_filter_block_start(struct rar5* rar,
/* 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) {
+ struct rar5 *rar5 = ar->format->data;
uint32_t block_start, block_length;
uint16_t filter_type;
struct filter_info* filt = NULL;
- struct rar5* rar = get_context(ar);
int ret;
/* Read the parameters from the input stream. */
- if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_start)))
return ret;
- if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar5, p, &block_length)))
return ret;
- if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+ if(ARCHIVE_OK != (ret = read_bits_16(ar, rar5, p, &filter_type)))
return ret;
filter_type >>= 13;
- skip_bits(rar, 3);
+ skip_bits(rar5, 3);
/* Perform some sanity checks on this filter parameters. */
if(block_length < 4 ||
block_length > 0x400000 ||
- !is_valid_filter_block_start(rar, block_start) ||
- (rar->cstate.window_size > 0 &&
- (ssize_t)block_length > rar->cstate.window_size >> 1))
+ !is_valid_filter_block_start(rar5, block_start) ||
+ (rar5->cstate.window_size > 0 &&
+ (ssize_t)block_length > rar5->cstate.window_size >> 1))
{
archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid filter encountered");
@@ -3096,7 +3090,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
}
/* Allocate a new filter. */
- filt = add_new_filter(rar);
+ filt = add_new_filter(rar5);
if(filt == NULL) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate memory for a filter descriptor");
@@ -3104,11 +3098,11 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
}
filt->type = filter_type;
- filt->block_start = rar->cstate.write_ptr + block_start;
+ filt->block_start = rar5->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;
+ rar5->cstate.last_block_start = filt->block_start;
+ rar5->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
@@ -3116,7 +3110,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
if(filter_type == FILTER_DELTA) {
int channels;
- if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+ if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar5, p, 5, &channels)))
return ret;
filt->channels = channels + 1;
@@ -3125,7 +3119,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
return ARCHIVE_OK;
}
-static int decode_code_length(struct archive_read* a, struct rar5* rar,
+static int decode_code_length(struct archive_read* a, struct rar5 *rar5,
const uint8_t* p, uint16_t code)
{
int lbits, length = 2;
@@ -3141,7 +3135,7 @@ static int decode_code_length(struct archive_read* a, struct rar5* rar,
if(lbits > 0) {
int add;
- if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
+ if(ARCHIVE_OK != read_consume_bits(a, rar5, p, lbits, &add))
return -1;
length += add;
@@ -3151,15 +3145,22 @@ static int decode_code_length(struct archive_read* a, struct rar5* rar,
}
static int copy_string(struct archive_read* a, int len, int dist) {
- struct rar5* rar = get_context(a);
- const ssize_t cmask = rar->cstate.window_mask;
- const uint64_t write_ptr = rar->cstate.write_ptr +
- rar->cstate.solid_offset;
+ struct rar5 *rar5 = a->format->data;
+ const ssize_t cmask = rar5->cstate.window_mask;
+ const uint64_t write_ptr = rar5->cstate.write_ptr +
+ rar5->cstate.solid_offset;
int i;
- if (rar->cstate.window_buf == NULL)
+ if (rar5->cstate.window_buf == NULL)
return ARCHIVE_FATAL;
+ if (rar5->cstate.write_ptr > rar5->file.unpacked_size ||
+ len > rar5->file.unpacked_size - rar5->cstate.write_ptr) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Uncompressed data exceeds declared size");
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ }
+
/* The unpacker spends most of the time in this function. It would be
* a good idea to introduce some optimizations here.
*
@@ -3170,26 +3171,26 @@ static int copy_string(struct archive_read* a, int len, int dist) {
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];
+ rar5->cstate.window_buf[write_idx] =
+ rar5->cstate.window_buf[read_idx];
}
- rar->cstate.write_ptr += len;
+ rar5->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);
+ struct rar5 *rar5 = a->format->data;
uint16_t num;
int ret;
- const uint64_t cmask = rar->cstate.window_mask;
- const struct compressed_block_header* hdr = &rar->last_block_hdr;
+ const uint64_t cmask = rar5->cstate.window_mask;
+ const struct compressed_block_header* hdr = &rar5->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)) {
+ if(rar5->cstate.write_ptr - rar5->cstate.last_write_ptr >
+ (rar5->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
@@ -3197,18 +3198,18 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
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(rar5->bits.in_addr > rar5->cstate.cur_block_size - 1 ||
+ (rar5->bits.in_addr == rar5->cstate.cur_block_size - 1 &&
+ rar5->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;
+ rar5->cstate.block_parsing_finished = 1;
break;
}
/* Decode the next literal. */
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
+ if(ARCHIVE_OK != decode_number(a, &rar5->cstate.ld, p, &num)) {
return ARCHIVE_EOF;
}
@@ -3228,15 +3229,25 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
if(num < 256) {
/* Directly store the byte. */
- int64_t write_idx = rar->cstate.solid_offset +
- rar->cstate.write_ptr++;
+ int64_t write_idx;
+
+ /* A literal write emits one byte; copy_string() checks len. */
+ if(rar5->cstate.write_ptr >= rar5->file.unpacked_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Uncompressed data exceeds declared size");
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ }
+
+ write_idx = rar5->cstate.solid_offset +
+ rar5->cstate.write_ptr++;
- rar->cstate.window_buf[write_idx & cmask] =
+ rar5->cstate.window_buf[write_idx & cmask] =
(uint8_t) num;
continue;
} else if(num >= 262) {
uint16_t dist_slot;
- int len = decode_code_length(a, rar, p, num - 262),
+ int len = decode_code_length(a, rar5, p, num - 262),
dbits,
dist = 1;
@@ -3245,17 +3256,17 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the code length");
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
+ if(ARCHIVE_OK != decode_number(a, &rar5->cstate.dd, p,
&dist_slot))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the distance slot");
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
if(dist_slot < 4) {
@@ -3279,28 +3290,28 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
if(dbits > 4) {
if(ARCHIVE_OK != (ret = read_bits_32(
- a, rar, p, &add))) {
+ a, rar5, p, &add))) {
/* Return EOF if we
* can't read more
* data. */
return ret;
}
- skip_bits(rar, dbits - 4);
+ skip_bits(rar5, dbits - 4);
add = (add >> (
36 - dbits)) << 4;
dist += add;
}
if(ARCHIVE_OK != decode_number(a,
- &rar->cstate.ldd, p, &low_dist))
+ &rar5->cstate.ldd, p, &low_dist))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Failed to decode the "
"distance slot");
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
if(dist >= INT_MAX - low_dist - 1) {
@@ -3310,7 +3321,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
ARCHIVE_ERRNO_FILE_FORMAT,
"Distance pointer "
"overflow");
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
dist += low_dist;
@@ -3318,7 +3329,7 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
/* dbits is one of [0,1,2,3] */
int add;
- if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar5,
p, dbits, &add))) {
/* Return EOF if we can't read
* more data. */
@@ -3341,11 +3352,12 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
}
}
- dist_cache_push(rar, dist);
- rar->cstate.last_len = len;
+ dist_cache_push(rar5, dist);
+ rar5->cstate.last_len = len;
- if(ARCHIVE_OK != copy_string(a, len, dist))
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ ret = copy_string(a, len, dist);
+ if(ret != ARCHIVE_OK)
+ return ret;
continue;
} else if(num == 256) {
@@ -3356,38 +3368,38 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
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 rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
- }
+ if(rar5->cstate.last_len != 0) {
+ ret = copy_string(a,
+ rar5->cstate.last_len,
+ rar5->cstate.dist_cache[0]);
+ if(ret != ARCHIVE_OK)
+ return ret;
}
continue;
} else {
/* num < 262 */
const int idx = num - 258;
- const int dist = dist_cache_touch(rar, idx);
+ const int dist = dist_cache_touch(rar5, idx);
uint16_t len_slot;
int len;
- if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
+ if(ARCHIVE_OK != decode_number(a, &rar5->cstate.rd, p,
&len_slot)) {
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
- len = decode_code_length(a, rar, p, len_slot);
+ len = decode_code_length(a, rar5, p, len_slot);
if (len == -1) {
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ return rar5->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
}
- rar->cstate.last_len = len;
+ rar5->cstate.last_len = len;
- if(ARCHIVE_OK != copy_string(a, len, dist))
- return rar->main.solid ? ARCHIVE_FATAL : ARCHIVE_FAILED;
+ ret = copy_string(a, len, dist);
+ if(ret != ARCHIVE_OK)
+ return ret;
continue;
}
@@ -3445,15 +3457,15 @@ static int scan_for_signature(struct archive_read* a) {
/* 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) {
+ struct rar5 *rar5 = a->format->data;
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
+ * The state machine starts with `rar5->main.endarc` == 0. It also
* assumes that current stream pointer points to some base block
* header.
*
@@ -3462,10 +3474,10 @@ static int advance_multivolume(struct archive_read* a) {
*/
while(1) {
- if(rar->main.endarc == 1) {
+ if(rar5->main.endarc == 1) {
int looping = 1;
- rar->main.endarc = 0;
+ rar5->main.endarc = 0;
while(looping) {
lret = skip_base_block(a);
@@ -3503,7 +3515,7 @@ static int advance_multivolume(struct archive_read* a) {
/* If there was an error during skipping, or we
* have just skipped a FILE base block... */
- if(rar->main.endarc == 0) {
+ if(rar5->main.endarc == 0) {
return lret;
} else {
continue;
@@ -3522,12 +3534,12 @@ static int advance_multivolume(struct archive_read* a) {
static int merge_block(struct archive_read* a, ssize_t block_size,
const uint8_t** p)
{
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
ssize_t cur_block_size, partial_offset = 0;
const uint8_t* lp;
int ret;
- if(rar->merge_mode) {
+ if(rar5->merge_mode) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Recursive merge is not allowed");
@@ -3535,27 +3547,27 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
}
/* Set a flag that we're in the switching mode. */
- rar->cstate.switch_multivolume = 1;
+ rar5->cstate.switch_multivolume = 1;
/* Reallocate the memory which will hold the whole block. */
- if(rar->vol.push_buf)
- free((void*) rar->vol.push_buf);
+ if(rar5->vol.push_buf)
+ free((void*) rar5->vol.push_buf);
/* Increasing the allocation block by 8 is due to bit reading functions,
* which are using additional 2 or 4 bytes. Allocating the block size
* 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) {
+ rar5->vol.push_buf = malloc(block_size + 8);
+ if(!rar5->vol.push_buf) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for a merge block buffer");
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
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);
+ memset(&rar5->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
@@ -3564,7 +3576,7 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
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,
+ cur_block_size = rar5_min(rar5->file.bytes_remaining,
block_size - partial_offset);
if(cur_block_size < 1) {
@@ -3574,12 +3586,12 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Encountered invalid block size during block merge");
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
return ARCHIVE_FATAL;
}
if(!read_ahead(a, cur_block_size, &lp)) {
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
return ARCHIVE_EOF;
}
@@ -3589,27 +3601,27 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
archive_set_error(&a->archive,
ARCHIVE_ERRNO_PROGRAMMER,
"Consumed too much data when merging blocks");
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
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);
+ memcpy(&rar5->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)) {
/* Data was copied but stream pointer didn't advance;
* stream position is unrecoverable. */
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
return ARCHIVE_FATAL;
}
/* 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;
+ rar5->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
@@ -3620,18 +3632,18 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
/* If we don't have any bytes to read, this means we should
* switch to another multivolume archive file. */
- if(rar->file.bytes_remaining == 0) {
- rar->merge_mode++;
+ if(rar5->file.bytes_remaining == 0) {
+ rar5->merge_mode++;
ret = advance_multivolume(a);
- rar->merge_mode--;
+ rar5->merge_mode--;
if(ret != ARCHIVE_OK) {
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
return ret;
}
}
}
- *p = rar->vol.push_buf;
+ *p = rar5->vol.push_buf;
/* If we're here, we can resume unpacking by processing the block
* pointed to by the `*p` memory pointer. */
@@ -3640,19 +3652,19 @@ static int merge_block(struct archive_read* a, ssize_t block_size,
}
static int process_block(struct archive_read* a) {
+ struct rar5 *rar5 = a->format->data;
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) {
+ if(rar5->main.volume && rar5->file.bytes_remaining == 0) {
ret = advance_multivolume(a);
if(ret != ARCHIVE_OK)
return ret;
}
- if(rar->cstate.block_parsing_finished) {
+ if(rar5->cstate.block_parsing_finished) {
ssize_t block_size;
ssize_t to_skip;
ssize_t cur_block_size;
@@ -3672,7 +3684,7 @@ static int process_block(struct archive_read* a) {
* `parse_block_header` as the second argument. */
ret = parse_block_header(a, p, &block_size,
- &rar->last_block_hdr);
+ &rar5->last_block_hdr);
if(ret != ARCHIVE_OK) {
return ret;
}
@@ -3680,11 +3692,11 @@ static int process_block(struct archive_read* a) {
/* Skip block header. Next data is huffman tables,
* if present. */
to_skip = sizeof(struct compressed_block_header) +
- bf_byte_count(&rar->last_block_hdr) + 1;
+ bf_byte_count(&rar5->last_block_hdr) + 1;
/* If the block header's to_skip value exceeds the declared
* remaining data, the archive is malformed. */
- if(to_skip > rar->file.bytes_remaining) {
+ if(to_skip > rar5->file.bytes_remaining) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Block header size exceeds remaining file data");
@@ -3694,7 +3706,7 @@ static int process_block(struct archive_read* a) {
if(ARCHIVE_OK != consume(a, to_skip))
return ARCHIVE_EOF;
- rar->file.bytes_remaining -= to_skip;
+ rar5->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
@@ -3703,9 +3715,9 @@ static int process_block(struct archive_read* a) {
* part of the data will be in another file. */
cur_block_size =
- rar5_min(rar->file.bytes_remaining, block_size);
+ rar5_min(rar5->file.bytes_remaining, block_size);
- if(block_size > rar->file.bytes_remaining) {
+ if(block_size > rar5->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
@@ -3729,7 +3741,7 @@ static int process_block(struct archive_read* a) {
* the *whole* block (merged from partial blocks
* stored in multiple archives files). */
} else {
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
/* Read the whole block size into memory. This can take
* up to 8 megabytes of memory in theoretical cases.
@@ -3741,16 +3753,16 @@ static int process_block(struct archive_read* a) {
}
}
- rar->cstate.block_buf = p;
- rar->cstate.cur_block_size = cur_block_size;
- rar->cstate.block_parsing_finished = 0;
+ rar5->cstate.block_buf = p;
+ rar5->cstate.cur_block_size = cur_block_size;
+ rar5->cstate.block_parsing_finished = 0;
- rar->bits.in_addr = 0;
- rar->bits.bit_addr = 0;
+ rar5->bits.in_addr = 0;
+ rar5->bits.bit_addr = 0;
- if(bf_is_table_present(&rar->last_block_hdr)) {
+ if(bf_is_table_present(&rar5->last_block_hdr)) {
/* Load Huffman tables. */
- ret = parse_tables(a, rar, p);
+ ret = parse_tables(a, rar5, p);
if(ret != ARCHIVE_OK) {
/* Error during decompression of Huffman
* tables. */
@@ -3759,7 +3771,7 @@ static int process_block(struct archive_read* a) {
}
} else {
/* Block parsing not finished, reuse previous memory buffer. */
- p = rar->cstate.block_buf;
+ p = rar5->cstate.block_buf;
}
/* Uncompress the block, or a part of it, depending on how many bytes
@@ -3772,22 +3784,22 @@ static int process_block(struct archive_read* a) {
return ret;
}
- if(rar->cstate.block_parsing_finished &&
- rar->cstate.switch_multivolume == 0 &&
- rar->cstate.cur_block_size > 0)
+ if(rar5->cstate.block_parsing_finished &&
+ rar5->cstate.switch_multivolume == 0 &&
+ rar5->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))
+ if(ARCHIVE_OK != consume(a, rar5->cstate.cur_block_size))
return ARCHIVE_FATAL;
- rar->file.bytes_remaining -= rar->cstate.cur_block_size;
- } else if(rar->cstate.switch_multivolume) {
+ rar5->file.bytes_remaining -= rar5->cstate.cur_block_size;
+ } else if(rar5->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;
+ rar5->cstate.switch_multivolume = 0;
}
return ARCHIVE_OK;
@@ -3797,13 +3809,13 @@ 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,
+static int use_data(struct rar5 *rar5, const void** buf, size_t* size,
int64_t* offset)
{
int i;
- for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
- struct data_ready *d = &rar->cstate.dready[i];
+ for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) {
+ struct data_ready *d = &rar5->cstate.dready[i];
if(d->used) {
if(buf) *buf = d->buf;
@@ -3818,14 +3830,14 @@ static int use_data(struct rar5* rar, const void** buf, size_t* size,
return ARCHIVE_RETRY;
}
-static void clear_data_ready_stack(struct rar5* rar) {
- memset(&rar->cstate.dready, 0, sizeof(rar->cstate.dready));
+static void clear_data_ready_stack(struct rar5 *rar5) {
+ memset(&rar5->cstate.dready, 0, sizeof(rar5->cstate.dready));
}
-/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
+/* Pushes the `buf`, `size` and `offset` arguments to the rar5->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,
+static int push_data_ready(struct archive_read* a, struct rar5 *rar5,
const uint8_t* buf, size_t size, int64_t offset)
{
int i;
@@ -3836,18 +3848,18 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar,
* 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)
+ if(rar5->skip_mode)
return ARCHIVE_OK;
/* Sanity check. */
- if(offset != rar->file.last_offset + rar->file.last_size) {
+ if(offset != rar5->file.last_offset + rar5->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];
+ for(i = 0; i < rar5_countof(rar5->cstate.dready); i++) {
+ struct data_ready* d = &rar5->cstate.dready[i];
if(!d->used) {
d->used = 1;
d->buf = buf;
@@ -3855,21 +3867,21 @@ static int push_data_ready(struct archive_read* a, struct rar5* rar,
d->offset = offset;
/* These fields are used only in sanity checking. */
- rar->file.last_offset = offset;
- rar->file.last_size = size;
+ rar5->file.last_offset = offset;
+ rar5->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);
+ update_crc(rar5, 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. */
+ /* Program counter will reach this code if the
+ * `rar5->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,
"Premature end of data_ready stack");
@@ -3919,23 +3931,23 @@ 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);
+ struct rar5 *rar5 = a->format->data;
int ret;
int64_t max_end_pos;
- if(!rar->cstate.initialized) {
+ if(!rar5->cstate.initialized) {
/* Don't perform full context reinitialization if we're
* processing a solid archive. */
- if(!rar->main.solid || !rar->cstate.window_buf) {
- if((ret = init_unpack(rar)) != ARCHIVE_OK)
+ if(!rar5->main.solid || !rar5->cstate.window_buf) {
+ if((ret = init_unpack(rar5)) != ARCHIVE_OK)
return ret;
}
- rar->cstate.initialized = 1;
+ rar5->cstate.initialized = 1;
}
/* Don't allow extraction if window_size is invalid. */
- if(rar->cstate.window_size == 0) {
+ if(rar5->cstate.window_size == 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Invalid window size declaration in this file");
@@ -3944,7 +3956,7 @@ static int do_uncompress_file(struct archive_read* a) {
return ARCHIVE_FAILED;
}
- if(rar->cstate.all_filters_applied == 1) {
+ if(rar5->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
@@ -3955,12 +3967,12 @@ static int do_uncompress_file(struct archive_read* a) {
if(ret != ARCHIVE_OK)
return ret;
- if(rar->cstate.last_write_ptr ==
- rar->cstate.write_ptr) {
+ if(rar5->cstate.last_write_ptr ==
+ rar5->cstate.write_ptr) {
/* The block didn't generate any new data,
* so just process a new block if this one
* wasn't the last block in the file. */
- if (bf_is_last_block(&rar->last_block_hdr)) {
+ if (bf_is_last_block(&rar5->last_block_hdr)) {
return ARCHIVE_EOF;
}
@@ -3982,13 +3994,13 @@ static int do_uncompress_file(struct archive_read* a) {
return ret;
}
- if(cdeque_size(&rar->cstate.filters) > 0) {
+ if(cdeque_size(&rar5->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,
+ if(CDE_OK != cdeque_front(&rar5->cstate.filters,
cdeque_filter_p(&flt)))
{
archive_set_error(&a->archive,
@@ -3998,15 +4010,15 @@ static int do_uncompress_file(struct archive_read* a) {
}
max_end_pos = rar5_min(flt->block_start,
- rar->cstate.write_ptr);
+ rar5->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;
+ max_end_pos = rar5->cstate.write_ptr;
}
- if(max_end_pos == rar->cstate.last_write_ptr) {
+ if(max_end_pos == rar5->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
@@ -4023,9 +4035,9 @@ static int do_uncompress_file(struct archive_read* a) {
* 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,
+ push_window_data(a, rar5, rar5->cstate.last_write_ptr,
max_end_pos);
- rar->cstate.last_write_ptr = max_end_pos;
+ rar5->cstate.last_write_ptr = max_end_pos;
}
return ARCHIVE_OK;
@@ -4046,19 +4058,19 @@ static int uncompress_file(struct archive_read* a) {
static int do_unstore_file(struct archive_read* a,
- struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
+ struct rar5 *rar5, const void** buf, size_t* size, int64_t* offset)
{
size_t to_read;
const uint8_t* p;
- if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
- rar->generic.split_after > 0)
+ if(rar5->file.bytes_remaining == 0 && rar5->main.volume > 0 &&
+ rar5->generic.split_after > 0)
{
int ret;
- rar->cstate.switch_multivolume = 1;
+ rar5->cstate.switch_multivolume = 1;
ret = advance_multivolume(a);
- rar->cstate.switch_multivolume = 0;
+ rar5->cstate.switch_multivolume = 0;
if(ret != ARCHIVE_OK) {
/* Failed to advance to next multivolume archive
@@ -4067,7 +4079,7 @@ static int do_unstore_file(struct archive_read* a,
}
}
- to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+ to_read = rar5_min(rar5->file.bytes_remaining, 64 * 1024);
if(to_read == 0) {
return ARCHIVE_EOF;
}
@@ -4084,16 +4096,16 @@ static int do_unstore_file(struct archive_read* a,
if(buf) *buf = p;
if(size) *size = to_read;
- if(offset) *offset = rar->cstate.last_unstore_ptr;
+ if(offset) *offset = rar5->cstate.last_unstore_ptr;
- rar->file.bytes_remaining -= to_read;
- rar->cstate.last_unstore_ptr += to_read;
+ rar5->file.bytes_remaining -= to_read;
+ rar5->cstate.last_unstore_ptr += to_read;
- update_crc(rar, p, to_read);
+ update_crc(rar5, p, to_read);
return ARCHIVE_OK;
}
-static int do_unpack(struct archive_read* a, struct rar5* rar,
+static int do_unpack(struct archive_read* a, struct rar5 *rar5,
const void** buf, size_t* size, int64_t* offset)
{
enum COMPRESSION_METHOD {
@@ -4101,12 +4113,12 @@ static int do_unpack(struct archive_read* a, struct rar5* rar,
BEST = 5
};
- if(rar->file.service > 0) {
- return do_unstore_file(a, rar, buf, size, offset);
+ if(rar5->file.service > 0) {
+ return do_unstore_file(a, rar5, buf, size, offset);
} else {
- switch(rar->cstate.method) {
+ switch(rar5->cstate.method) {
case STORE:
- return do_unstore_file(a, rar, buf, size,
+ return do_unstore_file(a, rar5, buf, size,
offset);
case FASTEST:
/* fallthrough */
@@ -4123,13 +4135,13 @@ static int do_unpack(struct archive_read* a, struct rar5* rar,
* accordingly. At this point the decoder doesn't have any
* pending uncompressed data blocks, so the current position in
* the output file should be last_write_ptr. */
- if (offset) *offset = rar->cstate.last_write_ptr;
+ if (offset) *offset = rar5->cstate.last_write_ptr;
return uncompress_file(a);
default:
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Compression method not supported: 0x%x",
- (unsigned int)rar->cstate.method);
+ (unsigned int)rar5->cstate.method);
return ARCHIVE_FATAL;
}
@@ -4142,15 +4154,15 @@ static int do_unpack(struct archive_read* a, struct rar5* rar,
}
static int verify_checksums(struct archive_read* a) {
+ struct rar5 *rar5 = a->format->data;
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) {
+ if(!rar5->skip_mode) {
/* Always check checksums if we're not in skip mode */
verify_crc = 1;
} else {
@@ -4174,20 +4186,20 @@ static int verify_checksums(struct archive_read* a) {
* 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) {
+ if(rar5->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) {
+ if(rar5->file.calculated_crc32 !=
+ rar5->file.stored_crc32) {
/* Checksums do not match; the unpacked file
* is corrupted. */
DEBUG_CODE {
printf("Checksum error: CRC32 "
"(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
- rar->file.calculated_crc32,
- rar->file.stored_crc32);
+ rar5->file.calculated_crc32,
+ rar5->file.stored_crc32);
}
#ifndef DONT_FAIL_ON_CRC_ERROR
@@ -4200,13 +4212,13 @@ static int verify_checksums(struct archive_read* a) {
DEBUG_CODE {
printf("Checksum OK: CRC32 "
"(%08" PRIx32 "/%08" PRIx32 ")\n",
- rar->file.stored_crc32,
- rar->file.calculated_crc32);
+ rar5->file.stored_crc32,
+ rar5->file.calculated_crc32);
}
}
}
- if(rar->file.has_blake2 > 0) {
+ if(rar5->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.
@@ -4221,9 +4233,9 @@ static int verify_checksums(struct archive_read* a) {
* This is why we're explicitly ignoring it. */
uint8_t b2_buf[32];
- (void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
+ (void) blake2sp_final(&rar5->file.b2state, b2_buf, 32);
- if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
+ if(memcmp(&rar5->file.blake2sp, b2_buf, 32) != 0) {
#ifndef DONT_FAIL_ON_CRC_ERROR
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
@@ -4257,23 +4269,23 @@ static void rar5_signature(char *buf) {
static int rar5_read_data(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset) {
+ struct rar5 *rar5 = a->format->data;
int ret;
- struct rar5* rar = get_context(a);
if (size)
*size = 0;
- if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
- rar->has_encrypted_entries = 0;
+ if (rar5->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar5->has_encrypted_entries = 0;
}
- if (rar->headers_are_encrypted || rar->cstate.data_encrypted) {
+ if (rar5->headers_are_encrypted || rar5->cstate.data_encrypted) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Reading encrypted data is not currently supported");
return ARCHIVE_FAILED;
}
- if(rar->file.dir > 0) {
+ if(rar5->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
@@ -4283,28 +4295,28 @@ static int rar5_read_data(struct archive_read *a, const void **buff,
return ARCHIVE_FATAL;
}
- if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
+ if(!rar5->skip_mode && (rar5->cstate.last_write_ptr > rar5->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);
+ ret = use_data(rar5, buff, size, offset);
if(ret == ARCHIVE_OK) {
return ret;
}
- if(rar->file.eof == 1) {
+ if(rar5->file.eof == 1) {
return ARCHIVE_EOF;
}
- ret = do_unpack(a, rar, buff, size, offset);
+ ret = do_unpack(a, rar5, 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(rar5->file.bytes_remaining == 0 &&
+ rar5->cstate.last_write_ptr == rar5->file.unpacked_size)
{
/* If all bytes of current file were processed, run
* finalization.
@@ -4314,7 +4326,7 @@ static int rar5_read_data(struct archive_read *a, const void **buff,
* value in the last `archive_read_data` call to signal an error
* to the user. */
- rar->file.eof = 1;
+ rar5->file.eof = 1;
return verify_global_checksums(a);
}
@@ -4322,21 +4334,21 @@ static int rar5_read_data(struct archive_read *a, const void **buff,
}
static int rar5_read_data_skip(struct archive_read *a) {
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
- if(rar->main.solid && (rar->cstate.data_encrypted == 0)) {
+ if(rar5->main.solid && (rar5->cstate.data_encrypted == 0)) {
/* 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. Note that
- * this is only possible when data withing this solid block is
+ * this is only possible when data within this solid block is
* not encrypted, in which case we'll skip and fail if the user
* tries to read data. */
int ret;
/* Make sure to process all blocks in the compressed stream. */
- while(rar->file.bytes_remaining > 0) {
+ while(rar5->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
@@ -4345,14 +4357,14 @@ static int rar5_read_data_skip(struct archive_read *a) {
* This is incremented instead of setting to 1 because
* this data skipping function can be called
* recursively. */
- rar->skip_mode++;
+ rar5->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--;
+ rar5->skip_mode--;
if(ret < 0 || ret == ARCHIVE_EOF) {
/* Propagate any potential error conditions
@@ -4365,11 +4377,11 @@ static int rar5_read_data_skip(struct archive_read *a) {
* stream. Each file in non-solid archives starts from an empty
* window buffer. */
- if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
+ if(ARCHIVE_OK != consume(a, rar5->file.bytes_remaining)) {
return ARCHIVE_FATAL;
}
- rar->file.bytes_remaining = 0;
+ rar5->file.bytes_remaining = 0;
}
return ARCHIVE_OK;
@@ -4378,28 +4390,30 @@ static int rar5_read_data_skip(struct archive_read *a) {
static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
int whence)
{
- (void) a;
(void) offset;
(void) whence;
- /* We're a streaming unpacker, and we don't support seeking. */
+ /* We're a streaming unpacker, and we don't support seeking.
+ * That's a capability gap, not a fatal error. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Seeking of RAR5 files is unsupported");
- return ARCHIVE_FATAL;
+ return (ARCHIVE_FAILED);
}
static int rar5_cleanup(struct archive_read *a) {
- struct rar5* rar = get_context(a);
+ struct rar5 *rar5 = a->format->data;
- free(rar->cstate.window_buf);
- free(rar->cstate.filtered_buf);
- clear_data_ready_stack(rar);
+ free(rar5->cstate.window_buf);
+ free(rar5->cstate.filtered_buf);
+ clear_data_ready_stack(rar5);
- free(rar->vol.push_buf);
+ free(rar5->vol.push_buf);
- free_filters(rar);
- rar5_deinit(rar);
+ free_filters(rar5);
+ rar5_deinit(rar5);
- free(rar);
+ free(rar5);
a->format->data = NULL;
return ARCHIVE_OK;
@@ -4413,9 +4427,9 @@ static int rar5_capabilities(struct archive_read * a) {
static int rar5_has_encrypted_entries(struct archive_read *_a) {
if (_a && _a->format) {
- struct rar5 *rar = (struct rar5 *)_a->format->data;
- if (rar) {
- return rar->has_encrypted_entries;
+ struct rar5 *rar5 = _a->format->data;
+ if (rar5) {
+ return rar5->has_encrypted_entries;
}
}
@@ -4423,50 +4437,50 @@ static int rar5_has_encrypted_entries(struct archive_read *_a) {
}
/* Must match deallocations in rar5_deinit */
-static int rar5_init(struct rar5* rar) {
- memset(rar, 0, sizeof(struct rar5));
+static int rar5_init(struct rar5 *rar5) {
+ memset(rar5, 0, sizeof(struct rar5));
- if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
+ if(CDE_OK != cdeque_init(&rar5->cstate.filters, 8192))
return ARCHIVE_FATAL;
/*
* Until enough data has been read, we cannot tell about
* any encrypted entries yet.
*/
- rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+ rar5->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
return ARCHIVE_OK;
}
/* Must match allocations in rar5_init */
-static void rar5_deinit(struct rar5* rar) {
- cdeque_free(&rar->cstate.filters);
+static void rar5_deinit(struct rar5 *rar5) {
+ cdeque_free(&rar5->cstate.filters);
}
int archive_read_support_format_rar5(struct archive *_a) {
struct archive_read* ar;
int ret;
- struct rar5* rar;
+ struct rar5 *rar5;
if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
return ret;
- rar = malloc(sizeof(*rar));
- if(rar == NULL) {
+ rar5 = malloc(sizeof(*rar5));
+ if(rar5 == NULL) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate rar5 data");
return ARCHIVE_FATAL;
}
- if(ARCHIVE_OK != rar5_init(rar)) {
+ if(ARCHIVE_OK != rar5_init(rar5)) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate rar5 filter buffer");
- free(rar);
+ free(rar5);
return ARCHIVE_FATAL;
}
ret = __archive_read_register_format(ar,
- rar,
+ rar5,
"rar5",
rar5_bid,
rar5_options,
@@ -4479,8 +4493,8 @@ int archive_read_support_format_rar5(struct archive *_a) {
rar5_has_encrypted_entries);
if(ret != ARCHIVE_OK) {
- rar5_deinit(rar);
- free(rar);
+ rar5_deinit(rar5);
+ free(rar5);
}
return ARCHIVE_OK;
diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c
index e935396dda6f..cda3c1e49c59 100644
--- a/libarchive/archive_read_support_format_raw.c
+++ b/libarchive/archive_read_support_format_raw.c
@@ -34,10 +34,11 @@
#include "archive.h"
#include "archive_entry.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_read_private.h"
-struct raw_info {
+struct raw {
int64_t offset; /* Current position in the file. */
int64_t unconsumed;
int end_of_file;
@@ -54,22 +55,22 @@ static int archive_read_format_raw_read_header(struct archive_read *,
int
archive_read_support_format_raw(struct archive *_a)
{
- struct raw_info *info;
struct archive_read *a = (struct archive_read *)_a;
+ struct raw *raw;
int r;
archive_check_magic(_a, ARCHIVE_READ_MAGIC,
ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
- info = calloc(1, sizeof(*info));
- if (info == NULL) {
+ raw = calloc(1, sizeof(*raw));
+ if (raw == NULL) {
archive_set_error(&a->archive, ENOMEM,
- "Can't allocate raw_info data");
+ "Can't allocate raw data");
return (ARCHIVE_FATAL);
}
r = __archive_read_register_format(a,
- info,
+ raw,
"raw",
archive_read_format_raw_bid,
NULL,
@@ -81,7 +82,7 @@ archive_read_support_format_raw(struct archive *_a)
NULL,
NULL);
if (r != ARCHIVE_OK)
- free(info);
+ free(raw);
return (r);
}
@@ -107,10 +108,9 @@ static int
archive_read_format_raw_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct raw_info *info;
+ struct raw *raw = a->format->data;
- info = (struct raw_info *)(a->format->data);
- if (info->end_of_file)
+ if (raw->end_of_file)
return (ARCHIVE_EOF);
a->archive.archive_format = ARCHIVE_FORMAT_RAW;
@@ -128,39 +128,43 @@ static int
archive_read_format_raw_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct raw_info *info;
+ struct raw *raw = a->format->data;
ssize_t avail;
- info = (struct raw_info *)(a->format->data);
-
/* Consume the bytes we read last time. */
- if (info->unconsumed) {
- __archive_read_consume(a, info->unconsumed);
- info->unconsumed = 0;
+ if (raw->unconsumed) {
+ __archive_read_consume(a, raw->unconsumed);
+ raw->unconsumed = 0;
}
- if (info->end_of_file)
+ if (raw->end_of_file)
return (ARCHIVE_EOF);
/* Get whatever bytes are immediately available. */
*buff = __archive_read_ahead(a, 1, &avail);
if (avail > 0) {
/* Return the bytes we just read */
+ *offset = raw->offset;
+ if (archive_ckd_add_i64(&raw->offset, raw->offset, avail)) {
+ avail = INT64_MAX - *offset;
+ if (avail == 0) {
+ *size = 0;
+ return (ARCHIVE_FATAL);
+ }
+ }
*size = avail;
- *offset = info->offset;
- info->offset += *size;
- info->unconsumed = avail;
+ raw->unconsumed = avail;
return (ARCHIVE_OK);
} else if (0 == avail) {
/* Record and return end-of-file. */
- info->end_of_file = 1;
+ raw->end_of_file = 1;
*size = 0;
- *offset = info->offset;
+ *offset = raw->offset;
return (ARCHIVE_EOF);
} else {
/* Record and return an error. */
*size = 0;
- *offset = info->offset;
+ *offset = raw->offset;
return ((int)avail);
}
}
@@ -168,24 +172,23 @@ archive_read_format_raw_read_data(struct archive_read *a,
static int
archive_read_format_raw_read_data_skip(struct archive_read *a)
{
- struct raw_info *info = (struct raw_info *)(a->format->data);
+ struct raw *raw = a->format->data;
/* Consume the bytes we read last time. */
- if (info->unconsumed) {
- __archive_read_consume(a, info->unconsumed);
- info->unconsumed = 0;
+ if (raw->unconsumed) {
+ __archive_read_consume(a, raw->unconsumed);
+ raw->unconsumed = 0;
}
- info->end_of_file = 1;
+ raw->end_of_file = 1;
return (ARCHIVE_OK);
}
static int
archive_read_format_raw_cleanup(struct archive_read *a)
{
- struct raw_info *info;
+ struct raw *raw = a->format->data;
- info = (struct raw_info *)(a->format->data);
- free(info);
+ free(raw);
a->format->data = NULL;
return (ARCHIVE_OK);
}
diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c
index ac3dd446cb68..b73c0d9372e4 100644
--- a/libarchive/archive_read_support_format_tar.c
+++ b/libarchive/archive_read_support_format_tar.c
@@ -251,36 +251,6 @@ static const size_t fflags_limit = 512; /* Longest fflags */
static const size_t acl_limit = 131072; /* Longest textual ACL: 128kiB */
static const int64_t entry_limit = 0xfffffffffffffffLL; /* 2^60 bytes = 1 ExbiByte */
-/*
- * There's no standard for TIME_T_MAX. So we compute it
- * here. TODO: Move this to configure time, but be careful
- * about cross-compile environments.
- */
-static time_t
-get_time_t_max(void)
-{
-#if defined(TIME_T_MAX)
- return TIME_T_MAX;
-#else
- /* ISO C allows time_t to be a floating-point type,
- but POSIX requires an integer type. The following
- should work on any system that follows the POSIX
- conventions. */
- if (((time_t)0) < ((time_t)-1)) {
- /* Time_t is unsigned */
- return (~(time_t)0);
- } else {
- /* Time_t is signed. */
- /* Assume it's the same as int64_t or int32_t */
- if (sizeof(time_t) == sizeof(int64_t)) {
- return (time_t)INT64_MAX;
- } else {
- return (time_t)INT32_MAX;
- }
- }
-#endif
-}
-
int
archive_read_support_format_gnutar(struct archive *a)
{
@@ -330,9 +300,8 @@ archive_read_support_format_tar(struct archive *_a)
static int
archive_read_format_tar_cleanup(struct archive_read *a)
{
- struct tar *tar;
+ struct tar *tar = a->format->data;
- tar = (struct tar *)(a->format->data);
gnu_clear_sparse_list(tar);
archive_string_free(&tar->entry_pathname);
archive_string_free(&tar->entry_pathname_override);
@@ -342,7 +311,7 @@ archive_read_format_tar_cleanup(struct archive_read *a)
archive_string_free(&tar->line);
archive_string_free(&tar->localname);
free(tar);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
@@ -469,10 +438,9 @@ static int
archive_read_format_tar_options(struct archive_read *a,
const char *key, const char *val)
{
- struct tar *tar;
+ struct tar *tar = a->format->data;
int ret = ARCHIVE_FAILED;
- tar = (struct tar *)(a->format->data);
if (strcmp(key, "compat-2x") == 0) {
/* Handle UTF-8 filenames as libarchive 2.x */
tar->compat_2x = (val != NULL && val[0] != 0);
@@ -555,15 +523,13 @@ archive_read_format_tar_read_header(struct archive_read *a,
* probably not worthwhile just to support the relatively
* obscure tar->cpio conversion case.
*/
- struct tar *tar;
+ struct tar *tar = a->format->data;
const char *p;
const wchar_t *wp;
int r;
size_t l;
int64_t unconsumed = 0;
- tar = (struct tar *)(a->format->data);
-
/* Assign default device/inode values. */
archive_entry_set_dev(entry, 1 + tar->default_dev); /* Don't use zero. */
archive_entry_set_ino(entry, ++tar->default_inode); /* Don't use zero. */
@@ -639,12 +605,10 @@ static int
archive_read_format_tar_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
+ struct tar *tar = a->format->data;
ssize_t bytes_read;
- struct tar *tar;
struct sparse_block *p;
- tar = (struct tar *)(a->format->data);
-
for (;;) {
/* Remove exhausted entries from sparse list. */
while (tar->sparse_list != NULL &&
@@ -703,10 +667,8 @@ archive_read_format_tar_read_data(struct archive_read *a,
static int
archive_read_format_tar_skip(struct archive_read *a)
{
+ struct tar *tar = a->format->data;
int64_t request;
- struct tar* tar;
-
- tar = (struct tar *)(a->format->data);
request = tar->entry_bytes_remaining + tar->entry_padding +
tar->entry_bytes_unconsumed;
@@ -1104,21 +1066,19 @@ static int
header_Solaris_ACL(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h, int64_t *unconsumed)
{
- const struct archive_entry_header_ustar *header;
struct archive_string acl_text;
size_t size;
int err, acl_type;
uint64_t type;
char *acl, *p;
- header = (const struct archive_entry_header_ustar *)h;
- size = (size_t)tar_atol(header->size, sizeof(header->size));
archive_string_init(&acl_text);
err = read_body_to_string(a, tar, &acl_text, h, unconsumed);
if (err != ARCHIVE_OK) {
archive_string_free(&acl_text);
return (err);
}
+ size = archive_strlen(&acl_text);
/* TODO: Examine the first characters to see if this
* is an AIX ACL descriptor. We'll likely never support
@@ -1377,6 +1337,7 @@ header_common(struct archive_read *a, struct tar *tar,
const struct archive_entry_header_ustar *header;
const char *existing_linkpath;
const wchar_t *existing_wcs_linkpath;
+ mode_t header_mode;
int err = ARCHIVE_OK;
header = (const struct archive_entry_header_ustar *)h;
@@ -1384,12 +1345,10 @@ header_common(struct archive_read *a, struct tar *tar,
/* Parse out the numeric fields (all are octal) */
/* Split mode handling: Set filetype always, perm only if not already set */
- archive_entry_set_filetype(entry,
- (mode_t)tar_atol(header->mode, sizeof(header->mode)));
- if (!archive_entry_perm_is_set(entry)) {
- archive_entry_set_perm(entry,
- (mode_t)tar_atol(header->mode, sizeof(header->mode)));
- }
+ header_mode = (mode_t)tar_atol(header->mode, sizeof(header->mode));
+ archive_entry_set_filetype(entry, header_mode);
+ if (!archive_entry_perm_is_set(entry))
+ archive_entry_set_perm(entry, header_mode);
/* Set uid, gid, mtime if not already set */
if (!archive_entry_uid_is_set(entry)) {
@@ -1402,7 +1361,7 @@ header_common(struct archive_read *a, struct tar *tar,
int64_t t64 = tar_atol(header->mtime, sizeof(header->mtime));
time_t t = (time_t)t64;
if ((int64_t)t != t64) { /* time_t overflowed */
- t = get_time_t_max();
+ t = TIME_MAX;
}
archive_entry_set_mtime(entry, t, 0);
}
@@ -1679,33 +1638,33 @@ is_mac_metadata_entry(struct archive_entry *entry) {
const char *p, *name;
const wchar_t *wp, *wname;
- wname = wp = archive_entry_pathname_w(entry);
- if (wp != NULL) {
+ name = p = archive_entry_pathname(entry);
+ if (p != NULL) {
/* Find the last path element. */
- for (; *wp != L'\0'; ++wp) {
- if (wp[0] == '/' && wp[1] != L'\0')
- wname = wp + 1;
+ for (; *p != '\0'; ++p) {
+ if (p[0] == '/' && p[1] != '\0')
+ name = p + 1;
}
/*
* If last path element starts with "._", then
* this is a Mac extension.
*/
- if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0')
+ if (name[0] == '.' && name[1] == '_' && name[2] != '\0')
return 1;
} else {
/* Find the last path element. */
- name = p = archive_entry_pathname(entry);
- if (p == NULL)
- return (ARCHIVE_FAILED);
- for (; *p != '\0'; ++p) {
- if (p[0] == '/' && p[1] != '\0')
- name = p + 1;
+ wname = wp = archive_entry_pathname_w(entry);
+ if (wp == NULL)
+ return 0;
+ for (; *wp != L'\0'; ++wp) {
+ if (wp[0] == L'/' && wp[1] != L'\0')
+ wname = wp + 1;
}
/*
* If last path element starts with "._", then
* this is a Mac extension.
*/
- if (name[0] == '.' && name[1] == '_' && name[2] != '\0')
+ if (wname[0] == L'.' && wname[1] == L'_' && wname[2] != L'\0')
return 1;
}
/* Not a mac extension */
@@ -1787,7 +1746,6 @@ header_pax_global(struct archive_read *a, struct tar *tar,
const struct archive_entry_header_ustar *header;
int64_t size, to_consume;
- (void)a; /* UNUSED */
(void)tar; /* UNUSED */
(void)entry; /* UNUSED */
@@ -1799,6 +1757,11 @@ header_pax_global(struct archive_read *a, struct tar *tar,
(long long)size);
return (ARCHIVE_FATAL);
}
+ if (size == 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid empty pax global extended header");
+ return (ARCHIVE_FATAL);
+ }
to_consume = ((size + 511) & ~511);
*unconsumed += to_consume;
return (ARCHIVE_OK);
@@ -1945,6 +1908,11 @@ header_pax_extension(struct archive_read *a, struct tar *tar,
(long long)ext_size);
return (ARCHIVE_FATAL);
}
+ if (ext_size == 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid empty pax extended header");
+ return (ARCHIVE_FATAL);
+ }
ext_padding = 0x1ff & (-ext_size);
if (ext_size > ext_size_limit) {
@@ -3056,6 +3024,7 @@ static int
header_gnutar(struct archive_read *a, struct tar *tar,
struct archive_entry *entry, const void *h, int64_t *unconsumed)
{
+ struct archive_entry_header_gnutar header_copy;
const struct archive_entry_header_gnutar *header;
int64_t t;
int err = ARCHIVE_OK;
@@ -3066,8 +3035,14 @@ header_gnutar(struct archive_read *a, struct tar *tar,
* filename is stored as in old-style archives.
*/
+ /*
+ * Sparse extension reads can reuse the callback buffer containing h,
+ * so preserve the main header until common fields are parsed.
+ */
+ memcpy(&header_copy, h, sizeof(header_copy));
+ header = &header_copy;
+
/* Copy filename over (to ensure null termination). */
- header = (const struct archive_entry_header_gnutar *)h;
const char *existing_pathname = archive_entry_pathname(entry);
const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
if ((existing_pathname == NULL || existing_pathname[0] == '\0')
@@ -3146,7 +3121,7 @@ header_gnutar(struct archive_read *a, struct tar *tar,
}
/* Grab fields common to all tar variants. */
- err = header_common(a, tar, entry, h);
+ err = header_common(a, tar, entry, header);
if (err == ARCHIVE_FATAL)
return (err);
@@ -3469,13 +3444,10 @@ pax_attribute_SUN_holesdata(struct archive_read *a, struct tar *tar,
return (ARCHIVE_FATAL);
tar->sparse_last->hole = hole;
}
- if (length == 0 || *e == '\n') {
- if (length == 0 && *e == '\n') {
- return (ARCHIVE_OK);
- } else {
- return (ARCHIVE_WARN);
- }
- }
+ if (length == 0)
+ return (ARCHIVE_OK);
+ if (*e == '\n')
+ return (ARCHIVE_WARN);
p = e + 1;
length--;
hole = hole == 0;
@@ -3538,17 +3510,17 @@ tar_atol_base_n(const char *p, size_t char_cnt, int base)
}
l = 0;
- if (char_cnt != 0) {
+ while (char_cnt != 0) {
digit = *p - '0';
- while (digit >= 0 && digit < base && char_cnt != 0) {
- if (archive_ckd_mul_i64(&l, l, base) ||
- archive_ckd_add_i64(&l, l, sign * digit)) {
- /* Truncate on overflow. */
- return sign < 0 ? INT64_MIN : INT64_MAX;
- }
- digit = *++p - '0';
- char_cnt--;
+ if (digit < 0 || digit >= base)
+ break;
+ if (archive_ckd_mul_i64(&l, l, base) ||
+ archive_ckd_add_i64(&l, l, sign * digit)) {
+ /* Truncate on overflow. */
+ return sign < 0 ? INT64_MIN : INT64_MAX;
}
+ p++;
+ char_cnt--;
}
return l;
}
diff --git a/libarchive/archive_read_support_format_warc.c b/libarchive/archive_read_support_format_warc.c
index d8f188cf0b44..d70c718320e1 100644
--- a/libarchive/archive_read_support_format_warc.c
+++ b/libarchive/archive_read_support_format_warc.c
@@ -25,28 +25,35 @@
#include "archive_platform.h"
-/**
- * WARC is standardised by ISO TC46/SC4/WG12 and currently available as
- * ISO 28500:2009.
- * For the purposes of this file we used the final draft from:
+/*
+ * An overview of WARC format:
+ *
+ * WARC files are laid out as a sequence of records. Each record has
+ * a text header followed by a content block whose size is given by
+ * Content-Length. This reader supports WARC/0.12 through WARC/1.0
+ * and was written using the final draft that became ISO 28500:2009:
* http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf
*
- * Todo:
- * [ ] real-world warcs can contain resources at endpoints ending in /
- * e.g. http://bibnum.bnf.fr/warc/
- * if you're lucky their response contains a Content-Location: header
- * pointing to a unix-compliant filename, in the example above it's
- * Content-Location: http://bibnum.bnf.fr/warc/index.html
- * however, that's not mandated and github for example doesn't follow
- * this convention.
- * We need a set of archive options to control what to do with
- * entries like these, at the moment care is taken to skip them.
+ * This reader exposes resource and response records as regular files
+ * when they have a usable WARC-Target-URI. WARC-Date is exposed as
+ * ctime, and a Last-Modified record header is exposed as mtime when
+ * present.
*
- **/
+ * TODO: Real-world WARCs can contain resources at endpoints ending in
+ * a slash, for example http://bibnum.bnf.fr/warc/. Some responses
+ * include a Content-Location header that points to a Unix-compatible
+ * filename such as http://bibnum.bnf.fr/warc/index.html, but WARC does
+ * not require that convention and some sites do not follow it. Until
+ * archive options exist to control these entries, this reader skips
+ * them instead of creating directory endpoints as files.
+ */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
@@ -56,40 +63,35 @@
#ifdef HAVE_STRING_H
#include <string.h>
#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include "archive.h"
#include "archive_entry.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_read_private.h"
typedef enum {
WT_NONE,
- /* warcinfo */
+ /* WARC info */
WT_INFO,
- /* metadata */
+ /* Metadata */
WT_META,
- /* resource */
+ /* Resource */
WT_RSRC,
- /* request, unsupported */
+ /* Request, unsupported */
WT_REQ,
- /* response, unsupported */
+ /* Response */
WT_RSP,
- /* revisit, unsupported */
+ /* Revisit, unsupported */
WT_RVIS,
- /* conversion, unsupported */
+ /* Conversion, unsupported */
WT_CONV,
- /* continuation, unsupported at the moment */
+ /* Continuation, currently unsupported */
WT_CONT,
- /* invalid type */
+ /* Invalid type */
LAST_WT
} warc_type_t;
@@ -103,82 +105,92 @@ typedef struct {
char *str;
} warc_strbuf_t;
-struct warc_s {
- /* content length ahead */
- size_t cntlen;
- /* and how much we've processed so far */
- size_t cntoff;
- /* and how much we need to consume between calls */
- size_t unconsumed;
+struct warc {
+ /* Content length of the current record */
+ int64_t cntlen;
+ /* Bytes processed from the current record */
+ int64_t cntoff;
+ /* Bytes to consume before the next read */
+ int64_t unconsumed;
- /* string pool */
+ /* String pool */
warc_strbuf_t pool;
- /* previous version */
+ /* Previous version */
unsigned int pver;
- /* stringified format name */
+ /* Stringified format name */
struct archive_string sver;
};
-static int _warc_bid(struct archive_read *a, int);
-static int _warc_cleanup(struct archive_read *a);
-static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);
-static int _warc_skip(struct archive_read *a);
-static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
+static int archive_read_format_warc_bid(struct archive_read *, int);
+static int archive_read_format_warc_cleanup(struct archive_read *);
+static int archive_read_format_warc_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_warc_skip(struct archive_read *);
+static int archive_read_format_warc_read_header(struct archive_read *,
+ struct archive_entry *);
-/* private routines */
-static unsigned int _warc_rdver(const char *buf, size_t bsz);
-static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
-static warc_string_t _warc_rduri(const char *buf, size_t bsz);
-static ssize_t _warc_rdlen(const char *buf, size_t bsz);
-static time_t _warc_rdrtm(const char *buf, size_t bsz);
-static time_t _warc_rdmtm(const char *buf, size_t bsz);
-static const char *_warc_find_eoh(const char *buf, size_t bsz);
-static const char *_warc_find_eol(const char *buf, size_t bsz);
+/* Private routines */
+static unsigned int warc_read_version(const char *, size_t);
+static unsigned int warc_read_type(const char *, size_t);
+static warc_string_t warc_read_uri(const char *, size_t);
+static int64_t warc_read_length(const char *, size_t);
+static time_t warc_read_date(const char *, size_t);
+static time_t warc_read_last_modified(const char *, size_t);
+static const char *warc_find_eoh(const char *, size_t);
+static const char *warc_find_eol(const char *, size_t);
int
archive_read_support_format_warc(struct archive *_a)
{
struct archive_read *a = (struct archive_read *)_a;
- struct warc_s *w;
+ struct warc *warc;
int r;
archive_check_magic(_a, ARCHIVE_READ_MAGIC,
ARCHIVE_STATE_NEW, "archive_read_support_format_warc");
- if ((w = calloc(1, sizeof(*w))) == NULL) {
+ if ((warc = calloc(1, sizeof(*warc))) == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate warc data");
return (ARCHIVE_FATAL);
}
- r = __archive_read_register_format(
- a, w, "warc",
- _warc_bid, NULL, _warc_rdhdr, _warc_read,
- _warc_skip, NULL, _warc_cleanup, NULL, NULL);
+ r = __archive_read_register_format(a,
+ warc,
+ "warc",
+ archive_read_format_warc_bid,
+ NULL,
+ archive_read_format_warc_read_header,
+ archive_read_format_warc_read_data,
+ archive_read_format_warc_skip,
+ NULL,
+ archive_read_format_warc_cleanup,
+ NULL,
+ NULL);
if (r != ARCHIVE_OK) {
- free(w);
+ free(warc);
return (r);
}
return (ARCHIVE_OK);
}
static int
-_warc_cleanup(struct archive_read *a)
+archive_read_format_warc_cleanup(struct archive_read *a)
{
- struct warc_s *w = a->format->data;
+ struct warc *warc = a->format->data;
- if (w->pool.len > 0U) {
- free(w->pool.str);
+ if (warc->pool.len > 0U) {
+ free(warc->pool.str);
}
- archive_string_free(&w->sver);
- free(w);
+ archive_string_free(&warc->sver);
+ free(warc);
a->format->data = NULL;
return (ARCHIVE_OK);
}
static int
-_warc_bid(struct archive_read *a, int best_bid)
+archive_read_format_warc_bid(struct archive_read *a, int best_bid)
{
const char *hdr;
ssize_t nrd;
@@ -186,55 +198,52 @@ _warc_bid(struct archive_read *a, int best_bid)
(void)best_bid; /* UNUSED */
- /* check first line of file, it should be a record already */
- if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {
- /* no idea what to do */
- return -1;
- } else if (nrd < 12) {
- /* nah, not for us, our magic cookie is at least 12 bytes */
+ /* Check the first line, which should already be a record header. */
+ if ((hdr = __archive_read_ahead(a, 12, &nrd)) == NULL) {
+ /* Not enough data to identify this format. */
return -1;
}
- /* otherwise snarf the record's version number */
- ver = _warc_rdver(hdr, nrd);
+ /* Parse the record version number. */
+ ver = warc_read_version(hdr, nrd);
if (ver < 1200U || ver > 10000U) {
- /* we only support WARC 0.12 to 1.0 */
+ /* Only WARC 0.12 through WARC 1.0 are supported. */
return -1;
}
- /* otherwise be confident */
+ /* WARC magic and version checks passed. */
return (64);
}
static int
-_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
+archive_read_format_warc_read_header(struct archive_read *a,
+ struct archive_entry *entry)
{
#define HDR_PROBE_LEN (12U)
- struct warc_s *w = a->format->data;
+ struct warc *warc = a->format->data;
unsigned int ver;
const char *buf;
ssize_t nrd;
const char *eoh;
char *tmp;
- /* for the file name, saves some strndup()'ing */
+ /* Reuse the header buffer while parsing the file name. */
warc_string_t fnam;
- /* warc record type, not that we really use it a lot */
+ /* WARC record type */
warc_type_t ftyp;
- /* content-length+error monad */
- ssize_t cntlen;
- /* record time is the WARC-Date time we reinterpret it as ctime */
+ /* Content length, or a negative error indicator */
+ int64_t cntlen;
+ /* WARC-Date is exposed as the entry ctime. */
time_t rtime;
- /* mtime is the Last-Modified time which will be the entry's mtime */
+ /* A Last-Modified record header is exposed as the entry mtime. */
time_t mtime;
start_over:
- /* just use read_ahead() they keep track of unconsumed
- * bits and bobs for us; no need to put an extra shift in
- * and reproduce that functionality here */
+ /* Use read_ahead(); it already tracks unconsumed bytes, so this
+ * reader does not need a separate shift buffer. */
buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);
if (nrd < 0) {
- /* no good */
+ /* I/O or stream error. */
archive_set_error(
&a->archive, ARCHIVE_ERRNO_MISC,
"Bad record header");
@@ -244,19 +253,17 @@ start_over:
* must be EOF therefore */
return (ARCHIVE_EOF);
}
- /* looks good so far, try and find the end of the header now */
- eoh = _warc_find_eoh(buf, nrd);
+ /* Locate the end of the record header. */
+ eoh = warc_find_eoh(buf, nrd);
if (eoh == NULL) {
- /* still no good, the header end might be beyond the
- * probe we've requested, but then again who'd cram
- * so much stuff into the header *and* be 28500-compliant */
+ /* The header terminator was not found in the probed data. */
archive_set_error(
&a->archive, ARCHIVE_ERRNO_MISC,
"Bad record header");
return (ARCHIVE_FATAL);
}
- ver = _warc_rdver(buf, eoh - buf);
- /* we currently support WARC 0.12 to 1.0 */
+ ver = warc_read_version(buf, eoh - buf);
+ /* Only WARC 0.12 through WARC 1.0 are supported. */
if (ver == 0U) {
archive_set_error(
&a->archive, ARCHIVE_ERRNO_MISC,
@@ -269,78 +276,73 @@ start_over:
ver / 10000, (ver % 10000) / 100);
return (ARCHIVE_FATAL);
}
- cntlen = _warc_rdlen(buf, eoh - buf);
+ cntlen = warc_read_length(buf, eoh - buf);
if (cntlen < 0) {
- /* nightmare! the specs say content-length is mandatory
- * so I don't feel overly bad stopping the reader here */
+ /* This reader requires Content-Length before processing a record. */
archive_set_error(
&a->archive, EINVAL,
"Bad content length");
return (ARCHIVE_FATAL);
}
- rtime = _warc_rdrtm(buf, eoh - buf);
+ rtime = warc_read_date(buf, eoh - buf);
if (rtime == (time_t)-1) {
- /* record time is mandatory as per WARC/1.0,
- * so just barf here, fast and loud */
+ /* This reader requires WARC-Date before processing a record. */
archive_set_error(
&a->archive, EINVAL,
"Bad record time");
return (ARCHIVE_FATAL);
}
- /* let the world know we're a WARC archive */
+ /* Report this archive as WARC. */
a->archive.archive_format = ARCHIVE_FORMAT_WARC;
- if (ver != w->pver) {
- /* stringify this entry's version */
- archive_string_sprintf(&w->sver,
+ if (ver != warc->pver) {
+ /* Format this entry's WARC version. */
+ archive_string_sprintf(&warc->sver,
"WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
- /* remember the version */
- w->pver = ver;
+ /* Remember the version for later entries. */
+ warc->pver = ver;
}
- /* start off with the type */
- ftyp = _warc_rdtyp(buf, eoh - buf);
- /* and let future calls know about the content */
- w->cntlen = cntlen;
- w->cntoff = 0U;
- mtime = 0;/* Avoid compiling error on some platform. */
+ /* Parse the record type. */
+ ftyp = warc_read_type(buf, eoh - buf);
+ /* Save content state for subsequent read calls. */
+ warc->cntlen = cntlen;
+ warc->cntoff = 0;
+ mtime = 0;/* Avoid compiler warnings on some platforms. */
switch (ftyp) {
case WT_RSRC:
case WT_RSP:
- /* only try and read the filename in the cases that are
- * guaranteed to have one */
- fnam = _warc_rduri(buf, eoh - buf);
- /* check the last character in the URI to avoid creating
- * directory endpoints as files, see Todo above */
+ /* Read the filename only for record types that are expected to
+ * have a target URI. */
+ fnam = warc_read_uri(buf, eoh - buf);
+ /* Avoid creating directory endpoints as files. */
if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {
- /* break here for now */
+ /* Skip this record. */
fnam.len = 0U;
fnam.str = NULL;
break;
}
- /* bang to our string pool, so we save a
- * malloc()+free() roundtrip */
- if (fnam.len + 1U > w->pool.len) {
- w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
- tmp = realloc(w->pool.str, w->pool.len);
+ /* Copy the name into the reusable string pool to avoid a malloc/free
+ * roundtrip for each entry. */
+ if (fnam.len + 1U > warc->pool.len) {
+ warc->pool.len = ((fnam.len + 64U) / 64U) * 64U;
+ tmp = realloc(warc->pool.str, warc->pool.len);
if (tmp == NULL) {
archive_set_error(
&a->archive, ENOMEM,
"Out of memory");
return (ARCHIVE_FATAL);
}
- w->pool.str = tmp;
+ warc->pool.str = tmp;
}
- memcpy(w->pool.str, fnam.str, fnam.len);
- w->pool.str[fnam.len] = '\0';
- /* let no one else know about the pool, it's a secret, shhh */
- fnam.str = w->pool.str;
+ memcpy(warc->pool.str, fnam.str, fnam.len);
+ warc->pool.str[fnam.len] = '\0';
+ /* Hide the pool implementation behind the parsed string. */
+ fnam.str = warc->pool.str;
- /* snarf mtime or deduce from rtime
- * this is a custom header added by our writer, it's quite
- * hard to believe anyone else would go through with it
- * (apart from being part of some http responses of course) */
- if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {
+ /* Use a Last-Modified record header when present; otherwise fall back
+ * to WARC-Date. */
+ if ((mtime = warc_read_last_modified(buf, eoh - buf)) == (time_t)-1) {
mtime = rtime;
}
break;
@@ -358,19 +360,19 @@ start_over:
break;
}
- /* now eat some of those delicious buffer bits */
+ /* Consume the record header. */
__archive_read_consume(a, eoh - buf);
switch (ftyp) {
case WT_RSRC:
case WT_RSP:
if (fnam.len > 0U) {
- /* populate entry object */
+ /* Populate the entry object. */
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_copy_pathname(entry, fnam.str);
archive_entry_set_size(entry, cntlen);
archive_entry_set_perm(entry, 0644);
- /* rtime is the new ctime, mtime stays mtime */
+ /* WARC-Date becomes ctime; mtime comes from Last-Modified or WARC-Date. */
archive_entry_set_ctime(entry, rtime, 0L);
archive_entry_set_mtime(entry, mtime, 0L);
break;
@@ -385,8 +387,8 @@ start_over:
case WT_CONT:
case LAST_WT:
default:
- /* consume the content and start over */
- if (_warc_skip(a) < 0)
+ /* Skip this record body and look for the next one. */
+ if (archive_read_format_warc_skip(a) < 0)
return (ARCHIVE_FATAL);
goto start_over;
}
@@ -394,62 +396,69 @@ start_over:
}
static int
-_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
+archive_read_format_warc_read_data(struct archive_read *a, const void **buf,
+ size_t *bsz, int64_t *off)
{
- struct warc_s *w = a->format->data;
+ struct warc *warc = a->format->data;
const char *rab;
ssize_t nrd;
- if (w->cntoff >= w->cntlen) {
- eof:
- /* it's our lucky day, no work, we can leave early */
+ if (warc->unconsumed) {
+ __archive_read_consume(a, warc->unconsumed);
+ warc->unconsumed = 0;
+ }
+
+ if (warc->cntoff >= warc->cntlen) {
+ /* No data is available to return for this entry. */
*buf = NULL;
*bsz = 0U;
- *off = w->cntoff;
- w->unconsumed = 0U;
+ *off = warc->cntoff;
return (ARCHIVE_EOF);
}
- if (w->unconsumed) {
- __archive_read_consume(a, w->unconsumed);
- w->unconsumed = 0U;
- }
-
rab = __archive_read_ahead(a, 1U, &nrd);
if (nrd < 0) {
*bsz = 0U;
- /* big catastrophe */
+ /* Propagate the read error. */
return (int)nrd;
} else if (nrd == 0) {
- goto eof;
- } else if ((size_t)nrd > w->cntlen - w->cntoff) {
- /* clamp to content-length */
- nrd = w->cntlen - w->cntoff;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated WARC file data");
+ return (ARCHIVE_FATAL);
+ } else if ((int64_t)nrd > warc->cntlen - warc->cntoff) {
+ /* Clamp reads to Content-Length. */
+ nrd = warc->cntlen - warc->cntoff;
}
- *off = w->cntoff;
+ *off = warc->cntoff;
*bsz = nrd;
*buf = rab;
- w->cntoff += nrd;
- w->unconsumed = (size_t)nrd;
+ warc->cntoff += nrd;
+ warc->unconsumed = nrd;
return (ARCHIVE_OK);
}
static int
-_warc_skip(struct archive_read *a)
+archive_read_format_warc_skip(struct archive_read *a)
{
- struct warc_s *w = a->format->data;
+ struct warc *warc = a->format->data;
- if (__archive_read_consume(a, w->cntlen) < 0 ||
+ if (warc->cntoff > warc->cntlen)
+ return (ARCHIVE_FATAL);
+ if (warc->unconsumed) {
+ __archive_read_consume(a, warc->unconsumed);
+ warc->unconsumed = 0;
+ }
+ if (__archive_read_consume(a, warc->cntlen - warc->cntoff) < 0 ||
__archive_read_consume(a, 4U/*\r\n\r\n separator*/) < 0)
return (ARCHIVE_FATAL);
- w->cntlen = 0U;
- w->cntoff = 0U;
+ warc->cntlen = 0;
+ warc->cntoff = 0;
return (ARCHIVE_OK);
}
-/* private routines */
+/* Private routines */
static void*
deconst(const void *c)
{
@@ -469,44 +478,41 @@ xmemmem(const char *hay, const size_t haysize,
unsigned int nsum;
unsigned int eqp;
- /* trivial checks first
- * a 0-sized needle is defined to be found anywhere in haystack
- * then run strchr() to find a candidate in HAYSTACK (i.e. a portion
- * that happens to begin with *NEEDLE) */
+ /* Handle trivial cases first. A zero-sized needle is defined to be
+ * found anywhere in the haystack; otherwise find the first candidate
+ * that begins with *NEEDLE. */
if (needlesize == 0UL) {
return deconst(hay);
} else if ((hay = memchr(hay, *needle, haysize)) == NULL) {
- /* trivial */
+ /* No candidate match remains. */
return NULL;
}
- /* First characters of haystack and needle are the same now. Both are
- * guaranteed to be at least one character long. Now computes the sum
- * of characters values of needle together with the sum of the first
- * needle_len characters of haystack. */
+ /* The first characters of haystack and needle already match, and both
+ * strings are at least one character long. Compute the rolling XOR
+ * values for the needle and the first NEEDLESIZE characters of haystack. */
for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;
hp < eoh && np < eon;
hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);
/* HP now references the (NEEDLESIZE + 1)-th character. */
if (np < eon) {
- /* haystack is smaller than needle, :O */
+ /* The haystack is smaller than the needle. */
return NULL;
} else if (eqp) {
- /* found a match */
+ /* Found a match. */
return deconst(hay);
}
- /* now loop through the rest of haystack,
- * updating the sum iteratively */
+ /* Loop through the rest of the haystack and update the rolling XOR
+ * iteratively. */
for (cand = hay; hp < eoh; hp++) {
hsum ^= *cand++;
hsum ^= *hp;
- /* Since the sum of the characters is already known to be
- * equal at that point, it is enough to check just NEEDLESIZE - 1
- * characters for equality,
- * also CAND is by design < HP, so no need for range checks */
+ /* When the rolling XOR values match, it is enough to check
+ * NEEDLESIZE - 1 characters for equality. CAND is always before
+ * HP by design, so no range check is needed. */
if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {
return deconst(cand);
}
@@ -519,7 +525,7 @@ strtoi_lim(const char *str, const char **ep, int llim, int ulim)
{
int res = 0;
const char *sp;
- /* we keep track of the number of digits via rulim */
+ /* Track the number of digits with rulim. */
int rulim;
for (sp = str, rulim = ulim > 10 ? ulim : 10;
@@ -546,11 +552,11 @@ time_from_tm(struct tm *t)
/* Use platform timegm() if available. */
return (timegm(t));
#else
- /* Else use direct calculation using POSIX assumptions. */
- /* First, fix up tm_yday based on the year/month/day. */
+ /* Otherwise, calculate directly using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year, month, and day. */
if (mktime(t) == (time_t)-1)
return ((time_t)-1);
- /* Then we can compute timegm() from first principles. */
+ /* Then compute timegm() from first principles. */
return (t->tm_sec
+ t->tm_min * 60
+ t->tm_hour * 3600
@@ -565,48 +571,48 @@ time_from_tm(struct tm *t)
static time_t
xstrpisotime(const char *s, char **endptr)
{
-/** like strptime() but strictly for ISO 8601 Zulu strings */
+/* Like strptime(), but only for ISO 8601 Zulu strings. */
struct tm tm;
time_t res = (time_t)-1;
- /* make sure tm is clean */
+ /* Clear the tm structure. */
memset(&tm, 0, sizeof(tm));
- /* as a courtesy to our callers, and since this is a non-standard
- * routine, we skip leading whitespace */
+ /* This is a non-standard routine, so skip leading whitespace for
+ * caller convenience. */
while (*s == ' ' || *s == '\t')
++s;
- /* read year */
+ /* Read the year. */
if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {
goto out;
}
- /* read month */
+ /* Read the month. */
if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {
goto out;
}
- /* read day-of-month */
+ /* Read the day of the month. */
if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {
goto out;
}
- /* read hour */
+ /* Read the hour. */
if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {
goto out;
}
- /* read minute */
+ /* Read the minute. */
if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {
goto out;
}
- /* read second */
+ /* Read the second. */
if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {
goto out;
}
- /* massage TM to fulfill some of POSIX' constraints */
+ /* Adjust tm fields to satisfy POSIX constraints. */
tm.tm_year -= 1900;
tm.tm_mon--;
- /* now convert our custom tm struct to a unix stamp using UTC */
+ /* Convert the tm structure to a Unix timestamp in UTC. */
res = time_from_tm(&tm);
out:
@@ -616,8 +622,14 @@ out:
return res;
}
+static int
+warc_isdigit(const char c)
+{
+ return c >= '0' && c <= '9';
+}
+
static unsigned int
-_warc_rdver(const char *buf, size_t bsz)
+warc_read_version(const char *buf, size_t bsz)
{
static const char magic[] = "WARC/";
const char *c;
@@ -625,35 +637,34 @@ _warc_rdver(const char *buf, size_t bsz)
unsigned int end = 0U;
if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
- /* buffer too small or invalid magic */
+ /* Buffer too small or invalid magic. */
return ver;
}
- /* looks good so far, read the version number for a laugh */
+ /* Parse the version number. */
buf += sizeof(magic) - 1U;
- if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
- isdigit((unsigned char)buf[2U])) {
- /* we support a maximum of 2 digits in the minor version */
- if (isdigit((unsigned char)buf[3U]))
+ if (warc_isdigit(buf[0]) && buf[1] == '.' && warc_isdigit(buf[2])) {
+ /* Support at most two digits in the minor version. */
+ if (warc_isdigit(buf[3]))
end = 1U;
- /* set up major version */
+ /* Set up the major version. */
ver = (buf[0U] - '0') * 10000U;
- /* set up minor version */
+ /* Set up the minor version. */
if (end == 1U) {
ver += (buf[2U] - '0') * 1000U;
ver += (buf[3U] - '0') * 100U;
} else
ver += (buf[2U] - '0') * 100U;
/*
- * WARC below version 0.12 has a space-separated header
- * WARC 0.12 and above terminates the version with a CRLF
+ * WARC versions before 0.12 use a space-separated header.
+ * WARC 0.12 and later terminate the version with CRLF.
*/
c = buf + 3U + end;
if (ver >= 1200U) {
if (memcmp(c, "\r\n", 2U) != 0)
ver = 0U;
} else {
- /* ver < 1200U */
+ /* Version is below WARC 0.12. */
if (*c != ' ' && *c != '\t')
ver = 0U;
}
@@ -662,22 +673,22 @@ _warc_rdver(const char *buf, size_t bsz)
}
static unsigned int
-_warc_rdtyp(const char *buf, size_t bsz)
+warc_read_type(const char *buf, size_t bsz)
{
static const char _key[] = "\r\nWARC-Type:";
const char *val, *eol;
if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
- /* no bother */
+ /* Header field is absent. */
return WT_NONE;
}
val += sizeof(_key) - 1U;
- if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
- /* no end of line */
+ if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* Header field has no end of line. */
return WT_NONE;
}
- /* overread whitespace */
+ /* Skip leading whitespace. */
while (val < eol && (*val == ' ' || *val == '\t'))
++val;
@@ -691,55 +702,55 @@ _warc_rdtyp(const char *buf, size_t bsz)
}
static warc_string_t
-_warc_rduri(const char *buf, size_t bsz)
+warc_read_uri(const char *buf, size_t bsz)
{
static const char _key[] = "\r\nWARC-Target-URI:";
const char *val, *uri, *eol, *p;
warc_string_t res = {0U, NULL};
if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
- /* no bother */
+ /* Header field is absent. */
return res;
}
- /* overread whitespace */
+ /* Skip leading whitespace. */
val += sizeof(_key) - 1U;
- if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
- /* no end of line */
+ if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* Header field has no end of line. */
return res;
}
while (val < eol && (*val == ' ' || *val == '\t'))
++val;
- /* overread URL designators */
+ /* Locate the :// separator. */
if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
- /* not touching that! */
+ /* Ignore values without a :// separator. */
return res;
}
- /* spaces inside uri are not allowed, CRLF should follow */
+ /* Spaces inside a URI are not allowed; CRLF should follow. */
for (p = val; p < eol; p++) {
if (isspace((unsigned char)*p))
return res;
}
- /* there must be at least space for ftp */
+ /* Require enough room for the shortest supported scheme. */
if (uri < (val + 3U))
return res;
- /* move uri to point to after :// */
+ /* Move uri past the :// separator. */
uri += 3U;
- /* now then, inspect the URI */
+ /* Inspect the scheme prefix. */
if (memcmp(val, "file", 4U) == 0) {
- /* perfect, nothing left to do here */
+ /* Keep file:// paths as-is. */
} else if (memcmp(val, "http", 4U) == 0 ||
memcmp(val, "ftp", 3U) == 0) {
- /* overread domain, and the first / */
+ /* Skip the domain and the first slash. */
while (uri < eol && *uri++ != '/');
} else {
- /* not sure what to do? best to bugger off */
+ /* Unsupported URI scheme. */
return res;
}
res.str = uri;
@@ -747,42 +758,49 @@ _warc_rduri(const char *buf, size_t bsz)
return res;
}
-static ssize_t
-_warc_rdlen(const char *buf, size_t bsz)
+static int64_t
+warc_read_length(const char *buf, size_t bsz)
{
static const char _key[] = "\r\nContent-Length:";
- const char *val, *eol;
- char *on = NULL;
- long int len;
+ const char *val, *eol, *p;
+ int64_t len;
if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
- /* no bother */
+ /* Header field is absent. */
return -1;
}
val += sizeof(_key) - 1U;
- if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
- /* no end of line */
+
+ if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* Malformed field with no end of line. */
return -1;
}
- /* skip leading whitespace */
+ /* Skip leading whitespace. */
while (val < eol && (*val == ' ' || *val == '\t'))
val++;
- /* there must be at least one digit */
- if (!isdigit((unsigned char)*val))
- return -1;
- errno = 0;
- len = strtol(val, &on, 10);
- if (errno != 0 || on != eol) {
- /* line must end here */
+
+ /* Require at least one digit. */
+ if (val >= eol || *val < '0' || *val > '9')
return -1;
+
+ len = 0;
+ for (p = val; p < eol; p++) {
+ int64_t digit;
+
+ if (*p < '0' || *p > '9')
+ return -1;
+ digit = *p - '0';
+ if (archive_ckd_mul_i64(&len, len, 10) ||
+ archive_ckd_add_i64(&len, len, digit))
+ return -1;
}
- return (size_t)len;
+ return len;
}
static time_t
-_warc_rdrtm(const char *buf, size_t bsz)
+warc_read_date(const char *buf, size_t bsz)
{
static const char _key[] = "\r\nWARC-Date:";
const char *val, *eol;
@@ -790,26 +808,26 @@ _warc_rdrtm(const char *buf, size_t bsz)
time_t res;
if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
- /* no bother */
+ /* Header field is absent. */
return (time_t)-1;
}
val += sizeof(_key) - 1U;
- if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
- /* no end of line */
+ if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* Header field has no end of line. */
return -1;
}
- /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ /* xstrpisotime() skips leading whitespace. */
res = xstrpisotime(val, &on);
if (on != eol) {
- /* line must end here */
+ /* The field must end here. */
return -1;
}
return res;
}
static time_t
-_warc_rdmtm(const char *buf, size_t bsz)
+warc_read_last_modified(const char *buf, size_t bsz)
{
static const char _key[] = "\r\nLast-Modified:";
const char *val, *eol;
@@ -817,26 +835,26 @@ _warc_rdmtm(const char *buf, size_t bsz)
time_t res;
if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
- /* no bother */
+ /* Header field is absent. */
return (time_t)-1;
}
val += sizeof(_key) - 1U;
- if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
- /* no end of line */
+ if ((eol = warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* Header field has no end of line. */
return -1;
}
- /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ /* xstrpisotime() skips leading whitespace. */
res = xstrpisotime(val, &on);
if (on != eol) {
- /* line must end here */
+ /* The field must end here. */
return -1;
}
return res;
}
-static const char*
-_warc_find_eoh(const char *buf, size_t bsz)
+static const char *
+warc_find_eoh(const char *buf, size_t bsz)
{
static const char _marker[] = "\r\n\r\n";
const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
@@ -847,12 +865,11 @@ _warc_find_eoh(const char *buf, size_t bsz)
return hit;
}
-static const char*
-_warc_find_eol(const char *buf, size_t bsz)
+static const char *
+warc_find_eol(const char *buf, size_t bsz)
{
static const char _marker[] = "\r\n";
const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
return hit;
}
-/* archive_read_support_format_warc.c ends here */
diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c
index e61ca0e6423f..dbe2c25d93a1 100644
--- a/libarchive/archive_read_support_format_xar.c
+++ b/libarchive/archive_read_support_format_xar.c
@@ -108,10 +108,14 @@ archive_read_support_format_xar(struct archive *_a)
#define CKSUM_NONE 0
#define CKSUM_SHA1 1
#define CKSUM_MD5 2
+#define CKSUM_SHA256 3
+#define CKSUM_SHA512 4
#define MD5_SIZE 16
#define SHA1_SIZE 20
-#define MAX_SUM_SIZE 20
+#define SHA256_SIZE 32
+#define SHA512_SIZE 64
+#define MAX_SUM_SIZE 64
enum enctype {
NONE,
@@ -135,6 +139,12 @@ struct chksumwork {
#ifdef ARCHIVE_HAS_SHA1
archive_sha1_ctx sha1ctx;
#endif
+#ifdef ARCHIVE_HAS_SHA256
+ archive_sha256_ctx sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ archive_sha512_ctx sha512ctx;
+#endif
};
struct xattr {
@@ -214,8 +224,8 @@ struct hdlink {
struct heap_queue {
struct xar_file **files;
- int allocated;
- int used;
+ size_t allocated;
+ size_t used;
};
enum xmlstatus {
@@ -396,7 +406,7 @@ static int heap_add_entry(struct archive_read *a,
static struct xar_file *heap_get_entry(struct heap_queue *);
static int add_link(struct archive_read *,
struct xar *, struct xar_file *);
-static void checksum_init(struct archive_read *, int, int);
+static int checksum_init(struct archive_read *, int, int);
static void checksum_update(struct archive_read *, const void *,
size_t, const void *, size_t);
static int checksum_final(struct archive_read *, const void *,
@@ -524,6 +534,8 @@ xar_bid(struct archive_read *a, int best_bid)
case CKSUM_NONE:
case CKSUM_SHA1:
case CKSUM_MD5:
+ case CKSUM_SHA256:
+ case CKSUM_SHA512:
bid += 32;
break;
default:
@@ -536,7 +548,7 @@ xar_bid(struct archive_read *a, int best_bid)
static int
read_toc(struct archive_read *a)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
struct xar_file *file;
const unsigned char *b;
uint64_t toc_compressed_size;
@@ -545,8 +557,6 @@ read_toc(struct archive_read *a)
ssize_t bytes;
int r;
- xar = (struct xar *)(a->format->data);
-
/*
* Read xar header.
*/
@@ -674,12 +684,11 @@ read_toc(struct archive_read *a)
static int
xar_read_header(struct archive_read *a, struct archive_entry *entry)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
struct xar_file *file;
struct xattr *xattr;
int r;
- xar = (struct xar *)(a->format->data);
r = ARCHIVE_OK;
if (xar->offset == 0) {
@@ -729,6 +738,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Gname");
+ file_free(file);
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
@@ -744,6 +754,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Uname");
+ file_free(file);
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
@@ -758,6 +769,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
+ file_free(file);
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
@@ -774,6 +786,7 @@ xar_read_header(struct archive_read *a, struct archive_entry *entry)
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Linkname");
+ file_free(file);
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
@@ -874,12 +887,10 @@ static int
xar_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
size_t used = 0;
int r;
- xar = (struct xar *)(a->format->data);
-
if (xar->entry_unconsumed) {
__archive_read_consume(a, xar->entry_unconsumed);
xar->entry_unconsumed = 0;
@@ -937,10 +948,9 @@ abort_read_data:
static int
xar_read_data_skip(struct archive_read *a)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
int64_t bytes_skipped;
- xar = (struct xar *)(a->format->data);
if (xar->end_of_file)
return (ARCHIVE_EOF);
bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
@@ -955,12 +965,11 @@ xar_read_data_skip(struct archive_read *a)
static int
xar_cleanup(struct archive_read *a)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
struct hdlink *hdlink;
- int i;
+ size_t i;
int r;
- xar = (struct xar *)(a->format->data);
checksum_cleanup(a);
r = decompression_cleanup(a);
hdlink = xar->hdlink_list;
@@ -990,9 +999,8 @@ xar_cleanup(struct archive_read *a)
static int
move_reading_point(struct archive_read *a, uint64_t offset)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
- xar = (struct xar *)(a->format->data);
if (xar->offset - xar->h_base != offset) {
/* Seek forward to the start of file contents. */
int64_t step;
@@ -1027,8 +1035,7 @@ rd_contents_init(struct archive_read *a, enum enctype encoding,
if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
return (r);
/* Init checksum library. */
- checksum_init(a, a_sum_alg, e_sum_alg);
- return (ARCHIVE_OK);
+ return (checksum_init(a, a_sum_alg, e_sum_alg));
}
static int
@@ -1077,16 +1084,14 @@ atou64(const char *p, size_t char_cnt, int base, uint64_t *val)
uint64_t l;
l = 0;
- if (char_cnt > 0) {
- int digit;
+ while (char_cnt-- > 0) {
+ int digit = *p++ - '0';
- digit = *p - '0';
- while (digit >= 0 && digit < base && char_cnt-- > 0) {
- if (archive_ckd_mul_u64(&l, l, base) ||
- archive_ckd_add_u64(&l, l, digit))
- return (ARCHIVE_FATAL);
- digit = *++p - '0';
- }
+ if (digit < 0 || digit >= base)
+ break;
+ if (archive_ckd_mul_u64(&l, l, base) ||
+ archive_ckd_add_u64(&l, l, digit))
+ return (ARCHIVE_FATAL);
}
*val = l;
@@ -1166,31 +1171,31 @@ parse_time(const char *p, size_t n)
p += 4;
if (*p++ != '-')
return (t);
- if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 12)
+ if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 12)
return (t);
tm.tm_mon = (int)data -1;
p += 2;
if (*p++ != '-')
return (t);
- if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data < 1 || data > 31)
+ if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data < 1 || data > 31)
return (t);
tm.tm_mday = (int)data;
p += 2;
if (*p++ != 'T')
return (t);
- if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 23)
+ if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 23)
return (t);
tm.tm_hour = (int)data;
p += 2;
if (*p++ != ':')
return (t);
- if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 59)
+ if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 59)
return (t);
tm.tm_min = (int)data;
p += 2;
if (*p++ != ':')
return (t);
- if (atou64(p, 4, 10, &data) != ARCHIVE_OK || data > 60)
+ if (atou64(p, 2, 10, &data) != ARCHIVE_OK || data > 60)
return (t);
tm.tm_sec = (int)data;
#if 0
@@ -1209,19 +1214,17 @@ heap_add_entry(struct archive_read *a,
struct heap_queue *heap, struct xar_file *file)
{
uint64_t file_id, parent_id;
- int hole, parent;
+ size_t hole, parent;
/* Expand our pending files list as necessary. */
if (heap->used >= heap->allocated) {
struct xar_file **new_pending_files;
- int new_size;
+ size_t new_size;
if (heap->allocated < 1024)
new_size = 1024;
- else
- new_size = heap->allocated * 2;
- /* Overflow might keep us from growing the list. */
- if (new_size <= heap->allocated) {
+ else if (archive_ckd_mul_size(&new_size, heap->allocated, 2)) {
+ /* Overflow keeps us from growing the list. */
archive_set_error(&a->archive,
ENOMEM, "Out of memory");
return (ARCHIVE_FATAL);
@@ -1249,7 +1252,7 @@ heap_add_entry(struct archive_read *a,
*/
hole = heap->used++;
while (hole > 0) {
- parent = (hole - 1)/2;
+ parent = (hole - 1) / 2;
parent_id = heap->files[parent]->id;
if (file_id >= parent_id) {
heap->files[hole] = file;
@@ -1268,7 +1271,7 @@ static struct xar_file *
heap_get_entry(struct heap_queue *heap)
{
uint64_t a_id, b_id, c_id;
- int a, b, c;
+ size_t a, b, c;
struct xar_file *r, *tmp;
if (heap->used < 1)
@@ -1338,7 +1341,7 @@ add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)
return (ARCHIVE_OK);
}
-static void
+static int
_checksum_init(struct chksumwork *sumwrk, int sum_alg)
{
sumwrk->alg = sum_alg;
@@ -1351,7 +1354,20 @@ _checksum_init(struct chksumwork *sumwrk, int sum_alg)
case CKSUM_MD5:
archive_md5_init(&(sumwrk->md5ctx));
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_init(&(sumwrk->sha256ctx));
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_init(&(sumwrk->sha512ctx));
+ break;
+#endif
+ default:
+ return (ARCHIVE_FATAL);
}
+ return (ARCHIVE_OK);
}
static void
@@ -1367,6 +1383,16 @@ _checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
case CKSUM_MD5:
archive_md5_update(&(sumwrk->md5ctx), buff, size);
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_update(&(sumwrk->sha256ctx), buff, size);
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_update(&(sumwrk->sha512ctx), buff, size);
+ break;
+#endif
}
}
@@ -1391,27 +1417,48 @@ _checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)
memcmp(val, sum, MD5_SIZE) != 0)
r = ARCHIVE_FAILED;
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_final(&(sumwrk->sha256ctx), sum);
+ if (len != SHA256_SIZE ||
+ memcmp(val, sum, SHA256_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_final(&(sumwrk->sha512ctx), sum);
+ if (len != SHA512_SIZE ||
+ memcmp(val, sum, SHA512_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+#endif
+ default:
+ r = ARCHIVE_FAILED;
}
return (r);
}
-static void
+static int
checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
- xar = (struct xar *)(a->format->data);
- _checksum_init(&(xar->a_sumwrk), a_sum_alg);
- _checksum_init(&(xar->e_sumwrk), e_sum_alg);
+ if (_checksum_init(&(xar->a_sumwrk), a_sum_alg) != ARCHIVE_OK ||
+ _checksum_init(&(xar->e_sumwrk), e_sum_alg) != ARCHIVE_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Unsupported checksum");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
}
static void
checksum_update(struct archive_read *a, const void *abuff, size_t asize,
const void *ebuff, size_t esize)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
- xar = (struct xar *)(a->format->data);
_checksum_update(&(xar->a_sumwrk), abuff, asize);
_checksum_update(&(xar->e_sumwrk), ebuff, esize);
}
@@ -1420,27 +1467,24 @@ static int
checksum_final(struct archive_read *a, const void *a_sum_val,
size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
{
- struct xar *xar;
- int r;
+ struct xar *xar = a->format->data;
- xar = (struct xar *)(a->format->data);
- r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
- if (r == ARCHIVE_OK)
- r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
- if (r != ARCHIVE_OK)
+ if (_checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len) != ARCHIVE_OK ||
+ _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len) != ARCHIVE_OK) {
archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
- "Sumcheck error");
- return (r);
+ "Checksum error");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
}
static int
decompression_init(struct archive_read *a, enum enctype encoding)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
const char *detail;
int r;
- xar = (struct xar *)(a->format->data);
xar->rd_encoding = encoding;
switch (encoding) {
case NONE:
@@ -1576,12 +1620,11 @@ static int
decompress(struct archive_read *a, const void **buff, size_t *outbytes,
const void *b, size_t *used)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
void *outbuff;
size_t avail_in, avail_out;
int r;
- xar = (struct xar *)(a->format->data);
avail_in = *used;
outbuff = (void *)(uintptr_t)*buff;
if (outbuff == NULL) {
@@ -1704,10 +1747,9 @@ decompress(struct archive_read *a, const void **buff, size_t *outbytes,
static int
decompression_cleanup(struct archive_read *a)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
int r;
- xar = (struct xar *)(a->format->data);
r = ARCHIVE_OK;
if (xar->stream_valid) {
if (inflateEnd(&(xar->stream)) != Z_OK) {
@@ -1736,9 +1778,7 @@ decompression_cleanup(struct archive_read *a)
static void
checksum_cleanup(struct archive_read *a) {
- struct xar *xar;
-
- xar = (struct xar *)(a->format->data);
+ struct xar *xar = a->format->data;
_checksum_final(&(xar->a_sumwrk), NULL, 0);
_checksum_final(&(xar->e_sumwrk), NULL, 0);
@@ -1791,8 +1831,11 @@ file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
}
xar->file = file;
file->nlink = 1;
- if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+ if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) {
+ xar->file = file->parent;
+ file_free(file);
return (ARCHIVE_FATAL);
+ }
return (ARCHIVE_OK);
}
@@ -1897,9 +1940,16 @@ getsumalgorithm(struct xmlattr_list *list)
const char *v = attr->value;
if ((v[0] == 'S' || v[0] == 's') &&
(v[1] == 'H' || v[1] == 'h') &&
- (v[2] == 'A' || v[2] == 'a') &&
- v[3] == '1' && v[4] == '\0')
- alg = CKSUM_SHA1;
+ (v[2] == 'A' || v[2] == 'a')) {
+ if (v[3] == '1' && v[4] == '\0')
+ alg = CKSUM_SHA1;
+ else if (v[3] == '2' && v[4] == '5' &&
+ v[5] == '6' && v[6] == '\0')
+ alg = CKSUM_SHA256;
+ else if (v[3] == '5' && v[4] == '1' &&
+ v[5] == '2' && v[6] == '\0')
+ alg = CKSUM_SHA512;
+ }
if ((v[0] == 'M' || v[0] == 'm') &&
(v[1] == 'D' || v[1] == 'd') &&
v[2] == '5' && v[3] == '\0')
@@ -1957,11 +2007,9 @@ unknowntag_end(struct xar *xar, const char *name)
static int
xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
struct xmlattr *attr;
- xar = (struct xar *)(a->format->data);
-
#if DEBUG
fprintf(stderr, "xml_sta:[%s]\n", name);
for (attr = list->first; attr != NULL; attr = attr->next)
@@ -2253,11 +2301,8 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
static void
xml_end(void *userData, const char *name)
{
- struct archive_read *a;
- struct xar *xar;
-
- a = (struct archive_read *)userData;
- xar = (struct xar *)(a->format->data);
+ struct archive_read *a = (struct archive_read *)userData;
+ struct xar *xar = a->format->data;
#if DEBUG
fprintf(stderr, "xml_end:[%s]\n", name);
@@ -2631,7 +2676,7 @@ strappend_base64(struct xar *xar,
len = 0;
out = buff;
b = (const unsigned char *)s;
- while (l > 0) {
+ while (l > 1) {
int n = 0;
if (base64[b[0]] < 0 || base64[b[1]] < 0)
@@ -2679,14 +2724,11 @@ is_string(const char *known, const char *data, size_t len)
static int
xml_data(void *userData, const char *s, size_t len)
{
+ struct archive_read *a = (struct archive_read *)userData;
+ struct xar *xar = a->format->data;
uint64_t val;
- struct archive_read *a;
- struct xar *xar;
int r;
- a = (struct archive_read *)userData;
- xar = (struct xar *)(a->format->data);
-
#if DEBUG
{
char buff[1024];
@@ -3166,16 +3208,13 @@ xml2_xmlattr_setup(struct archive_read *a,
static int
xml2_read_cb(void *context, char *buffer, int len)
{
- struct archive_read *a;
- struct xar *xar;
+ struct archive_read *a = (struct archive_read *)context;
+ struct xar *xar = a->format->data;
const void *d;
size_t outbytes;
size_t used = 0;
int r;
- a = (struct archive_read *)context;
- xar = (struct xar *)(a->format->data);
-
if (xar->toc_remaining <= 0)
return (0);
d = buffer;
@@ -3204,10 +3243,9 @@ static void
xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
xmlTextReaderLocatorPtr locator)
{
- struct archive_read *a;
+ struct archive_read *a = (struct archive_read *)arg;
(void)locator; /* UNUSED */
- a = (struct archive_read *)arg;
switch (severity) {
case XML_PARSER_SEVERITY_VALIDITY_WARNING:
case XML_PARSER_SEVERITY_WARNING:
@@ -3357,15 +3395,13 @@ expat_data_cb(void *userData, const XML_Char *s, int len)
static int
expat_read_toc(struct archive_read *a)
{
- struct xar *xar;
+ struct xar *xar = a->format->data;
XML_Parser parser;
struct expat_userData ud;
ud.state = ARCHIVE_OK;
ud.archive = a;
- xar = (struct xar *)(a->format->data);
-
/* Initialize XML Parser library. */
parser = XML_ParserCreate(NULL);
if (parser == NULL) {
@@ -3449,16 +3485,13 @@ static HRESULT STDMETHODCALLTYPE
asaRead(ISequentialStream *this, void *pv, ULONG cb, ULONG *pcbRead)
{
struct ArchiveStreamAdapter *asa = (struct ArchiveStreamAdapter *)this;
- struct archive_read *a;
- struct xar *xar;
+ struct archive_read *a = asa->a;
+ struct xar *xar = a->format->data;
const void *d = pv;
size_t outbytes = cb;
size_t used = 0;
int r;
- a = asa->a;
- xar = (struct xar *)(a->format->data);
-
*pcbRead = 0;
if (xar->toc_remaining <= 0)
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index fa9995d963d4..4148c4c90412 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -152,8 +152,6 @@ struct zip {
struct archive_string format_name;
int64_t central_directory_offset;
int64_t central_directory_offset_adjusted;
- size_t central_directory_entries_total;
- size_t central_directory_entries_on_this_disk;
int has_encrypted_entries;
/* List of entries (seekable Zip only) */
@@ -261,6 +259,15 @@ struct zip {
/* Many systems define min or MIN, but not all. */
#define zipmin(a,b) ((a) < (b) ? (a) : (b))
+/* True if this entry has declared a specific uncompressed size. */
+static int
+zip_entry_size_is_set(const struct zip_entry *zip_entry)
+{
+ return (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ || (zip_entry->uncompressed_size > 0
+ && zip_entry->uncompressed_size != 0xffffffff));
+}
+
#ifdef HAVE_ZLIB_H
static int
zip_read_data_deflate(struct archive_read *a, const void **buff,
@@ -284,7 +291,7 @@ static Byte
ppmd_read(void* p) {
/* Get the handle to current decompression context. */
struct archive_read *a = ((IByteIn*)p)->a;
- struct zip *zip = (struct zip*) a->format->data;
+ struct zip *zip = a->format->data;
/* Fetch next byte. */
const uint8_t* data = __archive_read_ahead(a, 1, NULL);
@@ -430,7 +437,7 @@ static int
zipx_read_header_and_decrypt(struct archive_read *a, const void **buf, size_t in_len,
size_t *out_len, size_t *consumed)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
const void *raw;
ssize_t bytes_avail;
size_t to_decrypt;
@@ -482,6 +489,13 @@ zip_read_decrypt(struct zip *zip, const void *compressed_buff,
{
*sp = compressed_buff;
+ /* Safety check to prevent potential OOB reads if something went wrong
+ * previously. We should not have a negative bytes_avail count here.
+ * If we do, set them to zero so that reading the ZIP will fail later,
+ * safely as corrupted instead of crashing. */
+ if (bytes_avail < 0)
+ bytes_avail = 0;
+
if (zip->tctx_valid || zip->cctx_valid) {
if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
size_t buff_remaining =
@@ -642,8 +656,8 @@ static int
process_extra(struct archive_read *a, struct archive_entry *entry,
const char *p, size_t extra_length, struct zip_entry* zip_entry)
{
+ struct zip *zip = a->format->data;
unsigned offset = 0;
- struct zip *zip = (struct zip *)(a->format->data);
if (extra_length == 0) {
return ARCHIVE_OK;
@@ -742,6 +756,22 @@ process_extra(struct archive_read *a, struct archive_entry *entry,
* on which file starts, but we don't handle
* multi-volume Zip files. */
break;
+ case 0x000d:
+ /* PKWARE Unix Extra Field fixed metadata. */
+ if (datasize >= 12) {
+ zip_entry->atime = archive_le32dec(p + offset);
+ zip_entry->mtime =
+ archive_le32dec(p + offset + 4);
+ zip_entry->uid =
+ archive_le16dec(p + offset + 8);
+ zip_entry->gid =
+ archive_le16dec(p + offset + 10);
+ /*
+ * APPNOTE.TXT also defines additional data after
+ * this fixed metadata, depending on file type.
+ */
+ }
+ break;
#ifdef DEBUG
case 0x0017:
{
@@ -1430,10 +1460,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
"Read error skipping symlink target name");
return ARCHIVE_FATAL;
}
- } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
- || (zip_entry->uncompressed_size > 0
- && zip_entry->uncompressed_size != 0xffffffff)) {
- /* Set the size only if it's meaningful. */
+ } else if (zip_entry_size_is_set(zip_entry)) {
archive_entry_set_size(entry, zip_entry->uncompressed_size);
}
zip->entry_bytes_remaining = zip_entry->compressed_size;
@@ -1456,7 +1483,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
static int
check_authentication_code(struct archive_read *a, const void *_p)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
/* Check authentication code. */
if (zip->hctx_valid) {
@@ -1683,6 +1710,26 @@ consume_end_of_file_marker(struct archive_read *a, struct zip *zip)
return;
}
+ /* None of the exact patterns matched. If entry size was unknown
+ * (ZIP_LENGTH_AT_END flag), before treating this as
+ * corruption, check whether the next ZIP record follows the data
+ * immediately: a length-at-end entry whose compression format has
+ * its own end-of-stream marker (e.g. PPMd) may be written with no
+ * data descriptor at all. In that case the byte counts we measured
+ * during decompression are authoritative, so trust them and leave
+ * the stream untouched. */
+ if (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ {
+ const uint32_t sig = archive_le32dec(p);
+ if (sig == 0x04034b50U /* Local file header */
+ || sig == 0x02014b50U /* Central directory record */
+ || sig == 0x06054b50U) /* End of central directory */ {
+ zip->entry->compressed_size = compressed_actual;
+ zip->entry->uncompressed_size = uncompressed_actual;
+ return;
+ }
+ }
+
/* If none of the above patterns gives us a full exact match,
* then there's something definitely amiss. The fallback code
* below will parse out some plausible values for error
@@ -1760,7 +1807,7 @@ static int
zip_read_data_none(struct archive_read *a, const void **_buff,
size_t *size, int64_t *offset)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
const char *buff;
ssize_t bytes_avail;
ssize_t trailing_extra;
@@ -1768,7 +1815,6 @@ zip_read_data_none(struct archive_read *a, const void **_buff,
(void)offset; /* UNUSED */
- zip = (struct zip *)(a->format->data);
trailing_extra = zip->hctx_valid ? AUTH_CODE_SIZE : 0;
if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
@@ -1986,7 +2032,13 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
*/
/* Read magic1,magic2,lzma_params from the ZIPX stream. */
- if(zip->entry_bytes_remaining < 9) {
+ /* When the compressed size is unknown (e.g. ZIP_LENGTH_AT_END read
+ * from a non-seekable source), entry_bytes_remaining is 0 or negative
+ * here. We can still attempt to read the 9-byte header; if the data
+ * is truly truncated, the __archive_read_ahead calls below will catch
+ * it. */
+ if(zip->entry_bytes_remaining > 0
+ && zip->entry_bytes_remaining < 9) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated lzma data");
return (ARCHIVE_FATAL);
@@ -2060,8 +2112,10 @@ zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
/* We've already consumed some bytes, so take this into account. */
__archive_read_consume(a, 9);
- zip->entry_bytes_remaining -= 9;
zip->entry_compressed_bytes_read += 9;
+ if (zip->entry_bytes_remaining > 0) {
+ zip->entry_bytes_remaining -= 9;
+ }
zip->decompress_init = 1;
return (ARCHIVE_OK);
@@ -2071,12 +2125,12 @@ static int
zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip* zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
int ret;
lzma_ret lz_ret;
const void* compressed_buf;
const void* sp;
- ssize_t bytes_avail, in_bytes, to_consume = 0;
+ ssize_t bytes_avail, to_consume = 0;
(void) offset; /* UNUSED */
@@ -2088,19 +2142,21 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
}
compressed_buf = sp = __archive_read_ahead(a, 1, &bytes_avail);
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
if (bytes_avail < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated xz file body");
return (ARCHIVE_FATAL);
}
- in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-
- zip_read_decrypt(zip, compressed_buf, in_bytes,
- &compressed_buf, &in_bytes, &sp);
+ zip_read_decrypt(zip, compressed_buf, bytes_avail,
+ &compressed_buf, &bytes_avail, &sp);
zip->zipx_lzma_stream.next_in = compressed_buf;
- zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.avail_in = bytes_avail;
zip->zipx_lzma_stream.total_in = 0;
zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
@@ -2127,8 +2183,10 @@ zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
lzma_end(&zip->zipx_lzma_stream);
zip->zipx_lzma_valid = 0;
- if((int64_t) zip->zipx_lzma_stream.total_in !=
- zip->entry_bytes_remaining)
+ /* This assertion is only possible if the size of the compressed data
+ * stream is known -> !ZIP_LENGTH_AT_END */
+ if((int64_t) zip->zipx_lzma_stream.total_in != zip->entry_bytes_remaining
+ && !(zip->entry->zip_flags & ZIP_LENGTH_AT_END))
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
@@ -2167,12 +2225,12 @@ static int
zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip* zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
int ret;
lzma_ret lz_ret;
const void* compressed_buf;
const void* sp;
- ssize_t bytes_avail, in_bytes, to_consume;
+ ssize_t bytes_avail, to_consume;
(void) offset; /* UNUSED */
@@ -2192,29 +2250,38 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
* data.
*/
compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+ if (zip->entry_bytes_remaining > 0
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
if (bytes_avail < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated lzma file body");
return (ARCHIVE_FATAL);
}
- /* Set decompressor parameters. */
- in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
-
- zip_read_decrypt(zip, compressed_buf, in_bytes,
- &compressed_buf, &in_bytes, &sp);
+ zip_read_decrypt(zip, compressed_buf, bytes_avail,
+ &compressed_buf, &bytes_avail, &sp);
zip->zipx_lzma_stream.next_in = compressed_buf;
- zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.avail_in = bytes_avail;
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. */
- (size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
- zip->entry->uncompressed_size -
- zip->entry_uncompressed_bytes_read);
+ /* These lzma_alone streams lack an end of stream marker in some
+ * cases, so when the uncompressed size is known we cap avail_out to
+ * make sure the unpacker won't try to unpack more than it's supposed
+ * to. When the compressed size is unknown (entry_bytes_remaining <= 0,
+ * e.g. ZIP_LENGTH_AT_END from a non-seekable source) we must use the
+ * full buffer and rely on the LZMA stream end marker to detect the end
+ * of the entry. */
+ if (zip->entry_bytes_remaining <= 0) {
+ zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+ } else {
+ zip->zipx_lzma_stream.avail_out =
+ (size_t)zipmin((int64_t) zip->uncompressed_buffer_size,
+ zip->entry->uncompressed_size -
+ zip->entry_uncompressed_bytes_read);
+ }
zip->zipx_lzma_stream.total_out = 0;
/* Perform the decompression. */
@@ -2228,8 +2295,11 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
/* This case is optional in lzma alone format. It can happen,
* but most of the files don't have it. (GitHub #1257) */
case LZMA_STREAM_END:
+ /* This assertion is only possible if the size of the
+ * compressed data stream is known. */
if((int64_t) zip->zipx_lzma_stream.total_in !=
- zip->entry_bytes_remaining)
+ zip->entry_bytes_remaining
+ && zip->entry_bytes_remaining > 0)
{
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
@@ -2245,7 +2315,13 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
case LZMA_BUF_ERROR:
if (zip->zipx_lzma_stream.avail_out == 0) {
- zip->end_of_entry = 1;
+ /* The output buffer was filled exactly. When
+ * the uncompressed size is known this means we
+ * have decompressed all expected bytes. When
+ * the size is unknown a full buffer just means
+ * we need another iteration. */
+ if (zip->entry_bytes_remaining > 0)
+ zip->end_of_entry = 1;
break;
}
/* FALL THROUGH */
@@ -2259,14 +2335,16 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
/* Update pointers. */
__archive_read_consume(a, to_consume);
- zip->entry_bytes_remaining -= to_consume;
zip->entry_compressed_bytes_read += to_consume;
zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
zip_read_decrypt_update(zip, to_consume, sp);
- if(zip->entry_bytes_remaining == 0) {
- zip->end_of_entry = 1;
+ if(zip->entry_bytes_remaining > 0) {
+ zip->entry_bytes_remaining -= to_consume;
+ if(zip->entry_bytes_remaining == 0) {
+ zip->end_of_entry = 1;
+ }
}
if(zip->end_of_entry && zip->entry_bytes_remaining > 0) {
@@ -2417,7 +2495,7 @@ static int
zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip* zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
int ret;
size_t consumed_bytes = 0;
@@ -2536,8 +2614,8 @@ static int
zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip *zip = (struct zip *)(a->format->data);
- ssize_t bytes_avail = 0, in_bytes, to_consume;
+ struct zip *zip = a->format->data;
+ ssize_t bytes_avail = 0, to_consume;
const void *compressed_buff;
const void *sp;
int r;
@@ -2554,30 +2632,26 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
/* Fetch more compressed bytes. */
compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
- if(bytes_avail < 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Truncated bzip2 file body");
- return (ARCHIVE_FATAL);
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
}
-
- in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
- if(in_bytes < 1) {
+ if(bytes_avail < 1) {
/* libbz2 doesn't complain when caller feeds avail_in == 0.
* It will actually return success in this case, which is
* undesirable. This is why we need to make this check
* manually. */
-
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Truncated bzip2 file body");
return (ARCHIVE_FATAL);
}
- zip_read_decrypt(zip, compressed_buff, in_bytes,
- &compressed_buff, &in_bytes, &sp);
+ zip_read_decrypt(zip, compressed_buff, bytes_avail,
+ &compressed_buff, &bytes_avail, &sp);
/* Setup buffer boundaries. */
zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
- zip->bzstream.avail_in = (uint32_t)in_bytes;
+ zip->bzstream.avail_in = (uint32_t)bytes_avail;
zip->bzstream.total_in_hi32 = 0;
zip->bzstream.total_in_lo32 = 0;
zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
@@ -2691,8 +2765,8 @@ static int
zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip *zip = (struct zip *)(a->format->data);
- ssize_t bytes_avail = 0, in_bytes, to_consume;
+ struct zip *zip = a->format->data;
+ ssize_t bytes_avail = 0, to_consume;
const void *compressed_buff;
const void *sp;
int r;
@@ -2712,14 +2786,11 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
/* Fetch more compressed bytes */
compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
- if(bytes_avail < 0) {
- archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
- "Truncated zstd file body");
- return (ARCHIVE_FATAL);
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
}
-
- in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
- if(in_bytes < 1) {
+ if(bytes_avail < 1) {
/* zstd 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
@@ -2729,12 +2800,12 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
return (ARCHIVE_FATAL);
}
- zip_read_decrypt(zip, compressed_buff, in_bytes,
- &compressed_buff, &in_bytes, &sp);
+ zip_read_decrypt(zip, compressed_buff, bytes_avail,
+ &compressed_buff, &bytes_avail, &sp);
/* Setup buffer boundaries */
in.src = compressed_buff;
- in.size = in_bytes;
+ in.size = bytes_avail;
in.pos = 0;
out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
@@ -2746,6 +2817,10 @@ zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
ZSTD_getErrorName(ret));
return (ARCHIVE_FATAL);
}
+ /* End of stream handling for zips with ZIP_LENGTH_AT_END flag */
+ if (ret == 0 && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+ zip->end_of_entry = 1;
+ }
/* Check end of the stream. */
if (ret == 0) {
@@ -2812,7 +2887,7 @@ static int
zip_read_data_deflate(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
ssize_t bytes_avail, to_consume = 0;
const void *compressed_buff;
const void *sp;
@@ -2820,8 +2895,6 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
(void)offset; /* UNUSED */
- zip = (struct zip *)(a->format->data);
-
/* If the buffer hasn't been allocated, allocate it now. */
if (zip->uncompressed_buffer == NULL) {
zip->uncompressed_buffer_size = 256 * 1024;
@@ -2914,7 +2987,7 @@ zip_read_data_deflate(struct archive_read *a, const void **buff,
static int
read_decryption_header(struct archive_read *a)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
const char *p;
unsigned int remaining_size;
unsigned int ts;
@@ -3108,7 +3181,7 @@ nomem:
static int
zip_alloc_decryption_buffer(struct archive_read *a)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
size_t bs = 256 * 1024;
if (zip->decrypted_buffer == NULL) {
@@ -3127,7 +3200,7 @@ zip_alloc_decryption_buffer(struct archive_read *a)
static int
init_traditional_PKWARE_decryption(struct archive_read *a)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
const void *p;
int retry;
int r;
@@ -3199,7 +3272,7 @@ init_traditional_PKWARE_decryption(struct archive_read *a)
static int
init_WinZip_AES_decryption(struct archive_read *a)
{
- struct zip *zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
const void *p;
const uint8_t *pv;
size_t key_len, salt_len;
@@ -3293,8 +3366,8 @@ static int
archive_read_format_zip_read_data(struct archive_read *a,
const void **buff, size_t *size, int64_t *offset)
{
+ struct zip *zip = a->format->data;
int r;
- struct zip *zip = (struct zip *)(a->format->data);
if (zip->has_encrypted_entries ==
ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
@@ -3372,6 +3445,25 @@ archive_read_format_zip_read_data(struct archive_read *a,
}
if (r != ARCHIVE_OK)
return (r);
+
+ /*
+ * FAIL if there are more uncompressed bytes than were
+ * initially advertised. The end-of-entry check below also
+ * compares these values, but only once decoding reaches its
+ * own natural end. Fail the entry so a caller relying on
+ * archive_entry_size() as a hard boundary is never misled;
+ * later entries in the archive can still be read normally.
+ */
+ if (*size > 0 && zip_entry_size_is_set(zip->entry) &&
+ zip->entry_uncompressed_bytes_read > zip->entry->uncompressed_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP uncompressed data is larger than the declared "
+ "entry size (read at least %jd, expected %jd)",
+ (intmax_t)zip->entry_uncompressed_bytes_read,
+ (intmax_t)zip->entry->uncompressed_size);
+ return (ARCHIVE_FAILED);
+ }
+
if (*size > 0) {
zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff,
(unsigned)*size);
@@ -3402,10 +3494,8 @@ archive_read_format_zip_read_data(struct archive_read *a,
(intmax_t)zip->entry->compressed_size);
return (ARCHIVE_FAILED);
}
- /* Size field only stores the lower 32 bits of the actual
- * size. */
- if ((zip->entry->uncompressed_size & UINT32_MAX)
- != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+ if (zip->entry->uncompressed_size !=
+ zip->entry_uncompressed_bytes_read) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"ZIP uncompressed data is wrong size "
"(read %jd, expected %jd)",
@@ -3421,11 +3511,9 @@ archive_read_format_zip_read_data(struct archive_read *a,
static int
archive_read_format_zip_cleanup(struct archive_read *a)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
struct zip_entry *zip_entry, *next_zip_entry;
- zip = (struct zip *)(a->format->data);
-
#ifdef HAVE_ZLIB_H
if (zip->stream_valid)
inflateEnd(&zip->stream);
@@ -3473,7 +3561,7 @@ archive_read_format_zip_cleanup(struct archive_read *a)
free(zip->v_data);
archive_string_free(&zip->format_name);
free(zip);
- (a->format->data) = NULL;
+ a->format->data = NULL;
return (ARCHIVE_OK);
}
@@ -3481,7 +3569,7 @@ static int
archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
{
if (_a && _a->format) {
- struct zip * zip = (struct zip *)_a->format->data;
+ struct zip *zip = _a->format->data;
if (zip) {
return zip->has_encrypted_entries;
}
@@ -3493,10 +3581,9 @@ static int
archive_read_format_zip_options(struct archive_read *a,
const char *key, const char *val)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
int ret = ARCHIVE_FAILED;
- zip = (struct zip *)(a->format->data);
if (strcmp(key, "compat-2x") == 0) {
/* Handle filenames as libarchive 2.x */
zip->init_default_conversion = (val != NULL) ? 1 : 0;
@@ -3603,14 +3690,12 @@ static int
archive_read_format_zip_streamable_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
if (a->archive.archive_format_name == NULL)
a->archive.archive_format_name = "ZIP";
- zip = (struct zip *)(a->format->data);
-
/*
* It should be sufficient to call archive_read_next_header() for
* a reader to determine if an entry is encrypted or not. If the
@@ -3696,10 +3781,9 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a,
static int
archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
{
- struct zip *zip;
+ struct zip *zip = a->format->data;
int64_t bytes_skipped;
- zip = (struct zip *)(a->format->data);
bytes_skipped = __archive_read_consume(a, zip->unconsumed);
zip->unconsumed = 0;
if (bytes_skipped < 0)
@@ -3941,7 +4025,7 @@ read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
static int
archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
{
- struct zip *zip = (struct zip *)a->format->data;
+ struct zip *zip = a->format->data;
int64_t file_size, current_offset;
const char *p;
int i, tail;
@@ -4147,7 +4231,6 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
__archive_rb_tree_init(&zip->tree, &rb_ops);
__archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
- zip->central_directory_entries_total = 0;
while (1) {
struct zip_entry *zip_entry;
size_t filename_length, extra_length, comment_length;
@@ -4176,7 +4259,6 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
zip_entry->next = zip->zip_entries;
zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
zip->zip_entries = zip_entry;
- zip->central_directory_entries_total++;
/* version = p[4]; */
zip_entry->system = p[5];
@@ -4256,12 +4338,14 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
r = rsrc_basename(name, filename_length);
if (filename_length >= 9 &&
strncmp("__MACOSX/", name, 9) == 0) {
+ const char *name_end;
+
+ name_end = name + filename_length;
/* If this file is not a resource fork nor
* 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] != '_')) {
+ (name_end - r < 2 || r[0] != '.' || r[1] != '_')) {
__archive_rb_tree_insert_node(
&zip->tree, &zip_entry->node);
/* Expose its parent directories. */
@@ -4339,7 +4423,7 @@ static int
zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
struct zip_entry *rsrc)
{
- struct zip *zip = (struct zip *)a->format->data;
+ struct zip *zip = a->format->data;
unsigned char *metadata, *mp;
int64_t offset = archive_filter_bytes(&a->archive, 0);
size_t remaining_bytes, metadata_bytes;
@@ -4490,7 +4574,7 @@ static int
archive_read_format_zip_seekable_read_header(struct archive_read *a,
struct archive_entry *entry)
{
- struct zip *zip = (struct zip *)a->format->data;
+ struct zip *zip = a->format->data;
struct zip_entry *rsrc;
int64_t offset;
int r, ret = ARCHIVE_OK;
@@ -4570,8 +4654,7 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a,
static int
archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
{
- struct zip *zip;
- zip = (struct zip *)(a->format->data);
+ struct zip *zip = a->format->data;
zip->unconsumed = 0;
return (ARCHIVE_OK);
diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c
index bbcec444b843..87e21c8d7f2e 100644
--- a/libarchive/archive_string.c
+++ b/libarchive/archive_string.c
@@ -450,6 +450,8 @@ default_iconv_charset(const char *charset) {
return locale_charset();
#elif HAVE_NL_LANGINFO
return nl_langinfo(CODESET);
+#elif defined(__BIONIC__)
+ return "UTF-8";
#else
return "";
#endif
@@ -2325,7 +2327,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
remaining = length;
itp = (const uint8_t *)_p;
- while (*itp && remaining > 0) {
+ while (remaining > 0 && *itp) {
if (*itp > 127) {
// Non-ASCII: Substitute with suitable replacement
if (sc->flag & SCONV_TO_UTF8) {
@@ -2340,6 +2342,7 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
archive_strappend_char(as, *itp);
}
++itp;
+ --remaining;
}
return (return_value);
}
diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c
index 68f452ec30e3..e1a3ffebb1ea 100644
--- a/libarchive/archive_write.c
+++ b/libarchive/archive_write.c
@@ -69,7 +69,7 @@ static int _archive_write_header(struct archive *, struct archive_entry *);
static int _archive_write_finish_entry(struct archive *);
static ssize_t _archive_write_data(struct archive *, const void *, size_t);
-struct archive_none {
+struct client {
size_t buffer_size;
size_t avail;
char *buffer;
@@ -135,7 +135,7 @@ archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
if (bytes_per_block < 0) {
// Do nothing if the bytes_per_block is negative
- return 0;
+ return ARCHIVE_OK;
}
a->bytes_per_block = bytes_per_block;
return (ARCHIVE_OK);
@@ -240,8 +240,10 @@ __archive_write_filter(struct archive_write_filter *f,
/* If unset, a fatal error has already occurred, so this filter
* didn't open. We cannot write anything. */
return(ARCHIVE_FATAL);
+ if (length > (uint64_t)(INT64_MAX - f->bytes_written))
+ return(ARCHIVE_FATAL);
r = (f->write)(f, buff, length);
- f->bytes_written += length;
+ f->bytes_written += (int64_t)length;
return (r);
}
@@ -338,7 +340,7 @@ __archive_write_filters_flush(struct archive_write *a)
}
int
-__archive_write_nulls(struct archive_write *a, size_t length)
+__archive_write_nulls(struct archive_write *a, uint64_t length)
{
if (length == 0)
return (ARCHIVE_OK);
@@ -357,7 +359,7 @@ static int
archive_write_client_open(struct archive_write_filter *f)
{
struct archive_write *a = (struct archive_write *)f->archive;
- struct archive_none *state;
+ struct client *client;
void *buffer;
size_t buffer_size;
@@ -366,21 +368,21 @@ archive_write_client_open(struct archive_write_filter *f)
archive_write_get_bytes_in_last_block(f->archive);
buffer_size = f->bytes_per_block;
- state = calloc(1, sizeof(*state));
+ client = calloc(1, sizeof(*client));
buffer = malloc(buffer_size);
- if (state == NULL || buffer == NULL) {
- free(state);
+ if (client == NULL || buffer == NULL) {
+ free(client);
free(buffer);
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for output buffering");
return (ARCHIVE_FATAL);
}
- state->buffer_size = buffer_size;
- state->buffer = buffer;
- state->next = state->buffer;
- state->avail = state->buffer_size;
- f->data = state;
+ client->buffer_size = buffer_size;
+ client->buffer = buffer;
+ client->next = client->buffer;
+ client->avail = client->buffer_size;
+ f->data = client;
if (a->client_opener == NULL)
return (ARCHIVE_OK);
@@ -392,7 +394,7 @@ archive_write_client_write(struct archive_write_filter *f,
const void *_buff, size_t length)
{
struct archive_write *a = (struct archive_write *)f->archive;
- struct archive_none *state = (struct archive_none *)f->data;
+ struct client *client = f->data;
const char *buff = (const char *)_buff;
ssize_t remaining, to_copy;
ssize_t bytes_written;
@@ -405,7 +407,7 @@ archive_write_client_write(struct archive_write_filter *f,
* particular, this supports "no write delay" operation for
* special applications. Just set the block size to zero.
*/
- if (state->buffer_size == 0) {
+ if (client->buffer_size == 0) {
while (remaining > 0) {
bytes_written = (a->client_writer)(&a->archive,
a->client_data, buff, remaining);
@@ -418,20 +420,20 @@ archive_write_client_write(struct archive_write_filter *f,
}
/* If the copy buffer isn't empty, try to fill it. */
- if (state->avail < state->buffer_size) {
+ if (client->avail < client->buffer_size) {
/* If buffer is not empty... */
/* ... copy data into buffer ... */
- to_copy = ((size_t)remaining > state->avail) ?
- state->avail : (size_t)remaining;
- memcpy(state->next, buff, to_copy);
- state->next += to_copy;
- state->avail -= to_copy;
+ to_copy = ((size_t)remaining > client->avail) ?
+ client->avail : (size_t)remaining;
+ memcpy(client->next, buff, to_copy);
+ client->next += to_copy;
+ client->avail -= to_copy;
buff += to_copy;
remaining -= to_copy;
/* ... if it's full, write it out. */
- if (state->avail == 0) {
- char *p = state->buffer;
- size_t to_write = state->buffer_size;
+ if (client->avail == 0) {
+ char *p = client->buffer;
+ size_t to_write = client->buffer_size;
while (to_write > 0) {
bytes_written = (a->client_writer)(&a->archive,
a->client_data, p, to_write);
@@ -445,15 +447,15 @@ archive_write_client_write(struct archive_write_filter *f,
p += bytes_written;
to_write -= bytes_written;
}
- state->next = state->buffer;
- state->avail = state->buffer_size;
+ client->next = client->buffer;
+ client->avail = client->buffer_size;
}
}
- while ((size_t)remaining >= state->buffer_size) {
+ while ((size_t)remaining >= client->buffer_size) {
/* Write out full blocks directly to client. */
bytes_written = (a->client_writer)(&a->archive,
- a->client_data, buff, state->buffer_size);
+ a->client_data, buff, client->buffer_size);
if (bytes_written <= 0)
return (ARCHIVE_FATAL);
buff += bytes_written;
@@ -462,9 +464,9 @@ archive_write_client_write(struct archive_write_filter *f,
if (remaining > 0) {
/* Copy last bit into copy buffer. */
- memcpy(state->next, buff, remaining);
- state->next += remaining;
- state->avail -= remaining;
+ memcpy(client->next, buff, remaining);
+ client->next += remaining;
+ client->avail -= remaining;
}
return (ARCHIVE_OK);
}
@@ -473,7 +475,7 @@ static int
archive_write_client_free(struct archive_write_filter *f)
{
struct archive_write *a = (struct archive_write *)f->archive;
- struct archive_none *state = (struct archive_none *)f->data;
+ struct client *client = f->data;
if (a->client_freer)
(*a->client_freer)(&a->archive, a->client_data);
@@ -487,9 +489,9 @@ archive_write_client_free(struct archive_write_filter *f)
}
/* Free state. */
- if (state != NULL) {
- free(state->buffer);
- free(state);
+ if (client != NULL) {
+ free(client->buffer);
+ free(client);
f->data = NULL;
}
@@ -500,7 +502,7 @@ static int
archive_write_client_close(struct archive_write_filter *f)
{
struct archive_write *a = (struct archive_write *)f->archive;
- struct archive_none *state = (struct archive_none *)f->data;
+ struct client *client = f->data;
ssize_t block_length;
ssize_t target_block_length;
ssize_t bytes_written;
@@ -509,8 +511,8 @@ archive_write_client_close(struct archive_write_filter *f)
int ret = ARCHIVE_OK;
/* If there's pending data, pad and write the last block */
- if (state->next != state->buffer) {
- block_length = state->buffer_size - state->avail;
+ if (client->next != client->buffer) {
+ block_length = client->buffer_size - client->avail;
/* Tricky calculation to determine size of last block */
if (a->bytes_in_last_block <= 0)
@@ -524,11 +526,11 @@ archive_write_client_close(struct archive_write_filter *f)
if (target_block_length > a->bytes_per_block)
target_block_length = a->bytes_per_block;
if (block_length < target_block_length) {
- memset(state->next, 0,
+ memset(client->next, 0,
target_block_length - block_length);
block_length = target_block_length;
}
- p = state->buffer;
+ p = client->buffer;
to_write = block_length;
while (to_write > 0) {
bytes_written = (a->client_writer)(&a->archive,
@@ -683,6 +685,29 @@ __archive_write_filters_free(struct archive *_a)
a->filter_last = NULL;
}
+int
+__archive_write_unregister_format(struct archive_write *a)
+{
+ int r = ARCHIVE_OK;
+
+ if (a->format_free != NULL)
+ r = (a->format_free)(a);
+
+ a->format_data = NULL;
+ a->format_name = NULL;
+ a->format_init = NULL;
+ a->format_options = NULL;
+ a->format_finish_entry = NULL;
+ a->format_write_header = NULL;
+ a->format_write_data = NULL;
+ a->format_close = NULL;
+ a->format_free = NULL;
+ a->archive.archive_format = 0;
+ a->archive.archive_format_name = NULL;
+
+ return (r);
+}
+
/*
* Destroy the archive structure.
*
@@ -705,11 +730,9 @@ _archive_write_free(struct archive *_a)
r = archive_write_close(&a->archive);
/* Release format resources. */
- if (a->format_free != NULL) {
- r1 = (a->format_free)(a);
- if (r1 < r)
- r = r1;
- }
+ r1 = __archive_write_unregister_format(a);
+ if (r1 < r)
+ r = r1;
__archive_write_filters_free(_a);
diff --git a/libarchive/archive_write_add_filter_b64encode.c b/libarchive/archive_write_add_filter_b64encode.c
index 2c4e5ba6ee50..a285a54d48eb 100644
--- a/libarchive/archive_write_add_filter_b64encode.c
+++ b/libarchive/archive_write_add_filter_b64encode.c
@@ -46,7 +46,7 @@
#define LBYTES 57
-struct private_b64encode {
+struct b64encode {
int mode;
struct archive_string name;
struct archive_string encoded_buff;
@@ -64,6 +64,7 @@ static int archive_filter_b64encode_close(struct archive_write_filter *);
static int archive_filter_b64encode_free(struct archive_write_filter *);
static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
static int64_t atol8(const char *, size_t);
+static void free_data(struct b64encode *);
static const char base64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
@@ -80,34 +81,38 @@ static const char base64[] = {
* Add a compress filter to this write handle.
*/
int
-archive_write_add_filter_b64encode(struct archive *_a)
+archive_write_add_filter_b64encode(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_b64encode *state;
+ struct archive_write_filter *f;
+ struct b64encode *b64encode;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_b64encode");
- state = calloc(1, sizeof(*state));
- if (state == NULL) {
- archive_set_error(f->archive, ENOMEM,
- "Can't allocate data for b64encode filter");
- return (ARCHIVE_FATAL);
- }
- archive_strcpy(&state->name, "-");
- state->mode = 0644;
+ b64encode = calloc(1, sizeof(*b64encode));
+ if (b64encode == NULL)
+ goto memerr;
+ archive_strcpy(&b64encode->name, "-");
+ b64encode->mode = 0644;
- f->data = state;
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
f->name = "b64encode";
f->code = ARCHIVE_FILTER_UU;
- f->open = archive_filter_b64encode_open;
+ f->data = b64encode;
f->options = archive_filter_b64encode_options;
+ f->open = archive_filter_b64encode_open;
f->write = archive_filter_b64encode_write;
f->close = archive_filter_b64encode_close;
f->free = archive_filter_b64encode_free;
return (ARCHIVE_OK);
+memerr:
+ free_data(b64encode);
+ archive_set_error(a, ENOMEM,
+ "Can't allocate data for b64encode filter");
+ return (ARCHIVE_FATAL);
}
/*
@@ -117,7 +122,7 @@ static int
archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct private_b64encode *state = (struct private_b64encode *)f->data;
+ struct b64encode *b64encode = f->data;
if (strcmp(key, "mode") == 0) {
int64_t val;
@@ -133,7 +138,7 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key
"invalid mode option");
return (ARCHIVE_FAILED);
}
- state->mode = (int)val & 0777;
+ b64encode->mode = (int)val & 0777;
return (ARCHIVE_OK);
} else if (strcmp(key, "name") == 0) {
if (value == NULL) {
@@ -141,7 +146,7 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key
"name option requires a string");
return (ARCHIVE_FAILED);
}
- archive_strcpy(&state->name, value);
+ archive_strcpy(&b64encode->name, value);
return (ARCHIVE_OK);
}
@@ -157,7 +162,7 @@ archive_filter_b64encode_options(struct archive_write_filter *f, const char *key
static int
archive_filter_b64encode_open(struct archive_write_filter *f)
{
- struct private_b64encode *state = (struct private_b64encode *)f->data;
+ struct b64encode *b64encode = f->data;
size_t bs = 65536, bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
@@ -170,18 +175,17 @@ archive_filter_b64encode_open(struct archive_write_filter *f)
bs -= bs % bpb;
}
- state->bs = bs;
- if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ b64encode->bs = bs;
+ if (archive_string_ensure(&b64encode->encoded_buff, bs + 512) == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for b64encode buffer");
return (ARCHIVE_FATAL);
}
- archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
- (unsigned int)state->mode, state->name.s);
+ archive_string_sprintf(&b64encode->encoded_buff, "begin-base64 %o %s\n",
+ (unsigned int)b64encode->mode, b64encode->name.s);
- f->data = state;
- return (0);
+ return (ARCHIVE_OK);
}
static void
@@ -225,39 +229,39 @@ static int
archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_b64encode *state = (struct private_b64encode *)f->data;
+ struct b64encode *b64encode = f->data;
const unsigned char *p = buff;
int ret = ARCHIVE_OK;
if (length == 0)
return (ret);
- if (state->hold_len) {
- while (state->hold_len < LBYTES && length > 0) {
- state->hold[state->hold_len++] = *p++;
+ if (b64encode->hold_len) {
+ while (b64encode->hold_len < LBYTES && length > 0) {
+ b64encode->hold[b64encode->hold_len++] = *p++;
length--;
}
- if (state->hold_len < LBYTES)
+ if (b64encode->hold_len < LBYTES)
return (ret);
- la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
- state->hold_len = 0;
+ la_b64_encode(&b64encode->encoded_buff, b64encode->hold, LBYTES);
+ b64encode->hold_len = 0;
}
for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
- la_b64_encode(&state->encoded_buff, p, LBYTES);
+ la_b64_encode(&b64encode->encoded_buff, p, LBYTES);
/* Save remaining bytes. */
if (length > 0) {
- memcpy(state->hold, p, length);
- state->hold_len = length;
+ memcpy(b64encode->hold, p, length);
+ b64encode->hold_len = length;
}
- while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ while (archive_strlen(&b64encode->encoded_buff) >= b64encode->bs) {
ret = __archive_write_filter(f->next_filter,
- state->encoded_buff.s, state->bs);
- memmove(state->encoded_buff.s,
- state->encoded_buff.s + state->bs,
- state->encoded_buff.length - state->bs);
- state->encoded_buff.length -= state->bs;
+ b64encode->encoded_buff.s, b64encode->bs);
+ memmove(b64encode->encoded_buff.s,
+ b64encode->encoded_buff.s + b64encode->bs,
+ b64encode->encoded_buff.length - b64encode->bs);
+ b64encode->encoded_buff.length -= b64encode->bs;
}
return (ret);
@@ -270,26 +274,24 @@ archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
static int
archive_filter_b64encode_close(struct archive_write_filter *f)
{
- struct private_b64encode *state = (struct private_b64encode *)f->data;
+ struct b64encode *b64encode = f->data;
/* Flush remaining bytes. */
- if (state->hold_len != 0)
- la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
- archive_string_sprintf(&state->encoded_buff, "====\n");
+ if (b64encode->hold_len != 0)
+ la_b64_encode(&b64encode->encoded_buff, b64encode->hold,
+ b64encode->hold_len);
+ archive_string_sprintf(&b64encode->encoded_buff, "====\n");
/* Write the last block */
archive_write_set_bytes_in_last_block(f->archive, 1);
return __archive_write_filter(f->next_filter,
- state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+ b64encode->encoded_buff.s, archive_strlen(&b64encode->encoded_buff));
}
static int
archive_filter_b64encode_free(struct archive_write_filter *f)
{
- struct private_b64encode *state = (struct private_b64encode *)f->data;
-
- archive_string_free(&state->name);
- archive_string_free(&state->encoded_buff);
- free(state);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
@@ -316,3 +318,12 @@ atol8(const char *p, size_t char_cnt)
return (l);
}
+static void
+free_data(struct b64encode *b64encode)
+{
+ if (b64encode != NULL) {
+ archive_string_free(&b64encode->name);
+ archive_string_free(&b64encode->encoded_buff);
+ free(b64encode);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c
index 209b15285415..53908783a363 100644
--- a/libarchive/archive_write_add_filter_bzip2.c
+++ b/libarchive/archive_write_add_filter_bzip2.c
@@ -53,7 +53,7 @@ archive_write_set_compression_bzip2(struct archive *a)
}
#endif
-struct private_data {
+struct bzip2 {
int compression_level;
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
bz_stream stream;
@@ -71,48 +71,64 @@ static int archive_compressor_bzip2_options(struct archive_write_filter *,
const char *, const char *);
static int archive_compressor_bzip2_write(struct archive_write_filter *,
const void *, size_t);
+static void free_data(struct bzip2 *);
/*
* Add a bzip2 compression filter to this write handle.
*/
int
-archive_write_add_filter_bzip2(struct archive *_a)
+archive_write_add_filter_bzip2(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_data *data;
+ struct archive_write_filter *f;
+ struct bzip2 *bzip2;
+ int r;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- data->compression_level = 9; /* default */
-
- f->data = data;
- f->options = &archive_compressor_bzip2_options;
- f->close = &archive_compressor_bzip2_close;
- f->free = &archive_compressor_bzip2_free;
- f->open = &archive_compressor_bzip2_open;
- f->code = ARCHIVE_FILTER_BZIP2;
- f->name = "bzip2";
+ bzip2 = calloc(1, sizeof(*bzip2));
+ if (bzip2 == NULL)
+ goto memerr;
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
- return (ARCHIVE_OK);
+ bzip2->compression_level = 9;
+
+ r = ARCHIVE_OK;
#else
- data->pdata = __archive_write_program_allocate("bzip2");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- data->compression_level = 0;
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ bzip2->pdata = __archive_write_program_allocate("bzip2");
+ if (bzip2->pdata == NULL)
+ goto memerr;
+ bzip2->compression_level = 0;
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external bzip2 program");
- return (ARCHIVE_WARN);
+ r = ARCHIVE_WARN;
#endif
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = "bzip2";
+ f->code = ARCHIVE_FILTER_BZIP2;
+ f->data = bzip2;
+ f->options = archive_compressor_bzip2_options;
+ f->open = archive_compressor_bzip2_open;
+ f->write = archive_compressor_bzip2_write;
+ f->close = archive_compressor_bzip2_close;
+ f->free = archive_compressor_bzip2_free;
+
+ return (r);
+memerr:
+ free_data(bzip2);
+ archive_set_error(a, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+ free_data(f->data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
}
/*
@@ -122,7 +138,7 @@ static int
archive_compressor_bzip2_options(struct archive_write_filter *f,
const char *key, const char *value)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -131,12 +147,12 @@ archive_compressor_bzip2_options(struct archive_write_filter *f,
"compression-level invalid");
return (ARCHIVE_FAILED);
}
- data->compression_level = value[0] - '0';
+ bzip2->compression_level = value[0] - '0';
/* Make '0' be a synonym for '1'. */
/* This way, bzip2 compressor supports the same 0..9
* range of levels as gzip. */
- if (data->compression_level < 1)
- data->compression_level = 1;
+ if (bzip2->compression_level < 1)
+ bzip2->compression_level = 1;
return (ARCHIVE_OK);
}
@@ -156,7 +172,7 @@ archive_compressor_bzip2_options(struct archive_write_filter *f,
#define SET_NEXT_IN(st,src) \
(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
static int drive_compressor(struct archive_write_filter *,
- struct private_data *, int finishing);
+ struct bzip2 *, int finishing);
/*
* Setup callback.
@@ -164,10 +180,10 @@ static int drive_compressor(struct archive_write_filter *,
static int
archive_compressor_bzip2_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
int ret;
- if (data->compressed == NULL) {
+ if (bzip2->compressed == NULL) {
size_t bs = 65536, bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of the bytes
@@ -178,25 +194,23 @@ archive_compressor_bzip2_open(struct archive_write_filter *f)
else if (bpb != 0)
bs -= bs % bpb;
}
- data->compressed_buffer_size = bs;
- data->compressed = malloc(data->compressed_buffer_size);
- if (data->compressed == NULL) {
+ bzip2->compressed_buffer_size = bs;
+ bzip2->compressed = malloc(bzip2->compressed_buffer_size);
+ if (bzip2->compressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- memset(&data->stream, 0, sizeof(data->stream));
- data->stream.next_out = data->compressed;
- data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
- f->write = archive_compressor_bzip2_write;
+ memset(&bzip2->stream, 0, sizeof(bzip2->stream));
+ bzip2->stream.next_out = bzip2->compressed;
+ bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size;
/* Initialize compression library */
- ret = BZ2_bzCompressInit(&(data->stream),
- data->compression_level, 0, 30);
+ ret = BZ2_bzCompressInit(&(bzip2->stream),
+ bzip2->compression_level, 0, 30);
if (ret == BZ_OK) {
- f->data = data;
return (ARCHIVE_OK);
}
@@ -236,12 +250,12 @@ static int
archive_compressor_bzip2_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
/* Compress input data to output buffer */
- SET_NEXT_IN(data, buff);
- data->stream.avail_in = (uint32_t)length;
- if (drive_compressor(f, data, 0))
+ SET_NEXT_IN(bzip2, buff);
+ bzip2->stream.avail_in = (uint32_t)length;
+ if (drive_compressor(f, bzip2, 0))
return (ARCHIVE_FATAL);
return (ARCHIVE_OK);
}
@@ -253,19 +267,19 @@ archive_compressor_bzip2_write(struct archive_write_filter *f,
static int
archive_compressor_bzip2_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
int ret;
/* Finish compression cycle. */
- ret = drive_compressor(f, data, 1);
+ ret = drive_compressor(f, bzip2, 1);
if (ret == ARCHIVE_OK) {
/* Write the last block */
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size - data->stream.avail_out);
+ bzip2->compressed,
+ bzip2->compressed_buffer_size - bzip2->stream.avail_out);
}
- switch (BZ2_bzCompressEnd(&(data->stream))) {
+ switch (BZ2_bzCompressEnd(&(bzip2->stream))) {
case BZ_OK:
break;
default:
@@ -276,20 +290,6 @@ archive_compressor_bzip2_close(struct archive_write_filter *f)
return ret;
}
-static int
-archive_compressor_bzip2_free(struct archive_write_filter *f)
-{
- struct private_data *data = (struct private_data *)f->data;
-
- /* May already have been called, but not necessarily. */
- (void)BZ2_bzCompressEnd(&(data->stream));
-
- free(data->compressed);
- free(data);
- f->data = NULL;
- return (ARCHIVE_OK);
-}
-
/*
* Utility function to push input data through compressor, writing
* full output blocks as necessary.
@@ -299,35 +299,35 @@ archive_compressor_bzip2_free(struct archive_write_filter *f)
*/
static int
drive_compressor(struct archive_write_filter *f,
- struct private_data *data, int finishing)
+ struct bzip2 *bzip2, int finishing)
{
int ret;
for (;;) {
- if (data->stream.avail_out == 0) {
+ if (bzip2->stream.avail_out == 0) {
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size);
+ bzip2->compressed,
+ bzip2->compressed_buffer_size);
if (ret != ARCHIVE_OK) {
/* TODO: Handle this write failure */
return (ARCHIVE_FATAL);
}
- data->stream.next_out = data->compressed;
- data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+ bzip2->stream.next_out = bzip2->compressed;
+ bzip2->stream.avail_out = (uint32_t)bzip2->compressed_buffer_size;
}
/* If there's nothing to do, we're done. */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && bzip2->stream.avail_in == 0)
return (ARCHIVE_OK);
- ret = BZ2_bzCompress(&(data->stream),
+ ret = BZ2_bzCompress(&(bzip2->stream),
finishing ? BZ_FINISH : BZ_RUN);
switch (ret) {
case BZ_RUN_OK:
/* In non-finishing case, did compressor
* consume everything? */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && bzip2->stream.avail_in == 0)
return (ARCHIVE_OK);
break;
case BZ_FINISH_OK: /* Finishing: There's more work to do */
@@ -347,12 +347,24 @@ drive_compressor(struct archive_write_filter *f,
}
}
+static void
+free_data(struct bzip2 *bzip2)
+{
+ if (bzip2 != NULL) {
+ /* May already have been called, but not necessarily. */
+ (void)BZ2_bzCompressEnd(&(bzip2->stream));
+
+ free(bzip2->compressed);
+ free(bzip2);
+ }
+}
+
#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
static int
archive_compressor_bzip2_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
struct archive_string as;
int r;
@@ -360,13 +372,12 @@ archive_compressor_bzip2_open(struct archive_write_filter *f)
archive_strcpy(&as, "bzip2");
/* Specify compression level. */
- if (data->compression_level > 0) {
+ if (bzip2->compression_level > 0) {
archive_strcat(&as, " -");
- archive_strappend_char(&as, '0' + data->compression_level);
+ archive_strappend_char(&as, '0' + bzip2->compression_level);
}
- f->write = archive_compressor_bzip2_write;
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, bzip2->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -375,27 +386,26 @@ static int
archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, bzip2->pdata, buff, length);
}
static int
archive_compressor_bzip2_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct bzip2 *bzip2 = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, bzip2->pdata);
}
-static int
-archive_compressor_bzip2_free(struct archive_write_filter *f)
+static void
+free_data(struct bzip2 *bzip2)
{
- struct private_data *data = (struct private_data *)f->data;
-
- __archive_write_program_free(data->pdata);
- free(data);
- return (ARCHIVE_OK);
+ if (bzip2 != NULL) {
+ __archive_write_program_free(bzip2->pdata);
+ free(bzip2);
+ }
}
#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/libarchive/archive_write_add_filter_compress.c b/libarchive/archive_write_add_filter_compress.c
index a54a85754293..f8824204933c 100644
--- a/libarchive/archive_write_add_filter_compress.c
+++ b/libarchive/archive_write_add_filter_compress.c
@@ -85,7 +85,7 @@
#define FIRST 257 /* First free entry. */
#define CLEAR 256 /* Table clear output code. */
-struct private_data {
+struct compress {
int64_t in_count, out_count, checkpoint;
int code_len; /* Number of bits/code. */
@@ -111,6 +111,7 @@ static int archive_compressor_compress_write(struct archive_write_filter *,
const void *, size_t);
static int archive_compressor_compress_close(struct archive_write_filter *);
static int archive_compressor_compress_free(struct archive_write_filter *);
+static void free_data(struct compress *);
#if ARCHIVE_VERSION_NUMBER < 4000000
int
@@ -125,17 +126,35 @@ archive_write_set_compression_compress(struct archive *a)
* Add a compress filter to this write handle.
*/
int
-archive_write_add_filter_compress(struct archive *_a)
+archive_write_add_filter_compress(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct archive_write_filter *f;
+ struct compress *compress;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");
- f->open = &archive_compressor_compress_open;
- f->code = ARCHIVE_FILTER_COMPRESS;
+
+ compress = calloc(1, sizeof(*compress));
+ if (compress == NULL)
+ goto memerr;
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
f->name = "compress";
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->data = compress;
+ f->open = archive_compressor_compress_open;
+ f->write = archive_compressor_compress_write;
+ f->close = archive_compressor_compress_close;
+ f->free = archive_compressor_compress_free;
+
return (ARCHIVE_OK);
+memerr:
+ free_data(compress);
+ archive_set_error(a, ENOMEM,
+ "Can't allocate data for compression");
+ return (ARCHIVE_FATAL);
}
/*
@@ -144,19 +163,9 @@ archive_write_add_filter_compress(struct archive *_a)
static int
archive_compressor_compress_open(struct archive_write_filter *f)
{
- struct private_data *state;
+ struct compress *compress = f->data;
size_t bs = 65536, bpb;
- f->code = ARCHIVE_FILTER_COMPRESS;
- f->name = "compress";
-
- state = calloc(1, sizeof(*state));
- if (state == NULL) {
- archive_set_error(f->archive, ENOMEM,
- "Can't allocate data for compression");
- return (ARCHIVE_FATAL);
- }
-
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of the bytes
* per block for performance. */
@@ -166,41 +175,38 @@ archive_compressor_compress_open(struct archive_write_filter *f)
else if (bpb != 0)
bs -= bs % bpb;
}
- state->compressed_buffer_size = bs;
- state->compressed = malloc(state->compressed_buffer_size);
+ compress->compressed_buffer_size = bs;
+ compress->compressed = malloc(compress->compressed_buffer_size);
- if (state->compressed == NULL) {
+ if (compress->compressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
- free(state);
return (ARCHIVE_FATAL);
}
- f->write = archive_compressor_compress_write;
- f->close = archive_compressor_compress_close;
- f->free = archive_compressor_compress_free;
-
- state->max_maxcode = 0x10000; /* Should NEVER generate this code. */
- state->in_count = 0; /* Length of input. */
- state->bit_buf = 0;
- state->bit_offset = 0;
- state->out_count = 3; /* Includes 3-byte header mojo. */
- state->compress_ratio = 0;
- state->checkpoint = CHECK_GAP;
- state->code_len = 9;
- state->cur_maxcode = MAXCODE(state->code_len);
- state->first_free = FIRST;
+ /* Should NEVER generate this code. */
+ compress->max_maxcode = 0x10000;
+ /* Length of input. */
+ compress->in_count = 0;
+ compress->bit_buf = 0;
+ compress->bit_offset = 0;
+ /* Includes 3-byte header mojo. */
+ compress->out_count = 3;
+ compress->compress_ratio = 0;
+ compress->checkpoint = CHECK_GAP;
+ compress->code_len = 9;
+ compress->cur_maxcode = MAXCODE(compress->code_len);
+ compress->first_free = FIRST;
- memset(state->hashtab, 0xff, sizeof(state->hashtab));
+ memset(compress->hashtab, 0xff, sizeof(compress->hashtab));
/* Prime output buffer with a gzip header. */
- state->compressed[0] = 0x1f; /* Compress */
- state->compressed[1] = 0x9d;
- state->compressed[2] = 0x90; /* Block mode, 16bit max */
- state->compressed_offset = 3;
+ compress->compressed[0] = 0x1f; /* Compress */
+ compress->compressed[1] = 0x9d;
+ compress->compressed[2] = 0x90; /* Block mode, 16bit max */
+ compress->compressed_offset = 3;
- f->data = state;
- return (0);
+ return (ARCHIVE_OK);
}
/*-
@@ -224,17 +230,17 @@ static const unsigned char rmask[9] =
static int
output_byte(struct archive_write_filter *f, unsigned char c)
{
- struct private_data *state = f->data;
+ struct compress *compress = f->data;
- state->compressed[state->compressed_offset++] = c;
- ++state->out_count;
+ compress->compressed[compress->compressed_offset++] = c;
+ ++compress->out_count;
- if (state->compressed_buffer_size == state->compressed_offset) {
+ if (compress->compressed_buffer_size == compress->compressed_offset) {
int ret = __archive_write_filter(f->next_filter,
- state->compressed, state->compressed_buffer_size);
+ compress->compressed, compress->compressed_buffer_size);
if (ret != ARCHIVE_OK)
return ARCHIVE_FATAL;
- state->compressed_offset = 0;
+ compress->compressed_offset = 0;
}
return ARCHIVE_OK;
@@ -243,7 +249,7 @@ output_byte(struct archive_write_filter *f, unsigned char c)
static int
output_code(struct archive_write_filter *f, int ocode)
{
- struct private_data *state = f->data;
+ struct compress *compress = f->data;
int bits, ret, clear_flg, bit_offset;
clear_flg = ocode == CLEAR;
@@ -252,11 +258,11 @@ output_code(struct archive_write_filter *f, int ocode)
* Since ocode is always >= 8 bits, only need to mask the first
* hunk on the left.
*/
- bit_offset = state->bit_offset % 8;
- state->bit_buf |= (ocode << bit_offset) & 0xff;
- output_byte(f, state->bit_buf);
+ bit_offset = compress->bit_offset % 8;
+ compress->bit_buf |= (ocode << bit_offset) & 0xff;
+ output_byte(f, compress->bit_buf);
- bits = state->code_len - (8 - bit_offset);
+ bits = compress->code_len - (8 - bit_offset);
ocode >>= 8 - bit_offset;
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
if (bits >= 8) {
@@ -265,41 +271,41 @@ output_code(struct archive_write_filter *f, int ocode)
bits -= 8;
}
/* Last bits. */
- state->bit_offset += state->code_len;
- state->bit_buf = ocode & rmask[bits];
- if (state->bit_offset == state->code_len * 8)
- state->bit_offset = 0;
+ compress->bit_offset += compress->code_len;
+ compress->bit_buf = ocode & rmask[bits];
+ if (compress->bit_offset == compress->code_len * 8)
+ compress->bit_offset = 0;
/*
* If the next entry is going to be too big for the ocode size,
* then increase it, if possible.
*/
- if (clear_flg || state->first_free > state->cur_maxcode) {
+ if (clear_flg || compress->first_free > compress->cur_maxcode) {
/*
* Write the whole buffer, because the input side won't
* discover the size increase until after it has read it.
*/
- if (state->bit_offset > 0) {
- while (state->bit_offset < state->code_len * 8) {
- ret = output_byte(f, state->bit_buf);
+ if (compress->bit_offset > 0) {
+ while (compress->bit_offset < compress->code_len * 8) {
+ ret = output_byte(f, compress->bit_buf);
if (ret != ARCHIVE_OK)
return ret;
- state->bit_offset += 8;
- state->bit_buf = 0;
+ compress->bit_offset += 8;
+ compress->bit_buf = 0;
}
}
- state->bit_buf = 0;
- state->bit_offset = 0;
+ compress->bit_buf = 0;
+ compress->bit_offset = 0;
if (clear_flg) {
- state->code_len = 9;
- state->cur_maxcode = MAXCODE(state->code_len);
+ compress->code_len = 9;
+ compress->cur_maxcode = MAXCODE(compress->code_len);
} else {
- state->code_len++;
- if (state->code_len == 16)
- state->cur_maxcode = state->max_maxcode;
+ compress->code_len++;
+ if (compress->code_len == 16)
+ compress->cur_maxcode = compress->max_maxcode;
else
- state->cur_maxcode = MAXCODE(state->code_len);
+ compress->cur_maxcode = MAXCODE(compress->code_len);
}
}
@@ -309,13 +315,13 @@ output_code(struct archive_write_filter *f, int ocode)
static int
output_flush(struct archive_write_filter *f)
{
- struct private_data *state = f->data;
+ struct compress *compress = f->data;
int ret;
/* At EOF, write the rest of the buffer. */
- if (state->bit_offset % 8) {
- state->code_len = (state->bit_offset % 8 + 7) / 8;
- ret = output_byte(f, state->bit_buf);
+ if (compress->bit_offset % 8) {
+ compress->code_len = (compress->bit_offset % 8 + 7) / 8;
+ ret = output_byte(f, compress->bit_buf);
if (ret != ARCHIVE_OK)
return ret;
}
@@ -330,7 +336,7 @@ static int
archive_compressor_compress_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct private_data *state = (struct private_data *)f->data;
+ struct compress *compress = f->data;
int i;
int ratio;
int c, disp, ret;
@@ -341,23 +347,24 @@ archive_compressor_compress_write(struct archive_write_filter *f,
bp = buff;
- if (state->in_count == 0) {
- state->cur_code = *bp++;
- ++state->in_count;
+ if (compress->in_count == 0) {
+ compress->cur_code = *bp++;
+ ++compress->in_count;
--length;
}
while (length--) {
c = *bp++;
- state->in_count++;
- state->cur_fcode = (c << 16) | state->cur_code;
- i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */
+ compress->in_count++;
+ compress->cur_fcode = (c << 16) | compress->cur_code;
+ /* Xor hashing. */
+ i = ((c << HSHIFT) ^ compress->cur_code);
- if (state->hashtab[i] == state->cur_fcode) {
- state->cur_code = state->codetab[i];
+ if (compress->hashtab[i] == compress->cur_fcode) {
+ compress->cur_code = compress->codetab[i];
continue;
}
- if (state->hashtab[i] < 0) /* Empty slot. */
+ if (compress->hashtab[i] < 0) /* Empty slot. */
goto nomatch;
/* Secondary hash (after G. Knott). */
if (i == 0)
@@ -368,40 +375,40 @@ archive_compressor_compress_write(struct archive_write_filter *f,
if ((i -= disp) < 0)
i += HSIZE;
- if (state->hashtab[i] == state->cur_fcode) {
- state->cur_code = state->codetab[i];
+ if (compress->hashtab[i] == compress->cur_fcode) {
+ compress->cur_code = compress->codetab[i];
continue;
}
- if (state->hashtab[i] >= 0)
+ if (compress->hashtab[i] >= 0)
goto probe;
nomatch:
- ret = output_code(f, state->cur_code);
+ ret = output_code(f, compress->cur_code);
if (ret != ARCHIVE_OK)
return ret;
- state->cur_code = c;
- if (state->first_free < state->max_maxcode) {
- state->codetab[i] = state->first_free++; /* code -> hashtable */
- state->hashtab[i] = state->cur_fcode;
+ compress->cur_code = c;
+ if (compress->first_free < compress->max_maxcode) {
+ compress->codetab[i] = compress->first_free++; /* code -> hashtable */
+ compress->hashtab[i] = compress->cur_fcode;
continue;
}
- if (state->in_count < state->checkpoint)
+ if (compress->in_count < compress->checkpoint)
continue;
- state->checkpoint = state->in_count + CHECK_GAP;
+ compress->checkpoint = compress->in_count + CHECK_GAP;
- if (state->in_count <= 0x007fffff && state->out_count != 0)
- ratio = (int)(state->in_count * 256 / state->out_count);
- else if ((ratio = (int)(state->out_count / 256)) == 0)
+ if (compress->in_count <= 0x007fffff && compress->out_count != 0)
+ ratio = (int)(compress->in_count * 256 / compress->out_count);
+ else if ((ratio = (int)(compress->out_count / 256)) == 0)
ratio = 0x7fffffff;
else
- ratio = (int)(state->in_count / ratio);
+ ratio = (int)(compress->in_count / ratio);
- if (ratio > state->compress_ratio)
- state->compress_ratio = ratio;
+ if (ratio > compress->compress_ratio)
+ compress->compress_ratio = ratio;
else {
- state->compress_ratio = 0;
- memset(state->hashtab, 0xff, sizeof(state->hashtab));
- state->first_free = FIRST;
+ compress->compress_ratio = 0;
+ memset(compress->hashtab, 0xff, sizeof(compress->hashtab));
+ compress->first_free = FIRST;
ret = output_code(f, CLEAR);
if (ret != ARCHIVE_OK)
return ret;
@@ -418,10 +425,10 @@ archive_compressor_compress_write(struct archive_write_filter *f,
static int
archive_compressor_compress_close(struct archive_write_filter *f)
{
- struct private_data *state = (struct private_data *)f->data;
+ struct compress *compress = f->data;
int ret;
- ret = output_code(f, state->cur_code);
+ ret = output_code(f, compress->cur_code);
if (ret != ARCHIVE_OK)
return ret;
ret = output_flush(f);
@@ -430,16 +437,23 @@ archive_compressor_compress_close(struct archive_write_filter *f)
/* Write the last block */
ret = __archive_write_filter(f->next_filter,
- state->compressed, state->compressed_offset);
+ compress->compressed, compress->compressed_offset);
return (ret);
}
static int
archive_compressor_compress_free(struct archive_write_filter *f)
{
- struct private_data *state = (struct private_data *)f->data;
-
- free(state->compressed);
- free(state);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
+
+static void
+free_data(struct compress *compress)
+{
+ if (compress != NULL) {
+ free(compress->compressed);
+ free(compress);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_grzip.c b/libarchive/archive_write_add_filter_grzip.c
index f8bb886061e5..be686c6176ea 100644
--- a/libarchive/archive_write_add_filter_grzip.c
+++ b/libarchive/archive_write_add_filter_grzip.c
@@ -35,7 +35,7 @@
#include "archive.h"
#include "archive_write_private.h"
-struct write_grzip {
+struct grzip {
struct archive_write_program_data *pdata;
};
@@ -46,42 +46,45 @@ static int archive_write_grzip_write(struct archive_write_filter *,
const void *, size_t);
static int archive_write_grzip_close(struct archive_write_filter *);
static int archive_write_grzip_free(struct archive_write_filter *);
+static void free_data(struct grzip *);
int
-archive_write_add_filter_grzip(struct archive *_a)
+archive_write_add_filter_grzip(struct archive *a)
{
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct write_grzip *data;
+ struct archive_write_filter *f;
+ struct grzip *grzip;
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
- data->pdata = __archive_write_program_allocate("grzip");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
+ grzip = calloc(1, sizeof(*grzip));
+ if (grzip == NULL)
+ goto memerr;
+ grzip->pdata = __archive_write_program_allocate("grzip");
+ if (grzip->pdata == NULL)
+ goto memerr;
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
f->name = "grzip";
f->code = ARCHIVE_FILTER_GRZIP;
- f->data = data;
- f->open = archive_write_grzip_open;
+ f->data = grzip;
f->options = archive_write_grzip_options;
+ f->open = archive_write_grzip_open;
f->write = archive_write_grzip_write;
f->close = archive_write_grzip_close;
f->free = archive_write_grzip_free;
/* Note: This filter always uses an external program, so we
* return "warn" to inform of the fact. */
- archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external grzip program for grzip compression");
return (ARCHIVE_WARN);
+memerr:
+ free_data(grzip);
+ archive_set_error(a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
}
static int
@@ -100,34 +103,41 @@ archive_write_grzip_options(struct archive_write_filter *f, const char *key,
static int
archive_write_grzip_open(struct archive_write_filter *f)
{
- struct write_grzip *data = (struct write_grzip *)f->data;
+ struct grzip *grzip = f->data;
- return __archive_write_program_open(f, data->pdata, "grzip");
+ return __archive_write_program_open(f, grzip->pdata, "grzip");
}
static int
archive_write_grzip_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct write_grzip *data = (struct write_grzip *)f->data;
+ struct grzip *grzip = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, grzip->pdata, buff, length);
}
static int
archive_write_grzip_close(struct archive_write_filter *f)
{
- struct write_grzip *data = (struct write_grzip *)f->data;
+ struct grzip *grzip = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, grzip->pdata);
}
static int
archive_write_grzip_free(struct archive_write_filter *f)
{
- struct write_grzip *data = (struct write_grzip *)f->data;
-
- __archive_write_program_free(data->pdata);
- free(data);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
+
+static void
+free_data(struct grzip *grzip)
+{
+ if (grzip != NULL) {
+ __archive_write_program_free(grzip->pdata);
+ free(grzip);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c
index 7112ef372862..a7679a728f4a 100644
--- a/libarchive/archive_write_add_filter_gzip.c
+++ b/libarchive/archive_write_add_filter_gzip.c
@@ -56,7 +56,7 @@ archive_write_set_compression_gzip(struct archive *a)
/* Don't compile this if we don't have zlib. */
-struct private_data {
+struct gzip {
int compression_level;
int timestamp;
char *original_filename;
@@ -87,65 +87,66 @@ static int archive_compressor_gzip_close(struct archive_write_filter *);
static int archive_compressor_gzip_free(struct archive_write_filter *);
#ifdef HAVE_ZLIB_H
static int drive_compressor(struct archive_write_filter *,
- struct private_data *, int finishing);
+ struct gzip *, int finishing);
#endif
+static void free_data(struct gzip *);
/*
* Add a gzip compression filter to this write handle.
*/
int
-archive_write_add_filter_gzip(struct archive *_a)
+archive_write_add_filter_gzip(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_data *data;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
- ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
+ struct archive_write_filter *f;
+ struct gzip *gzip;
+ int r;
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- f->data = data;
- f->open = &archive_compressor_gzip_open;
- f->options = &archive_compressor_gzip_options;
- f->close = &archive_compressor_gzip_close;
- f->free = &archive_compressor_gzip_free;
- f->code = ARCHIVE_FILTER_GZIP;
- f->name = "gzip";
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
- data->original_filename = NULL;
+ gzip = calloc(1, sizeof(*gzip));
+ if (gzip == NULL)
+ goto memerr;
+ gzip->original_filename = NULL;
#ifdef HAVE_ZLIB_H
- data->compression_level = Z_DEFAULT_COMPRESSION;
- return (ARCHIVE_OK);
+ gzip->compression_level = Z_DEFAULT_COMPRESSION;
+
+ r = ARCHIVE_OK;
#else
- data->pdata = __archive_write_program_allocate("gzip");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- data->compression_level = 0;
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ gzip->pdata = __archive_write_program_allocate("gzip");
+ if (gzip->pdata == NULL)
+ goto memerr;
+ gzip->compression_level = 0;
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external gzip program");
- return (ARCHIVE_WARN);
+ r = ARCHIVE_WARN;
#endif
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = "gzip";
+ f->code = ARCHIVE_FILTER_GZIP;
+ f->data = gzip;
+ f->options = archive_compressor_gzip_options;
+ f->open = archive_compressor_gzip_open;
+ f->write = archive_compressor_gzip_write;
+ f->close = archive_compressor_gzip_close;
+ f->free = archive_compressor_gzip_free;
+
+ return (r);
+memerr:
+ free_data(gzip);
+ archive_set_error(a, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
}
static int
archive_compressor_gzip_free(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
-
-#ifdef HAVE_ZLIB_H
- free(data->compressed);
-#else
- __archive_write_program_free(data->pdata);
-#endif
- free((void*)data->original_filename);
- free(data);
+ free_data(f->data);
f->data = NULL;
return (ARCHIVE_OK);
}
@@ -157,7 +158,7 @@ static int
archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -166,19 +167,19 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
"compression-level invalid");
return (ARCHIVE_FAILED);
}
- data->compression_level = value[0] - '0';
+ gzip->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
if (strcmp(key, "timestamp") == 0) {
- data->timestamp = (value == NULL)?-1:1;
+ gzip->timestamp = (value == NULL)?-1:1;
return (ARCHIVE_OK);
}
if (strcmp(key, "original-filename") == 0) {
- free((void*)data->original_filename);
- data->original_filename = NULL;
+ free((void*)gzip->original_filename);
+ gzip->original_filename = NULL;
if (value) {
- data->original_filename = strdup(value);
- if (data->original_filename == NULL)
+ gzip->original_filename = strdup(value);
+ if (gzip->original_filename == NULL)
return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
@@ -197,11 +198,11 @@ archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
static int
archive_compressor_gzip_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
int ret = ARCHIVE_OK;
int init_success;
- if (data->compressed == NULL) {
+ if (gzip->compressed == NULL) {
size_t bs = 65536, bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of
@@ -212,60 +213,60 @@ archive_compressor_gzip_open(struct archive_write_filter *f)
else if (bpb != 0)
bs -= bs % bpb;
}
- data->compressed_buffer_size = bs;
- data->compressed = malloc(data->compressed_buffer_size);
- if (data->compressed == NULL) {
+ gzip->compressed_buffer_size = bs;
+ gzip->compressed = malloc(gzip->compressed_buffer_size);
+ if (gzip->compressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- data->crc = crc32(0L, NULL, 0);
- data->stream.next_out = data->compressed;
- data->stream.avail_out = (uInt)data->compressed_buffer_size;
+ gzip->crc = crc32(0L, NULL, 0);
+ gzip->stream.next_out = gzip->compressed;
+ gzip->stream.avail_out = (uInt)gzip->compressed_buffer_size;
/* Prime output buffer with a gzip header. */
- data->compressed[0] = 0x1f; /* GZip signature bytes */
- data->compressed[1] = 0x8b;
- data->compressed[2] = 0x08; /* "Deflate" compression */
- data->compressed[3] = 0x00; /* Flags */
- if (data->timestamp >= 0) {
+ gzip->compressed[0] = 0x1f; /* GZip signature bytes */
+ gzip->compressed[1] = 0x8b;
+ gzip->compressed[2] = 0x08; /* "Deflate" compression */
+ gzip->compressed[3] = 0x00; /* Flags */
+ if (gzip->timestamp >= 0) {
uint32_t t = (uint32_t)time(NULL);
- archive_le32enc(data->compressed + 4, t); /* Timestamp */
+ archive_le32enc(gzip->compressed + 4, t); /* Timestamp */
} else {
- memset(&data->compressed[4], 0, 4);
+ memset(&gzip->compressed[4], 0, 4);
}
- if (data->compression_level == 9) {
- data->compressed[8] = 2;
- } else if(data->compression_level == 1) {
- data->compressed[8] = 4;
+ if (gzip->compression_level == 9) {
+ gzip->compressed[8] = 2;
+ } else if(gzip->compression_level == 1) {
+ gzip->compressed[8] = 4;
} else {
- data->compressed[8] = 0;
+ gzip->compressed[8] = 0;
}
- data->compressed[9] = 3; /* OS=Unix */
- data->stream.next_out += 10;
- data->stream.avail_out -= 10;
+ gzip->compressed[9] = 3; /* OS=Unix */
+ gzip->stream.next_out += 10;
+ gzip->stream.avail_out -= 10;
- if (data->original_filename != NULL) {
+ if (gzip->original_filename != NULL) {
/* Limit "original filename" to 32k or the
* remaining space in the buffer, whichever is smaller.
*/
- size_t ofn_length = strlen(data->original_filename);
+ size_t ofn_length = strlen(gzip->original_filename);
size_t ofn_max_length = 32768;
- size_t ofn_space_available = data->compressed
- + data->compressed_buffer_size
- - data->stream.next_out
+ size_t ofn_space_available = gzip->compressed
+ + gzip->compressed_buffer_size
+ - gzip->stream.next_out
- 1;
if (ofn_max_length > ofn_space_available) {
ofn_max_length = ofn_space_available;
}
if (ofn_length < ofn_max_length) {
- data->compressed[3] |= 0x8;
- strcpy((char*)data->compressed + 10,
- data->original_filename);
- data->stream.next_out += ofn_length + 1;
- data->stream.avail_out -= ofn_length + 1;
+ gzip->compressed[3] |= 0x8;
+ strcpy((char*)gzip->compressed + 10,
+ gzip->original_filename);
+ gzip->stream.next_out += ofn_length + 1;
+ gzip->stream.avail_out -= ofn_length + 1;
} else {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"Gzip 'Original Filename' ignored because it is too long");
@@ -273,18 +274,15 @@ archive_compressor_gzip_open(struct archive_write_filter *f)
}
}
- f->write = archive_compressor_gzip_write;
-
/* Initialize compression library. */
- init_success = deflateInit2(&(data->stream),
- data->compression_level,
+ init_success = deflateInit2(&(gzip->stream),
+ gzip->compression_level,
Z_DEFLATED,
-15 /* < 0 to suppress zlib header */,
8,
Z_DEFAULT_STRATEGY);
if (init_success == Z_OK) {
- f->data = data;
return (ret);
}
@@ -320,17 +318,17 @@ static int
archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
int ret;
/* Update statistics */
- data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);
- data->total_in += length;
+ gzip->crc = crc32(gzip->crc, (const Bytef *)buff, (uInt)length);
+ gzip->total_in += length;
/* Compress input data to output buffer */
- SET_NEXT_IN(data, buff);
- data->stream.avail_in = (uInt)length;
- if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ SET_NEXT_IN(gzip, buff);
+ gzip->stream.avail_in = (uInt)length;
+ if ((ret = drive_compressor(f, gzip, 0)) != ARCHIVE_OK)
return (ret);
return (ARCHIVE_OK);
@@ -342,26 +340,26 @@ archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
static int
archive_compressor_gzip_close(struct archive_write_filter *f)
{
+ struct gzip *gzip = f->data;
unsigned char trailer[8];
- struct private_data *data = (struct private_data *)f->data;
int ret;
/* Finish compression cycle */
- ret = drive_compressor(f, data, 1);
+ ret = drive_compressor(f, gzip, 1);
if (ret == ARCHIVE_OK) {
/* Write the last compressed data. */
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size - data->stream.avail_out);
+ gzip->compressed,
+ gzip->compressed_buffer_size - gzip->stream.avail_out);
}
if (ret == ARCHIVE_OK) {
/* Build and write out 8-byte trailer. */
- archive_le32enc(trailer, data->crc);
- archive_le32enc(trailer + 4, data->total_in);
+ archive_le32enc(trailer, gzip->crc);
+ archive_le32enc(trailer + 4, gzip->total_in);
ret = __archive_write_filter(f->next_filter, trailer, 8);
}
- switch (deflateEnd(&(data->stream))) {
+ switch (deflateEnd(&(gzip->stream))) {
case Z_OK:
break;
default:
@@ -381,34 +379,34 @@ archive_compressor_gzip_close(struct archive_write_filter *f)
*/
static int
drive_compressor(struct archive_write_filter *f,
- struct private_data *data, int finishing)
+ struct gzip *gzip, int finishing)
{
int ret;
for (;;) {
- if (data->stream.avail_out == 0) {
+ if (gzip->stream.avail_out == 0) {
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size);
+ gzip->compressed,
+ gzip->compressed_buffer_size);
if (ret != ARCHIVE_OK)
return (ARCHIVE_FATAL);
- data->stream.next_out = data->compressed;
- data->stream.avail_out =
- (uInt)data->compressed_buffer_size;
+ gzip->stream.next_out = gzip->compressed;
+ gzip->stream.avail_out =
+ (uInt)gzip->compressed_buffer_size;
}
/* If there's nothing to do, we're done. */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && gzip->stream.avail_in == 0)
return (ARCHIVE_OK);
- ret = deflate(&(data->stream),
+ ret = deflate(&(gzip->stream),
finishing ? Z_FINISH : Z_NO_FLUSH );
switch (ret) {
case Z_OK:
/* In non-finishing case, check if compressor
* consumed everything */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && gzip->stream.avail_in == 0)
return (ARCHIVE_OK);
/* In finishing case, this return always means
* there's more work */
@@ -427,12 +425,22 @@ drive_compressor(struct archive_write_filter *f,
}
}
+static void
+free_data(struct gzip *gzip)
+{
+ if (gzip != NULL) {
+ free(gzip->compressed);
+ free(gzip->original_filename);
+ free(gzip);
+ }
+}
+
#else /* HAVE_ZLIB_H */
static int
archive_compressor_gzip_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
struct archive_string as;
int r;
@@ -440,19 +448,18 @@ archive_compressor_gzip_open(struct archive_write_filter *f)
archive_strcpy(&as, "gzip");
/* Specify compression level. */
- if (data->compression_level > 0) {
+ if (gzip->compression_level > 0) {
archive_strcat(&as, " -");
- archive_strappend_char(&as, '0' + data->compression_level);
+ archive_strappend_char(&as, '0' + gzip->compression_level);
}
- if (data->timestamp < 0)
+ if (gzip->timestamp < 0)
/* Do not save timestamp. */
archive_strcat(&as, " -n");
- else if (data->timestamp > 0)
+ else if (gzip->timestamp > 0)
/* Save timestamp. */
archive_strcat(&as, " -N");
- f->write = archive_compressor_gzip_write;
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, gzip->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -461,17 +468,26 @@ static int
archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, gzip->pdata, buff, length);
}
static int
archive_compressor_gzip_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct gzip *gzip = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, gzip->pdata);
}
+static void
+free_data(struct gzip *gzip)
+{
+ if (gzip != NULL) {
+ __archive_write_program_free(gzip->pdata);
+ free(gzip->original_filename);
+ free(gzip);
+ }
+}
#endif /* HAVE_ZLIB_H */
diff --git a/libarchive/archive_write_add_filter_lrzip.c b/libarchive/archive_write_add_filter_lrzip.c
index cda34dc738d3..3bd5bc64257b 100644
--- a/libarchive/archive_write_add_filter_lrzip.c
+++ b/libarchive/archive_write_add_filter_lrzip.c
@@ -39,7 +39,7 @@
#include "archive_string.h"
#include "archive_write_private.h"
-struct write_lrzip {
+struct lrzip {
struct archive_write_program_data *pdata;
int compression_level;
enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;
@@ -52,49 +52,52 @@ static int archive_write_lrzip_write(struct archive_write_filter *,
const void *, size_t);
static int archive_write_lrzip_close(struct archive_write_filter *);
static int archive_write_lrzip_free(struct archive_write_filter *);
+static void free_data(struct lrzip *);
int
-archive_write_add_filter_lrzip(struct archive *_a)
+archive_write_add_filter_lrzip(struct archive *a)
{
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct write_lrzip *data;
+ struct archive_write_filter *f;
+ struct lrzip *lrzip;
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
- data->pdata = __archive_write_program_allocate("lrzip");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
+ lrzip = calloc(1, sizeof(*lrzip));
+ if (lrzip == NULL)
+ goto memerr;
+ lrzip->pdata = __archive_write_program_allocate("lrzip");
+ if (lrzip->pdata == NULL)
+ goto memerr;
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
f->name = "lrzip";
f->code = ARCHIVE_FILTER_LRZIP;
- f->data = data;
- f->open = archive_write_lrzip_open;
+ f->data = lrzip;
f->options = archive_write_lrzip_options;
+ f->open = archive_write_lrzip_open;
f->write = archive_write_lrzip_write;
f->close = archive_write_lrzip_close;
f->free = archive_write_lrzip_free;
/* Note: This filter always uses an external program, so we
* return "warn" to inform of the fact. */
- archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external lrzip program for lrzip compression");
return (ARCHIVE_WARN);
+memerr:
+ free_data(lrzip);
+ archive_set_error(a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
}
static int
archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct lrzip *lrzip = f->data;
if (strcmp(key, "compression") == 0) {
if (value == NULL) {
@@ -103,15 +106,15 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
return (ARCHIVE_FAILED);
}
else if (strcmp(value, "bzip2") == 0)
- data->compression = bzip2;
+ lrzip->compression = bzip2;
else if (strcmp(value, "gzip") == 0)
- data->compression = gzip;
+ lrzip->compression = gzip;
else if (strcmp(value, "lzo") == 0)
- data->compression = lzo;
+ lrzip->compression = lzo;
else if (strcmp(value, "none") == 0)
- data->compression = none;
+ lrzip->compression = none;
else if (strcmp(value, "zpaq") == 0)
- data->compression = zpaq;
+ lrzip->compression = zpaq;
else {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"compression invalid");
@@ -125,7 +128,7 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
"compression-level invalid");
return (ARCHIVE_FAILED);
}
- data->compression_level = value[0] - '0';
+ lrzip->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
@@ -137,7 +140,7 @@ archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
static int
archive_write_lrzip_open(struct archive_write_filter *f)
{
- struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct lrzip *lrzip = f->data;
struct archive_string as;
int r;
@@ -145,7 +148,7 @@ archive_write_lrzip_open(struct archive_write_filter *f)
archive_strcpy(&as, "lrzip -q");
/* Specify compression type. */
- switch (data->compression) {
+ switch (lrzip->compression) {
case lzma:/* default compression */
break;
case bzip2:
@@ -166,12 +169,12 @@ archive_write_lrzip_open(struct archive_write_filter *f)
}
/* Specify compression level. */
- if (data->compression_level > 0) {
+ if (lrzip->compression_level > 0) {
archive_strcat(&as, " -L ");
- archive_strappend_char(&as, '0' + data->compression_level);
+ archive_strappend_char(&as, '0' + lrzip->compression_level);
}
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, lrzip->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -180,25 +183,32 @@ static int
archive_write_lrzip_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct lrzip *lrzip = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, lrzip->pdata, buff, length);
}
static int
archive_write_lrzip_close(struct archive_write_filter *f)
{
- struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct lrzip *lrzip = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, lrzip->pdata);
}
static int
archive_write_lrzip_free(struct archive_write_filter *f)
{
- struct write_lrzip *data = (struct write_lrzip *)f->data;
-
- __archive_write_program_free(data->pdata);
- free(data);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
+
+static void
+free_data(struct lrzip *lrzip)
+{
+ if (lrzip != NULL) {
+ __archive_write_program_free(lrzip->pdata);
+ free(lrzip);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c
index de5283f15658..8b3b575fae4d 100644
--- a/libarchive/archive_write_add_filter_lz4.c
+++ b/libarchive/archive_write_add_filter_lz4.c
@@ -50,7 +50,7 @@
#define LZ4_MAGICNUMBER 0x184d2204
-struct private_data {
+struct lz4 {
int compression_level;
unsigned header_written:1;
unsigned version_number:1;
@@ -85,66 +85,81 @@ static int archive_filter_lz4_options(struct archive_write_filter *,
const char *, const char *);
static int archive_filter_lz4_write(struct archive_write_filter *,
const void *, size_t);
+static void free_data(struct lz4 *);
/*
* Add an lz4 compression filter to this write handle.
*/
int
-archive_write_add_filter_lz4(struct archive *_a)
+archive_write_add_filter_lz4(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_data *data;
+ struct archive_write_filter *f;
+ struct lz4 *lz4;
+ int r;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
-
+ lz4 = calloc(1, sizeof(*lz4));
+ if (lz4 == NULL)
+ goto memerr;
/*
* Setup default settings.
*/
- data->compression_level = 1;
- data->version_number = 0x01;
- data->block_independence = 1;
- data->block_checksum = 0;
- data->stream_size = 0;
- data->stream_checksum = 1;
- data->preset_dictionary = 0;
- data->block_maximum_size = 7;
-
- /*
- * Setup a filter setting.
- */
- f->data = data;
- f->options = &archive_filter_lz4_options;
- f->close = &archive_filter_lz4_close;
- f->free = &archive_filter_lz4_free;
- f->open = &archive_filter_lz4_open;
- f->code = ARCHIVE_FILTER_LZ4;
- f->name = "lz4";
+ lz4->version_number = 0x01;
+ lz4->block_independence = 1;
+ lz4->block_checksum = 0;
+ lz4->stream_size = 0;
+ lz4->stream_checksum = 1;
+ lz4->preset_dictionary = 0;
+ lz4->block_maximum_size = 7;
#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
- return (ARCHIVE_OK);
+ lz4->compression_level = 1;
+
+ r = ARCHIVE_OK;
#else
/*
* We don't have lz4 library, and execute external lz4 program
* instead.
*/
- data->pdata = __archive_write_program_allocate("lz4");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- data->compression_level = 0;
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ lz4->pdata = __archive_write_program_allocate("lz4");
+ if (lz4->pdata == NULL)
+ goto memerr;
+ lz4->compression_level = 0;
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external lz4 program");
- return (ARCHIVE_WARN);
+ r = ARCHIVE_WARN;
#endif
+
+ /*
+ * Setup a filter setting.
+ */
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = "lz4";
+ f->code = ARCHIVE_FILTER_LZ4;
+ f->data = lz4;
+ f->options = archive_filter_lz4_options;
+ f->open = archive_filter_lz4_open;
+ f->write = archive_filter_lz4_write;
+ f->close = archive_filter_lz4_close;
+ f->free = archive_filter_lz4_free;
+
+ return (r);
+memerr:
+ free_data(lz4);
+ archive_set_error(a, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+ free_data(f->data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
}
/*
@@ -154,7 +169,7 @@ static int
archive_filter_lz4_options(struct archive_write_filter *f,
const char *key, const char *value)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
if (strcmp(key, "compression-level") == 0) {
int val;
@@ -173,15 +188,15 @@ archive_filter_lz4_options(struct archive_write_filter *f,
return (ARCHIVE_FATAL);
}
#endif
- data->compression_level = val;
+ lz4->compression_level = val;
return (ARCHIVE_OK);
}
if (strcmp(key, "stream-checksum") == 0) {
- data->stream_checksum = value != NULL;
+ lz4->stream_checksum = value != NULL;
return (ARCHIVE_OK);
}
if (strcmp(key, "block-checksum") == 0) {
- data->block_checksum = value != NULL;
+ lz4->block_checksum = value != NULL;
return (ARCHIVE_OK);
}
if (strcmp(key, "block-size") == 0) {
@@ -191,11 +206,11 @@ archive_filter_lz4_options(struct archive_write_filter *f,
"block-size invalid");
return (ARCHIVE_FAILED);
}
- data->block_maximum_size = value[0] - '0';
+ lz4->block_maximum_size = value[0] - '0';
return (ARCHIVE_OK);
}
if (strcmp(key, "block-dependence") == 0) {
- data->block_independence = value == NULL;
+ lz4->block_independence = value == NULL;
return (ARCHIVE_OK);
}
@@ -225,21 +240,21 @@ static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
static int
archive_filter_lz4_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
size_t required_size;
static const size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
4 * 1024 * 1024 };
size_t pre_block_size;
- if (data->block_maximum_size < 4)
- data->block_size = bkmap[0];
+ if (lz4->block_maximum_size < 4)
+ lz4->block_size = bkmap[0];
else
- data->block_size = bkmap[data->block_maximum_size - 4];
+ lz4->block_size = bkmap[lz4->block_maximum_size - 4];
- required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
- if (data->out_buffer_size < required_size) {
+ required_size = 4 + 15 + 4 + lz4->block_size + 4 + 4;
+ if (lz4->out_buffer_size < required_size) {
size_t bs = required_size, bpb;
- free(data->out_buffer);
+ free(lz4->out_buffer);
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of
* the bytes per block for performance. */
@@ -251,34 +266,32 @@ archive_filter_lz4_open(struct archive_write_filter *f)
bs -= bs % bpb;
}
}
- data->out_block_size = bs;
+ lz4->out_block_size = bs;
bs += required_size;
- data->out_buffer = malloc(bs);
- data->out = data->out_buffer;
- data->out_buffer_size = bs;
+ lz4->out_buffer = malloc(bs);
+ lz4->out = lz4->out_buffer;
+ lz4->out_buffer_size = bs;
}
- pre_block_size = (data->block_independence)? 0: 64 * 1024;
- if (data->in_buffer_size < data->block_size + pre_block_size) {
- free(data->in_buffer_allocated);
- data->in_buffer_size = data->block_size;
- data->in_buffer_allocated =
- malloc(data->in_buffer_size + pre_block_size);
- data->in_buffer = data->in_buffer_allocated + pre_block_size;
- if (!data->block_independence && data->compression_level >= 3)
- data->in_buffer = data->in_buffer_allocated;
- data->in = data->in_buffer;
- data->in_buffer_size = data->block_size;
+ pre_block_size = (lz4->block_independence)? 0: 64 * 1024;
+ if (lz4->in_buffer_size < lz4->block_size + pre_block_size) {
+ free(lz4->in_buffer_allocated);
+ lz4->in_buffer_size = lz4->block_size;
+ lz4->in_buffer_allocated =
+ malloc(lz4->in_buffer_size + pre_block_size);
+ lz4->in_buffer = lz4->in_buffer_allocated + pre_block_size;
+ if (!lz4->block_independence && lz4->compression_level >= 3)
+ lz4->in_buffer = lz4->in_buffer_allocated;
+ lz4->in = lz4->in_buffer;
+ lz4->in_buffer_size = lz4->block_size;
}
- if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
+ if (lz4->out_buffer == NULL || lz4->in_buffer_allocated == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
- f->write = archive_filter_lz4_write;
-
return (ARCHIVE_OK);
}
@@ -291,18 +304,18 @@ static int
archive_filter_lz4_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
int ret = ARCHIVE_OK;
const char *p;
size_t remaining;
ssize_t size;
/* If we haven't written a stream descriptor, we have to do it first. */
- if (!data->header_written) {
+ if (!lz4->header_written) {
ret = lz4_write_stream_descriptor(f);
if (ret != ARCHIVE_OK)
return (ret);
- data->header_written = 1;
+ lz4->header_written = 1;
}
p = (const char *)buff;
@@ -313,14 +326,14 @@ archive_filter_lz4_write(struct archive_write_filter *f,
size = lz4_write_one_block(f, p, remaining);
if (size < ARCHIVE_OK)
return (ARCHIVE_FATAL);
- l = data->out - data->out_buffer;
- if (l >= data->out_block_size) {
+ l = lz4->out - lz4->out_buffer;
+ if (l >= lz4->out_block_size) {
ret = __archive_write_filter(f->next_filter,
- data->out_buffer, data->out_block_size);
- l -= data->out_block_size;
- memcpy(data->out_buffer,
- data->out_buffer + data->out_block_size, l);
- data->out = data->out_buffer + l;
+ lz4->out_buffer, lz4->out_block_size);
+ l -= lz4->out_block_size;
+ memcpy(lz4->out_buffer,
+ lz4->out_buffer + lz4->out_block_size, l);
+ lz4->out = lz4->out_buffer + l;
if (ret < ARCHIVE_WARN)
break;
}
@@ -337,7 +350,7 @@ archive_filter_lz4_write(struct archive_write_filter *f,
static int
archive_filter_lz4_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
int ret;
/* Finish compression cycle. */
@@ -348,77 +361,48 @@ archive_filter_lz4_close(struct archive_write_filter *f)
*/
/* Write End Of Stream. */
- memset(data->out, 0, 4); data->out += 4;
+ memset(lz4->out, 0, 4); lz4->out += 4;
/* Write Stream checksum if needed. */
- if (data->stream_checksum) {
+ if (lz4->stream_checksum) {
unsigned int checksum;
checksum = __archive_xxhash.XXH32_digest(
- data->xxh32_state);
- data->xxh32_state = NULL;
- archive_le32enc(data->out, checksum);
- data->out += 4;
+ lz4->xxh32_state);
+ lz4->xxh32_state = NULL;
+ archive_le32enc(lz4->out, checksum);
+ lz4->out += 4;
}
ret = __archive_write_filter(f->next_filter,
- data->out_buffer, data->out - data->out_buffer);
+ lz4->out_buffer, lz4->out - lz4->out_buffer);
}
return ret;
}
static int
-archive_filter_lz4_free(struct archive_write_filter *f)
-{
- struct private_data *data = (struct private_data *)f->data;
-
- if (data->lz4_stream != NULL) {
-#ifdef HAVE_LZ4HC_H
- if (data->compression_level >= 3)
-#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
- LZ4_freeStreamHC(data->lz4_stream);
-#else
- LZ4_freeHC(data->lz4_stream);
-#endif
- else
-#endif
-#if LZ4_VERSION_MINOR >= 3
- LZ4_freeStream(data->lz4_stream);
-#else
- LZ4_free(data->lz4_stream);
-#endif
- }
- free(data->out_buffer);
- free(data->in_buffer_allocated);
- free(data->xxh32_state);
- free(data);
- f->data = NULL;
- return (ARCHIVE_OK);
-}
-
-static int
lz4_write_stream_descriptor(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
uint8_t *sd;
- sd = (uint8_t *)data->out;
+ sd = (uint8_t *)lz4->out;
/* Write Magic Number. */
archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
/* FLG */
- sd[4] = (data->version_number << 6)
- | (data->block_independence << 5)
- | (data->block_checksum << 4)
- | (data->stream_size << 3)
- | (data->stream_checksum << 2)
- | (data->preset_dictionary << 0);
+ sd[4] = (lz4->version_number << 6)
+ | (lz4->block_independence << 5)
+ | (lz4->block_checksum << 4)
+ | (lz4->stream_size << 3)
+ | (lz4->stream_checksum << 2)
+ | (lz4->preset_dictionary << 0);
/* BD */
- sd[5] = (data->block_maximum_size << 4);
+ sd[5] = (lz4->block_maximum_size << 4);
sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
- data->out += 7;
- if (data->stream_checksum) {
- data->xxh32_state = __archive_xxhash.XXH32_init(0);
- if (data->xxh32_state == NULL)
+ lz4->out += 7;
+ if (lz4->stream_checksum) {
+ lz4->xxh32_state = __archive_xxhash.XXH32_init(0);
+ if (lz4->xxh32_state == NULL)
return (ARCHIVE_FATAL);
} else
- data->xxh32_state = NULL;
+ lz4->xxh32_state = NULL;
return (ARCHIVE_OK);
}
@@ -426,36 +410,36 @@ static ssize_t
lz4_write_one_block(struct archive_write_filter *f, const char *p,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
ssize_t r;
if (p == NULL) {
/* Compress remaining uncompressed data. */
- if (data->in_buffer == data->in)
+ if (lz4->in_buffer == lz4->in)
return 0;
else {
- size_t l = data->in - data->in_buffer;
- r = drive_compressor(f, data->in_buffer, l);
+ size_t l = lz4->in - lz4->in_buffer;
+ r = drive_compressor(f, lz4->in_buffer, l);
if (r == ARCHIVE_OK)
r = (ssize_t)l;
}
- } else if ((data->block_independence || data->compression_level < 3) &&
- data->in_buffer == data->in && length >= data->block_size) {
- r = drive_compressor(f, p, data->block_size);
+ } else if ((lz4->block_independence || lz4->compression_level < 3) &&
+ lz4->in_buffer == lz4->in && length >= lz4->block_size) {
+ r = drive_compressor(f, p, lz4->block_size);
if (r == ARCHIVE_OK)
- r = (ssize_t)data->block_size;
+ r = (ssize_t)lz4->block_size;
} else {
- size_t remaining_size = data->in_buffer_size -
- (data->in - data->in_buffer);
+ size_t remaining_size = lz4->in_buffer_size -
+ (lz4->in - lz4->in_buffer);
size_t l = (remaining_size > length)? length: remaining_size;
- memcpy(data->in, p, l);
- data->in += l;
+ memcpy(lz4->in, p, l);
+ lz4->in += l;
if (l == remaining_size) {
- r = drive_compressor(f, data->in_buffer,
- data->block_size);
+ r = drive_compressor(f, lz4->in_buffer,
+ lz4->block_size);
if (r == ARCHIVE_OK)
r = (ssize_t)l;
- data->in = data->in_buffer;
+ lz4->in = lz4->in_buffer;
} else
r = (ssize_t)l;
}
@@ -474,12 +458,12 @@ lz4_write_one_block(struct archive_write_filter *f, const char *p,
static int
drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
- if (data->stream_checksum)
- __archive_xxhash.XXH32_update(data->xxh32_state,
+ if (lz4->stream_checksum)
+ __archive_xxhash.XXH32_update(lz4->xxh32_state,
p, (int)length);
- if (data->block_independence)
+ if (lz4->block_independence)
return drive_compressor_independence(f, p, length);
else
return drive_compressor_dependence(f, p, length);
@@ -489,48 +473,48 @@ static int
drive_compressor_independence(struct archive_write_filter *f, const char *p,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
unsigned int outsize;
#ifdef HAVE_LZ4HC_H
- if (data->compression_level >= 3)
+ if (lz4->compression_level >= 3)
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
- outsize = LZ4_compress_HC(p, data->out + 4,
- (int)length, (int)data->block_size,
- data->compression_level);
+ outsize = LZ4_compress_HC(p, lz4->out + 4,
+ (int)length, (int)lz4->block_size,
+ lz4->compression_level);
#else
- outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
- (int)length, (int)data->block_size,
- data->compression_level);
+ outsize = LZ4_compressHC2_limitedOutput(p, lz4->out + 4,
+ (int)length, (int)lz4->block_size,
+ lz4->compression_level);
#endif
else
#endif
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
- outsize = LZ4_compress_default(p, data->out + 4,
- (int)length, (int)data->block_size);
+ outsize = LZ4_compress_default(p, lz4->out + 4,
+ (int)length, (int)lz4->block_size);
#else
- outsize = LZ4_compress_limitedOutput(p, data->out + 4,
- (int)length, (int)data->block_size);
+ outsize = LZ4_compress_limitedOutput(p, lz4->out + 4,
+ (int)length, (int)lz4->block_size);
#endif
if (outsize) {
/* The buffer is compressed. */
- archive_le32enc(data->out, outsize);
- data->out += 4;
+ archive_le32enc(lz4->out, outsize);
+ lz4->out += 4;
} else {
/* The buffer is not compressed. The compressed size was
* bigger than its uncompressed size. */
- archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
- data->out += 4;
- memcpy(data->out, p, length);
+ archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000));
+ lz4->out += 4;
+ memcpy(lz4->out, p, length);
outsize = (uint32_t)length;
}
- data->out += outsize;
- if (data->block_checksum) {
+ lz4->out += outsize;
+ if (lz4->block_checksum) {
unsigned int checksum =
- __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
- archive_le32enc(data->out, checksum);
- data->out += 4;
+ __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0);
+ archive_le32enc(lz4->out, checksum);
+ lz4->out += 4;
}
return (ARCHIVE_OK);
}
@@ -539,21 +523,21 @@ static int
drive_compressor_dependence(struct archive_write_filter *f, const char *p,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
int outsize;
#define DICT_SIZE (64 * 1024)
#ifdef HAVE_LZ4HC_H
- if (data->compression_level >= 3) {
- if (data->lz4_stream == NULL) {
+ if (lz4->compression_level >= 3) {
+ if (lz4->lz4_stream == NULL) {
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
- data->lz4_stream = LZ4_createStreamHC();
- LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
+ lz4->lz4_stream = LZ4_createStreamHC();
+ LZ4_resetStreamHC(lz4->lz4_stream, lz4->compression_level);
#else
- data->lz4_stream =
- LZ4_createHC(data->in_buffer_allocated);
+ lz4->lz4_stream =
+ LZ4_createHC(lz4->in_buffer_allocated);
#endif
- if (data->lz4_stream == NULL) {
+ if (lz4->lz4_stream == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression"
" buffer");
@@ -561,23 +545,23 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p,
}
}
else
- LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+ LZ4_loadDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
outsize = LZ4_compress_HC_continue(
- data->lz4_stream, p, data->out + 4, (int)length,
- (int)data->block_size);
+ lz4->lz4_stream, p, lz4->out + 4, (int)length,
+ (int)lz4->block_size);
#else
outsize = LZ4_compressHC2_limitedOutput_continue(
- data->lz4_stream, p, data->out + 4, (int)length,
- (int)data->block_size, data->compression_level);
+ lz4->lz4_stream, p, lz4->out + 4, (int)length,
+ (int)lz4->block_size, lz4->compression_level);
#endif
} else
#endif
{
- if (data->lz4_stream == NULL) {
- data->lz4_stream = LZ4_createStream();
- if (data->lz4_stream == NULL) {
+ if (lz4->lz4_stream == NULL) {
+ lz4->lz4_stream = LZ4_createStream();
+ if (lz4->lz4_stream == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression"
" buffer");
@@ -585,64 +569,91 @@ drive_compressor_dependence(struct archive_write_filter *f, const char *p,
}
}
else
- LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+ LZ4_loadDict(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
outsize = LZ4_compress_fast_continue(
- data->lz4_stream, p, data->out + 4, (int)length,
- (int)data->block_size, 1);
+ lz4->lz4_stream, p, lz4->out + 4, (int)length,
+ (int)lz4->block_size, 1);
#else
outsize = LZ4_compress_limitedOutput_continue(
- data->lz4_stream, p, data->out + 4, (int)length,
- (int)data->block_size);
+ lz4->lz4_stream, p, lz4->out + 4, (int)length,
+ (int)lz4->block_size);
#endif
}
if (outsize) {
/* The buffer is compressed. */
- archive_le32enc(data->out, outsize);
- data->out += 4;
+ archive_le32enc(lz4->out, outsize);
+ lz4->out += 4;
} else {
/* The buffer is not compressed. The compressed size was
* bigger than its uncompressed size. */
- archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
- data->out += 4;
- memcpy(data->out, p, length);
+ archive_le32enc(lz4->out, (uint32_t)(length | 0x80000000));
+ lz4->out += 4;
+ memcpy(lz4->out, p, length);
outsize = (uint32_t)length;
}
- data->out += outsize;
- if (data->block_checksum) {
+ lz4->out += outsize;
+ if (lz4->block_checksum) {
unsigned int checksum =
- __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
- archive_le32enc(data->out, checksum);
- data->out += 4;
+ __archive_xxhash.XXH32(lz4->out - outsize, outsize, 0);
+ archive_le32enc(lz4->out, checksum);
+ lz4->out += 4;
}
- if (length == data->block_size) {
+ if (length == lz4->block_size) {
#ifdef HAVE_LZ4HC_H
- if (data->compression_level >= 3) {
+ if (lz4->compression_level >= 3) {
#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
- LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+ LZ4_saveDictHC(lz4->lz4_stream, lz4->in_buffer_allocated, DICT_SIZE);
#else
- LZ4_slideInputBufferHC(data->lz4_stream);
+ LZ4_slideInputBufferHC(lz4->lz4_stream);
#endif
- data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
+ lz4->in_buffer = lz4->in_buffer_allocated + DICT_SIZE;
}
else
#endif
- LZ4_saveDict(data->lz4_stream,
- data->in_buffer_allocated, DICT_SIZE);
+ LZ4_saveDict(lz4->lz4_stream,
+ lz4->in_buffer_allocated, DICT_SIZE);
#undef DICT_SIZE
}
return (ARCHIVE_OK);
}
+static void
+free_data(struct lz4 *lz4)
+{
+ if (lz4 != NULL) {
+ if (lz4->lz4_stream != NULL) {
+#ifdef HAVE_LZ4HC_H
+ if (lz4->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ LZ4_freeStreamHC(lz4->lz4_stream);
+#else
+ LZ4_freeHC(lz4->lz4_stream);
+#endif
+ else
+#endif
+#if LZ4_VERSION_MINOR >= 3
+ LZ4_freeStream(lz4->lz4_stream);
+#else
+ LZ4_free(lz4->lz4_stream);
+#endif
+ }
+ free(lz4->out_buffer);
+ free(lz4->in_buffer_allocated);
+ free(lz4->xxh32_state);
+ free(lz4);
+ }
+}
+
#else /* HAVE_LIBLZ4 */
static int
archive_filter_lz4_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
struct archive_string as;
int r;
@@ -650,24 +661,22 @@ archive_filter_lz4_open(struct archive_write_filter *f)
archive_strcpy(&as, "lz4 -z -q -q");
/* Specify a compression level. */
- if (data->compression_level > 0) {
+ if (lz4->compression_level > 0) {
archive_strcat(&as, " -");
- archive_strappend_char(&as, '0' + data->compression_level);
+ archive_strappend_char(&as, '0' + lz4->compression_level);
}
/* Specify a block size. */
archive_strcat(&as, " -B");
- archive_strappend_char(&as, '0' + data->block_maximum_size);
+ archive_strappend_char(&as, '0' + lz4->block_maximum_size);
- if (data->block_checksum)
+ if (lz4->block_checksum)
archive_strcat(&as, " -BX");
- if (data->stream_checksum == 0)
+ if (lz4->stream_checksum == 0)
archive_strcat(&as, " --no-frame-crc");
- if (data->block_independence == 0)
+ if (lz4->block_independence == 0)
archive_strcat(&as, " -BD");
- f->write = archive_filter_lz4_write;
-
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, lz4->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -676,27 +685,26 @@ static int
archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, lz4->pdata, buff, length);
}
static int
archive_filter_lz4_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct lz4 *lz4 = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, lz4->pdata);
}
-static int
-archive_filter_lz4_free(struct archive_write_filter *f)
+static void
+free_data(struct lz4 *lz4)
{
- struct private_data *data = (struct private_data *)f->data;
-
- __archive_write_program_free(data->pdata);
- free(data);
- return (ARCHIVE_OK);
+ if (lz4 != NULL) {
+ __archive_write_program_free(lz4->pdata);
+ free(lz4);
+ }
}
#endif /* HAVE_LIBLZ4 */
diff --git a/libarchive/archive_write_add_filter_lzop.c b/libarchive/archive_write_add_filter_lzop.c
index 42d62db63a13..3ed721e1ea1b 100644
--- a/libarchive/archive_write_add_filter_lzop.c
+++ b/libarchive/archive_write_add_filter_lzop.c
@@ -55,7 +55,7 @@ enum lzo_method {
METHOD_LZO1X_1_15 = 2,
METHOD_LZO1X_999 = 3
};
-struct write_lzop {
+struct lzop {
int compression_level;
#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
unsigned char *uncompressed;
@@ -80,6 +80,7 @@ static int archive_write_lzop_write(struct archive_write_filter *,
const void *, size_t);
static int archive_write_lzop_close(struct archive_write_filter *);
static int archive_write_lzop_free(struct archive_write_filter *);
+static void free_data(struct lzop *);
#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
/* Maximum block size. */
@@ -133,73 +134,72 @@ static const unsigned char header[] = {
#endif
int
-archive_write_add_filter_lzop(struct archive *_a)
+archive_write_add_filter_lzop(struct archive *a)
{
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct write_lzop *data;
+ struct archive_write_filter *f;
+ struct lzop *lzop;
+ int r;
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
-
- f->name = "lzop";
- f->code = ARCHIVE_FILTER_LZOP;
- f->data = data;
- f->open = archive_write_lzop_open;
- f->options = archive_write_lzop_options;
- f->write = archive_write_lzop_write;
- f->close = archive_write_lzop_close;
- f->free = archive_write_lzop_free;
+ lzop = calloc(1, sizeof(*lzop));
+ if (lzop == NULL)
+ goto memerr;
#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
if (lzo_init() != LZO_E_OK) {
- free(data);
- archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ free_data(lzop);
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"lzo_init(type check) failed");
return (ARCHIVE_FATAL);
}
if (lzo_version() < 0x940) {
- free(data);
- archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ free_data(lzop);
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"liblzo library is too old(%s < 0.940)",
lzo_version_string());
return (ARCHIVE_FATAL);
}
- data->compression_level = 5;
- return (ARCHIVE_OK);
+ lzop->compression_level = 5;
+
+ r = ARCHIVE_OK;
#else
- data->pdata = __archive_write_program_allocate("lzop");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(_a, ENOMEM, "Can't allocate memory");
- return (ARCHIVE_FATAL);
- }
- data->compression_level = 0;
+ lzop->pdata = __archive_write_program_allocate("lzop");
+ if (lzop->pdata == NULL)
+ goto memerr;
+ lzop->compression_level = 0;
+
/* Note: We return "warn" to inform of using an external lzop
* program. */
- archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external lzop program for lzop compression");
- return (ARCHIVE_WARN);
+ r = ARCHIVE_WARN;
#endif
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = "lzop";
+ f->code = ARCHIVE_FILTER_LZOP;
+ f->data = lzop;
+ f->options = archive_write_lzop_options;
+ f->open = archive_write_lzop_open;
+ f->write = archive_write_lzop_write;
+ f->close = archive_write_lzop_close;
+ f->free = archive_write_lzop_free;
+
+ return (r);
+memerr:
+ free_data(lzop);
+ archive_set_error(a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
}
static int
archive_write_lzop_free(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
-
-#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
- free(data->uncompressed);
- free(data->compressed);
- free(data->work_buffer);
-#else
- __archive_write_program_free(data->pdata);
-#endif
- free(data);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
@@ -207,7 +207,7 @@ static int
archive_write_lzop_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
@@ -216,7 +216,7 @@ archive_write_lzop_options(struct archive_write_filter *f, const char *key,
"compression-level invalid");
return (ARCHIVE_FAILED);
}
- data->compression_level = value[0] - '0';
+ lzop->compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
@@ -229,58 +229,58 @@ archive_write_lzop_options(struct archive_write_filter *f, const char *key,
static int
archive_write_lzop_open(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
- switch (data->compression_level) {
+ switch (lzop->compression_level) {
case 1:
- data->method = METHOD_LZO1X_1_15; data->level = 1; break;
+ lzop->method = METHOD_LZO1X_1_15; lzop->level = 1; break;
default:
case 2: case 3: case 4: case 5: case 6:
- data->method = METHOD_LZO1X_1; data->level = 5; break;
+ lzop->method = METHOD_LZO1X_1; lzop->level = 5; break;
case 7:
- data->method = METHOD_LZO1X_999; data->level = 7; break;
+ lzop->method = METHOD_LZO1X_999; lzop->level = 7; break;
case 8:
- data->method = METHOD_LZO1X_999; data->level = 8; break;
+ lzop->method = METHOD_LZO1X_999; lzop->level = 8; break;
case 9:
- data->method = METHOD_LZO1X_999; data->level = 9; break;
+ lzop->method = METHOD_LZO1X_999; lzop->level = 9; break;
}
- switch (data->method) {
+ switch (lzop->method) {
case METHOD_LZO1X_1:
- data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
+ lzop->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
case METHOD_LZO1X_1_15:
- data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
+ lzop->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
case METHOD_LZO1X_999:
- data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
+ lzop->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
}
- if (data->work_buffer == NULL) {
- data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
- if (data->work_buffer == NULL) {
+ if (lzop->work_buffer == NULL) {
+ lzop->work_buffer = (lzo_voidp)malloc(lzop->work_buffer_size);
+ if (lzop->work_buffer == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- if (data->compressed == NULL) {
- data->compressed_buffer_size = sizeof(header) +
+ if (lzop->compressed == NULL) {
+ lzop->compressed_buffer_size = sizeof(header) +
BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
- data->compressed = (unsigned char *)
- malloc(data->compressed_buffer_size);
- if (data->compressed == NULL) {
+ lzop->compressed = (unsigned char *)
+ malloc(lzop->compressed_buffer_size);
+ if (lzop->compressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- if (data->uncompressed == NULL) {
- data->uncompressed_buffer_size = BLOCK_SIZE;
- data->uncompressed = (unsigned char *)
- malloc(data->uncompressed_buffer_size);
- if (data->uncompressed == NULL) {
+ if (lzop->uncompressed == NULL) {
+ lzop->uncompressed_buffer_size = BLOCK_SIZE;
+ lzop->uncompressed = (unsigned char *)
+ malloc(lzop->uncompressed_buffer_size);
+ if (lzop->uncompressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
- data->uncompressed_avail_bytes = BLOCK_SIZE;
+ lzop->uncompressed_avail_bytes = BLOCK_SIZE;
}
return (ARCHIVE_OK);
}
@@ -288,68 +288,68 @@ archive_write_lzop_open(struct archive_write_filter *f)
static int
make_header(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
int64_t t;
uint32_t checksum;
- memcpy(data->compressed, header, sizeof(header));
+ memcpy(lzop->compressed, header, sizeof(header));
/* Overwrite library version. */
- data->compressed[HEADER_LIBVERSION] = (unsigned char )
+ lzop->compressed[HEADER_LIBVERSION] = (unsigned char )
(lzo_version() >> 8) & 0xff;
- data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
+ lzop->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
lzo_version() & 0xff;
/* Overwrite method and level. */
- data->compressed[HEADER_METHOD] = (unsigned char)data->method;
- data->compressed[HEADER_LEVEL] = data->level;
+ lzop->compressed[HEADER_METHOD] = (unsigned char)lzop->method;
+ lzop->compressed[HEADER_LEVEL] = lzop->level;
/* Overwrite mtime with current time. */
t = (int64_t)time(NULL);
- archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
+ archive_be32enc(&lzop->compressed[HEADER_MTIME_LOW],
(uint32_t)(t & 0xffffffff));
- archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
+ archive_be32enc(&lzop->compressed[HEADER_MTIME_HIGH],
(uint32_t)((t >> 32) & 0xffffffff));
/* Overwrite header checksum with calculated value. */
- checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
+ checksum = lzo_adler32(1, lzop->compressed + HEADER_VERSION,
(lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
- archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
+ archive_be32enc(&lzop->compressed[HEADER_H_CHECKSUM], checksum);
return (sizeof(header));
}
static int
drive_compressor(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
unsigned char *p;
const int block_info_bytes = 12;
int header_bytes, r;
lzo_uint usize, csize;
uint32_t checksum;
- if (!data->header_written) {
+ if (!lzop->header_written) {
header_bytes = make_header(f);
- data->header_written = 1;
+ lzop->header_written = 1;
} else
header_bytes = 0;
- p = data->compressed;
+ p = lzop->compressed;
usize = (lzo_uint)
- (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
+ (lzop->uncompressed_buffer_size - lzop->uncompressed_avail_bytes);
csize = 0;
- switch (data->method) {
+ switch (lzop->method) {
default:
case METHOD_LZO1X_1:
- r = lzo1x_1_compress(data->uncompressed, usize,
+ r = lzo1x_1_compress(lzop->uncompressed, usize,
p + header_bytes + block_info_bytes, &csize,
- data->work_buffer);
+ lzop->work_buffer);
break;
case METHOD_LZO1X_1_15:
- r = lzo1x_1_15_compress(data->uncompressed, usize,
+ r = lzo1x_1_15_compress(lzop->uncompressed, usize,
p + header_bytes + block_info_bytes, &csize,
- data->work_buffer);
+ lzop->work_buffer);
break;
case METHOD_LZO1X_999:
- r = lzo1x_999_compress_level(data->uncompressed, usize,
+ r = lzo1x_999_compress_level(lzop->uncompressed, usize,
p + header_bytes + block_info_bytes, &csize,
- data->work_buffer, NULL, 0, 0, data->level);
+ lzop->work_buffer, NULL, 0, 0, lzop->level);
break;
}
if (r != LZO_E_OK) {
@@ -361,13 +361,13 @@ drive_compressor(struct archive_write_filter *f)
/* Store uncompressed size. */
archive_be32enc(p + header_bytes, (uint32_t)usize);
/* Store the checksum of the uncompressed data. */
- checksum = lzo_adler32(1, data->uncompressed, usize);
+ checksum = lzo_adler32(1, lzop->uncompressed, usize);
archive_be32enc(p + header_bytes + 8, checksum);
if (csize < usize) {
/* Store compressed size. */
archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
- r = __archive_write_filter(f->next_filter, data->compressed,
+ r = __archive_write_filter(f->next_filter, lzop->compressed,
header_bytes + block_info_bytes + csize);
} else {
/*
@@ -375,11 +375,11 @@ drive_compressor(struct archive_write_filter *f)
*/
/* Store uncompressed size as compressed size. */
archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
- r = __archive_write_filter(f->next_filter, data->compressed,
+ r = __archive_write_filter(f->next_filter, lzop->compressed,
header_bytes + block_info_bytes);
if (r != ARCHIVE_OK)
return (ARCHIVE_FATAL);
- r = __archive_write_filter(f->next_filter, data->uncompressed,
+ r = __archive_write_filter(f->next_filter, lzop->uncompressed,
usize);
}
@@ -392,30 +392,30 @@ static int
archive_write_lzop_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
const char *p = buff;
int r;
do {
- if (data->uncompressed_avail_bytes > length) {
- memcpy(data->uncompressed
- + data->uncompressed_buffer_size
- - data->uncompressed_avail_bytes,
+ if (lzop->uncompressed_avail_bytes > length) {
+ memcpy(lzop->uncompressed
+ + lzop->uncompressed_buffer_size
+ - lzop->uncompressed_avail_bytes,
p, length);
- data->uncompressed_avail_bytes -= length;
+ lzop->uncompressed_avail_bytes -= length;
return (ARCHIVE_OK);
}
- memcpy(data->uncompressed + data->uncompressed_buffer_size
- - data->uncompressed_avail_bytes,
- p, data->uncompressed_avail_bytes);
- length -= data->uncompressed_avail_bytes;
- p += data->uncompressed_avail_bytes;
- data->uncompressed_avail_bytes = 0;
+ memcpy(lzop->uncompressed + lzop->uncompressed_buffer_size
+ - lzop->uncompressed_avail_bytes,
+ p, lzop->uncompressed_avail_bytes);
+ length -= lzop->uncompressed_avail_bytes;
+ p += lzop->uncompressed_avail_bytes;
+ lzop->uncompressed_avail_bytes = 0;
r = drive_compressor(f);
if (r != ARCHIVE_OK) return (r);
- data->uncompressed_avail_bytes = BLOCK_SIZE;
+ lzop->uncompressed_avail_bytes = BLOCK_SIZE;
} while (length);
return (ARCHIVE_OK);
@@ -424,11 +424,11 @@ archive_write_lzop_write(struct archive_write_filter *f,
static int
archive_write_lzop_close(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
const uint32_t endmark = 0;
int r;
- if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
+ if (lzop->uncompressed_avail_bytes < BLOCK_SIZE) {
/* Compress and output remaining data. */
r = drive_compressor(f);
if (r != ARCHIVE_OK)
@@ -439,24 +439,35 @@ archive_write_lzop_close(struct archive_write_filter *f)
return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
}
+static void
+free_data(struct lzop *lzop)
+{
+ if (lzop != NULL) {
+ free(lzop->uncompressed);
+ free(lzop->compressed);
+ free(lzop->work_buffer);
+ free(lzop);
+ }
+}
+
#else
static int
archive_write_lzop_open(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
struct archive_string as;
int r;
archive_string_init(&as);
archive_strcpy(&as, "lzop");
/* Specify compression level. */
- if (data->compression_level > 0) {
+ if (lzop->compression_level > 0) {
archive_strappend_char(&as, ' ');
archive_strappend_char(&as, '-');
- archive_strappend_char(&as, '0' + data->compression_level);
+ archive_strappend_char(&as, '0' + lzop->compression_level);
}
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, lzop->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -465,16 +476,25 @@ static int
archive_write_lzop_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, lzop->pdata, buff, length);
}
static int
archive_write_lzop_close(struct archive_write_filter *f)
{
- struct write_lzop *data = (struct write_lzop *)f->data;
+ struct lzop *lzop = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, lzop->pdata);
+}
+
+static void
+free_data(struct lzop *lzop)
+{
+ if (lzop != NULL) {
+ __archive_write_program_free(lzop->pdata);
+ free(lzop);
+ }
}
#endif
diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c
index cb92dc04f0d1..ee0869717e95 100644
--- a/libarchive/archive_write_add_filter_program.c
+++ b/libarchive/archive_write_add_filter_program.c
@@ -70,7 +70,7 @@ struct archive_write_program_data {
char *program_name;
};
-struct private_data {
+struct program {
struct archive_write_program_data *pdata;
struct archive_string description;
char *cmd;
@@ -81,51 +81,53 @@ static int archive_compressor_program_write(struct archive_write_filter *,
const void *, size_t);
static int archive_compressor_program_close(struct archive_write_filter *);
static int archive_compressor_program_free(struct archive_write_filter *);
+static void free_data(struct program *);
/*
* Add a filter to this write handle that passes all data through an
* external program.
*/
int
-archive_write_add_filter_program(struct archive *_a, const char *cmd)
+archive_write_add_filter_program(struct archive *a, const char *cmd)
{
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_data *data;
+ struct archive_write_filter *f;
+ struct program *program;
static const char prefix[] = "Program: ";
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
- f->data = calloc(1, sizeof(*data));
- if (f->data == NULL)
+ program = calloc(1, sizeof(*program));
+ if (program == NULL)
goto memerr;
- data = (struct private_data *)f->data;
-
- data->cmd = strdup(cmd);
- if (data->cmd == NULL)
+ program->cmd = strdup(cmd);
+ if (program->cmd == NULL)
goto memerr;
-
- data->pdata = __archive_write_program_allocate(cmd);
- if (data->pdata == NULL)
+ program->pdata = __archive_write_program_allocate(cmd);
+ if (program->pdata == NULL)
goto memerr;
-
/* Make up a description string. */
- if (archive_string_ensure(&data->description,
+ if (archive_string_ensure(&program->description,
strlen(prefix) + strlen(cmd) + 1) == NULL)
goto memerr;
- archive_strcpy(&data->description, prefix);
- archive_strcat(&data->description, cmd);
+ archive_strcpy(&program->description, prefix);
+ archive_strcat(&program->description, cmd);
- f->name = data->description.s;
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = program->description.s;
f->code = ARCHIVE_FILTER_PROGRAM;
+ f->data = program;
f->open = archive_compressor_program_open;
f->write = archive_compressor_program_write;
f->close = archive_compressor_program_close;
f->free = archive_compressor_program_free;
+
return (ARCHIVE_OK);
memerr:
- archive_compressor_program_free(f);
- archive_set_error(_a, ENOMEM,
+ free_data(program);
+ archive_set_error(a, ENOMEM,
"Can't allocate memory for filter program");
return (ARCHIVE_FATAL);
}
@@ -133,40 +135,33 @@ memerr:
static int
archive_compressor_program_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct program *program = f->data;
- return __archive_write_program_open(f, data->pdata, data->cmd);
+ return __archive_write_program_open(f, program->pdata, program->cmd);
}
static int
archive_compressor_program_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct program *program = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, program->pdata, buff, length);
}
static int
archive_compressor_program_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct program *program = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, program->pdata);
}
static int
archive_compressor_program_free(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
-
- if (data) {
- free(data->cmd);
- archive_string_free(&data->description);
- __archive_write_program_free(data->pdata);
- free(data);
- f->data = NULL;
- }
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
@@ -391,3 +386,13 @@ cleanup:
return ret;
}
+static void
+free_data(struct program *program)
+{
+ if (program) {
+ free(program->cmd);
+ archive_string_free(&program->description);
+ __archive_write_program_free(program->pdata);
+ free(program);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_uuencode.c b/libarchive/archive_write_add_filter_uuencode.c
index b338b4aff083..83ec2ebffb9f 100644
--- a/libarchive/archive_write_add_filter_uuencode.c
+++ b/libarchive/archive_write_add_filter_uuencode.c
@@ -46,7 +46,7 @@
#define LBYTES 45
-struct private_uuencode {
+struct uuencode {
int mode;
struct archive_string name;
struct archive_string encoded_buff;
@@ -64,39 +64,44 @@ static int archive_filter_uuencode_close(struct archive_write_filter *);
static int archive_filter_uuencode_free(struct archive_write_filter *);
static void uu_encode(struct archive_string *, const unsigned char *, size_t);
static int64_t atol8(const char *, size_t);
+static void free_data(struct uuencode *);
/*
* Add a compress filter to this write handle.
*/
int
-archive_write_add_filter_uuencode(struct archive *_a)
+archive_write_add_filter_uuencode(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_uuencode *state;
+ struct archive_write_filter *f;
+ struct uuencode *uuencode;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
- ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_uuencode");
- state = calloc(1, sizeof(*state));
- if (state == NULL) {
- archive_set_error(f->archive, ENOMEM,
- "Can't allocate data for uuencode filter");
- return (ARCHIVE_FATAL);
- }
- archive_strcpy(&state->name, "-");
- state->mode = 0644;
+ uuencode = calloc(1, sizeof(*uuencode));
+ if (uuencode == NULL)
+ goto memerr;
+ archive_strcpy(&uuencode->name, "-");
+ uuencode->mode = 0644;
- f->data = state;
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
f->name = "uuencode";
f->code = ARCHIVE_FILTER_UU;
- f->open = archive_filter_uuencode_open;
+ f->data = uuencode;
f->options = archive_filter_uuencode_options;
+ f->open = archive_filter_uuencode_open;
f->write = archive_filter_uuencode_write;
f->close = archive_filter_uuencode_close;
f->free = archive_filter_uuencode_free;
return (ARCHIVE_OK);
+memerr:
+ free_data(uuencode);
+ archive_set_error(a, ENOMEM,
+ "Can't allocate data for uuencode filter");
+ return (ARCHIVE_FATAL);
}
/*
@@ -106,7 +111,7 @@ static int
archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct private_uuencode *state = (struct private_uuencode *)f->data;
+ struct uuencode *uuencode = f->data;
if (strcmp(key, "mode") == 0) {
int64_t val;
@@ -122,7 +127,7 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
"invalid mode option");
return (ARCHIVE_FAILED);
}
- state->mode = (int)val & 0777;
+ uuencode->mode = (int)val & 0777;
return (ARCHIVE_OK);
} else if (strcmp(key, "name") == 0) {
if (value == NULL) {
@@ -130,7 +135,7 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
"name option requires a string");
return (ARCHIVE_FAILED);
}
- archive_strcpy(&state->name, value);
+ archive_strcpy(&uuencode->name, value);
return (ARCHIVE_OK);
}
@@ -146,7 +151,7 @@ archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
static int
archive_filter_uuencode_open(struct archive_write_filter *f)
{
- struct private_uuencode *state = (struct private_uuencode *)f->data;
+ struct uuencode *uuencode = f->data;
size_t bs = 65536, bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
@@ -159,18 +164,17 @@ archive_filter_uuencode_open(struct archive_write_filter *f)
bs -= bs % bpb;
}
- state->bs = bs;
- if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ uuencode->bs = bs;
+ if (archive_string_ensure(&uuencode->encoded_buff, bs + 512) == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for uuencode buffer");
return (ARCHIVE_FATAL);
}
- archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
- (unsigned int)state->mode, state->name.s);
+ archive_string_sprintf(&uuencode->encoded_buff, "begin %o %s\n",
+ (unsigned int)uuencode->mode, uuencode->name.s);
- f->data = state;
- return (0);
+ return (ARCHIVE_OK);
}
static void
@@ -216,39 +220,39 @@ static int
archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_uuencode *state = (struct private_uuencode *)f->data;
+ struct uuencode *uuencode = f->data;
const unsigned char *p = buff;
int ret = ARCHIVE_OK;
if (length == 0)
return (ret);
- if (state->hold_len) {
- while (state->hold_len < LBYTES && length > 0) {
- state->hold[state->hold_len++] = *p++;
+ if (uuencode->hold_len) {
+ while (uuencode->hold_len < LBYTES && length > 0) {
+ uuencode->hold[uuencode->hold_len++] = *p++;
length--;
}
- if (state->hold_len < LBYTES)
+ if (uuencode->hold_len < LBYTES)
return (ret);
- uu_encode(&state->encoded_buff, state->hold, LBYTES);
- state->hold_len = 0;
+ uu_encode(&uuencode->encoded_buff, uuencode->hold, LBYTES);
+ uuencode->hold_len = 0;
}
for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
- uu_encode(&state->encoded_buff, p, LBYTES);
+ uu_encode(&uuencode->encoded_buff, p, LBYTES);
/* Save remaining bytes. */
if (length > 0) {
- memcpy(state->hold, p, length);
- state->hold_len = length;
+ memcpy(uuencode->hold, p, length);
+ uuencode->hold_len = length;
}
- while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ while (archive_strlen(&uuencode->encoded_buff) >= uuencode->bs) {
ret = __archive_write_filter(f->next_filter,
- state->encoded_buff.s, state->bs);
- memmove(state->encoded_buff.s,
- state->encoded_buff.s + state->bs,
- state->encoded_buff.length - state->bs);
- state->encoded_buff.length -= state->bs;
+ uuencode->encoded_buff.s, uuencode->bs);
+ memmove(uuencode->encoded_buff.s,
+ uuencode->encoded_buff.s + uuencode->bs,
+ uuencode->encoded_buff.length - uuencode->bs);
+ uuencode->encoded_buff.length -= uuencode->bs;
}
return (ret);
@@ -261,26 +265,24 @@ archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
static int
archive_filter_uuencode_close(struct archive_write_filter *f)
{
- struct private_uuencode *state = (struct private_uuencode *)f->data;
+ struct uuencode *uuencode = f->data;
/* Flush remaining bytes. */
- if (state->hold_len != 0)
- uu_encode(&state->encoded_buff, state->hold, state->hold_len);
- archive_string_sprintf(&state->encoded_buff, "`\nend\n");
+ if (uuencode->hold_len != 0)
+ uu_encode(&uuencode->encoded_buff, uuencode->hold,
+ uuencode->hold_len);
+ archive_string_sprintf(&uuencode->encoded_buff, "`\nend\n");
/* Write the last block */
archive_write_set_bytes_in_last_block(f->archive, 1);
return __archive_write_filter(f->next_filter,
- state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+ uuencode->encoded_buff.s, archive_strlen(&uuencode->encoded_buff));
}
static int
archive_filter_uuencode_free(struct archive_write_filter *f)
{
- struct private_uuencode *state = (struct private_uuencode *)f->data;
-
- archive_string_free(&state->name);
- archive_string_free(&state->encoded_buff);
- free(state);
+ free_data(f->data);
+ f->data = NULL;
return (ARCHIVE_OK);
}
@@ -307,3 +309,12 @@ atol8(const char *p, size_t char_cnt)
return (l);
}
+static void
+free_data(struct uuencode *uuencode)
+{
+ if (uuencode != NULL) {
+ archive_string_free(&uuencode->name);
+ archive_string_free(&uuencode->encoded_buff);
+ free(uuencode);
+ }
+}
diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c
index 56cf02070754..599ee413b0e1 100644
--- a/libarchive/archive_write_add_filter_xz.c
+++ b/libarchive/archive_write_add_filter_xz.c
@@ -99,7 +99,7 @@ archive_write_add_filter_lzip(struct archive *a)
#else
/* Don't compile this if we don't have liblzma. */
-struct private_data {
+struct xz {
int compression_level;
uint32_t threads;
lzma_stream stream;
@@ -121,7 +121,8 @@ static int archive_compressor_xz_write(struct archive_write_filter *,
static int archive_compressor_xz_close(struct archive_write_filter *);
static int archive_compressor_xz_free(struct archive_write_filter *);
static int drive_compressor(struct archive_write_filter *,
- struct private_data *, int finishing);
+ struct xz *, int finishing);
+static void free_data(struct xz *);
struct option_value {
uint32_t dict_size;
@@ -142,85 +143,72 @@ static const struct option_value option_values[] = {
};
static int
-common_setup(struct archive_write_filter *f)
+common_setup(struct archive *a, const char *name, int code)
{
- struct private_data *data;
- struct archive_write *a = (struct archive_write *)f->archive;
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- f->data = data;
- data->compression_level = LZMA_PRESET_DEFAULT;
- data->threads = 1;
- f->open = &archive_compressor_xz_open;
+ struct xz *xz;
+ struct archive_write_filter *f;
+
+ xz = calloc(1, sizeof(*xz));
+ if (xz == NULL)
+ goto memerr;
+ xz->compression_level = LZMA_PRESET_DEFAULT;
+ xz->threads = 1;
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = name;
+ f->code = code;
+ f->data = xz;
+ f->options = archive_compressor_xz_options;
+ f->open = archive_compressor_xz_open;
+ f->write = archive_compressor_xz_write;
f->close = archive_compressor_xz_close;
f->free = archive_compressor_xz_free;
- f->options = &archive_compressor_xz_options;
+
return (ARCHIVE_OK);
+memerr:
+ free_data(xz);
+ archive_set_error(a, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
}
/*
* Add an xz compression filter to this write handle.
*/
int
-archive_write_add_filter_xz(struct archive *_a)
+archive_write_add_filter_xz(struct archive *a)
{
- struct archive_write_filter *f;
- int r;
-
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");
- f = __archive_write_allocate_filter(_a);
- r = common_setup(f);
- if (r == ARCHIVE_OK) {
- f->code = ARCHIVE_FILTER_XZ;
- f->name = "xz";
- }
- return (r);
+
+ return common_setup(a, "xz", ARCHIVE_FILTER_XZ);
}
/* LZMA is handled identically, we just need a different compression
* code set. (The liblzma setup looks at the code to determine
* the one place that XZ and LZMA require different handling.) */
int
-archive_write_add_filter_lzma(struct archive *_a)
+archive_write_add_filter_lzma(struct archive *a)
{
- struct archive_write_filter *f;
- int r;
-
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");
- f = __archive_write_allocate_filter(_a);
- r = common_setup(f);
- if (r == ARCHIVE_OK) {
- f->code = ARCHIVE_FILTER_LZMA;
- f->name = "lzma";
- }
- return (r);
+
+ return common_setup(a, "lzma", ARCHIVE_FILTER_LZMA);
}
int
-archive_write_add_filter_lzip(struct archive *_a)
+archive_write_add_filter_lzip(struct archive *a)
{
- struct archive_write_filter *f;
- int r;
-
- archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");
- f = __archive_write_allocate_filter(_a);
- r = common_setup(f);
- if (r == ARCHIVE_OK) {
- f->code = ARCHIVE_FILTER_LZIP;
- f->name = "lzip";
- }
- return (r);
+
+ return common_setup(a, "lzip", ARCHIVE_FILTER_LZIP);
}
static int
archive_compressor_xz_init_stream(struct archive_write_filter *f,
- struct private_data *data)
+ struct xz *xz)
{
static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
int ret;
@@ -228,27 +216,27 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f,
lzma_mt mt_options;
#endif
- data->stream = lzma_stream_init_data;
- data->stream.next_out = data->compressed;
- data->stream.avail_out = data->compressed_buffer_size;
+ xz->stream = lzma_stream_init_data;
+ xz->stream.next_out = xz->compressed;
+ xz->stream.avail_out = xz->compressed_buffer_size;
if (f->code == ARCHIVE_FILTER_XZ) {
#ifdef HAVE_LZMA_STREAM_ENCODER_MT
- if (data->threads != 1) {
+ if (xz->threads != 1) {
memset(&mt_options, 0, sizeof(mt_options));
- mt_options.threads = data->threads;
+ mt_options.threads = xz->threads;
mt_options.timeout = 300;
- mt_options.filters = data->lzmafilters;
+ mt_options.filters = xz->lzmafilters;
mt_options.check = LZMA_CHECK_CRC64;
- ret = lzma_stream_encoder_mt(&(data->stream),
+ ret = lzma_stream_encoder_mt(&(xz->stream),
&mt_options);
} else
#endif
- ret = lzma_stream_encoder(&(data->stream),
- data->lzmafilters, LZMA_CHECK_CRC64);
+ ret = lzma_stream_encoder(&(xz->stream),
+ xz->lzmafilters, LZMA_CHECK_CRC64);
} else if (f->code == ARCHIVE_FILTER_LZMA) {
- ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);
+ ret = lzma_alone_encoder(&(xz->stream), &xz->lzma_opt);
} else { /* ARCHIVE_FILTER_LZIP */
- int dict_size = data->lzma_opt.dict_size;
+ int dict_size = xz->lzma_opt.dict_size;
int ds, log2dic, wedges;
/* Calculate a coded dictionary size */
@@ -270,18 +258,18 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f,
wedges = 0;
ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);
- data->crc32 = 0;
+ xz->crc32 = 0;
/* Make a header */
- data->compressed[0] = 0x4C;
- data->compressed[1] = 0x5A;
- data->compressed[2] = 0x49;
- data->compressed[3] = 0x50;
- data->compressed[4] = 1;/* Version */
- data->compressed[5] = (unsigned char)ds;
- data->stream.next_out += 6;
- data->stream.avail_out -= 6;
+ xz->compressed[0] = 0x4C;
+ xz->compressed[1] = 0x5A;
+ xz->compressed[2] = 0x49;
+ xz->compressed[3] = 0x50;
+ xz->compressed[4] = 1;/* Version */
+ xz->compressed[5] = (unsigned char)ds;
+ xz->stream.next_out += 6;
+ xz->stream.avail_out -= 6;
- ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);
+ ret = lzma_raw_encoder(&(xz->stream), xz->lzmafilters);
}
if (ret == LZMA_OK)
return (ARCHIVE_OK);
@@ -307,10 +295,10 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f,
static int
archive_compressor_xz_open(struct archive_write_filter *f)
{
- struct private_data *data = f->data;
+ struct xz *xz = f->data;
int ret;
- if (data->compressed == NULL) {
+ if (xz->compressed == NULL) {
size_t bs = 65536, bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of the bytes
@@ -321,49 +309,46 @@ archive_compressor_xz_open(struct archive_write_filter *f)
else if (bpb != 0)
bs -= bs % bpb;
}
- data->compressed_buffer_size = bs;
- data->compressed = malloc(data->compressed_buffer_size);
- if (data->compressed == NULL) {
+ xz->compressed_buffer_size = bs;
+ xz->compressed = malloc(xz->compressed_buffer_size);
+ if (xz->compressed == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- f->write = archive_compressor_xz_write;
-
/* Initialize compression library. */
if (f->code == ARCHIVE_FILTER_LZIP) {
const struct option_value *val =
- &option_values[data->compression_level];
+ &option_values[xz->compression_level];
- data->lzma_opt.dict_size = val->dict_size;
- data->lzma_opt.preset_dict = NULL;
- data->lzma_opt.preset_dict_size = 0;
- data->lzma_opt.lc = LZMA_LC_DEFAULT;
- data->lzma_opt.lp = LZMA_LP_DEFAULT;
- data->lzma_opt.pb = LZMA_PB_DEFAULT;
- data->lzma_opt.mode =
- data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
- data->lzma_opt.nice_len = val->nice_len;
- data->lzma_opt.mf = val->mf;
- data->lzma_opt.depth = 0;
- data->lzmafilters[0].id = LZMA_FILTER_LZMA1;
- data->lzmafilters[0].options = &data->lzma_opt;
- data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ xz->lzma_opt.dict_size = val->dict_size;
+ xz->lzma_opt.preset_dict = NULL;
+ xz->lzma_opt.preset_dict_size = 0;
+ xz->lzma_opt.lc = LZMA_LC_DEFAULT;
+ xz->lzma_opt.lp = LZMA_LP_DEFAULT;
+ xz->lzma_opt.pb = LZMA_PB_DEFAULT;
+ xz->lzma_opt.mode =
+ xz->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
+ xz->lzma_opt.nice_len = val->nice_len;
+ xz->lzma_opt.mf = val->mf;
+ xz->lzma_opt.depth = 0;
+ xz->lzmafilters[0].id = LZMA_FILTER_LZMA1;
+ xz->lzmafilters[0].options = &xz->lzma_opt;
+ xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
} else {
- if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {
+ if (lzma_lzma_preset(&xz->lzma_opt, xz->compression_level)) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library");
}
- data->lzmafilters[0].id = LZMA_FILTER_LZMA2;
- data->lzmafilters[0].options = &data->lzma_opt;
- data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ xz->lzmafilters[0].id = LZMA_FILTER_LZMA2;
+ xz->lzmafilters[0].options = &xz->lzma_opt;
+ xz->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
}
- ret = archive_compressor_xz_init_stream(f, data);
+ ret = archive_compressor_xz_init_stream(f, xz);
if (ret == LZMA_OK) {
- f->data = data;
- return (0);
+ return (ARCHIVE_OK);
}
return (ARCHIVE_FATAL);
}
@@ -375,7 +360,7 @@ static int
archive_compressor_xz_options(struct archive_write_filter *f,
const char *key, const char *value)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct xz *xz = f->data;
if (strcmp(key, "compression-level") == 0) {
if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
@@ -384,9 +369,9 @@ archive_compressor_xz_options(struct archive_write_filter *f,
"compression-level invalid");
return (ARCHIVE_FAILED);
}
- data->compression_level = value[0] - '0';
- if (data->compression_level > 9)
- data->compression_level = 9;
+ xz->compression_level = value[0] - '0';
+ if (xz->compression_level > 9)
+ xz->compression_level = 9;
return (ARCHIVE_OK);
} else if (strcmp(key, "threads") == 0) {
char *endptr;
@@ -400,17 +385,17 @@ archive_compressor_xz_options(struct archive_write_filter *f,
errno = 0;
val = strtoul(value, &endptr, 10);
if (errno != 0 || *endptr != '\0' || val > (unsigned)INT_MAX) {
- data->threads = 1;
+ xz->threads = 1;
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"threads invalid");
return (ARCHIVE_FAILED);
}
- data->threads = (int)val;
- if (data->threads == 0) {
+ xz->threads = (int)val;
+ if (xz->threads == 0) {
#ifdef HAVE_LZMA_STREAM_ENCODER_MT
- data->threads = lzma_cputhreads();
+ xz->threads = lzma_cputhreads();
#else
- data->threads = 1;
+ xz->threads = 1;
#endif
}
return (ARCHIVE_OK);
@@ -429,18 +414,18 @@ static int
archive_compressor_xz_write(struct archive_write_filter *f,
const void *buff, size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct xz *xz = f->data;
int ret;
/* Update statistics */
- data->total_in += length;
+ xz->total_in += length;
if (f->code == ARCHIVE_FILTER_LZIP)
- data->crc32 = lzma_crc32(buff, length, data->crc32);
+ xz->crc32 = lzma_crc32(buff, length, xz->crc32);
/* Compress input data to output buffer */
- data->stream.next_in = buff;
- data->stream.avail_in = length;
- if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ xz->stream.next_in = buff;
+ xz->stream.avail_in = length;
+ if ((ret = drive_compressor(f, xz, 0)) != ARCHIVE_OK)
return (ret);
return (ARCHIVE_OK);
@@ -453,34 +438,32 @@ archive_compressor_xz_write(struct archive_write_filter *f,
static int
archive_compressor_xz_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct xz *xz = f->data;
int ret;
- ret = drive_compressor(f, data, 1);
+ ret = drive_compressor(f, xz, 1);
if (ret == ARCHIVE_OK) {
- data->total_out +=
- data->compressed_buffer_size - data->stream.avail_out;
+ xz->total_out +=
+ xz->compressed_buffer_size - xz->stream.avail_out;
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size - data->stream.avail_out);
+ xz->compressed,
+ xz->compressed_buffer_size - xz->stream.avail_out);
if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {
- archive_le32enc(data->compressed, data->crc32);
- archive_le64enc(data->compressed+4, data->total_in);
- archive_le64enc(data->compressed+12, data->total_out + 20);
+ archive_le32enc(xz->compressed, xz->crc32);
+ archive_le64enc(xz->compressed+4, xz->total_in);
+ archive_le64enc(xz->compressed+12, xz->total_out + 20);
ret = __archive_write_filter(f->next_filter,
- data->compressed, 20);
+ xz->compressed, 20);
}
}
- lzma_end(&(data->stream));
+ lzma_end(&(xz->stream));
return ret;
}
static int
archive_compressor_xz_free(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
- free(data->compressed);
- free(data);
+ free_data(f->data);
f->data = NULL;
return (ARCHIVE_OK);
}
@@ -494,34 +477,34 @@ archive_compressor_xz_free(struct archive_write_filter *f)
*/
static int
drive_compressor(struct archive_write_filter *f,
- struct private_data *data, int finishing)
+ struct xz *xz, int finishing)
{
int ret;
for (;;) {
- if (data->stream.avail_out == 0) {
- data->total_out += data->compressed_buffer_size;
+ if (xz->stream.avail_out == 0) {
+ xz->total_out += xz->compressed_buffer_size;
ret = __archive_write_filter(f->next_filter,
- data->compressed,
- data->compressed_buffer_size);
+ xz->compressed,
+ xz->compressed_buffer_size);
if (ret != ARCHIVE_OK)
return (ARCHIVE_FATAL);
- data->stream.next_out = data->compressed;
- data->stream.avail_out = data->compressed_buffer_size;
+ xz->stream.next_out = xz->compressed;
+ xz->stream.avail_out = xz->compressed_buffer_size;
}
/* If there's nothing to do, we're done. */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && xz->stream.avail_in == 0)
return (ARCHIVE_OK);
- ret = lzma_code(&(data->stream),
+ ret = lzma_code(&(xz->stream),
finishing ? LZMA_FINISH : LZMA_RUN );
switch (ret) {
case LZMA_OK:
/* In non-finishing case, check if compressor
* consumed everything */
- if (!finishing && data->stream.avail_in == 0)
+ if (!finishing && xz->stream.avail_in == 0)
return (ARCHIVE_OK);
/* In finishing case, this return always means
* there's more work */
@@ -537,7 +520,7 @@ drive_compressor(struct archive_write_filter *f,
archive_set_error(f->archive, ENOMEM,
"lzma compression error: "
"%ju MiB would have been needed",
- (uintmax_t)((lzma_memusage(&(data->stream))
+ (uintmax_t)((lzma_memusage(&(xz->stream))
+ 1024 * 1024 -1)
/ (1024 * 1024)));
return (ARCHIVE_FATAL);
@@ -552,4 +535,13 @@ drive_compressor(struct archive_write_filter *f,
}
}
+static void
+free_data(struct xz *xz)
+{
+ if (xz != NULL) {
+ free(xz->compressed);
+ free(xz);
+ }
+}
+
#endif /* HAVE_LZMA_H */
diff --git a/libarchive/archive_write_add_filter_zstd.c b/libarchive/archive_write_add_filter_zstd.c
index 2c6b91eba18a..826227fd4764 100644
--- a/libarchive/archive_write_add_filter_zstd.c
+++ b/libarchive/archive_write_add_filter_zstd.c
@@ -55,7 +55,7 @@
/* Don't compile this if we don't have zstd.h */
-struct private_data {
+struct zstd {
int compression_level;
int threads;
int long_distance;
@@ -103,79 +103,77 @@ static int archive_compressor_zstd_close(struct archive_write_filter *);
static int archive_compressor_zstd_free(struct archive_write_filter *);
#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
static int drive_compressor(struct archive_write_filter *,
- struct private_data *, int, const void *, size_t);
+ struct zstd *, int, const void *, size_t);
#endif
+static void free_data(struct zstd *);
/*
* Add a zstd compression filter to this write handle.
*/
int
-archive_write_add_filter_zstd(struct archive *_a)
+archive_write_add_filter_zstd(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct archive_write_filter *f = __archive_write_allocate_filter(_a);
- struct private_data *data;
- archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ struct archive_write_filter *f;
+ struct zstd *zstd;
+ int r;
+
+ archive_check_magic(a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
- data = calloc(1, sizeof(*data));
- if (data == NULL) {
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- f->data = data;
- f->open = &archive_compressor_zstd_open;
- f->options = &archive_compressor_zstd_options;
- f->flush = &archive_compressor_zstd_flush;
- f->close = &archive_compressor_zstd_close;
- f->free = &archive_compressor_zstd_free;
- f->code = ARCHIVE_FILTER_ZSTD;
- f->name = "zstd";
- data->compression_level = CLEVEL_DEFAULT;
- data->threads = 0;
- data->long_distance = 0;
+ zstd = calloc(1, sizeof(*zstd));
+ if (zstd == NULL)
+ goto memerr;
+ zstd->compression_level = CLEVEL_DEFAULT;
+ zstd->threads = 0;
+ zstd->long_distance = 0;
#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
- data->frame_per_file = 0;
- data->min_frame_in = 0;
- data->max_frame_in = SIZE_MAX;
- data->min_frame_out = 0;
- data->max_frame_out = SIZE_MAX;
- data->cur_frame_in = 0;
- data->cur_frame_out = 0;
- data->cstream = ZSTD_createCStream();
- if (data->cstream == NULL) {
- free(data);
- archive_set_error(&a->archive, ENOMEM,
- "Failed to allocate zstd compressor object");
- return (ARCHIVE_FATAL);
- }
+ zstd->frame_per_file = 0;
+ zstd->min_frame_in = 0;
+ zstd->max_frame_in = SIZE_MAX;
+ zstd->min_frame_out = 0;
+ zstd->max_frame_out = SIZE_MAX;
+ zstd->cur_frame_in = 0;
+ zstd->cur_frame_out = 0;
+ zstd->cstream = ZSTD_createCStream();
+ if (zstd->cstream == NULL)
+ goto memerr;
- return (ARCHIVE_OK);
+ r = ARCHIVE_OK;
#else
- data->pdata = __archive_write_program_allocate("zstd");
- if (data->pdata == NULL) {
- free(data);
- archive_set_error(&a->archive, ENOMEM, "Out of memory");
- return (ARCHIVE_FATAL);
- }
- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ zstd->pdata = __archive_write_program_allocate("zstd");
+ if (zstd->pdata == NULL)
+ goto memerr;
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Using external zstd program");
- return (ARCHIVE_WARN);
+ r = ARCHIVE_WARN;
#endif
+
+ f = __archive_write_allocate_filter(a);
+ if (f == NULL)
+ goto memerr;
+ f->name = "zstd";
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->data = zstd;
+ f->options = archive_compressor_zstd_options;
+ f->open = archive_compressor_zstd_open;
+ f->write = archive_compressor_zstd_write;
+ f->flush = archive_compressor_zstd_flush;
+ f->close = archive_compressor_zstd_close;
+ f->free = archive_compressor_zstd_free;
+
+ return (r);
+memerr:
+ free_data(zstd);
+ archive_set_error(a, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
}
static int
archive_compressor_zstd_free(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
-#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
- ZSTD_freeCStream(data->cstream);
- free(data->out.dst);
-#else
- __archive_write_program_free(data->pdata);
-#endif
- free(data);
+ free_data(f->data);
f->data = NULL;
return (ARCHIVE_OK);
}
@@ -239,7 +237,7 @@ static int
archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
const char *value)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
if (strcmp(key, "compression-level") == 0) {
intmax_t level;
@@ -268,7 +266,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
"compression-level out of range");
return (ARCHIVE_FAILED);
}
- data->compression_level = (int)level;
+ zstd->compression_level = (int)level;
return (ARCHIVE_OK);
} else if (strcmp(key, "threads") == 0) {
intmax_t threads;
@@ -295,14 +293,14 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
"threads out of rnage");
return (ARCHIVE_FAILED);
}
- data->threads = (int)threads;
+ zstd->threads = (int)threads;
return (ARCHIVE_OK);
#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
} else if (strcmp(key, "frame-per-file") == 0) {
- data->frame_per_file = 1;
+ zstd->frame_per_file = 1;
return (ARCHIVE_OK);
} else if (strcmp(key, "min-frame-in") == 0) {
- if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) {
+ if (string_to_size(value, &zstd->min_frame_in) != ARCHIVE_OK) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"min-frame-in invalid");
return (ARCHIVE_FAILED);
@@ -310,7 +308,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
return (ARCHIVE_OK);
} else if (strcmp(key, "min-frame-out") == 0 ||
strcmp(key, "min-frame-size") == 0) {
- if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) {
+ if (string_to_size(value, &zstd->min_frame_out) != ARCHIVE_OK) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"min-frame-out invalid");
return (ARCHIVE_FAILED);
@@ -318,16 +316,16 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
return (ARCHIVE_OK);
} else if (strcmp(key, "max-frame-in") == 0 ||
strcmp(key, "max-frame-size") == 0) {
- if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK ||
- data->max_frame_in < 1024) {
+ if (string_to_size(value, &zstd->max_frame_in) != ARCHIVE_OK ||
+ zstd->max_frame_in < 1024) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"max-frame-size invalid");
return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
} else if (strcmp(key, "max-frame-out") == 0) {
- if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK ||
- data->max_frame_out < 1024) {
+ if (string_to_size(value, &zstd->max_frame_out) != ARCHIVE_OK ||
+ zstd->max_frame_out < 1024) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"max-frame-out invalid");
return (ARCHIVE_FAILED);
@@ -363,7 +361,7 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
if (((int)long_distance) < 10 || (int)long_distance > max_distance)
return (ARCHIVE_FAILED);
#endif
- data->long_distance = (int)long_distance;
+ zstd->long_distance = (int)long_distance;
return (ARCHIVE_OK);
}
@@ -380,9 +378,9 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
static int
archive_compressor_zstd_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- if (data->out.dst == NULL) {
+ if (zstd->out.dst == NULL) {
size_t bs = ZSTD_CStreamOutSize(), bpb;
if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
/* Buffer size should be a multiple number of
@@ -393,31 +391,29 @@ archive_compressor_zstd_open(struct archive_write_filter *f)
else if (bpb != 0)
bs -= bs % bpb;
}
- data->out.size = bs;
- data->out.pos = 0;
- data->out.dst = malloc(data->out.size);
- if (data->out.dst == NULL) {
+ zstd->out.size = bs;
+ zstd->out.pos = 0;
+ zstd->out.dst = malloc(zstd->out.size);
+ if (zstd->out.dst == NULL) {
archive_set_error(f->archive, ENOMEM,
"Can't allocate data for compression buffer");
return (ARCHIVE_FATAL);
}
}
- f->write = archive_compressor_zstd_write;
-
- if (ZSTD_isError(ZSTD_initCStream(data->cstream,
- data->compression_level))) {
+ if (ZSTD_isError(ZSTD_initCStream(zstd->cstream,
+ zstd->compression_level))) {
archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
"Internal error initializing zstd compressor object");
return (ARCHIVE_FATAL);
}
- ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
+ ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_nbWorkers, zstd->threads);
- ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_checksumFlag, 1);
+ ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_checksumFlag, 1);
#if ZSTD_VERSION_NUMBER >= MINVER_LONG
- ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance);
+ ZSTD_CCtx_setParameter(zstd->cstream, ZSTD_c_windowLog, zstd->long_distance);
#endif
return (ARCHIVE_OK);
@@ -430,9 +426,9 @@ static int
archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- return (drive_compressor(f, data, 0, buff, length));
+ return (drive_compressor(f, zstd, 0, buff, length));
}
/*
@@ -441,15 +437,15 @@ archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
static int
archive_compressor_zstd_flush(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- if (data->frame_per_file && data->state == running) {
- if (data->cur_frame_in > data->min_frame_in &&
- data->cur_frame_out > data->min_frame_out) {
- data->state = finishing;
+ if (zstd->frame_per_file && zstd->state == running) {
+ if (zstd->cur_frame_in > zstd->min_frame_in &&
+ zstd->cur_frame_out > zstd->min_frame_out) {
+ zstd->state = finishing;
}
}
- return (drive_compressor(f, data, 1, NULL, 0));
+ return (drive_compressor(f, zstd, 1, NULL, 0));
}
/*
@@ -458,11 +454,11 @@ archive_compressor_zstd_flush(struct archive_write_filter *f)
static int
archive_compressor_zstd_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- if (data->state == running)
- data->state = finishing;
- return (drive_compressor(f, data, 1, NULL, 0));
+ if (zstd->state == running)
+ zstd->state = finishing;
+ return (drive_compressor(f, zstd, 1, NULL, 0));
}
/*
@@ -471,7 +467,7 @@ archive_compressor_zstd_close(struct archive_write_filter *f)
*/
static int
drive_compressor(struct archive_write_filter *f,
- struct private_data *data, int flush, const void *src, size_t length)
+ struct zstd *zstd, int flush, const void *src, size_t length)
{
ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
size_t ipos, opos, zstdret = 0;
@@ -479,46 +475,46 @@ drive_compressor(struct archive_write_filter *f,
for (;;) {
ipos = in.pos;
- opos = data->out.pos;
- switch (data->state) {
+ opos = zstd->out.pos;
+ switch (zstd->state) {
case running:
if (in.pos == in.size)
return (ARCHIVE_OK);
- zstdret = ZSTD_compressStream(data->cstream,
- &data->out, &in);
+ zstdret = ZSTD_compressStream(zstd->cstream,
+ &zstd->out, &in);
if (ZSTD_isError(zstdret))
goto zstd_fatal;
break;
case finishing:
- zstdret = ZSTD_endStream(data->cstream, &data->out);
+ zstdret = ZSTD_endStream(zstd->cstream, &zstd->out);
if (ZSTD_isError(zstdret))
goto zstd_fatal;
if (zstdret == 0)
- data->state = resetting;
+ zstd->state = resetting;
break;
case resetting:
- ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
- data->cur_frame++;
- data->cur_frame_in = 0;
- data->cur_frame_out = 0;
- data->state = running;
+ ZSTD_CCtx_reset(zstd->cstream, ZSTD_reset_session_only);
+ zstd->cur_frame++;
+ zstd->cur_frame_in = 0;
+ zstd->cur_frame_out = 0;
+ zstd->state = running;
break;
}
- data->cur_frame_in += in.pos - ipos;
- data->cur_frame_out += data->out.pos - opos;
- if (data->state == running) {
- if (data->cur_frame_in >= data->max_frame_in ||
- data->cur_frame_out >= data->max_frame_out) {
- data->state = finishing;
+ zstd->cur_frame_in += in.pos - ipos;
+ zstd->cur_frame_out += zstd->out.pos - opos;
+ if (zstd->state == running) {
+ if (zstd->cur_frame_in >= zstd->max_frame_in ||
+ zstd->cur_frame_out >= zstd->max_frame_out) {
+ zstd->state = finishing;
}
}
- if (data->out.pos == data->out.size ||
- (flush && data->out.pos > 0)) {
+ if (zstd->out.pos == zstd->out.size ||
+ (flush && zstd->out.pos > 0)) {
ret = __archive_write_filter(f->next_filter,
- data->out.dst, data->out.pos);
+ zstd->out.dst, zstd->out.pos);
if (ret != ARCHIVE_OK)
goto fatal;
- data->out.pos = 0;
+ zstd->out.pos = 0;
}
}
zstd_fatal:
@@ -529,12 +525,22 @@ fatal:
return (ARCHIVE_FATAL);
}
+static void
+free_data(struct zstd *zstd)
+{
+ if (zstd != NULL) {
+ ZSTD_freeCStream(zstd->cstream);
+ free(zstd->out.dst);
+ free(zstd);
+ }
+}
+
#else /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */
static int
archive_compressor_zstd_open(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
struct archive_string as;
int r;
@@ -542,26 +548,25 @@ archive_compressor_zstd_open(struct archive_write_filter *f)
/* --no-check matches library default */
archive_strcpy(&as, "zstd --no-check");
- if (data->compression_level < CLEVEL_STD_MIN) {
- archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
+ if (zstd->compression_level < CLEVEL_STD_MIN) {
+ archive_string_sprintf(&as, " --fast=%d", -zstd->compression_level);
} else {
- archive_string_sprintf(&as, " -%d", data->compression_level);
+ archive_string_sprintf(&as, " -%d", zstd->compression_level);
}
- if (data->compression_level > CLEVEL_STD_MAX) {
+ if (zstd->compression_level > CLEVEL_STD_MAX) {
archive_strcat(&as, " --ultra");
}
- if (data->threads != 0) {
- archive_string_sprintf(&as, " --threads=%d", data->threads);
+ if (zstd->threads != 0) {
+ archive_string_sprintf(&as, " --threads=%d", zstd->threads);
}
- if (data->long_distance != 0) {
- archive_string_sprintf(&as, " --long=%d", data->long_distance);
+ if (zstd->long_distance != 0) {
+ archive_string_sprintf(&as, " --long=%d", zstd->long_distance);
}
- f->write = archive_compressor_zstd_write;
- r = __archive_write_program_open(f, data->pdata, as.s);
+ r = __archive_write_program_open(f, zstd->pdata, as.s);
archive_string_free(&as);
return (r);
}
@@ -570,9 +575,9 @@ static int
archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
size_t length)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- return __archive_write_program_write(f, data->pdata, buff, length);
+ return __archive_write_program_write(f, zstd->pdata, buff, length);
}
static int
@@ -586,9 +591,18 @@ archive_compressor_zstd_flush(struct archive_write_filter *f)
static int
archive_compressor_zstd_close(struct archive_write_filter *f)
{
- struct private_data *data = (struct private_data *)f->data;
+ struct zstd *zstd = f->data;
- return __archive_write_program_close(f, data->pdata);
+ return __archive_write_program_close(f, zstd->pdata);
+}
+
+static void
+free_data(struct zstd *zstd)
+{
+ if (zstd != NULL) {
+ __archive_write_program_free(zstd->pdata);
+ free(zstd);
+ }
}
#endif /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
index df46a5cc59ca..a27e73188b37 100644
--- a/libarchive/archive_write_disk_posix.c
+++ b/libarchive/archive_write_disk_posix.c
@@ -300,7 +300,7 @@ struct archive_write_disk {
uint32_t compressed_rsrc_position;
uint32_t compressed_rsrc_position_v;
/* Buffer for uncompressed data. */
- char *uncompressed_buffer;
+ unsigned char *uncompressed_buffer;
size_t block_remaining_bytes;
size_t file_remaining_bytes;
#ifdef HAVE_ZLIB_H
@@ -1254,7 +1254,7 @@ static int
hfs_decompress(struct archive_write_disk *a)
{
uint32_t *block_info;
- unsigned int block_count;
+ uint32_t block_count;
uint32_t data_pos, data_size;
ssize_t r;
ssize_t bytes_written, bytes_to_write;
@@ -1277,10 +1277,10 @@ hfs_decompress(struct archive_write_disk *a)
bytes_to_write = data_size -1;
b = a->compressed_buffer + 1;
} else {
- uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
+ size_t dest_len = MAX_DECMPFS_BLOCK_SIZE;
int zr;
- zr = uncompress((Bytef *)a->uncompressed_buffer,
+ zr = uncompress(a->uncompressed_buffer,
&dest_len, a->compressed_buffer, data_size);
if (zr != Z_OK) {
archive_set_error(&a->archive,
@@ -1289,7 +1289,7 @@ hfs_decompress(struct archive_write_disk *a)
return (ARCHIVE_WARN);
}
bytes_to_write = dest_len;
- b = (unsigned char *)a->uncompressed_buffer;
+ b = a->uncompressed_buffer;
}
do {
bytes_written = write(a->fd, b, bytes_to_write);
@@ -1472,8 +1472,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
if (a->decmpfs_block_count == (unsigned)-1) {
void *new_block;
- size_t new_size;
- unsigned int block_count;
+ size_t block_count, new_size;
if (a->decmpfs_header_p == NULL) {
new_block = malloc(MAX_DECMPFS_XATTR_SIZE
@@ -1493,13 +1492,20 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
a->filesize);
- /* Calculate a block count of the file. */
- block_count =
- (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
- MAX_DECMPFS_BLOCK_SIZE;
+ /*
+ * Calculate block count for the file.
+ */
+ block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE - 1) /
+ MAX_DECMPFS_BLOCK_SIZE;
+ if (block_count > (SIZE_MAX - RSRC_H_SIZE - 4 - RSRC_F_SIZE) /
+ (sizeof(uint32_t) * 2) || block_count > UINT32_MAX) {
+ archive_set_error(&a->archive, EFBIG, "File too big");
+ return (ARCHIVE_FATAL);
+ }
+
/*
* Allocate buffer for resource fork.
- * Set up related pointers;
+ * Set up related pointers.
*/
new_size =
RSRC_H_SIZE + /* header */
@@ -1540,7 +1546,7 @@ hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
a->decmpfs_block_info =
(uint32_t *)(a->resource_fork + RSRC_H_SIZE);
/* Set the block count to the resource fork. */
- archive_le32enc(a->decmpfs_block_info++, block_count);
+ archive_le32enc(a->decmpfs_block_info++, (uint32_t)block_count);
/* Get the position where we are going to set compressed
* data. */
a->compressed_rsrc_position =
@@ -2369,7 +2375,7 @@ create_filesystem_object(struct archive_write_disk *a)
#endif
if (r != 0)
r = errno;
- else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+ else if (S_ISREG(st.st_mode)) {
a->fd = open(a->name, O_WRONLY | O_TRUNC |
O_BINARY | O_CLOEXEC | O_NOFOLLOW);
__archive_ensure_cloexec_flag(a->fd);
@@ -4344,7 +4350,7 @@ copy_metadata(struct archive_write_disk *a, const char *metadata,
* with at least a writing mode(O_RDWR or O_WRONLY). it
* makes the data fork uncompressed.
*/
- dffd = open(datafork, 0);
+ dffd = open(datafork, O_SYMLINK);
if (dffd == -1) {
archive_set_error(&a->archive, errno,
"Failed to open the data fork for metadata");
@@ -4446,14 +4452,16 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
*/
archive_strncpy(&datafork, pathname, p - pathname);
archive_strcat(&datafork, p + 2);
- if (
#ifdef HAVE_LSTAT
- lstat(datafork.s, &st) == -1 ||
+ if (lstat(datafork.s, &st) == -1)
+ goto skip_appledouble;
#else
- la_stat(datafork.s, &st) == -1 ||
+ if (la_stat(datafork.s, &st) == -1)
+ goto skip_appledouble;
#endif
- (((st.st_mode & AE_IFMT) != AE_IFREG) &&
- ((st.st_mode & AE_IFMT) != AE_IFDIR)))
+ if (!S_ISREG(st.st_mode) &&
+ !S_ISDIR(st.st_mode) &&
+ !S_ISLNK(st.st_mode))
goto skip_appledouble;
/*
diff --git a/libarchive/archive_write_disk_set_standard_lookup.c b/libarchive/archive_write_disk_set_standard_lookup.c
index 964169898e45..5705a978f979 100644
--- a/libarchive/archive_write_disk_set_standard_lookup.c
+++ b/libarchive/archive_write_disk_set_standard_lookup.c
@@ -45,6 +45,7 @@
#endif
#include "archive.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_read_private.h"
#include "archive_write_disk_private.h"
@@ -134,7 +135,8 @@ lookup_gid(void *private_data, const char *gname, int64_t gid)
break;
if (r != ERANGE)
break;
- bufsize *= 2;
+ if (archive_ckd_mul_size(&bufsize, bufsize, 2))
+ break;
free(allocated);
allocated = malloc(bufsize);
if (allocated == NULL)
@@ -203,7 +205,8 @@ lookup_uid(void *private_data, const char *uname, int64_t uid)
break;
if (r != ERANGE)
break;
- bufsize *= 2;
+ if (archive_ckd_mul_size(&bufsize, bufsize, 2))
+ break;
free(allocated);
allocated = malloc(bufsize);
if (allocated == NULL)
diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c
index 435f0cdb9ced..a78c0d2d3476 100644
--- a/libarchive/archive_write_disk_windows.c
+++ b/libarchive/archive_write_disk_windows.c
@@ -194,6 +194,8 @@ struct archive_write_disk {
static int disk_unlink(const wchar_t *);
static int disk_rmdir(const wchar_t *);
+static int check_symlinks_by_path(struct archive_write_disk *,
+ wchar_t *, size_t *, int);
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 *,
@@ -244,8 +246,8 @@ static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
static int
-file_information(struct archive_write_disk *a, wchar_t *path,
- BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)
+file_information(wchar_t *path, BY_HANDLE_FILE_INFORMATION *st,
+ mode_t *mode, int sim_lstat)
{
HANDLE h;
int r;
@@ -282,10 +284,10 @@ file_information(struct archive_write_disk *a, wchar_t *path,
ZeroMemory(&createExParams, sizeof(createExParams));
createExParams.dwSize = sizeof(createExParams);
createExParams.dwFileFlags = flag;
- h = CreateFile2(a->name, 0, 0,
+ h = CreateFile2(path, 0, 0,
OPEN_EXISTING, &createExParams);
#else
- h = CreateFileW(a->name, 0, 0, NULL,
+ h = CreateFileW(path, 0, 0, NULL,
OPEN_EXISTING, flag, NULL);
#endif
if (h == INVALID_HANDLE_VALUE &&
@@ -629,6 +631,7 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
DWORD attrs = 0;
DWORD flags = 0;
DWORD newflags = 0;
+ DWORD lasterr = 0;
len = wcslen(target);
if (len == 0) {
@@ -693,10 +696,14 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
ret = CreateSymbolicLinkW(linkname, ttarget, newflags);
/*
* Prior to Windows 10 calling CreateSymbolicLinkW() will fail
- * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+ * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set; however,
+ * trying this fallback if we fail due to a bad path will replace
+ * ENOENT with EPERM and confuse later error recovery efforts.
*/
if (!ret) {
- ret = CreateSymbolicLinkW(linkname, ttarget, flags);
+ lasterr = GetLastError();
+ if (lasterr != ERROR_PATH_NOT_FOUND)
+ ret = CreateSymbolicLinkW(linkname, ttarget, flags);
}
free(ttarget);
#endif
@@ -741,7 +748,7 @@ lazy_stat(struct archive_write_disk *a)
* XXX At this point, symlinks should not be hit, otherwise
* XXX a race occurred. Do we want to check explicitly for that?
*/
- if (file_information(a, a->name, &a->st, NULL, 1) == 0) {
+ if (file_information(a->name, &a->st, NULL, 1) == 0) {
a->pst = &a->st;
return (ARCHIVE_OK);
}
@@ -1516,7 +1523,7 @@ restore_entry(struct archive_write_disk *a)
* such symlinks. We always need both source and target
* information.
*/
- r = file_information(a, a->name, &lst, &lst_mode, 1);
+ r = file_information(a->name, &lst, &lst_mode, 1);
if (r != 0) {
archive_set_error(&a->archive, errno,
"Can't stat existing object");
@@ -1525,7 +1532,7 @@ restore_entry(struct archive_write_disk *a)
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);
+ r = file_information(a->name, &a->st, &st_mode, 0);
if (r != 0) {
a->st = lst;
st_mode = lst_mode;
@@ -1653,6 +1660,7 @@ create_filesystem_object(struct archive_write_disk *a)
mode_t final_mode, mode;
int r;
DWORD attrs = 0;
+ DWORD file_flags = 0;
# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
CREATEFILE2_EXTENDED_PARAMETERS createExParams;
#endif
@@ -1675,6 +1683,22 @@ create_filesystem_object(struct archive_write_disk *a)
free(linksanitized);
return (r);
}
+
+ if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+ r = check_symlinks_by_path(a, linksanitized, NULL, 1);
+ if (r != ARCHIVE_OK) {
+ /*
+ * Prevent extracting a hardlink to a symlink.
+ * libarchive supports the POSIX feature of
+ * hardlinks with data payloads, which would
+ * provide a way to write through an otherwise
+ * disallowed symlink.
+ */
+ free(linksanitized);
+ return (r);
+ }
+ }
+
linkfull = __la_win_permissive_name_w(linksanitized);
free(linksanitized);
namefull = __la_win_permissive_name_w(a->name);
@@ -1752,6 +1776,13 @@ create_filesystem_object(struct archive_write_disk *a)
else
disk_unlink(a->name);
}
+
+ /*
+ * Since the symlink-safe path cache is just an optimization, and we
+ * are about to create a new symlink, invalidating the cache prevents
+ * incidentally writing through the entry we are about to create.
+ */
+ a->path_safe.s[0] = 0;
#if HAVE_SYMLINK
return symlink(linkname, a->name) ? errno : 0;
#else
@@ -1790,16 +1821,31 @@ create_filesystem_object(struct archive_write_disk *a)
case AE_IFREG:
a->tmpname = NULL;
fullname = a->name;
+ if ((a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+ /*
+ * Creating a regular file is expected to fail with EEXIST if *any
+ * object* exists at the target location.
+ * SECURE_SYMLINKS deletes any symbolic link that would have been
+ * written through. When it's off, though, we risk writing through
+ * extant but broken symlinks unless we allow CreateFile to fail
+ * on the link rather than creating its target.
+ *
+ * This is effectively O_EXCL.
+ */
+ file_flags |= FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS;
+ }
+
/* O_WRONLY | O_CREAT | O_EXCL */
# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
ZeroMemory(&createExParams, sizeof(createExParams));
createExParams.dwSize = sizeof(createExParams);
createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ createExParams.dwFileFlags = file_flags;
a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
CREATE_NEW, &createExParams);
#else
a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
- CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL);
#endif
if (a->fh == INVALID_HANDLE_VALUE &&
GetLastError() == ERROR_INVALID_NAME &&
@@ -1810,7 +1856,7 @@ create_filesystem_object(struct archive_write_disk *a)
CREATE_NEW, &createExParams);
#else
a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
- CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL | file_flags, NULL);
#endif
}
if (a->fh == INVALID_HANDLE_VALUE) {
@@ -2075,9 +2121,10 @@ current_fixup(struct archive_write_disk *a, const wchar_t *pathname)
* recent paths.
*/
static int
-check_symlinks(struct archive_write_disk *a)
+check_symlinks_by_path(struct archive_write_disk *a,
+ wchar_t *path, size_t *safe_len, int checking_linkname)
{
- wchar_t *pn, *p;
+ wchar_t *pn, *p, *su;
wchar_t c;
int r;
BY_HANDLE_FILE_INFORMATION st;
@@ -2088,13 +2135,17 @@ check_symlinks(struct archive_write_disk *a)
* destination would be altered by a symlink.
*/
/* Whatever we checked last time doesn't need to be re-checked. */
- pn = a->name;
+ pn = path;
p = a->path_safe.s;
while ((*pn != '\0') && (*p == *pn))
++p, ++pn;
/* Skip leading backslashes */
while (*pn == '\\')
++pn;
+ su = pn;
+ /* The end-of-safe region, however, should not cover a partial leaf name */
+ while (su > path && *su != '\\')
+ --su;
c = pn[0];
/* Keep going until we've checked the entire name. */
while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
@@ -2104,13 +2155,20 @@ check_symlinks(struct archive_write_disk *a)
c = pn[0];
pn[0] = '\0';
/* Check that we haven't hit a symlink. */
- r = file_information(a, a->name, &st, &st_mode, 1);
+ r = file_information(path, &st, &st_mode, 1);
if (r != 0) {
/* We've hit a dir that doesn't exist; stop now. */
if (errno == ENOENT)
break;
} else if (S_ISLNK(st_mode)) {
if (c == '\0') {
+ if (checking_linkname) {
+ archive_set_error(&a->archive, ELOOP,
+ "Cannot write hardlink to symlink %ls",
+ path);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
/*
* Last element is a file or directory symlink.
* Remove it so we can overwrite it with the
@@ -2122,14 +2180,14 @@ check_symlinks(struct archive_write_disk *a)
}
if (st.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY) {
- r = disk_rmdir(a->name);
+ r = disk_rmdir(path);
} else {
- r = disk_unlink(a->name);
+ r = disk_unlink(path);
}
if (r) {
archive_set_error(&a->archive, errno,
"Could not remove symlink %ls",
- a->name);
+ path);
pn[0] = c;
return (ARCHIVE_FAILED);
}
@@ -2143,11 +2201,16 @@ check_symlinks(struct archive_write_disk *a)
if (!S_ISLNK(a->mode)) {
archive_set_error(&a->archive, -1,
"Removing symlink %ls",
- a->name);
+ path);
+ /*
+ * We can safely return the entire path as a safe range,
+ * having deleted the symlink at the leaf with no intent
+ * to create another symlink.
+ */
+ su = pn;
}
/* Symlink gone. No more problem! */
- pn[0] = c;
- return (0);
+ break;
} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
/* User asked us to remove problems. */
if (a->flags &
@@ -2156,14 +2219,14 @@ check_symlinks(struct archive_write_disk *a)
}
if (st.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY) {
- r = disk_rmdir(a->name);
+ r = disk_rmdir(path);
} else {
- r = disk_unlink(a->name);
+ r = disk_unlink(path);
}
if (r != 0) {
archive_set_error(&a->archive, EIO,
"Cannot remove intervening "
- "symlink %ls", a->name);
+ "symlink %ls", path);
pn[0] = c;
return (ARCHIVE_FAILED);
}
@@ -2171,23 +2234,48 @@ check_symlinks(struct archive_write_disk *a)
} else {
archive_set_error(&a->archive, ELOOP,
"Cannot extract through symlink %ls",
- a->name);
+ path);
pn[0] = c;
return (ARCHIVE_FAILED);
}
}
+ su = pn;
if (!c)
break;
pn[0] = c;
pn++;
}
pn[0] = c;
- /* We've checked and/or cleaned the whole path, so remember it. */
- archive_wstrcpy(&a->path_safe, a->name);
+ if (safe_len)
+ *safe_len = su - path;
return (ARCHIVE_OK);
}
static int
+check_symlinks(struct archive_write_disk *a)
+{
+ int r;
+ size_t safe_len;
+ r = check_symlinks_by_path(a, a->name, &safe_len, 0);
+
+ if (r == ARCHIVE_OK) {
+ /*
+ * We've checked and/or cleaned the whole path, so remember the
+ * portion check_symlinks_by_path returned as "safe" (typically
+ * the parent of the entry we are about to create, unless that
+ * entry was just unlinked.)
+ *
+ * We only cache path safety info for entry paths (which we
+ * operate on in a->name). We do not cache safety info for
+ * any other paths.
+ */
+ archive_wstrncpy(&a->path_safe, a->name, safe_len);
+ }
+
+ return (r);
+}
+
+static int
guidword(wchar_t *p, int n)
{
int i;
@@ -2456,7 +2544,7 @@ create_dir(struct archive_write_disk *a, wchar_t *path)
* here loses the ability to extract through symlinks. Also note
* that this should not use the a->st cache.
*/
- if (file_information(a, path, &st, &st_mode, 0) == 0) {
+ if (file_information(path, &st, &st_mode, 0) == 0) {
if (S_ISDIR(st_mode))
return (ARCHIVE_OK);
if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
@@ -2523,7 +2611,7 @@ create_dir(struct archive_write_disk *a, wchar_t *path)
* don't add it to the fixup list here, as it's already been
* added.
*/
- if (file_information(a, path, &st, &st_mode, 0) == 0 &&
+ if (file_information(path, &st, &st_mode, 0) == 0 &&
S_ISDIR(st_mode))
return (ARCHIVE_OK);
diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h
index f259ccb16546..ca9393fb8ea0 100644
--- a/libarchive/archive_write_private.h
+++ b/libarchive/archive_write_private.h
@@ -69,7 +69,7 @@ void __archive_write_filters_free(struct archive *);
struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
int __archive_write_output(struct archive_write *, const void *, size_t);
-int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_nulls(struct archive_write *, uint64_t);
int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
struct archive_write {
@@ -132,6 +132,8 @@ struct archive_write {
void *passphrase_client_data;
};
+int __archive_write_unregister_format(struct archive_write *);
+
/*
* Utility function to format a USTAR header into a buffer. If
* "strict" is set, this tries to create the absolutely most portable
diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c
index 74da63abd1a8..b5cc8e5001be 100644
--- a/libarchive/archive_write_set_format_7zip.c
+++ b/libarchive/archive_write_set_format_7zip.c
@@ -215,8 +215,6 @@ struct _7zip {
size_t total_number_dir_entry;
size_t total_bytes_entry_name;
size_t total_number_time_defined[3];
- uint64_t total_bytes_compressed;
- uint64_t total_bytes_uncompressed;
uint64_t entry_bytes_remaining;
uint32_t entry_crc32;
uint32_t precode_crc32;
@@ -358,8 +356,7 @@ archive_write_set_format_7zip(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
/* If another format was already registered, unregister it. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
zip = calloc(1, sizeof(*zip));
if (zip == NULL) {
@@ -415,9 +412,7 @@ archive_write_set_format_7zip(struct archive *_a)
static int
_7z_options(struct archive_write *a, const char *key, const char *value)
{
- struct _7zip *zip;
-
- zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
if (strcmp(key, "compression") == 0) {
const char *name = NULL;
@@ -559,11 +554,10 @@ _7z_options(struct archive_write *a, const char *key, const char *value)
static int
_7z_write_header(struct archive_write *a, struct archive_entry *entry)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
struct file *file;
int r;
- zip = (struct _7zip *)a->format_data;
zip->cur_file = NULL;
zip->entry_bytes_remaining = 0;
@@ -667,12 +661,10 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry)
static int
write_to_temp(struct archive_write *a, const void *buff, size_t s)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
const unsigned char *p;
ssize_t ws;
- zip = (struct _7zip *)a->format_data;
-
/*
* Open a temporary file.
*/
@@ -705,7 +697,7 @@ static ssize_t
compress_out(struct archive_write *a, const void *buff, size_t s,
enum la_zaction run)
{
- struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
int r;
if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
@@ -751,11 +743,9 @@ compress_out(struct archive_write *a, const void *buff, size_t s,
static ssize_t
_7z_write_data(struct archive_write *a, const void *buff, size_t s)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
ssize_t bytes;
- zip = (struct _7zip *)a->format_data;
-
if (s > zip->entry_bytes_remaining)
s = (size_t)zip->entry_bytes_remaining;
if (s == 0 || zip->cur_file == NULL)
@@ -771,11 +761,10 @@ _7z_write_data(struct archive_write *a, const void *buff, size_t s)
static int
_7z_finish_entry(struct archive_write *a)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
size_t s;
ssize_t r;
- zip = (struct _7zip *)a->format_data;
if (zip->cur_file == NULL)
return (ARCHIVE_OK);
@@ -787,8 +776,6 @@ _7z_finish_entry(struct archive_write *a)
if (r < 0)
return ((int)r);
}
- zip->total_bytes_compressed += zip->stream.total_in;
- zip->total_bytes_uncompressed += zip->stream.total_out;
zip->cur_file->crc32 = zip->entry_crc32;
zip->cur_file = NULL;
@@ -798,11 +785,10 @@ _7z_finish_entry(struct archive_write *a)
static int
flush_wbuff(struct archive_write *a)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
int r;
size_t s;
- zip = (struct _7zip *)a->format_data;
s = sizeof(zip->wbuff) - zip->wbuff_remaining;
r = __archive_write_output(a, zip->wbuff, s);
if (r != ARCHIVE_OK)
@@ -814,10 +800,9 @@ flush_wbuff(struct archive_write *a)
static int
copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
int r;
- zip = (struct _7zip *)a->format_data;
if (zip->temp_offset > 0 &&
lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
archive_set_error(&(a->archive), errno, "lseek failed");
@@ -860,15 +845,13 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
static int
_7z_close(struct archive_write *a)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
unsigned char *wb;
uint64_t header_offset, header_size, header_unpacksize;
uint64_t length;
uint32_t header_crc32;
int r;
- zip = (struct _7zip *)a->format_data;
-
if (zip->total_number_entry > 0) {
struct archive_rb_node *n;
uint64_t data_offset, data_size, data_unpacksize;
@@ -1033,7 +1016,7 @@ enc_uint64(struct archive_write *a, uint64_t val)
static int
make_substreamsInfo(struct archive_write *a, struct coder *coders)
{
- struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
struct file *file;
int r;
@@ -1109,7 +1092,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
uint32_t header_crc)
{
- struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
uint8_t codec_buff[8];
int numFolders, fi;
int codec_size;
@@ -1301,8 +1284,8 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
static int
make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
{
+ struct _7zip *zip = a->format_data;
uint8_t filetime[8];
- struct _7zip *zip = (struct _7zip *)a->format_data;
struct file *file;
int r;
uint8_t b, mask;
@@ -1390,7 +1373,7 @@ static int
make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
uint64_t unpack_size, int codernum, struct coder *coders)
{
- struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
struct file *file;
int r;
uint8_t b, mask;
@@ -1598,7 +1581,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
static int
_7z_free(struct archive_write *a)
{
- struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct _7zip *zip = a->format_data;
/* Close the temporary file. */
if (zip->temp_fd >= 0)
@@ -1636,13 +1619,12 @@ static int
file_new(struct archive_write *a, struct archive_entry *entry,
struct file **newfile)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
struct file *file;
const char *u16;
size_t u16len;
int ret = ARCHIVE_OK;
- zip = (struct _7zip *)a->format_data;
*newfile = NULL;
file = calloc(1, sizeof(*file));
@@ -2260,7 +2242,7 @@ static void
ppmd_write(void *p, Byte b)
{
struct archive_write *a = ((IByteOut *)p)->a;
- struct _7zip *zip = (struct _7zip *)(a->format_data);
+ struct _7zip *zip = a->format_data;
struct la_zstream *lastrm = &(zip->stream);
struct ppmd_stream *strm;
@@ -2517,10 +2499,9 @@ static int
_7z_compression_init_encoder(struct archive_write *a, unsigned compression,
int compression_level)
{
- struct _7zip *zip;
+ struct _7zip *zip = a->format_data;
int r;
- zip = (struct _7zip *)a->format_data;
switch (compression) {
case _7Z_DEFLATE:
r = compression_init_encoder_deflate(
diff --git a/libarchive/archive_write_set_format_ar.c b/libarchive/archive_write_set_format_ar.c
index 9912835b8fcf..57572ea38bd3 100644
--- a/libarchive/archive_write_set_format_ar.c
+++ b/libarchive/archive_write_set_format_ar.c
@@ -40,10 +40,11 @@
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
+#include "archive_string.h"
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
-struct ar_w {
+struct ar {
uint64_t entry_bytes_remaining;
uint64_t entry_padding;
int is_strtab;
@@ -78,7 +79,6 @@ static ssize_t archive_write_ar_data(struct archive_write *,
static int archive_write_ar_free(struct archive_write *);
static int archive_write_ar_close(struct archive_write *);
static int archive_write_ar_finish_entry(struct archive_write *);
-static const char *ar_basename(const char *path);
static int format_octal(int64_t v, char *p, int s);
static int format_decimal(int64_t v, char *p, int s);
@@ -120,11 +120,10 @@ archive_write_set_format_ar_svr4(struct archive *_a)
static int
archive_write_set_format_ar(struct archive_write *a)
{
- struct ar_w *ar;
+ struct ar *ar;
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
ar = calloc(1, sizeof(*ar));
if (ar == NULL) {
@@ -147,16 +146,18 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
{
int ret, append_fn;
char buff[60];
- char *ss, *se;
- struct ar_w *ar;
+ char *ss;
+ struct ar *ar = a->format_data;
+ struct archive_string se;
const char *pathname;
const char *filename;
+ size_t filename_length;
int64_t size;
append_fn = 0;
- ar = (struct ar_w *)a->format_data;
ar->is_strtab = 0;
filename = NULL;
+ filename_length = 0;
size = archive_entry_size(entry);
@@ -215,12 +216,17 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* Otherwise, entry is a normal archive member.
* Strip leading paths from filenames, if any.
*/
- if ((filename = ar_basename(pathname)) == NULL) {
+ filename = strrchr(pathname, '/');
+ if (filename == NULL)
+ filename = pathname;
+ else if (filename[1] == '\0') {
/* Reject filenames with trailing "/" */
archive_set_error(&a->archive, EINVAL,
"Invalid filename");
return (ARCHIVE_WARN);
- }
+ } else
+ filename++;
+ filename_length = strlen(filename);
if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
/*
@@ -229,10 +235,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* So, the longest filename here (without extension) is
* actually 15 bytes.
*/
- if (strlen(filename) <= 15) {
+ if (filename_length <= 15) {
memcpy(&buff[AR_name_offset],
- filename, strlen(filename));
- buff[AR_name_offset + strlen(filename)] = '/';
+ filename, filename_length);
+ buff[AR_name_offset + filename_length] = '/';
} else {
/*
* For filename longer than 15 bytes, GNU variant
@@ -246,18 +252,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
- se = malloc(strlen(filename) + 3);
- if (se == NULL) {
- archive_set_error(&a->archive, ENOMEM,
- "Can't allocate filename buffer");
- return (ARCHIVE_FATAL);
- }
-
- memcpy(se, filename, strlen(filename));
- strcpy(se + strlen(filename), "/\n");
-
- ss = strstr(ar->strtab, se);
- free(se);
+ archive_string_init(&se);
+ archive_string_sprintf(&se, "%s/\n", filename);
+ ss = strstr(ar->strtab, se.s);
+ archive_string_free(&se);
if (ss == NULL) {
archive_set_error(&a->archive, EINVAL,
@@ -289,13 +287,13 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
* The name is then written immediately following the
* archive header.
*/
- if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
- memcpy(&buff[AR_name_offset], filename, strlen(filename));
- buff[AR_name_offset + strlen(filename)] = ' ';
+ if (filename_length <= 16 && strchr(filename, ' ') == NULL) {
+ memcpy(&buff[AR_name_offset], filename, filename_length);
+ buff[AR_name_offset + filename_length] = ' ';
}
else {
memcpy(buff + AR_name_offset, "#1/", 3);
- if (format_decimal(strlen(filename),
+ if (format_decimal(filename_length,
buff + AR_name_offset + 3,
AR_name_size - 3)) {
archive_set_error(&a->archive, ERANGE,
@@ -303,7 +301,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
append_fn = 1;
- size += strlen(filename);
+ size += filename_length;
}
}
@@ -353,10 +351,10 @@ size:
ar->entry_padding = ar->entry_bytes_remaining % 2;
if (append_fn > 0) {
- ret = __archive_write_output(a, filename, strlen(filename));
+ ret = __archive_write_output(a, filename, filename_length);
if (ret != ARCHIVE_OK)
return (ret);
- ar->entry_bytes_remaining -= strlen(filename);
+ ar->entry_bytes_remaining -= filename_length;
}
return (ARCHIVE_OK);
@@ -365,10 +363,9 @@ size:
static ssize_t
archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
{
- struct ar_w *ar;
+ struct ar *ar = a->format_data;
int ret;
- ar = (struct ar_w *)a->format_data;
if (s > ar->entry_bytes_remaining)
s = (size_t)ar->entry_bytes_remaining;
@@ -401,9 +398,7 @@ archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
static int
archive_write_ar_free(struct archive_write *a)
{
- struct ar_w *ar;
-
- ar = (struct ar_w *)a->format_data;
+ struct ar *ar = a->format_data;
if (ar == NULL)
return (ARCHIVE_OK);
@@ -421,14 +416,13 @@ archive_write_ar_free(struct archive_write *a)
static int
archive_write_ar_close(struct archive_write *a)
{
- struct ar_w *ar;
+ struct ar *ar = a->format_data;
int ret;
/*
* If we haven't written anything yet, we need to write
* the ar global header now to make it a valid ar archive.
*/
- ar = (struct ar_w *)a->format_data;
if (!ar->wrote_global_header) {
ar->wrote_global_header = 1;
ret = __archive_write_output(a, "!<arch>\n", 8);
@@ -441,11 +435,9 @@ archive_write_ar_close(struct archive_write *a)
static int
archive_write_ar_finish_entry(struct archive_write *a)
{
- struct ar_w *ar;
+ struct ar *ar = a->format_data;
int ret;
- ar = (struct ar_w *)a->format_data;
-
if (ar->entry_bytes_remaining != 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Entry remaining bytes larger than 0");
@@ -546,24 +538,3 @@ format_decimal(int64_t v, char *p, int s)
return (-1);
}
-
-static const char *
-ar_basename(const char *path)
-{
- const char *endp, *startp;
-
- endp = path + strlen(path) - 1;
- /*
- * For filename with trailing slash(es), we return
- * NULL indicating an error.
- */
- if (*endp == '/')
- return (NULL);
-
- /* Find the start of the base */
- startp = endp;
- while (startp > path && *(startp - 1) != '/')
- startp--;
-
- return (startp);
-}
diff --git a/libarchive/archive_write_set_format_cpio_binary.c b/libarchive/archive_write_set_format_cpio_binary.c
index b0864a0dc97c..6648b18d90ac 100644
--- a/libarchive/archive_write_set_format_cpio_binary.c
+++ b/libarchive/archive_write_set_format_cpio_binary.c
@@ -40,6 +40,7 @@
#include "archive.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
@@ -182,8 +183,7 @@ archive_write_set_format_cpio_binary(struct archive *_a, int format)
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
cpio = calloc(1, sizeof(*cpio));
if (cpio == NULL) {
@@ -235,7 +235,7 @@ static int
archive_write_binary_options(struct archive_write *a, const char *key,
const char *val)
{
- struct cpio *cpio = (struct cpio *)a->format_data;
+ struct cpio *cpio = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -307,10 +307,16 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
/* Ensure space for the new mapping. */
if (cpio->ino_list_size <= cpio->ino_list_next) {
- size_t newsize = cpio->ino_list_size < 512
- ? 512 : cpio->ino_list_size * 2;
- void *newlist = realloc(cpio->ino_list,
- sizeof(cpio->ino_list[0]) * newsize);
+ size_t newsize, size;
+ if (cpio->ino_list_size < 512)
+ newsize = 512;
+ else if (archive_ckd_mul_size(&newsize,
+ cpio->ino_list_size, 2))
+ return (-1);
+ if (archive_ckd_mul_size(&size,
+ newsize, sizeof(cpio->ino_list[0])))
+ return (-1);
+ void *newlist = realloc(cpio->ino_list, size);
if (newlist == NULL)
return (-1);
@@ -329,10 +335,9 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
static struct archive_string_conv *
get_sconv(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
struct archive_string_conv *sconv;
- cpio = (struct cpio *)a->format_data;
sconv = cpio->opt_sconv;
if (sconv == NULL) {
if (!cpio->init_default_conversion) {
@@ -378,7 +383,7 @@ archive_write_binary_header(struct archive_write *a, struct archive_entry *entry
static int
write_header(struct archive_write *a, struct archive_entry *entry)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
const char *p, *path;
int ret, ret_final;
int64_t ino;
@@ -387,7 +392,6 @@ write_header(struct archive_write *a, struct archive_entry *entry)
struct archive_entry *entry_main;
size_t len, pathlength;
- cpio = (struct cpio *)a->format_data;
ret_final = ARCHIVE_OK;
sconv = get_sconv(a);
@@ -561,10 +565,9 @@ exit_write_header:
static ssize_t
archive_write_binary_data(struct archive_write *a, const void *buff, size_t s)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
int ret;
- cpio = (struct cpio *)a->format_data;
if (s > cpio->entry_bytes_remaining)
s = (size_t)cpio->entry_bytes_remaining;
@@ -598,9 +601,8 @@ archive_write_binary_close(struct archive_write *a)
static int
archive_write_binary_free(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
free(cpio->ino_list);
free(cpio);
a->format_data = NULL;
@@ -610,9 +612,7 @@ archive_write_binary_free(struct archive_write *a)
static int
archive_write_binary_finish_entry(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
- return (__archive_write_nulls(a,
- (size_t)cpio->entry_bytes_remaining));
+ return (__archive_write_nulls(a, cpio->entry_bytes_remaining));
}
diff --git a/libarchive/archive_write_set_format_cpio_newc.c b/libarchive/archive_write_set_format_cpio_newc.c
index e281417bf6f9..b9de7d362e22 100644
--- a/libarchive/archive_write_set_format_cpio_newc.c
+++ b/libarchive/archive_write_set_format_cpio_newc.c
@@ -113,8 +113,7 @@ archive_write_set_format_cpio_newc(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
cpio = calloc(1, sizeof(*cpio));
if (cpio == NULL) {
@@ -138,7 +137,7 @@ static int
archive_write_newc_options(struct archive_write *a, const char *key,
const char *val)
{
- struct cpio *cpio = (struct cpio *)a->format_data;
+ struct cpio *cpio = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -166,10 +165,9 @@ archive_write_newc_options(struct archive_write *a, const char *key,
static struct archive_string_conv *
get_sconv(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
struct archive_string_conv *sconv;
- cpio = (struct cpio *)a->format_data;
sconv = cpio->opt_sconv;
if (sconv == NULL) {
if (!cpio->init_default_conversion) {
@@ -216,8 +214,8 @@ archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
static int
write_header(struct archive_write *a, struct archive_entry *entry)
{
+ struct cpio *cpio = a->format_data;
int64_t ino;
- struct cpio *cpio;
const char *p, *path;
int ret, ret_final;
char h[c_header_size];
@@ -226,7 +224,6 @@ write_header(struct archive_write *a, struct archive_entry *entry)
size_t len, pathlength;
int pad;
- cpio = (struct cpio *)a->format_data;
ret_final = ARCHIVE_OK;
sconv = get_sconv(a);
@@ -378,10 +375,9 @@ exit_write_header:
static ssize_t
archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
int ret;
- cpio = (struct cpio *)a->format_data;
if (s > cpio->entry_bytes_remaining)
s = (size_t)cpio->entry_bytes_remaining;
@@ -430,6 +426,8 @@ archive_write_newc_close(struct archive_write *a)
struct archive_entry *trailer;
trailer = archive_entry_new();
+ if (trailer == NULL)
+ return ARCHIVE_FATAL;
archive_entry_set_nlink(trailer, 1);
archive_entry_set_size(trailer, 0);
archive_entry_set_pathname(trailer, "TRAILER!!!");
@@ -442,9 +440,8 @@ archive_write_newc_close(struct archive_write *a)
static int
archive_write_newc_free(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
free(cpio);
a->format_data = NULL;
return (ARCHIVE_OK);
@@ -453,9 +450,8 @@ archive_write_newc_free(struct archive_write *a)
static int
archive_write_newc_finish_entry(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
return (__archive_write_nulls(a,
- (size_t)cpio->entry_bytes_remaining + cpio->padding));
+ cpio->entry_bytes_remaining + cpio->padding));
}
diff --git a/libarchive/archive_write_set_format_cpio_odc.c b/libarchive/archive_write_set_format_cpio_odc.c
index 6b9a7f4ec669..c384d4827288 100644
--- a/libarchive/archive_write_set_format_cpio_odc.c
+++ b/libarchive/archive_write_set_format_cpio_odc.c
@@ -40,6 +40,7 @@
#include "archive.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
@@ -107,8 +108,7 @@ archive_write_set_format_cpio_odc(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
cpio = calloc(1, sizeof(*cpio));
if (cpio == NULL) {
@@ -132,7 +132,7 @@ static int
archive_write_odc_options(struct archive_write *a, const char *key,
const char *val)
{
- struct cpio *cpio = (struct cpio *)a->format_data;
+ struct cpio *cpio = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -204,10 +204,16 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
/* Ensure space for the new mapping. */
if (cpio->ino_list_size <= cpio->ino_list_next) {
- size_t newsize = cpio->ino_list_size < 512
- ? 512 : cpio->ino_list_size * 2;
- void *newlist = realloc(cpio->ino_list,
- sizeof(cpio->ino_list[0]) * newsize);
+ size_t newsize, size;
+ if (cpio->ino_list_size < 512)
+ newsize = 512;
+ else if (archive_ckd_mul_size(&newsize,
+ cpio->ino_list_size, 2))
+ return (-1);
+ if (archive_ckd_mul_size(&size,
+ newsize, sizeof(cpio->ino_list[0])))
+ return (-1);
+ void *newlist = realloc(cpio->ino_list, size);
if (newlist == NULL)
return (-1);
@@ -226,10 +232,9 @@ synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
static struct archive_string_conv *
get_sconv(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
struct archive_string_conv *sconv;
- cpio = (struct cpio *)a->format_data;
sconv = cpio->opt_sconv;
if (sconv == NULL) {
if (!cpio->init_default_conversion) {
@@ -275,7 +280,7 @@ archive_write_odc_header(struct archive_write *a, struct archive_entry *entry)
static int
write_header(struct archive_write *a, struct archive_entry *entry)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
const char *p, *path;
int ret, ret_final;
int64_t ino;
@@ -284,7 +289,6 @@ write_header(struct archive_write *a, struct archive_entry *entry)
struct archive_entry *entry_main;
size_t len, pathlength;
- cpio = (struct cpio *)a->format_data;
ret_final = ARCHIVE_OK;
sconv = get_sconv(a);
@@ -420,10 +424,9 @@ exit_write_header:
static ssize_t
archive_write_odc_data(struct archive_write *a, const void *buff, size_t s)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
int ret;
- cpio = (struct cpio *)a->format_data;
if (s > cpio->entry_bytes_remaining)
s = (size_t)cpio->entry_bytes_remaining;
@@ -487,9 +490,8 @@ archive_write_odc_close(struct archive_write *a)
static int
archive_write_odc_free(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
free(cpio->ino_list);
free(cpio);
a->format_data = NULL;
@@ -499,9 +501,8 @@ archive_write_odc_free(struct archive_write *a)
static int
archive_write_odc_finish_entry(struct archive_write *a)
{
- struct cpio *cpio;
+ struct cpio *cpio = a->format_data;
- cpio = (struct cpio *)a->format_data;
- return (__archive_write_nulls(a,
- (size_t)cpio->entry_bytes_remaining));
+ return (__archive_write_nulls(a,
+ cpio->entry_bytes_remaining));
}
diff --git a/libarchive/archive_write_set_format_gnutar.c b/libarchive/archive_write_set_format_gnutar.c
index f2ba4a0db4dc..5a767e1bd72b 100644
--- a/libarchive/archive_write_set_format_gnutar.c
+++ b/libarchive/archive_write_set_format_gnutar.c
@@ -174,6 +174,11 @@ archive_write_set_format_gnutar(struct archive *_a)
struct archive_write *a = (struct archive_write *)_a;
struct gnutar *gnutar;
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_gnutar");
+
+ (void)__archive_write_unregister_format(a);
+
gnutar = calloc(1, sizeof(*gnutar));
if (gnutar == NULL) {
archive_set_error(&a->archive, ENOMEM,
@@ -197,7 +202,7 @@ static int
archive_write_gnutar_options(struct archive_write *a, const char *key,
const char *val)
{
- struct gnutar *gnutar = (struct gnutar *)a->format_data;
+ struct gnutar *gnutar = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -231,9 +236,8 @@ archive_write_gnutar_close(struct archive_write *a)
static int
archive_write_gnutar_free(struct archive_write *a)
{
- struct gnutar *gnutar;
+ struct gnutar *gnutar = a->format_data;
- gnutar = (struct gnutar *)a->format_data;
free(gnutar);
a->format_data = NULL;
return (ARCHIVE_OK);
@@ -242,12 +246,11 @@ archive_write_gnutar_free(struct archive_write *a)
static int
archive_write_gnutar_finish_entry(struct archive_write *a)
{
- struct gnutar *gnutar;
+ struct gnutar *gnutar = a->format_data;
int ret;
- gnutar = (struct gnutar *)a->format_data;
- ret = __archive_write_nulls(a, (size_t)
- (gnutar->entry_bytes_remaining + gnutar->entry_padding));
+ ret = __archive_write_nulls(a,
+ gnutar->entry_bytes_remaining + gnutar->entry_padding);
gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
return (ret);
}
@@ -255,10 +258,9 @@ archive_write_gnutar_finish_entry(struct archive_write *a)
static ssize_t
archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
{
- struct gnutar *gnutar;
+ struct gnutar *gnutar = a->format_data;
int ret;
- gnutar = (struct gnutar *)a->format_data;
if (s > gnutar->entry_bytes_remaining)
s = (size_t)gnutar->entry_bytes_remaining;
ret = __archive_write_output(a, buff, s);
@@ -272,15 +274,13 @@ static int
archive_write_gnutar_header(struct archive_write *a,
struct archive_entry *entry)
{
+ struct gnutar *gnutar = a->format_data;
char buff[512];
int r, ret, ret2 = ARCHIVE_OK;
char tartype;
- struct gnutar *gnutar;
struct archive_string_conv *sconv;
struct archive_entry *entry_main;
- gnutar = (struct gnutar *)a->format_data;
-
/* Setup default string conversion. */
if (gnutar->opt_sconv == NULL) {
if (!gnutar->init_default_conversion) {
@@ -494,6 +494,13 @@ archive_write_gnutar_header(struct archive_write *a,
size_t length = gnutar->linkname_length + 1;
struct archive_entry *temp = archive_entry_new2(&a->archive);
+ if (temp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
/* Uname/gname here don't really matter since no one reads them;
* these are the values that GNU tar happens to use on FreeBSD. */
archive_entry_set_uname(temp, "root");
@@ -524,6 +531,13 @@ archive_write_gnutar_header(struct archive_write *a,
size_t length = gnutar->pathname_length + 1;
struct archive_entry *temp = archive_entry_new2(&a->archive);
+ if (temp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
/* Uname/gname here don't really matter since no one reads them;
* these are the values that GNU tar happens to use on FreeBSD. */
archive_entry_set_uname(temp, "root");
@@ -589,13 +603,11 @@ static int
archive_format_gnutar_header(struct archive_write *a, char h[512],
struct archive_entry *entry, char tartype)
{
+ struct gnutar *gnutar = a->format_data;
unsigned int checksum;
int i, ret;
size_t copy_length;
const char *p;
- struct gnutar *gnutar;
-
- gnutar = (struct gnutar *)a->format_data;
ret = 0;
@@ -652,7 +664,7 @@ archive_format_gnutar_header(struct archive_write *a, char h[512],
copy_length = gnutar->gname_length;
}
if (copy_length > 0) {
- if (strlen(p) > GNUTAR_gname_size)
+ if (copy_length > GNUTAR_gname_size)
copy_length = GNUTAR_gname_size;
memcpy(h + GNUTAR_gname_offset, p, copy_length);
}
diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c
index e3cf9aad2a3b..ee50f7c0842b 100644
--- a/libarchive/archive_write_set_format_iso9660.c
+++ b/libarchive/archive_write_set_format_iso9660.c
@@ -1059,8 +1059,7 @@ archive_write_set_format_iso9660(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
/* If another format was already registered, unregister it. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
iso9660 = calloc(1, sizeof(*iso9660));
if (iso9660 == NULL) {
@@ -1427,8 +1426,8 @@ iso9660_options(struct archive_write *a, const char *key, const char *value)
break;
case 'i':
if (strcmp(key, "iso-level") == 0) {
- if (value != NULL && value[1] == '\0' &&
- (value[0] >= '1' && value[0] <= '4')) {
+ if (value != NULL && value[0] >= '1' &&
+ value[0] <= '4' && value[1] == '\0') {
iso9660->opt.iso_level = value[0]-'0';
return (ARCHIVE_OK);
}
@@ -1529,13 +1528,11 @@ invalid_value:
static int
iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
struct isofile *file;
struct isoent *isoent;
int r, ret = ARCHIVE_OK;
- iso9660 = a->format_data;
-
iso9660->cur_file = NULL;
iso9660->bytes_remaining = 0;
iso9660->need_multi_extent = 0;
@@ -1677,7 +1674,7 @@ wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)
* order to reduce a extra memory copy.
*/
if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
- struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ struct iso9660 *iso9660 = a->format_data;
xs = s % LOGICAL_BLOCK_SIZE;
iso9660->wbuff_offset += s - xs;
if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
@@ -1859,11 +1856,9 @@ iso9660_finish_entry(struct archive_write *a)
static int
iso9660_close(struct archive_write *a)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
int ret, blocks;
- iso9660 = a->format_data;
-
/*
* Write remaining data out to the temporary file.
*/
@@ -2121,11 +2116,9 @@ iso9660_close(struct archive_write *a)
static int
iso9660_free(struct archive_write *a)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
int i, ret;
- iso9660 = a->format_data;
-
/* Close the temporary file. */
if (iso9660->temp_fd >= 0)
close(iso9660->temp_fd);
@@ -2921,10 +2914,10 @@ set_directory_record_rr(unsigned char *bp, int dr_len,
extra_tell_used_size(&ctl, length);
if (extra_space(&ctl) < 6) {
bp = extra_next_record(&ctl, 6);
- nmmax = extra_space(&ctl);
- if (nmmax > 0xff)
- nmmax = 0xff;
}
+ nmmax = extra_space(&ctl);
+ if (nmmax > 0xff)
+ nmmax = 0xff;
if (bp != NULL) {
bp[1] = 'N';
bp[2] = 'M';
@@ -3619,7 +3612,7 @@ get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,
static inline unsigned char *
wb_buffptr(struct archive_write *a)
{
- struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ struct iso9660 *iso9660 = a->format_data;
return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
- iso9660->wbuff_remaining]));
@@ -3628,7 +3621,7 @@ wb_buffptr(struct archive_write *a)
static int
wb_write_out(struct archive_write *a)
{
- struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ struct iso9660 *iso9660 = a->format_data;
size_t wsize, nw;
int r;
@@ -3653,7 +3646,7 @@ wb_write_out(struct archive_write *a)
static int
wb_consume(struct archive_write *a, size_t size)
{
- struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ struct iso9660 *iso9660 = a->format_data;
if (size > iso9660->wbuff_remaining ||
iso9660->wbuff_remaining == 0) {
@@ -3674,7 +3667,7 @@ wb_consume(struct archive_write *a, size_t size)
static int
wb_set_offset(struct archive_write *a, int64_t off)
{
- struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ struct iso9660 *iso9660 = a->format_data;
int64_t used, ext_bytes;
if (iso9660->wbuff_type != WB_TO_TEMP) {
@@ -3841,7 +3834,7 @@ set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
static int
write_VD(struct archive_write *a, struct vdd *vdd)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
unsigned char *bp;
uint16_t volume_set_size = 1;
char identifier[256];
@@ -3850,7 +3843,6 @@ write_VD(struct archive_write *a, struct vdd *vdd)
unsigned char vd_ver, fst_ver;
int r;
- iso9660 = a->format_data;
switch (vdd->vdd_type) {
case VDD_JOLIET:
vdt = VDT_SUPPLEMENTARY;
@@ -3988,10 +3980,9 @@ write_VD(struct archive_write *a, struct vdd *vdd)
static int
write_VD_boot_record(struct archive_write *a)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
unsigned char *bp;
- iso9660 = a->format_data;
bp = wb_buffptr(a) -1;
/* Volume Descriptor Type */
set_VD_bp(bp, VDT_BOOT_RECORD, 1);
@@ -4059,7 +4050,7 @@ set_option_info(struct archive_string *info, int *opt, const char *key,
static int
write_information_block(struct archive_write *a)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
char buf[128];
const char *v;
int opt, r;
@@ -4067,7 +4058,6 @@ write_information_block(struct archive_write *a)
size_t info_size = LOGICAL_BLOCK_SIZE *
NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
- iso9660 = (struct iso9660 *)a->format_data;
if (info_size > wb_remaining(a)) {
r = wb_write_out(a);
if (r != ARCHIVE_OK)
@@ -4717,14 +4707,12 @@ cleanup_backslash_2(wchar_t *p)
static int
isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
const char *pathname;
char *p, *dirname, *slash;
size_t len;
int ret = ARCHIVE_OK;
- iso9660 = a->format_data;
-
archive_string_empty(&(file->parentdir));
archive_string_empty(&(file->basename));
archive_string_empty(&(file->basename_utf16));
@@ -5563,6 +5551,7 @@ get_path_component(char *name, size_t n, const char *fn)
static int
isoent_tree(struct archive_write *a, struct isoent **isoentpp)
{
+ struct iso9660 *iso9660 = a->format_data;
#if defined(_WIN32) && !defined(__CYGWIN__)
char name[_MAX_FNAME];/* Included null terminator size. */
#elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -5570,7 +5559,6 @@ isoent_tree(struct archive_write *a, struct isoent **isoentpp)
#else
char name[256];
#endif
- struct iso9660 *iso9660 = a->format_data;
struct isoent *dent, *isoent, *np;
struct isofile *f1, *f2;
const char *fn, *p;
@@ -5998,7 +5986,7 @@ static int
isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
struct idr *idr)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
struct isoent *np;
char *p;
int l, r;
@@ -6014,9 +6002,8 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
const int null_size = 1;
if (isoent->children.cnt == 0)
- return (0);
+ return (ARCHIVE_OK);
- iso9660 = a->format_data;
char_map = idr->char_map;
if (iso9660->opt.iso_level <= 3) {
allow_ldots = 0;
@@ -6258,7 +6245,7 @@ static int
isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
struct idr *idr)
{
- struct iso9660 *iso9660;
+ struct iso9660 *iso9660 = a->format_data;
struct isoent *np;
unsigned char *p;
size_t l;
@@ -6271,9 +6258,8 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
const int null_size = 2;
if (isoent->children.cnt == 0)
- return (0);
+ return (ARCHIVE_OK);
- iso9660 = a->format_data;
if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
ffmax = 206;
else
@@ -7522,8 +7508,9 @@ zisofs_init(struct archive_write *a, struct isofile *file)
(uint32_t)archive_entry_size(file->entry);
/* Calculate a size of Block Pointers of zisofs. */
- _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
- >> file->zisofs.log2_bs;
+ _ceil = (size_t)(((uint64_t)file->zisofs.uncompressed_size +
+ (ZF_BLOCK_SIZE - 1))
+ >> file->zisofs.log2_bs);
iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
iso9660->zisofs.block_pointers_idx = 0;
diff --git a/libarchive/archive_write_set_format_mtree.c b/libarchive/archive_write_set_format_mtree.c
index ed53849bfbbc..b3433507e91a 100644
--- a/libarchive/archive_write_set_format_mtree.c
+++ b/libarchive/archive_write_set_format_mtree.c
@@ -117,7 +117,7 @@ struct mtree_entry {
int64_t ino;
};
-struct mtree_writer {
+struct mtree {
struct mtree_entry *mtree_entry;
struct mtree_entry *root;
struct mtree_entry *cur_dirent;
@@ -220,10 +220,10 @@ static int attr_counter_inc(struct attr_counter **, struct attr_counter *,
struct attr_counter *, struct mtree_entry *);
static struct attr_counter * attr_counter_new(struct mtree_entry *,
struct attr_counter *);
-static int attr_counter_set_collect(struct mtree_writer *,
+static int attr_counter_set_collect(struct mtree *,
struct mtree_entry *);
-static void attr_counter_set_free(struct mtree_writer *);
-static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
+static void attr_counter_set_free(struct mtree *);
+static int get_global_set_keys(struct mtree *, struct mtree_entry *);
static int mtree_entry_add_child_tail(struct mtree_entry *,
struct mtree_entry *);
static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
@@ -236,14 +236,14 @@ static int mtree_entry_exchange_same_entry(struct archive_write *,
static void mtree_entry_free(struct mtree_entry *);
static int mtree_entry_new(struct archive_write *, struct archive_entry *,
struct mtree_entry **);
-static void mtree_entry_register_free(struct mtree_writer *);
-static void mtree_entry_register_init(struct mtree_writer *);
+static void mtree_entry_register_free(struct mtree *);
+static void mtree_entry_register_init(struct mtree *);
static int mtree_entry_setup_filenames(struct archive_write *,
struct mtree_entry *, struct archive_entry *);
static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
-static void sum_init(struct mtree_writer *);
-static void sum_update(struct mtree_writer *, const void *, size_t);
-static void sum_final(struct mtree_writer *, struct reg_info *);
+static void sum_init(struct mtree *);
+static void sum_update(struct mtree *, const void *, size_t);
+static void sum_final(struct mtree *, struct reg_info *);
static void sum_write(struct archive_string *, struct reg_info *);
static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
@@ -358,7 +358,7 @@ mtree_quote(struct archive_string *s, const char *str)
* Indent a line as the mtree utility does so it is readable for people.
*/
static void
-mtree_indent(struct mtree_writer *mtree)
+mtree_indent(struct mtree *mtree)
{
int i, fn, nd, pd;
const char *r, *s, *x;
@@ -434,7 +434,7 @@ mtree_indent(struct mtree_writer *mtree)
* collected by the attr_counter_set_collect() function.
*/
static void
-write_global(struct mtree_writer *mtree)
+write_global(struct mtree *mtree)
{
struct archive_string setstr;
struct archive_string unsetstr;
@@ -636,7 +636,7 @@ attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
* Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
*/
static int
-attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
+attr_counter_set_collect(struct mtree *mtree, struct mtree_entry *me)
{
struct attr_counter *ac, *last;
struct attr_counter_set *acs = &mtree->acs;
@@ -713,7 +713,7 @@ attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
}
static void
-attr_counter_set_free(struct mtree_writer *mtree)
+attr_counter_set_free(struct mtree *mtree)
{
struct attr_counter_set *acs = &mtree->acs;
@@ -724,7 +724,7 @@ attr_counter_set_free(struct mtree_writer *mtree)
}
static int
-get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
+get_global_set_keys(struct mtree *mtree, struct mtree_entry *me)
{
int keys;
@@ -913,7 +913,7 @@ static int
archive_write_mtree_header(struct archive_write *a,
struct archive_entry *entry)
{
- struct mtree_writer *mtree= a->format_data;
+ struct mtree *mtree = a->format_data;
struct mtree_entry *mtree_entry;
int r, r2;
@@ -956,7 +956,7 @@ archive_write_mtree_header(struct archive_write *a,
static int
write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
{
- struct mtree_writer *mtree = a->format_data;
+ struct mtree *mtree = a->format_data;
struct archive_string *str;
int keys, ret;
@@ -1107,7 +1107,7 @@ write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
static int
write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
{
- struct mtree_writer *mtree = a->format_data;
+ struct mtree *mtree = a->format_data;
int ret;
if (n->parentdir.s) {
@@ -1142,7 +1142,7 @@ write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
static int
write_mtree_entry_tree(struct archive_write *a)
{
- struct mtree_writer *mtree = a->format_data;
+ struct mtree *mtree = a->format_data;
struct mtree_entry *np = mtree->root;
struct archive_rb_node *n;
int ret;
@@ -1245,7 +1245,7 @@ write_mtree_entry_tree(struct archive_write *a)
static int
archive_write_mtree_finish_entry(struct archive_write *a)
{
- struct mtree_writer *mtree = a->format_data;
+ struct mtree *mtree = a->format_data;
struct mtree_entry *me;
if ((me = mtree->mtree_entry) == NULL)
@@ -1261,7 +1261,7 @@ archive_write_mtree_finish_entry(struct archive_write *a)
static int
archive_write_mtree_close(struct archive_write *a)
{
- struct mtree_writer *mtree= a->format_data;
+ struct mtree *mtree = a->format_data;
int ret;
if (mtree->root != NULL) {
@@ -1278,7 +1278,7 @@ archive_write_mtree_close(struct archive_write *a)
static ssize_t
archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
{
- struct mtree_writer *mtree= a->format_data;
+ struct mtree *mtree = a->format_data;
if (n > mtree->entry_bytes_remaining)
n = (size_t)mtree->entry_bytes_remaining;
@@ -1297,7 +1297,7 @@ archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
static int
archive_write_mtree_free(struct archive_write *a)
{
- struct mtree_writer *mtree= a->format_data;
+ struct mtree *mtree = a->format_data;
if (mtree == NULL)
return (ARCHIVE_OK);
@@ -1317,7 +1317,7 @@ static int
archive_write_mtree_options(struct archive_write *a, const char *key,
const char *value)
{
- struct mtree_writer *mtree= a->format_data;
+ struct mtree *mtree = a->format_data;
int keybit = 0;
switch (key[0]) {
@@ -1429,12 +1429,11 @@ static int
archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
{
struct archive_write *a = (struct archive_write *)_a;
- struct mtree_writer *mtree;
+ struct mtree *mtree;
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
archive_set_error(&a->archive, ENOMEM,
@@ -1481,9 +1480,7 @@ archive_write_set_format_mtree_classic(struct archive *_a)
"archive_write_set_format_mtree_classic");
if (r == ARCHIVE_OK) {
struct archive_write *a = (struct archive_write *)_a;
- struct mtree_writer *mtree;
-
- mtree = (struct mtree_writer *)a->format_data;
+ struct mtree *mtree = a->format_data;
/* Set to output a mtree archive in classic format. */
mtree->classic = 1;
@@ -1495,7 +1492,7 @@ archive_write_set_format_mtree_classic(struct archive *_a)
}
static void
-sum_init(struct mtree_writer *mtree)
+sum_init(struct mtree *mtree)
{
mtree->compute_sum = 0;
@@ -1556,7 +1553,7 @@ sum_init(struct mtree_writer *mtree)
}
static void
-sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+sum_update(struct mtree *mtree, const void *buff, size_t n)
{
if (mtree->compute_sum & F_CKSUM) {
/*
@@ -1614,7 +1611,7 @@ sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
}
static void
-sum_final(struct mtree_writer *mtree, struct reg_info *reg)
+sum_final(struct mtree *mtree, struct reg_info *reg)
{
struct ae_digest digest;
@@ -1995,7 +1992,7 @@ mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
}
static void
-mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
+mtree_entry_register_add(struct mtree *mtree, struct mtree_entry *file)
{
file->next = NULL;
*mtree->file_list.last = file;
@@ -2003,14 +2000,14 @@ mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
}
static void
-mtree_entry_register_init(struct mtree_writer *mtree)
+mtree_entry_register_init(struct mtree *mtree)
{
mtree->file_list.first = NULL;
mtree->file_list.last = &(mtree->file_list.first);
}
static void
-mtree_entry_register_free(struct mtree_writer *mtree)
+mtree_entry_register_free(struct mtree *mtree)
{
struct mtree_entry *file, *file_next;
@@ -2073,6 +2070,7 @@ get_path_component(char *name, size_t n, const char *fn)
static int
mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
{
+ struct mtree *mtree = a->format_data;
#if defined(_WIN32) && !defined(__CYGWIN__)
char name[_MAX_FNAME];/* Included null terminator size. */
#elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -2080,7 +2078,6 @@ mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
#else
char name[256];
#endif
- struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
struct mtree_entry *dent, *file, *np;
const char *fn, *p;
int l, r;
diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c
index cda1ffd904e4..1e27260501ba 100644
--- a/libarchive/archive_write_set_format_pax.c
+++ b/libarchive/archive_write_set_format_pax.c
@@ -45,6 +45,8 @@
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
+#define PAX_MAX_ALIGN (1U << 20)
+
struct sparse_block {
struct sparse_block *next;
int is_hole;
@@ -64,6 +66,10 @@ struct pax {
struct archive_string_conv *sconv_utf8;
int opt_binary;
+ /* If non-zero, align regular file data to this many bytes in the
+ * uncompressed stream (power of two, multiple of 512). */
+ size_t align;
+
unsigned flags;
#define WRITE_SCHILY_XATTR (1 << 0)
#define WRITE_LIBARCHIVE_XATTR (1 << 1)
@@ -79,6 +85,8 @@ static void add_pax_attr_int(struct archive_string *,
static void add_pax_attr_time(struct archive_string *,
const char *key, int64_t sec,
unsigned long nanos);
+static size_t add_pax_padding_prefix(struct archive_string *,
+ size_t cur_len, size_t target_len);
static int add_pax_acl(struct archive_write *,
struct archive_entry *, struct pax *, int);
static ssize_t archive_write_pax_data(struct archive_write *,
@@ -90,6 +98,7 @@ static int archive_write_pax_header(struct archive_write *,
struct archive_entry *);
static int archive_write_pax_options(struct archive_write *,
const char *, const char *);
+static int write_pax_padding(struct archive_write *, size_t);
static char *base64_encode(const char *src, size_t len);
static char *build_gnu_sparse_name(char *dest, const char *src);
static char *build_pax_attribute_name(char *dest, const char *src);
@@ -136,8 +145,7 @@ archive_write_set_format_pax(struct archive *_a)
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
pax = calloc(1, sizeof(*pax));
if (pax == NULL) {
@@ -164,7 +172,7 @@ static int
archive_write_pax_options(struct archive_write *a, const char *key,
const char *val)
{
- struct pax *pax = (struct pax *)a->format_data;
+ struct pax *pax = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -223,6 +231,25 @@ archive_write_pax_options(struct archive_write *a, const char *key,
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"pax: invalid xattr header name");
return (ret);
+ } else if (strcmp(key, "align") == 0) {
+ unsigned long v;
+ char *end;
+
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: align option needs a value");
+ return (ARCHIVE_FAILED);
+ }
+ v = strtoul(val, &end, 10);
+ if (*end != '\0' || v == 0 || (v % 512) != 0 ||
+ (v & (v - 1)) != 0 || v > PAX_MAX_ALIGN) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: align must be a power of two, a multiple "
+ "of 512, and no larger than 1 MiB");
+ return (ARCHIVE_FAILED);
+ }
+ pax->align = (size_t)v;
+ return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
@@ -361,6 +388,81 @@ add_pax_attr_binary(struct archive_string *as, const char *key,
archive_strappend_char(as, '\n');
}
+/*
+ * Append the length/key prefix of an ignorable "LIBARCHIVE.pad" record whose
+ * full length rounds the pax body up to 'target_len'. Return the number of
+ * value/newline bytes that must be emitted after the in-memory header.
+ */
+static size_t
+add_pax_padding_prefix(struct archive_string *as, size_t cur_len,
+ size_t target_len)
+{
+ static const char key[] = "LIBARCHIVE.pad";
+ char tmp[1 + 3 * sizeof(int64_t)];
+ char *length;
+ size_t prefix_len, record_len;
+
+ if (target_len <= cur_len)
+ return (0);
+ record_len = target_len - cur_len;
+ tmp[sizeof(tmp) - 1] = 0;
+ length = format_int(tmp + sizeof(tmp) - 1, (int64_t)record_len);
+ prefix_len = strlen(length) + 1 + sizeof(key) - 1 + 1;
+ if (record_len <= prefix_len)
+ return (0); /* not enough room; leave unaligned */
+
+ archive_strcat(as, length);
+ archive_strappend_char(as, ' ');
+ archive_strcat(as, key);
+ archive_strappend_char(as, '=');
+ return (record_len - prefix_len);
+}
+
+/* Emit a deferred pax padding value followed by its record newline. */
+static int
+write_pax_padding(struct archive_write *a, size_t length)
+{
+ char padding[1024];
+
+ if (length == 0)
+ return (ARCHIVE_OK);
+ memset(padding, 'X', sizeof(padding));
+ while (length > 1) {
+ size_t to_write = length - 1;
+ int r;
+
+ if (to_write > sizeof(padding))
+ to_write = sizeof(padding);
+ r = __archive_write_output(a, padding, to_write);
+ if (r != ARCHIVE_OK)
+ return (r);
+ length -= to_write;
+ }
+ return (__archive_write_output(a, "\n", 1));
+}
+
+/*
+ * For the pax "align" option: given entry offset 'off' (a multiple of 512) and
+ * current extended-header body length 'l0', return the body length needed so
+ * the file data (after the x header) lands on 'align', or 'l0' if already
+ * aligned.
+ */
+static size_t
+pax_align_body_len(uint64_t off, size_t l0, size_t align)
+{
+ uint64_t amask = (uint64_t)align - 1;
+ /* Data offset with no extra padding (no x header when l0 == 0). */
+ uint64_t natural_off = (l0 == 0) ? off + 512
+ : off + 1024 + (((uint64_t)l0 + 511) & ~(uint64_t)511);
+
+ if ((natural_off & amask) == 0)
+ return (l0);
+
+ /* Grow the body (past l0 plus pad-record room) to land data on align. */
+ uint64_t base = off + 1024;
+ return ((size_t)(((base + l0 + 64 + amask) & ~amask) - base));
+}
+
static void
archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
const void *value, size_t value_len)
@@ -578,6 +680,7 @@ static int
archive_write_pax_header(struct archive_write *a,
struct archive_entry *entry_original)
{
+ struct pax *pax = a->format_data;
struct archive_entry *entry_main;
const char *p;
const char *suffix;
@@ -585,7 +688,6 @@ archive_write_pax_header(struct archive_write *a,
int acl_types;
int sparse_count;
uint64_t sparse_total, real_size;
- struct pax *pax;
const char *hardlink;
const char *path = NULL, *linkpath = NULL;
const char *uname = NULL, *gname = NULL;
@@ -593,6 +695,7 @@ archive_write_pax_header(struct archive_write *a,
size_t mac_metadata_size;
struct archive_string_conv *sconv;
size_t hardlink_length, path_length, linkpath_length;
+ size_t pax_header_padding = 0;
size_t uname_length, gname_length;
char paxbuff[512];
@@ -604,7 +707,6 @@ archive_write_pax_header(struct archive_write *a,
ret = ARCHIVE_OK;
need_extension = 0;
- pax = (struct pax *)a->format_data;
const time_t ustar_max_mtime = get_ustar_max_mtime();
@@ -1338,14 +1440,6 @@ archive_write_pax_header(struct archive_write *a,
archive_entry_set_size(entry_main, 0);
/*
- * Pax-restricted does not store data for hardlinks, in order
- * to improve compatibility with ustar.
- */
- if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
- hardlink != NULL)
- archive_entry_set_size(entry_main, 0);
-
- /*
* XXX Full pax interchange format does permit a hardlink
* entry to have data associated with it. I'm not supporting
* that here because the client expects me to tell them whether
@@ -1411,6 +1505,21 @@ archive_write_pax_header(struct archive_write *a,
return (ARCHIVE_FATAL);
}
+ /* Pad the extended header so contiguous file data starts on a pax->align
+ * boundary (for reflinking); short and sparse files are left alone. */
+ if (pax->align > 0
+ && archive_entry_filetype(entry_main) == AE_IFREG
+ && hardlink == NULL
+ && sparse_count == 0
+ && real_size >= (uint64_t)pax->align) {
+ size_t l0 = archive_strlen(&(pax->pax_header));
+ size_t needed = pax_align_body_len(
+ (uint64_t)a->filter_first->bytes_written, l0, pax->align);
+ if (needed != l0)
+ pax_header_padding = add_pax_padding_prefix(
+ &(pax->pax_header), l0, needed);
+ }
+
/* If we built any extended attributes, write that entry first. */
if (archive_strlen(&(pax->pax_header)) > 0) {
struct archive_entry *pax_attr_entry;
@@ -1419,11 +1528,20 @@ archive_write_pax_header(struct archive_write *a,
__LA_MODE_T mode;
pax_attr_entry = archive_entry_new2(&a->archive);
+ if (pax_attr_entry == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
p = entry_name.s;
archive_entry_set_pathname(pax_attr_entry,
build_pax_attribute_name(pax_entry_name, p));
archive_entry_set_size(pax_attr_entry,
- archive_strlen(&(pax->pax_header)));
+ archive_strlen(&(pax->pax_header)) +
+ pax_header_padding);
/* Copy uid/gid (but clip to ustar limits). */
uid = archive_entry_uid(entry_main);
if (uid >= 1 << 18)
@@ -1489,7 +1607,8 @@ archive_write_pax_header(struct archive_write *a,
return (ARCHIVE_FATAL);
}
- pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
+ pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)) +
+ pax_header_padding;
pax->entry_padding =
0x1ff & (-(int64_t)pax->entry_bytes_remaining);
@@ -1501,8 +1620,14 @@ archive_write_pax_header(struct archive_write *a,
archive_string_free(&entry_name);
return (ARCHIVE_FATAL);
}
+ r = write_pax_padding(a, pax_header_padding);
+ if (r != ARCHIVE_OK) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
/* Pad out the end of the entry. */
- r = __archive_write_nulls(a, (size_t)pax->entry_padding);
+ r = __archive_write_nulls(a, pax->entry_padding);
if (r != ARCHIVE_OK) {
/* If a write fails, we're pretty much toast. */
archive_entry_free(entry_main);
@@ -1574,18 +1699,21 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length,
char *p;
int need_slash = 0; /* Was there a trailing slash? */
size_t suffix_length = 98; /* 99 - 1 for trailing slash */
- size_t insert_length;
+ size_t insert_length, insert_name_length;
/* Length of additional dir element to be added. */
- if (insert == NULL)
+ if (insert == NULL) {
+ insert_name_length = 0;
insert_length = 0;
- else
+ } else {
+ insert_name_length = strlen(insert);
/* +2 here allows for '/' before and after the insert. */
- insert_length = strlen(insert) + 2;
+ insert_length = insert_name_length + 2;
+ }
/* Step 0: Quick bailout in a common case. */
if (src_length < 100 && insert == NULL) {
- strncpy(dest, src, src_length);
+ memcpy(dest, src, src_length);
dest[src_length] = '\0';
return (dest);
}
@@ -1607,6 +1735,26 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length,
}
break;
}
+
+ /*
+ * Pathological case: after trimming trailing '/' characters and
+ * '/.' path elements, there is no filename component left. This
+ * happens for pathnames made entirely of '/' characters. Do not
+ * attempt to compute filename_end - 1 in that case, which would
+ * move the filename pointer in front of the input buffer and read
+ * one byte out of bounds. Emit a root-like ustar name instead.
+ */
+ if (filename_end == src) {
+ p = dest;
+ if (insert != NULL) {
+ strcpy(p, insert);
+ p += strlen(insert);
+ }
+ *p++ = '/';
+ *p = '\0';
+ return (dest);
+ }
+
if (need_slash)
suffix_length--;
/* Find start of filename. */
@@ -1647,24 +1795,22 @@ build_ustar_entry_name(char *dest, const char *src, size_t src_length,
suffix_end++;
/* Step 4: Build the new name. */
- /* The OpenBSD strlcpy function is safer, but less portable. */
- /* Rather than maintain two versions, just use the strncpy version. */
p = dest;
if (prefix_end > prefix) {
- strncpy(p, prefix, prefix_end - prefix);
+ memcpy(p, prefix, (size_t)(prefix_end - prefix));
p += prefix_end - prefix;
}
if (suffix_end > suffix) {
- strncpy(p, suffix, suffix_end - suffix);
+ memcpy(p, suffix, (size_t)(suffix_end - suffix));
p += suffix_end - suffix;
}
if (insert != NULL) {
/* Note: assume insert does not have leading or trailing '/' */
- strcpy(p, insert);
- p += strlen(insert);
+ memcpy(p, insert, insert_name_length);
+ p += insert_name_length;
*p++ = '/';
}
- strncpy(p, filename, filename_end - filename);
+ memcpy(p, filename, (size_t)(filename_end - filename));
p += filename_end - filename;
if (need_slash)
*p++ = '/';
@@ -1794,7 +1940,12 @@ build_gnu_sparse_name(char *dest, const char *src)
}
/* General case: build a ustar-compatible name adding
- * "/GNUSparseFile/". */
+ * "/GNUSparseFile.0/". */
+
+ if (p == src) {
+ strcpy(dest, "/GNUSparseFile.0/rootdir");
+ return (dest);
+ }
build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
return (dest);
@@ -1810,9 +1961,8 @@ archive_write_pax_close(struct archive_write *a)
static int
archive_write_pax_free(struct archive_write *a)
{
- struct pax *pax;
+ struct pax *pax = a->format_data;
- pax = (struct pax *)a->format_data;
if (pax == NULL)
return (ARCHIVE_OK);
@@ -1828,11 +1978,10 @@ archive_write_pax_free(struct archive_write *a)
static int
archive_write_pax_finish_entry(struct archive_write *a)
{
- struct pax *pax;
+ struct pax *pax = a->format_data;
uint64_t remaining;
int ret;
- pax = (struct pax *)a->format_data;
remaining = pax->entry_bytes_remaining;
if (remaining == 0) {
while (pax->sparse_list) {
@@ -1844,7 +1993,7 @@ archive_write_pax_finish_entry(struct archive_write *a)
pax->sparse_list = sb;
}
}
- ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));
+ ret = __archive_write_nulls(a, remaining + pax->entry_padding);
pax->entry_bytes_remaining = pax->entry_padding = 0;
return (ret);
}
@@ -1852,13 +2001,11 @@ archive_write_pax_finish_entry(struct archive_write *a)
static ssize_t
archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
{
- struct pax *pax;
+ struct pax *pax = a->format_data;
size_t ws;
size_t total;
int ret;
- pax = (struct pax *)a->format_data;
-
/*
* According to GNU PAX format 1.0, write a sparse map
* before the body.
@@ -1980,20 +2127,27 @@ base64_encode(const char *s, size_t len)
'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',
'8','9','+','/' };
- int v;
+ uint32_t v;
char *d, *out;
+ size_t out_len;
/* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */
- out = malloc((len * 4 + 2) / 3 + 1);
+ if (archive_ckd_mul_size(&out_len, len, 4) ||
+ archive_ckd_add_size(&out_len, out_len, 2))
+ return (NULL);
+ out_len = out_len / 3;
+ if (archive_ckd_add_size(&out_len, out_len, 1))
+ return (NULL);
+ out = malloc(out_len);
if (out == NULL)
return (NULL);
d = out;
/* Convert each group of 3 bytes into 4 characters. */
while (len >= 3) {
- v = (((int)s[0] << 16) & 0xff0000)
- | (((int)s[1] << 8) & 0xff00)
- | (((int)s[2]) & 0x00ff);
+ v = ((uint32_t)(unsigned char)s[0] << 16)
+ | ((uint32_t)(unsigned char)s[1] << 8)
+ | (uint32_t)(unsigned char)s[2];
s += 3;
len -= 3;
*d++ = digits[(v >> 18) & 0x3f];
@@ -2005,13 +2159,13 @@ base64_encode(const char *s, size_t len)
switch (len) {
case 0: break;
case 1:
- v = (((int)s[0] << 16) & 0xff0000);
+ v = ((uint32_t)(unsigned char)s[0] << 16);
*d++ = digits[(v >> 18) & 0x3f];
*d++ = digits[(v >> 12) & 0x3f];
break;
case 2:
- v = (((int)s[0] << 16) & 0xff0000)
- | (((int)s[1] << 8) & 0xff00);
+ v = ((uint32_t)(unsigned char)s[0] << 16)
+ | ((uint32_t)(unsigned char)s[1] << 8);
*d++ = digits[(v >> 18) & 0x3f];
*d++ = digits[(v >> 12) & 0x3f];
*d++ = digits[(v >> 6) & 0x3f];
diff --git a/libarchive/archive_write_set_format_raw.c b/libarchive/archive_write_set_format_raw.c
index ff3e9ae0ebdb..20c42e3d30f7 100644
--- a/libarchive/archive_write_set_format_raw.c
+++ b/libarchive/archive_write_set_format_raw.c
@@ -55,8 +55,7 @@ archive_write_set_format_raw(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_raw");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
raw = calloc(1, sizeof(*raw));
if (raw == NULL) {
@@ -82,7 +81,7 @@ archive_write_set_format_raw(struct archive *_a)
static int
archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)
{
- struct raw *raw = (struct raw *)a->format_data;
+ struct raw *raw = a->format_data;
if (archive_entry_filetype(entry) != AE_IFREG) {
archive_set_error(&a->archive, ERANGE,
@@ -116,9 +115,8 @@ archive_write_raw_data(struct archive_write *a, const void *buff, size_t s)
static int
archive_write_raw_free(struct archive_write *a)
{
- struct raw *raw;
+ struct raw *raw = a->format_data;
- raw = (struct raw *)a->format_data;
free(raw);
a->format_data = NULL;
return (ARCHIVE_OK);
diff --git a/libarchive/archive_write_set_format_shar.c b/libarchive/archive_write_set_format_shar.c
index ad8d5b78dc0e..ef4b3d907c08 100644
--- a/libarchive/archive_write_set_format_shar.c
+++ b/libarchive/archive_write_set_format_shar.c
@@ -98,11 +98,8 @@ shar_quote(struct archive_string *buf, const char *str, int in_shell)
}
}
-/*
- * Set output format to 'shar' format.
- */
-int
-archive_write_set_format_shar(struct archive *_a)
+static int
+shar_set_format(struct archive *_a, int dump)
{
struct archive_write *a = (struct archive_write *)_a;
struct shar *shar;
@@ -111,8 +108,7 @@ archive_write_set_format_shar(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
shar = calloc(1, sizeof(*shar));
if (shar == NULL) {
@@ -121,50 +117,54 @@ archive_write_set_format_shar(struct archive *_a)
}
archive_string_init(&shar->work);
archive_string_init(&shar->quoted_name);
+ if (dump) {
+ shar->dump = 1;
+ a->format_write_data = archive_write_shar_data_uuencode;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+ a->archive.archive_format_name = "shar dump";
+ } else {
+ a->format_write_data = archive_write_shar_data_sed;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+ a->archive.archive_format_name = "shar";
+ }
a->format_data = shar;
a->format_name = "shar";
a->format_write_header = archive_write_shar_header;
a->format_close = archive_write_shar_close;
a->format_free = archive_write_shar_free;
- a->format_write_data = archive_write_shar_data_sed;
a->format_finish_entry = archive_write_shar_finish_entry;
- a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
- a->archive.archive_format_name = "shar";
return (ARCHIVE_OK);
}
/*
+ * Set output format to 'shar' format.
+ */
+int
+archive_write_set_format_shar(struct archive *a)
+{
+ return shar_set_format(a, 0);
+}
+
+/*
* An alternate 'shar' that uses uudecode instead of 'sed' to encode
* file contents and can therefore be used to archive binary files.
* In addition, this variant also attempts to restore ownership, file modes,
* and other extended file information.
*/
int
-archive_write_set_format_shar_dump(struct archive *_a)
+archive_write_set_format_shar_dump(struct archive *a)
{
- struct archive_write *a = (struct archive_write *)_a;
- struct shar *shar;
-
- int ret = archive_write_set_format_shar(&a->archive);
- if (ret != ARCHIVE_OK)
- return ret;
- shar = (struct shar *)a->format_data;
- shar->dump = 1;
- a->format_write_data = archive_write_shar_data_uuencode;
- a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
- a->archive.archive_format_name = "shar dump";
- return (ARCHIVE_OK);
+ return shar_set_format(a, 1);
}
static int
archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
{
+ struct shar *shar = a->format_data;
const char *linkname;
const char *name;
char *p, *pp;
- struct shar *shar;
- shar = (struct shar *)a->format_data;
if (!shar->wrote_header) {
archive_strcat(&shar->work, "#!/bin/sh\n");
archive_strcat(&shar->work, "# This is a shell archive\n");
@@ -343,14 +343,13 @@ archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
static ssize_t
archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
{
+ struct shar *shar = a->format_data;
static const size_t ensured = 65533;
- struct shar *shar;
const char *src;
char *buf, *buf_end;
int ret;
size_t written = n;
- shar = (struct shar *)a->format_data;
if (!shar->has_data || n == 0)
return (0);
@@ -475,12 +474,11 @@ static ssize_t
archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
size_t length)
{
- struct shar *shar;
+ struct shar *shar = a->format_data;
const char *src;
size_t n;
int ret;
- shar = (struct shar *)a->format_data;
if (!shar->has_data)
return (ARCHIVE_OK);
src = (const char *)buff;
@@ -524,13 +522,12 @@ archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
static int
archive_write_shar_finish_entry(struct archive_write *a)
{
+ struct shar *shar = a->format_data;
const char *g, *p, *u;
- struct shar *shar;
int ret;
- shar = (struct shar *)a->format_data;
if (shar->entry == NULL)
- return (0);
+ return (ARCHIVE_OK);
if (shar->dump) {
/* Finish uuencoded data. */
@@ -602,7 +599,7 @@ archive_write_shar_finish_entry(struct archive_write *a)
static int
archive_write_shar_close(struct archive_write *a)
{
- struct shar *shar;
+ struct shar *shar = a->format_data;
int ret;
/*
@@ -610,8 +607,6 @@ archive_write_shar_close(struct archive_write *a)
* fix them all up at end-of-archive.
*/
- shar = (struct shar *)a->format_data;
-
/*
* Only write the end-of-archive markers if the archive was
* actually started. This avoids problems if someone sets
@@ -640,9 +635,8 @@ archive_write_shar_close(struct archive_write *a)
static int
archive_write_shar_free(struct archive_write *a)
{
- struct shar *shar;
+ struct shar *shar = a->format_data;
- shar = (struct shar *)a->format_data;
if (shar == NULL)
return (ARCHIVE_OK);
diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c
index e84152ea18f5..a55cbb75b0f7 100644
--- a/libarchive/archive_write_set_format_ustar.c
+++ b/libarchive/archive_write_set_format_ustar.c
@@ -172,8 +172,7 @@ archive_write_set_format_ustar(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
/* Basic internal sanity test. */
if (sizeof(template_header) != 512) {
@@ -206,7 +205,7 @@ static int
archive_write_ustar_options(struct archive_write *a, const char *key,
const char *val)
{
- struct ustar *ustar = (struct ustar *)a->format_data;
+ struct ustar *ustar = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -234,14 +233,12 @@ archive_write_ustar_options(struct archive_write *a, const char *key,
static int
archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
{
+ struct ustar *ustar = a->format_data;
char buff[512];
int ret, ret2;
- struct ustar *ustar;
struct archive_entry *entry_main;
struct archive_string_conv *sconv;
- ustar = (struct ustar *)a->format_data;
-
/* Setup default string conversion. */
if (ustar->opt_sconv == NULL) {
if (!ustar->init_default_conversion) {
@@ -736,9 +733,8 @@ archive_write_ustar_close(struct archive_write *a)
static int
archive_write_ustar_free(struct archive_write *a)
{
- struct ustar *ustar;
+ struct ustar *ustar = a->format_data;
- ustar = (struct ustar *)a->format_data;
free(ustar);
a->format_data = NULL;
return (ARCHIVE_OK);
@@ -747,12 +743,11 @@ archive_write_ustar_free(struct archive_write *a)
static int
archive_write_ustar_finish_entry(struct archive_write *a)
{
- struct ustar *ustar;
+ struct ustar *ustar = a->format_data;
int ret;
- ustar = (struct ustar *)a->format_data;
ret = __archive_write_nulls(a,
- (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
+ ustar->entry_bytes_remaining + ustar->entry_padding);
ustar->entry_bytes_remaining = ustar->entry_padding = 0;
return (ret);
}
@@ -760,10 +755,9 @@ archive_write_ustar_finish_entry(struct archive_write *a)
static ssize_t
archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
{
- struct ustar *ustar;
+ struct ustar *ustar = a->format_data;
int ret;
- ustar = (struct ustar *)a->format_data;
if (s > ustar->entry_bytes_remaining)
s = (size_t)ustar->entry_bytes_remaining;
ret = __archive_write_output(a, buff, s);
diff --git a/libarchive/archive_write_set_format_v7tar.c b/libarchive/archive_write_set_format_v7tar.c
index 91c5e8e42f0b..74875547b089 100644
--- a/libarchive/archive_write_set_format_v7tar.c
+++ b/libarchive/archive_write_set_format_v7tar.c
@@ -149,8 +149,7 @@ archive_write_set_format_v7tar(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
/* If someone else was already registered, unregister them. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
/* Basic internal sanity test. */
if (sizeof(template_header) != 512) {
@@ -183,7 +182,7 @@ static int
archive_write_v7tar_options(struct archive_write *a, const char *key,
const char *val)
{
- struct v7tar *v7tar = (struct v7tar *)a->format_data;
+ struct v7tar *v7tar = a->format_data;
int ret = ARCHIVE_FAILED;
if (strcmp(key, "hdrcharset") == 0) {
@@ -211,14 +210,12 @@ archive_write_v7tar_options(struct archive_write *a, const char *key,
static int
archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
{
+ struct v7tar *v7tar = a->format_data;
char buff[512];
int ret, ret2;
- struct v7tar *v7tar;
struct archive_entry *entry_main;
struct archive_string_conv *sconv;
- v7tar = (struct v7tar *)a->format_data;
-
/* Setup default string conversion. */
if (v7tar->opt_sconv == NULL) {
if (!v7tar->init_default_conversion) {
@@ -616,9 +613,8 @@ archive_write_v7tar_close(struct archive_write *a)
static int
archive_write_v7tar_free(struct archive_write *a)
{
- struct v7tar *v7tar;
+ struct v7tar *v7tar = a->format_data;
- v7tar = (struct v7tar *)a->format_data;
free(v7tar);
a->format_data = NULL;
return (ARCHIVE_OK);
@@ -627,12 +623,11 @@ archive_write_v7tar_free(struct archive_write *a)
static int
archive_write_v7tar_finish_entry(struct archive_write *a)
{
- struct v7tar *v7tar;
+ struct v7tar *v7tar = a->format_data;
int ret;
- v7tar = (struct v7tar *)a->format_data;
ret = __archive_write_nulls(a,
- (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
+ v7tar->entry_bytes_remaining + v7tar->entry_padding);
v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
return (ret);
}
@@ -640,10 +635,9 @@ archive_write_v7tar_finish_entry(struct archive_write *a)
static ssize_t
archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
{
- struct v7tar *v7tar;
+ struct v7tar *v7tar = a->format_data;
int ret;
- v7tar = (struct v7tar *)a->format_data;
if (s > v7tar->entry_bytes_remaining)
s = (size_t)v7tar->entry_bytes_remaining;
ret = __archive_write_output(a, buff, s);
diff --git a/libarchive/archive_write_set_format_warc.c b/libarchive/archive_write_set_format_warc.c
index 2192995362a5..d06abdb372db 100644
--- a/libarchive/archive_write_set_format_warc.c
+++ b/libarchive/archive_write_set_format_warc.c
@@ -49,68 +49,81 @@
#include "archive_write_private.h"
#include "archive_write_set_format_private.h"
-struct warc_s {
+/*
+ * Overview of the WARC writer:
+ *
+ * This writer emits WARC/1.0 resource records for regular files. It
+ * writes a warcinfo record once by default, unless the omit-warcinfo
+ * option is used. Each resource record gets a WARC-Target-URI from
+ * the entry pathname, a generated WARC-Record-ID, and a mandatory
+ * Content-Length header.
+ */
+
+struct warc {
unsigned int omit_warcinfo:1;
time_t now;
- mode_t typ;
- unsigned int rng;
- /* populated size */
- uint64_t populz;
+ mode_t filetype;
+ /* Remaining bytes to write for the current entry */
+ uint64_t entry_bytes_remaining;
};
-static const char warcinfo[] =
+#define WARC_HEADER_MAX_SIZE 512
+
+static const char warcinfo_payload[] =
"software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
"format: WARC file version 1.0\r\n";
-typedef enum {
- WT_NONE,
- /* warcinfo */
- WT_INFO,
- /* metadata */
- WT_META,
- /* resource */
- WT_RSRC,
- /* request, unsupported */
- WT_REQ,
- /* response, unsupported */
- WT_RSP,
- /* revisit, unsupported */
- WT_RVIS,
- /* conversion, unsupported */
- WT_CONV,
- /* continuation, unsupported at the moment */
- WT_CONT,
- /* invalid type */
- LAST_WT
-} warc_type_t;
+enum warc_type {
+ WARC_TYPE_NONE,
+ /* WARC info */
+ WARC_TYPE_INFO,
+ /* Metadata */
+ WARC_TYPE_METADATA,
+ /* Resource */
+ WARC_TYPE_RESOURCE,
+ /* Request, unsupported */
+ WARC_TYPE_REQUEST,
+ /* Response, unsupported by this writer */
+ WARC_TYPE_RESPONSE,
+ /* Revisit, unsupported */
+ WARC_TYPE_REVISIT,
+ /* Conversion, unsupported */
+ WARC_TYPE_CONVERSION,
+ /* Continuation, currently unsupported */
+ WARC_TYPE_CONTINUATION,
+ WARC_TYPE_LAST
+};
-typedef struct {
- warc_type_t type;
- const char *tgturi;
- const char *recid;
- time_t rtime;
- time_t mtime;
- const char *cnttyp;
- uint64_t cntlen;
-} warc_essential_hdr_t;
+struct warc_header {
+ enum warc_type type;
+ const char *target_uri;
+ const char *record_id;
+ time_t record_time;
+ time_t modification_time;
+ const char *content_type;
+ uint64_t content_length;
+};
-typedef struct {
- unsigned int u[4U];
-} warc_uuid_t;
+struct warc_uuid {
+ unsigned int value[4U];
+};
-static int _warc_options(struct archive_write*, const char *key, const char *v);
-static int _warc_header(struct archive_write *a, struct archive_entry *entry);
-static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
-static int _warc_finish_entry(struct archive_write *a);
-static int _warc_close(struct archive_write *a);
-static int _warc_free(struct archive_write *a);
+static int archive_write_warc_options(struct archive_write *,
+ const char *, const char *);
+static int archive_write_warc_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t archive_write_warc_data(struct archive_write *, const void *,
+ size_t);
+static int archive_write_warc_finish_entry(struct archive_write *);
+static int archive_write_warc_close(struct archive_write *);
+static int archive_write_warc_free(struct archive_write *);
-/* private routines */
-static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
-static int _gen_uuid(warc_uuid_t *tgt);
+static void warc_format_time(struct archive_string *, const char *, time_t);
+static ssize_t warc_populate_header(struct archive_string *, size_t,
+ struct warc_header);
+static void warc_generate_uuid(struct warc_uuid *);
-
/*
* Set output format to ISO 28500 (aka WARC) format.
*/
@@ -118,103 +131,110 @@ int
archive_write_set_format_warc(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
- struct warc_s *w;
+ struct warc *warc;
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
/* If another format was already registered, unregister it. */
- if (a->format_free != NULL) {
- (a->format_free)(a);
- }
+ (void)__archive_write_unregister_format(a);
- w = malloc(sizeof(*w));
- if (w == NULL) {
+ warc = malloc(sizeof(*warc));
+ if (warc == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate warc data");
return (ARCHIVE_FATAL);
}
- /* by default we're emitting a file wide header */
- w->omit_warcinfo = 0U;
- /* obtain current time for date fields */
- w->now = time(NULL);
- /* reset file type info */
- w->typ = 0;
- /* also initialise our rng */
- w->rng = (unsigned int)w->now;
+ /* Emit a warcinfo record by default. */
+ warc->omit_warcinfo = 0U;
+ /* Use the current time for WARC-Date values. */
+ warc->now = time(NULL);
+ /* Reset file type information. */
+ warc->filetype = 0;
- a->format_data = w;
+ a->format_data = warc;
a->format_name = "WARC/1.0";
- a->format_options = _warc_options;
- a->format_write_header = _warc_header;
- a->format_write_data = _warc_data;
- a->format_close = _warc_close;
- a->format_free = _warc_free;
- a->format_finish_entry = _warc_finish_entry;
+ a->format_options = archive_write_warc_options;
+ a->format_write_header = archive_write_warc_header;
+ a->format_write_data = archive_write_warc_data;
+ a->format_close = archive_write_warc_close;
+ a->format_free = archive_write_warc_free;
+ a->format_finish_entry = archive_write_warc_finish_entry;
a->archive.archive_format = ARCHIVE_FORMAT_WARC;
a->archive.archive_format_name = "WARC/1.0";
return (ARCHIVE_OK);
}
-
-/* archive methods */
static int
-_warc_options(struct archive_write *a, const char *key, const char *val)
+archive_write_warc_options(struct archive_write *a, const char *key,
+ const char *val)
{
- struct warc_s *w = a->format_data;
+ struct warc *warc = a->format_data;
if (strcmp(key, "omit-warcinfo") == 0) {
if (val == NULL || strcmp(val, "true") == 0) {
- /* great */
- w->omit_warcinfo = 1U;
+ /* Option accepted. */
+ warc->omit_warcinfo = 1U;
return (ARCHIVE_OK);
}
}
- /* Note: The "warn" return is just to inform the options
- * supervisor that we didn't handle it. It will generate
- * a suitable error if no one used this option. */
+ /* ARCHIVE_WARN tells the options supervisor that this option was not
+ * handled here. It will report an error if no module uses it. */
return (ARCHIVE_WARN);
}
static int
-_warc_header(struct archive_write *a, struct archive_entry *entry)
+archive_write_warc_header(struct archive_write *a, struct archive_entry *entry)
{
- struct warc_s *w = a->format_data;
- struct archive_string hdr;
-#define MAX_HDR_SIZE 512
+ struct warc *warc = a->format_data;
+ struct archive_string header;
- /* check whether warcinfo record needs outputting */
- if (!w->omit_warcinfo) {
- ssize_t r;
- warc_essential_hdr_t wi = {
- WT_INFO,
- /*uri*/NULL,
- /*urn*/NULL,
- /*rtm*/0,
- /*mtm*/0,
- /*cty*/"application/warc-fields",
- /*len*/sizeof(warcinfo) - 1U,
+ /* Emit the warcinfo record if needed. */
+ if (!warc->omit_warcinfo) {
+ ssize_t header_size;
+ int ret;
+ struct warc_header warcinfo_header = {
+ WARC_TYPE_INFO,
+ /* URI */NULL,
+ /* Record ID */NULL,
+ /* Record time */0,
+ /* Modified time */0,
+ /* Content type */"application/warc-fields",
+ /* Content length */sizeof(warcinfo_payload) - 1U,
};
- wi.rtime = w->now;
- wi.mtime = w->now;
+ warcinfo_header.record_time = warc->now;
+ warcinfo_header.modification_time = warc->now;
+
+ archive_string_init(&header);
+ header_size = warc_populate_header(&header,
+ WARC_HEADER_MAX_SIZE, warcinfo_header);
+ if (header_size < 0) {
+ archive_string_free(&header);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot archive warcinfo record");
+ return (ARCHIVE_FAILED);
+ }
- archive_string_init(&hdr);
- r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
- if (r >= 0) {
- /* jackpot! */
- /* now also use HDR buffer for the actual warcinfo */
- archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
+ /* Reuse the header buffer for the warcinfo payload. */
+ archive_strncat(&header, warcinfo_payload,
+ sizeof(warcinfo_payload) - 1);
- /* append end-of-record indicator */
- archive_strncat(&hdr, "\r\n\r\n", 4);
+ /* Append the end-of-record indicator. */
+ archive_strncat(&header, "\r\n\r\n", 4);
- /* write to output stream */
- __archive_write_output(a, hdr.s, archive_strlen(&hdr));
+ /* Write the warcinfo record to the output stream. */
+ ret = __archive_write_output(a, header.s,
+ archive_strlen(&header));
+ if (ret != ARCHIVE_OK) {
+ archive_string_free(&header);
+ return (ret);
}
- /* indicate we're done with file header writing */
- w->omit_warcinfo = 1U;
- archive_string_free(&hdr);
+
+ /* Mark the file header as written. */
+ warc->omit_warcinfo = 1U;
+ archive_string_free(&header);
}
if (archive_entry_pathname(entry) == NULL) {
@@ -223,222 +243,249 @@ _warc_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_WARN);
}
- w->typ = archive_entry_filetype(entry);
- w->populz = 0U;
- if (w->typ == AE_IFREG) {
- warc_essential_hdr_t rh = {
- WT_RSRC,
- /*uri*/NULL,
- /*urn*/NULL,
- /*rtm*/0,
- /*mtm*/0,
- /*cty*/NULL,
- /*len*/0,
+ warc->filetype = archive_entry_filetype(entry);
+ warc->entry_bytes_remaining = 0U;
+ if (warc->filetype == AE_IFREG) {
+ struct warc_header resource_header = {
+ WARC_TYPE_RESOURCE,
+ /* URI */NULL,
+ /* Record ID */NULL,
+ /* Record time */0,
+ /* Modified time */0,
+ /* Content type */NULL,
+ /* Content length */0,
};
- ssize_t r;
- rh.tgturi = archive_entry_pathname(entry);
- rh.rtime = w->now;
- rh.mtime = archive_entry_mtime(entry);
- rh.cntlen = (size_t)archive_entry_size(entry);
+ ssize_t header_size;
+ int ret;
+ int64_t size;
- archive_string_init(&hdr);
- r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
- if (r < 0) {
- /* don't bother */
- archive_string_free(&hdr);
- archive_set_error(
- &a->archive,
- ARCHIVE_ERRNO_FILE_FORMAT,
- "cannot archive file");
- return (ARCHIVE_WARN);
+ resource_header.target_uri = archive_entry_pathname(entry);
+ resource_header.record_time = warc->now;
+ resource_header.modification_time = archive_entry_mtime(entry);
+ if (!archive_entry_size_is_set(entry)) {
+ archive_set_error(&a->archive, -1,
+ "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ size = archive_entry_size(entry);
+ if (size < 0) {
+ archive_set_error(&a->archive, -1,
+ "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ resource_header.content_length = (uint64_t)size;
+
+ archive_string_init(&header);
+ header_size = warc_populate_header(&header,
+ WARC_HEADER_MAX_SIZE, resource_header);
+ if (header_size < 0) {
+ /* Header generation failed. */
+ archive_string_free(&header);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "WARC resource header is too large");
+ return (ARCHIVE_FATAL);
+ }
+ /* Append the header to the output stream. */
+ ret = __archive_write_output(a, header.s, header_size);
+ if (ret != ARCHIVE_OK) {
+ archive_string_free(&header);
+ return (ret);
}
- /* otherwise append to output stream */
- __archive_write_output(a, hdr.s, r);
- /* and let subsequent calls to _data() know about the size */
- w->populz = rh.cntlen;
- archive_string_free(&hdr);
+ /* Save the remaining size for subsequent data callbacks. */
+ warc->entry_bytes_remaining = resource_header.content_length;
+ archive_string_free(&header);
return (ARCHIVE_OK);
}
- /* just resort to erroring as per Tim's advice */
+ /* Report unsupported file types through the common helper. */
__archive_write_entry_filetype_unsupported(
&a->archive, entry, "WARC");
return (ARCHIVE_FAILED);
}
static ssize_t
-_warc_data(struct archive_write *a, const void *buf, size_t len)
+archive_write_warc_data(struct archive_write *a, const void *buf, size_t len)
{
- struct warc_s *w = a->format_data;
+ struct warc *warc = a->format_data;
- if (w->typ == AE_IFREG) {
- int rc;
+ if (warc->filetype == AE_IFREG) {
+ int ret;
- /* never write more bytes than announced */
- if (len > w->populz) {
- len = (size_t)w->populz;
+ /* Never write more bytes than announced. */
+ if ((uint64_t)len > warc->entry_bytes_remaining) {
+ len = (size_t)warc->entry_bytes_remaining;
}
- /* now then, out we put the whole shebang */
- rc = __archive_write_output(a, buf, len);
- if (rc != ARCHIVE_OK) {
- return rc;
+ /* Write the entry data. */
+ ret = __archive_write_output(a, buf, len);
+ if (ret != ARCHIVE_OK) {
+ return (ret);
}
+ warc->entry_bytes_remaining -= len;
}
- return len;
+ return (len);
}
static int
-_warc_finish_entry(struct archive_write *a)
+archive_write_warc_finish_entry(struct archive_write *a)
{
- static const char _eor[] = "\r\n\r\n";
- struct warc_s *w = a->format_data;
+ static const char end_of_record[] = "\r\n\r\n";
+ struct warc *warc = a->format_data;
- if (w->typ == AE_IFREG) {
- int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
+ if (warc->filetype == AE_IFREG) {
+ int ret;
- if (rc != ARCHIVE_OK) {
- return rc;
+ if (warc->entry_bytes_remaining != 0U) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "WARC entry is shorter than Content-Length");
+ return (ARCHIVE_FATAL);
+ }
+
+ ret = __archive_write_output(a, end_of_record,
+ sizeof(end_of_record) - 1U);
+ if (ret != ARCHIVE_OK) {
+ return (ret);
}
}
- /* reset type info */
- w->typ = 0;
+ /* Reset file type information. */
+ warc->filetype = 0;
return (ARCHIVE_OK);
}
static int
-_warc_close(struct archive_write *a)
+archive_write_warc_close(struct archive_write *a)
{
(void)a; /* UNUSED */
return (ARCHIVE_OK);
}
static int
-_warc_free(struct archive_write *a)
+archive_write_warc_free(struct archive_write *a)
{
- struct warc_s *w = a->format_data;
+ struct warc *warc = a->format_data;
- free(w);
+ free(warc);
a->format_data = NULL;
return (ARCHIVE_OK);
}
-
-/* private routines */
+/* Like strftime(3), but for time_t objects. */
static void
-xstrftime(struct archive_string *as, const char *fmt, time_t t)
+warc_format_time(struct archive_string *str, const char *format, time_t t)
{
-/** like strftime(3) but for time_t objects */
- struct tm *rt;
+ struct tm *tm;
#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
- struct tm timeHere;
+ struct tm tm_storage;
#endif
- char strtime[100];
+ char time_string[100];
size_t len;
#if defined(HAVE_GMTIME_S)
- rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+ tm = gmtime_s(&tm_storage, &t) ? NULL : &tm_storage;
#elif defined(HAVE_GMTIME_R)
- rt = gmtime_r(&t, &timeHere);
+ tm = gmtime_r(&t, &tm_storage);
#else
- rt = gmtime(&t);
+ tm = gmtime(&t);
#endif
- if (!rt)
+ if (tm == NULL)
return;
- /* leave the hard yacker to our role model strftime() */
- len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
- archive_strncat(as, strtime, len);
+ /* Let strftime() handle the actual formatting. */
+ len = strftime(time_string, sizeof(time_string) - 1, format, tm);
+ archive_strncat(str, time_string, len);
}
static ssize_t
-_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
+warc_populate_header(struct archive_string *header_string, size_t max_size,
+ struct warc_header header)
{
- static const char _ver[] = "WARC/1.0\r\n";
- static const char * const _typ[LAST_WT] = {
+ static const char version[] = "WARC/1.0\r\n";
+ static const char * const record_types[WARC_TYPE_LAST] = {
NULL, "warcinfo", "metadata", "resource", NULL
};
- char std_uuid[48U];
+ char generated_record_id[48U];
- if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
- /* brilliant, how exactly did we get here? */
- return -1;
+ if (header.type == WARC_TYPE_NONE || header.type > WARC_TYPE_RESOURCE) {
+ /* Invalid record type for this writer. */
+ return (-1);
}
- archive_strcpy(tgt, _ver);
+ archive_strcpy(header_string, version);
- archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
+ archive_string_sprintf(header_string, "WARC-Type: %s\r\n",
+ record_types[header.type]);
- if (hdr.tgturi != NULL) {
- /* check if there's a xyz:// */
- static const char _uri[] = "";
- static const char _fil[] = "file://";
- const char *u;
- const char *chk = strchr(hdr.tgturi, ':');
+ if (header.target_uri != NULL) {
+ const char *uri_prefix;
+ const char *scheme = strchr(header.target_uri, ':');
- if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
- /* yep, it's definitely a URI */
- u = _uri;
+ /* Check whether the value already contains ://. */
+ if (scheme != NULL && scheme[1U] == '/' && scheme[2U] == '/') {
+ /* Already has a scheme-style :// prefix. */
+ uri_prefix = "";
} else {
- /* hm, best to prepend file:// then */
- u = _fil;
+ /* Prepend file:// for local paths. */
+ uri_prefix = "file://";
}
- archive_string_sprintf(tgt,
- "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
+ archive_string_sprintf(header_string,
+ "WARC-Target-URI: %s%s\r\n", uri_prefix,
+ header.target_uri);
}
- /* record time is usually when the http is sent off,
- * just treat the archive writing as such for a moment */
- xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
+ /* Write WARC-Date from header.record_time. */
+ warc_format_time(header_string,
+ "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", header.record_time);
- /* while we're at it, record the mtime */
- xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
+ /* Also write Last-Modified from header.modification_time. */
+ warc_format_time(header_string,
+ "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n",
+ header.modification_time);
- if (hdr.recid == NULL) {
- /* generate one, grrrr */
- warc_uuid_t u;
+ if (header.record_id == NULL) {
+ /* Generate a record ID when one was not provided. */
+ struct warc_uuid uuid;
- _gen_uuid(&u);
- /* Unfortunately, archive_string_sprintf does not
- * handle the minimum number following '%'.
- * So we have to use snprintf function here instead
- * of archive_string_snprintf function. */
-#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
+ warc_generate_uuid(&uuid);
+ /* archive_string_sprintf() does not support minimum field widths, so
+ * use snprintf() for UUID formatting. */
+#if defined(_WIN32) && !defined(__CYGWIN__) && \
+ !(defined(_MSC_VER) && _MSC_VER >= 1900)
#define snprintf _snprintf
#endif
- snprintf(
- std_uuid, sizeof(std_uuid),
- "<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
- u.u[0U],
- u.u[1U] >> 16U, u.u[1U] & 0xffffU,
- u.u[2U] >> 16U, u.u[2U] & 0xffffU,
- u.u[3U]);
- hdr.recid = std_uuid;
+ snprintf(generated_record_id, sizeof(generated_record_id),
+ "<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
+ uuid.value[0U],
+ uuid.value[1U] >> 16U, uuid.value[1U] & 0xffffU,
+ uuid.value[2U] >> 16U, uuid.value[2U] & 0xffffU,
+ uuid.value[3U]);
+ header.record_id = generated_record_id;
}
- /* record-id is mandatory, fingers crossed we won't fail */
- archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
+ /* WARC-Record-ID is mandatory. */
+ archive_string_sprintf(header_string, "WARC-Record-ID: %s\r\n",
+ header.record_id);
- if (hdr.cnttyp != NULL) {
- archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
+ if (header.content_type != NULL) {
+ archive_string_sprintf(header_string, "Content-Type: %s\r\n",
+ header.content_type);
}
- /* next one is mandatory */
- archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
- /**/
- archive_strncat(tgt, "\r\n", 2);
+ /* Content-Length is mandatory. */
+ archive_string_sprintf(header_string, "Content-Length: %ju\r\n",
+ (uintmax_t)header.content_length);
+ /* End of header. */
+ archive_strncat(header_string, "\r\n", 2);
- return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
+ return (archive_strlen(header_string) >= max_size) ?
+ -1 : (ssize_t)archive_strlen(header_string);
}
-static int
-_gen_uuid(warc_uuid_t *tgt)
+static void
+warc_generate_uuid(struct warc_uuid *uuid)
{
- archive_random(tgt->u, sizeof(tgt->u));
- /* obey uuid version 4 rules */
- tgt->u[1U] &= 0xffff0fffU;
- tgt->u[1U] |= 0x4000U;
- tgt->u[2U] &= 0x3fffffffU;
- tgt->u[2U] |= 0x80000000U;
- return 0;
+ archive_random(uuid->value, sizeof(uuid->value));
+ /* Apply UUID version 4 rules. */
+ uuid->value[1U] &= 0xffff0fffU;
+ uuid->value[1U] |= 0x4000U;
+ uuid->value[2U] &= 0x3fffffffU;
+ uuid->value[2U] |= 0x80000000U;
}
-
-/* archive_write_set_format_warc.c ends here */
diff --git a/libarchive/archive_write_set_format_xar.c b/libarchive/archive_write_set_format_xar.c
index 98bc4952dad9..0058693792c1 100644
--- a/libarchive/archive_write_set_format_xar.c
+++ b/libarchive/archive_write_set_format_xar.c
@@ -111,7 +111,7 @@ static int xml_writer_start_element(struct xml_writer *ctx,
static int xml_writer_write_attribute(struct xml_writer *ctx, const char *key,
const char *value);
static int xml_writer_write_attributef(struct xml_writer *ctx, const char *key,
- const char *format, ...);
+ const char *format, ...) __LA_PRINTF(3, 4);
static int xml_writer_write_string(struct xml_writer *ctx, const char *string);
static int xml_writer_write_base64(struct xml_writer* ctx,
const char *data, size_t start, size_t len);
@@ -129,14 +129,20 @@ static int xml_writer_destroy(struct xml_writer *ctx);
enum sumalg {
CKSUM_NONE = 0,
CKSUM_SHA1 = 1,
- CKSUM_MD5 = 2
+ CKSUM_MD5 = 2,
+ CKSUM_SHA256 = 3,
+ CKSUM_SHA512 = 4
};
#define MD5_SIZE 16
#define SHA1_SIZE 20
-#define MAX_SUM_SIZE 20
+#define SHA256_SIZE 32
+#define SHA512_SIZE 64
+#define MAX_SUM_SIZE 64
#define MD5_NAME "md5"
#define SHA1_NAME "sha1"
+#define SHA256_NAME "sha256"
+#define SHA512_NAME "sha512"
enum enctype {
NONE,
@@ -154,6 +160,12 @@ struct chksumwork {
#ifdef ARCHIVE_HAS_SHA1
archive_sha1_ctx sha1ctx;
#endif
+#ifdef ARCHIVE_HAS_SHA256
+ archive_sha256_ctx sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ archive_sha512_ctx sha512ctx;
+#endif
};
enum la_zaction {
@@ -366,8 +378,7 @@ archive_write_set_format_xar(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
/* If another format was already registered, unregister it. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
xar = calloc(1, sizeof(*xar));
if (xar == NULL) {
@@ -380,22 +391,19 @@ archive_write_set_format_xar(struct archive *_a)
file_init_hardlinks(xar);
archive_string_init(&(xar->tstr));
archive_string_init(&(xar->vstr));
+ archive_string_init(&(xar->cur_dirstr));
/*
* Create the root directory.
*/
xar->root = file_create_virtual_dir(a, xar, "");
- if (xar->root == NULL) {
- free(xar);
- archive_set_error(&a->archive, ENOMEM,
- "Can't allocate xar data");
- return (ARCHIVE_FATAL);
- }
+ if (xar->root == NULL)
+ goto memerr;
xar->root->parent = xar->root;
file_register(xar, xar->root);
xar->cur_dirent = xar->root;
- archive_string_init(&(xar->cur_dirstr));
- archive_string_ensure(&(xar->cur_dirstr), 1);
+ if (archive_string_ensure(&(xar->cur_dirstr), 1) == NULL)
+ goto memerr;
xar->cur_dirstr.s[0] = 0;
/*
@@ -422,14 +430,23 @@ archive_write_set_format_xar(struct archive *_a)
a->archive.archive_format_name = "xar";
return (ARCHIVE_OK);
+
+memerr:
+ archive_string_free(&(xar->cur_dirstr));
+ archive_string_free(&(xar->tstr));
+ archive_string_free(&(xar->vstr));
+ file_free_hardlinks(xar);
+ file_free_register(xar);
+ free(xar);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
}
static int
xar_options(struct archive_write *a, const char *key, const char *value)
{
- struct xar *xar;
-
- xar = (struct xar *)a->format_data;
+ struct xar *xar = a->format_data;
if (strcmp(key, "checksum") == 0) {
if (value == NULL)
@@ -440,6 +457,14 @@ xar_options(struct archive_write *a, const char *key, const char *value)
xar->opt_sumalg = CKSUM_SHA1;
else if (strcmp(value, "md5") == 0)
xar->opt_sumalg = CKSUM_MD5;
+#ifdef ARCHIVE_HAS_SHA256
+ else if (strcmp(value, "sha256") == 0)
+ xar->opt_sumalg = CKSUM_SHA256;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ else if (strcmp(value, "sha512") == 0)
+ xar->opt_sumalg = CKSUM_SHA512;
+#endif
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
@@ -515,6 +540,14 @@ xar_options(struct archive_write *a, const char *key, const char *value)
xar->opt_toc_sumalg = CKSUM_SHA1;
else if (strcmp(value, "md5") == 0)
xar->opt_toc_sumalg = CKSUM_MD5;
+#ifdef ARCHIVE_HAS_SHA256
+ else if (strcmp(value, "sha256") == 0)
+ xar->opt_toc_sumalg = CKSUM_SHA256;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ else if (strcmp(value, "sha512") == 0)
+ xar->opt_toc_sumalg = CKSUM_SHA512;
+#endif
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
@@ -559,12 +592,11 @@ xar_options(struct archive_write *a, const char *key, const char *value)
static int
xar_write_header(struct archive_write *a, struct archive_entry *entry)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
struct file *file;
struct archive_entry *file_entry;
int r, r2;
- xar = (struct xar *)a->format_data;
xar->cur_file = NULL;
xar->bytes_remaining = 0;
@@ -582,8 +614,10 @@ xar_write_header(struct archive_write *a, struct archive_entry *entry)
return (ARCHIVE_FATAL);
}
r2 = file_gen_utility_names(a, file);
- if (r2 < ARCHIVE_WARN)
+ if (r2 < ARCHIVE_WARN) {
+ file_free(file);
return (r2);
+ }
/*
* Ignore a path which looks like the top of directory name
@@ -672,20 +706,25 @@ xar_write_header(struct archive_write *a, struct archive_entry *entry)
checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
r = xar_compression_init_encoder(a);
- if (r != ARCHIVE_OK)
+ if (r != ARCHIVE_OK) {
+ struct chksumval sumval;
+
+ checksum_final(&(xar->a_sumwrk), &sumval);
+ checksum_final(&(xar->e_sumwrk), &sumval);
+ xar->a_sumwrk.alg = CKSUM_NONE;
+ xar->e_sumwrk.alg = CKSUM_NONE;
return (r);
- else
+ } else
return (r2);
}
static int
write_to_temp(struct archive_write *a, const void *buff, size_t s)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
const unsigned char *p;
ssize_t ws;
- xar = (struct xar *)a->format_data;
p = (const unsigned char *)buff;
while (s) {
ws = write(xar->temp_fd, p, s);
@@ -704,14 +743,12 @@ write_to_temp(struct archive_write *a, const void *buff, size_t s)
static ssize_t
xar_write_data(struct archive_write *a, const void *buff, size_t s)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
enum la_zaction run;
size_t size = 0;
size_t rsize;
int r;
- xar = (struct xar *)a->format_data;
-
if (s > xar->bytes_remaining)
s = (size_t)xar->bytes_remaining;
if (s == 0 || xar->cur_file == NULL)
@@ -805,12 +842,11 @@ xar_write_data(struct archive_write *a, const void *buff, size_t s)
static int
xar_finish_entry(struct archive_write *a)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
struct file *file;
size_t s;
ssize_t w;
- xar = (struct xar *)a->format_data;
if (xar->cur_file == NULL)
return (ARCHIVE_OK);
@@ -907,14 +943,13 @@ xmlwrite_string(struct archive_write *a, struct xml_writer *writer,
return (ARCHIVE_OK);
}
-static int
+static int __LA_PRINTF(4, 5)
xmlwrite_fstring(struct archive_write *a, struct xml_writer *writer,
const char *key, const char *fmt, ...)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
va_list ap;
- xar = (struct xar *)a->format_data;
va_start(ap, fmt);
archive_string_empty(&xar->vstr);
archive_string_vsprintf(&xar->vstr, fmt, ap);
@@ -1202,7 +1237,7 @@ static int
make_file_entry(struct archive_write *a, struct xml_writer *writer,
struct file *file)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
const char *filetype, *filelink, *fflags;
struct archive_string linkto;
struct heap_data *heap;
@@ -1210,7 +1245,6 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer,
size_t len;
int r, r2;
- xar = (struct xar *)a->format_data;
r2 = ARCHIVE_OK;
/*
@@ -1333,11 +1367,11 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer,
return (ARCHIVE_FATAL);
}
r = xmlwrite_fstring(a, writer, "major",
- "%d", archive_entry_rdevmajor(file->entry));
+ "%ld", (long)archive_entry_rdevmajor(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_fstring(a, writer, "minor",
- "%d", archive_entry_rdevminor(file->entry));
+ "%ld", (long)archive_entry_rdevminor(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = xml_writer_end_element(writer);
@@ -1361,7 +1395,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer,
return (ARCHIVE_FATAL);
if (archive_entry_dev(file->entry) != 0) {
r = xmlwrite_fstring(a, writer, "deviceno",
- "%d", archive_entry_dev(file->entry));
+ "%ld", (long)archive_entry_dev(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
}
@@ -1572,7 +1606,7 @@ make_file_entry(struct archive_write *a, struct xml_writer *writer,
static int
make_toc(struct archive_write *a)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
struct file *np;
struct xml_writer *writer;
const char* content;
@@ -1580,8 +1614,6 @@ make_toc(struct archive_write *a)
int algsize;
int r, ret;
- xar = (struct xar *)a->format_data;
-
ret = ARCHIVE_FATAL;
/*
@@ -1822,11 +1854,10 @@ exit_toc:
static int
flush_wbuff(struct archive_write *a)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
int r;
size_t s;
- xar = (struct xar *)a->format_data;
s = sizeof(xar->wbuff) - xar->wbuff_remaining;
r = __archive_write_output(a, xar->wbuff, s);
if (r != ARCHIVE_OK)
@@ -1838,10 +1869,9 @@ flush_wbuff(struct archive_write *a)
static int
copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
int r;
- xar = (struct xar *)a->format_data;
if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
archive_set_error(&(a->archive), errno, "lseek failed");
return (ARCHIVE_FATAL);
@@ -1883,13 +1913,11 @@ copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
static int
xar_close(struct archive_write *a)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
unsigned char *wb;
uint64_t length;
int r;
- xar = (struct xar *)a->format_data;
-
/* Empty! */
if (xar->root->children.first == NULL)
return (ARCHIVE_OK);
@@ -1949,9 +1977,7 @@ xar_close(struct archive_write *a)
static int
xar_free(struct archive_write *a)
{
- struct xar *xar;
-
- xar = (struct xar *)a->format_data;
+ struct xar *xar = a->format_data;
/* Close the temporary file. */
if (xar->temp_fd >= 0)
@@ -2109,13 +2135,12 @@ cleanup_backslash(char *utf8, size_t len)
static int
file_gen_utility_names(struct archive_write *a, struct file *file)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
const char *pp;
char *p, *dirname, *slash;
size_t len;
int r = ARCHIVE_OK;
- xar = (struct xar *)a->format_data;
archive_string_empty(&(file->parentdir));
archive_string_empty(&(file->basename));
archive_string_empty(&(file->symlink));
@@ -2303,6 +2328,7 @@ get_path_component(char *name, int n, const char *fn)
static int
file_tree(struct archive_write *a, struct file **filepp)
{
+ struct xar *xar = a->format_data;
#if defined(_WIN32) && !defined(__CYGWIN__)
char name[_MAX_FNAME];/* Included null terminator size. */
#elif defined(NAME_MAX) && NAME_MAX >= 255
@@ -2310,7 +2336,6 @@ file_tree(struct archive_write *a, struct file **filepp)
#else
char name[256];
#endif
- struct xar *xar = (struct xar *)a->format_data;
struct file *dent, *file, *np;
struct archive_entry *ent;
const char *fn, *p;
@@ -2414,8 +2439,12 @@ file_tree(struct archive_write *a, struct file **filepp)
return (ARCHIVE_FATAL);
}
archive_string_free(&as);
- if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
+ if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED) {
+ file_free(vp);
+ file_free(file);
+ *filepp = NULL;
return (ARCHIVE_FATAL);
+ }
file_add_child_tail(dent, vp);
file_register(xar, vp);
np = vp;
@@ -2526,7 +2555,7 @@ file_free_register(struct xar *xar)
static int
file_register_hardlink(struct archive_write *a, struct file *file)
{
- struct xar *xar = (struct xar *)a->format_data;
+ struct xar *xar = a->format_data;
struct hardlink *hl;
const char *pathname;
@@ -2648,6 +2677,16 @@ checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
case CKSUM_MD5:
archive_md5_init(&(sumwrk->md5ctx));
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_init(&(sumwrk->sha256ctx));
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_init(&(sumwrk->sha512ctx));
+ break;
+#endif
}
}
@@ -2664,6 +2703,16 @@ checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
case CKSUM_MD5:
archive_md5_update(&(sumwrk->md5ctx), buff, size);
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_update(&(sumwrk->sha256ctx), buff, size);
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_update(&(sumwrk->sha512ctx), buff, size);
+ break;
+#endif
}
}
@@ -2683,6 +2732,18 @@ checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
archive_md5_final(&(sumwrk->md5ctx), sumval->val);
sumval->len = MD5_SIZE;
break;
+#ifdef ARCHIVE_HAS_SHA256
+ case CKSUM_SHA256:
+ archive_sha256_final(&(sumwrk->sha256ctx), sumval->val);
+ sumval->len = SHA256_SIZE;
+ break;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ case CKSUM_SHA512:
+ archive_sha512_final(&(sumwrk->sha512ctx), sumval->val);
+ sumval->len = SHA512_SIZE;
+ break;
+#endif
}
sumval->alg = sumwrk->alg;
}
@@ -3132,10 +3193,9 @@ compression_init_encoder_xz(struct archive *a,
static int
xar_compression_init_encoder(struct archive_write *a)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
int r;
- xar = (struct xar *)a->format_data;
switch (xar->opt_compression) {
case GZIP:
r = compression_init_encoder_gzip(
@@ -3192,14 +3252,13 @@ compression_end(struct archive *a, struct la_zstream *lastrm)
static int
save_xattrs(struct archive_write *a, struct file *file)
{
- struct xar *xar;
+ struct xar *xar = a->format_data;
const char *name;
const void *value;
struct heap_data *heap;
size_t size;
int count, r;
- xar = (struct xar *)a->format_data;
count = archive_entry_xattr_reset(file->entry);
if (count == 0)
return (ARCHIVE_OK);
@@ -3211,6 +3270,12 @@ save_xattrs(struct archive_write *a, struct file *file)
heap = calloc(1, sizeof(*heap));
if (heap == NULL) {
+ struct chksumval sumval;
+
+ checksum_final(&(xar->a_sumwrk), &sumval);
+ checksum_final(&(xar->e_sumwrk), &sumval);
+ xar->a_sumwrk.alg = CKSUM_NONE;
+ xar->e_sumwrk.alg = CKSUM_NONE;
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for xattr");
return (ARCHIVE_FATAL);
@@ -3248,6 +3313,10 @@ save_xattrs(struct archive_write *a, struct file *file)
*/
r = xar_compression_init_encoder(a);
if (r != ARCHIVE_OK) {
+ struct chksumval sumval;
+
+ checksum_final(&(xar->a_sumwrk), &sumval);
+ xar->a_sumwrk.alg = CKSUM_NONE;
free(heap);
return (ARCHIVE_FATAL);
}
@@ -3302,6 +3371,10 @@ getalgsize(enum sumalg sumalg)
return (SHA1_SIZE);
case CKSUM_MD5:
return (MD5_SIZE);
+ case CKSUM_SHA256:
+ return (SHA256_SIZE);
+ case CKSUM_SHA512:
+ return (SHA512_SIZE);
}
}
@@ -3316,6 +3389,10 @@ getalgname(enum sumalg sumalg)
return (SHA1_NAME);
case CKSUM_MD5:
return (MD5_NAME);
+ case CKSUM_SHA256:
+ return (SHA256_NAME);
+ case CKSUM_SHA512:
+ return (SHA512_NAME);
}
}
diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c
index 33d60ce864c6..9c7073fb76bd 100644
--- a/libarchive/archive_write_set_format_zip.c
+++ b/libarchive/archive_write_set_format_zip.c
@@ -68,6 +68,7 @@
#include "archive_entry.h"
#include "archive_entry_locale.h"
#include "archive_hmac_private.h"
+#include "archive_integer.h"
#include "archive_private.h"
#include "archive_random_private.h"
#include "archive_time_private.h"
@@ -154,7 +155,6 @@ struct zip {
enum compression entry_compression;
enum encryption entry_encryption;
int entry_flags;
- int experiments;
struct trad_enc_ctx tctx;
char tctx_valid;
unsigned char trad_chkdat;
@@ -235,7 +235,6 @@ static int archive_write_zip_header(struct archive_write *,
struct archive_entry *);
static int archive_write_zip_options(struct archive_write *,
const char *, const char *);
-static size_t path_length(struct archive_entry *);
static int write_path(struct archive_entry *, struct archive_write *);
static void copy_path(struct archive_entry *, unsigned char *);
static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
@@ -730,8 +729,7 @@ archive_write_set_format_zip(struct archive *_a)
ARCHIVE_STATE_NEW, "archive_write_set_format_zip");
/* If another format was already registered, unregister it. */
- if (a->format_free != NULL)
- (a->format_free)(a);
+ (void)__archive_write_unregister_format(a);
zip = calloc(1, sizeof(*zip));
if (zip == NULL) {
@@ -791,9 +789,9 @@ is_all_ascii(const char *p)
static int
archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
{
+ struct zip *zip = a->format_data;
unsigned char local_header[32];
unsigned char local_extra[144];
- struct zip *zip = a->format_data;
unsigned char *e;
unsigned char *cd_extra;
size_t filename_length;
@@ -959,8 +957,6 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
#endif
}
}
- filename_length = path_length(zip->entry);
-
/* Reject empty or overlong pathnames */
path = archive_entry_pathname(zip->entry);
if (path == NULL || path[0] == '\0') {
@@ -968,6 +964,10 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
"ZIP format requires a non-empty pathname");
return (ARCHIVE_FAILED);
}
+ filename_length = strlen(path);
+ /* Include the trailing slash added to directories. */
+ if (type == AE_IFDIR && path[filename_length - 1] != '/')
+ filename_length++;
if (filename_length > 0xffff) {
archive_set_error(&a->archive, ENAMETOOLONG,
"Pathname too long for ZIP format");
@@ -1066,8 +1066,13 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
default:
break;
}
- if (zip->entry_compression == COMPRESSION_STORE)
- zip->entry_compressed_size += additional_size;
+ if (zip->entry_compression == COMPRESSION_STORE &&
+ archive_ckd_add_i64(&zip->entry_compressed_size,
+ zip->entry_compressed_size, additional_size)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File size too large for encrypted ZIP entry");
+ return (ARCHIVE_FAILED);
+ }
}
/*
@@ -1081,7 +1086,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
* (compression might make file larger)
*/
if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)
- || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)
+ || (zip->entry_uncompressed_size > ZIP_4GB_MAX - additional_size)
|| (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED
&& zip->entry_compression != COMPRESSION_STORE)) {
MIN_VERSION_NEEDED(45);
@@ -1515,8 +1520,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
static ssize_t
archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
{
- int ret;
struct zip *zip = a->format_data;
+ int ret;
if ((int64_t)s > zip->entry_uncompressed_limit)
s = (size_t)zip->entry_uncompressed_limit;
@@ -2184,9 +2189,9 @@ archive_write_zip_finish_entry(struct archive_write *a)
static int
archive_write_zip_close(struct archive_write *a)
{
+ struct zip *zip = a->format_data;
uint8_t buff[64];
int64_t offset_start, offset_end;
- struct zip *zip = a->format_data;
struct cd_segment *segment;
int ret;
@@ -2256,10 +2261,9 @@ archive_write_zip_close(struct archive_write *a)
static int
archive_write_zip_free(struct archive_write *a)
{
- struct zip *zip;
+ struct zip *zip = a->format_data;
struct cd_segment *segment;
- zip = a->format_data;
while (zip->central_directory != NULL) {
segment = zip->central_directory;
zip->central_directory = segment->next;
@@ -2279,24 +2283,6 @@ archive_write_zip_free(struct archive_write *a)
return (ARCHIVE_OK);
}
-static size_t
-path_length(struct archive_entry *entry)
-{
- mode_t type;
- const char *path;
- size_t len;
-
- type = archive_entry_filetype(entry);
- path = archive_entry_pathname(entry);
-
- if (path == NULL)
- return (0);
- len = strlen(path);
- if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))
- ++len; /* Space for the trailing / */
- return len;
-}
-
static int
write_path(struct archive_entry *entry, struct archive_write *archive)
{
diff --git a/libarchive/archive_write_set_options.3 b/libarchive/archive_write_set_options.3
index c3de50c8fe91..e03b5175771d 100644
--- a/libarchive/archive_write_set_options.3
+++ b/libarchive/archive_write_set_options.3
@@ -555,6 +555,20 @@ used when translating file names.
.El
.It Format pax
.Bl -tag -compact -width indent
+.It Cm align
+Pad the extended header of each regular file so that the file data that
+follows begins on a multiple of the given number of bytes within the
+uncompressed archive stream.
+The value must be a power of two and a multiple of 512.
+Because the alignment is applied to the uncompressed stream, it is
+independent of any compression filter: the decompressed archive is
+aligned regardless of whether the archive is compressed.
+This allows tools to share file contents out of an
+uncompressed archive (or an uncompressed copy of one) using
+reflinks or
+.Xr copy_file_range 2 ,
+at the cost of some extra padding.
+Files shorter than the alignment are not padded.
.It Cm hdrcharset
The value is used as a character set name that will be
used when translating file, group and user names.
@@ -608,9 +622,11 @@ as file checksum method.
Supported values are
.Dq none ,
.Dq md5 ,
-and
.Dq sha1
-.Pq default .
+.Pq default ,
+.Dq sha256 ,
+and
+.Dq sha512 .
.It Cm compression Ns = Ns Ar type
Use
.Ar type
@@ -631,10 +647,12 @@ Use
as table of contents checksum method.
Supported values are
.Dq none ,
-.Dq md5
-and
+.Dq md5 ,
.Dq sha1
-.Pq default .
+.Pq default ,
+.Dq sha256 ,
+and
+.Dq sha512 .
.El
.It Format zip
.Bl -tag -compact -width indent
diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
index a107b3999808..44f839f87f5d 100644
--- a/libarchive/test/CMakeLists.txt
+++ b/libarchive/test/CMakeLists.txt
@@ -10,6 +10,7 @@ IF(ENABLE_TEST)
read_open_memory.c
test.h
test_7zip_filename_encoding.c
+ test_acl_from_text_overread.c
test_acl_nfs4.c
test_acl_nfs4_null_id_overflow.c
test_acl_nfs4_text.c
@@ -33,27 +34,21 @@ IF(ENABLE_TEST)
test_archive_read.c
test_archive_read_add_passphrase.c
test_archive_read_close_twice.c
- test_archive_read_close_twice_open_fd.c
- test_archive_read_close_twice_open_filename.c
test_archive_read_multiple_data_objects.c
test_archive_read_next_header_empty.c
test_archive_read_next_header_raw.c
test_archive_read_open2.c
- test_archive_read_set_filter_option.c
- test_archive_read_set_format_option.c
- test_archive_read_set_option.c
test_archive_read_set_options.c
test_archive_read_support.c
+ test_archive_seek_data_unsupported.c
test_archive_set_error.c
test_archive_string.c
test_archive_string_conversion.c
+ test_archive_string_conversion_overread.c
test_archive_write_add_filter.c
test_archive_write_add_filter_by_name.c
- test_archive_write_set_filter_option.c
test_archive_write_set_format_by_name.c
test_archive_write_set_format_filter_by_ext.c
- test_archive_write_set_format_option.c
- test_archive_write_set_option.c
test_archive_write_set_options.c
test_archive_write_set_passphrase.c
test_bad_fd.c
@@ -93,6 +88,7 @@ IF(ENABLE_TEST)
test_open_filename.c
test_pax_filename_encoding.c
test_pax_xattr_header.c
+ test_read_data_into_fd_size_exceeds_declared.c
test_read_data_large.c
test_read_disk.c
test_read_disk_directory_traversals.c
@@ -117,12 +113,15 @@ IF(ENABLE_TEST)
test_read_format_7zip_encryption_header.c
test_read_format_7zip_encryption_partially.c
test_read_format_7zip_entries_oom.c
+ test_read_format_7zip_folders_oom.c
test_read_format_7zip_issue2765.c
test_read_format_7zip_malformed.c
test_read_format_7zip_packinfo_digests.c
test_read_format_ar.c
test_read_format_cab.c
test_read_format_cab_filename.c
+ test_read_format_cab_lzx_16bit.c
+ test_read_format_cab_lzx_e8.c
test_read_format_cab_lzx_oob.c
test_read_format_cab_mszip_oob.c
test_read_format_cab_multi.c
@@ -143,6 +142,7 @@ IF(ENABLE_TEST)
test_read_format_cpio_svr4_gzip.c
test_read_format_cpio_svr4_gzip_rpm.c
test_read_format_cpio_svr4c_Z.c
+ test_read_format_cpio_symlink_trailer.c
test_read_format_empty.c
test_read_format_gtar_filename.c
test_read_format_gtar_gz.c
@@ -156,22 +156,24 @@ IF(ENABLE_TEST)
test_read_format_iso_joliet_utf16be_overflow.c
test_read_format_iso_multi_extent.c
test_read_format_iso_xorriso.c
- test_read_format_isojoliet_bz2.c
+ test_read_format_isojoliet_compress.c
test_read_format_isojoliet_long.c
test_read_format_isojoliet_rr.c
test_read_format_isojoliet_versioned.c
- test_read_format_isorr_bz2.c
+ test_read_format_isorr_compress.c
test_read_format_isorr_ce.c
test_read_format_isorr_ce_loop.c
- test_read_format_isorr_new_bz2.c
+ test_read_format_isorr_new_compress.c
test_read_format_isorr_rr_moved.c
- test_read_format_isozisofs_bz2.c
+ test_read_format_isozisofs_compress.c
+ test_read_format_iso_rockridge_zf_overflow.c
test_read_format_iso_zisofs_overflow.c
test_read_format_lha.c
test_read_format_lha_bugfix_0.c
test_read_format_lha_filename.c
test_read_format_lha_filename_utf16.c
test_read_format_lha_oversize_header.c
+ test_read_format_lha_symlink_missing_target.c
test_read_format_mtree.c
test_read_format_mtree_crash747.c
test_read_format_pax_bz2.c
@@ -202,6 +204,7 @@ IF(ENABLE_TEST)
test_read_format_tar_pax_g_large.c
test_read_format_tar_pax_large_attr.c
test_read_format_tar_pax_negative_time.c
+ test_read_format_tar_pax_sun_holesdata.c
test_read_format_tar_pax_timestamps.c
test_read_format_tar_timestamp_overflow.c
test_read_format_tbz.c
@@ -230,7 +233,10 @@ IF(ENABLE_TEST)
test_read_format_zip_nofiletype.c
test_read_format_zip_padded.c
test_read_format_zip_sfx.c
+ test_read_format_zip_size_exceeds_declared.c
+ test_read_format_zip_symlink_unsupported_compression.c
test_read_format_zip_traditional_encryption_data.c
+ test_read_format_zip_uncompressed_size_off_by_4gib.c
test_read_format_zip_winzip_aes.c
test_read_format_zip_winzip_aes_large.c
test_read_format_zip_zip64.c
@@ -297,13 +303,16 @@ IF(ENABLE_TEST)
test_write_format_cpio_odc.c
test_write_format_gnutar.c
test_write_format_gnutar_filenames.c
+ test_write_format_gnutar_huge.c
test_write_format_iso9660.c
test_write_format_iso9660_boot.c
test_write_format_iso9660_empty.c
test_write_format_iso9660_filename.c
test_write_format_iso9660_joliet_id.c
test_write_format_iso9660_rockridge.c
+ test_write_format_iso9660_rockridge_overflow.c
test_write_format_iso9660_zisofs.c
+ test_write_format_iso9660_zisofs_overflow.c
test_write_format_iso9660_bugs.c
test_write_format_mtree.c
test_write_format_mtree_absolute.c
@@ -317,6 +326,7 @@ IF(ENABLE_TEST)
test_write_format_mtree_quoted_filename.c
test_write_format_mtree_null_deref.c
test_write_format_pax.c
+ test_write_format_pax_align.c
test_write_format_raw.c
test_write_format_raw_b64.c
test_write_format_shar_empty.c
@@ -336,7 +346,7 @@ IF(ENABLE_TEST)
test_write_format_zip64_stream.c
test_write_format_zip_compression_store.c
test_write_format_zip_compression_bzip2.c
- test_write_format_zip_compression_lzmaxz.c
+ test_write_format_zip_compression_lzma_xz.c
test_write_format_zip_compression_zstd.c
test_write_format_zip_empty.c
test_write_format_zip_empty_pathname.c
diff --git a/libarchive/test/read_open_memory.c b/libarchive/test/read_open_memory.c
index 9262ab9d30b3..0c714a31e809 100644
--- a/libarchive/test/read_open_memory.c
+++ b/libarchive/test/read_open_memory.c
@@ -86,6 +86,7 @@ read_open_memory_internal(struct archive *a, const void *buff,
size_t size, size_t read_size, int level)
{
struct read_memory_data *mine = NULL;
+ int r;
switch (level) {
case 3:
@@ -112,7 +113,12 @@ read_open_memory_internal(struct archive *a, const void *buff,
archive_read_set_read_callback(a, memory_read);
archive_read_set_close_callback(a, memory_read_close);
- archive_read_set_callback_data(a, mine);
+ r = archive_read_set_callback_data(a, mine);
+ if (r < 0)
+ return (r);
+ __LA_FALLTHROUGH;
+ default:
+ break;
}
return archive_read_open1(a);
}
diff --git a/libarchive/test/test_acl_from_text_overread.c b/libarchive/test/test_acl_from_text_overread.c
new file mode 100644
index 000000000000..ecf6cc78eb87
--- /dev/null
+++ b/libarchive/test/test_acl_from_text_overread.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2026 Kaif Khan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+#define __LIBARCHIVE_TEST
+#include "archive_acl_private.h"
+
+/*
+ * archive_acl_from_text_nl() takes a pointer and a length and does not
+ * require the buffer to be NUL terminated. An entry that is only
+ * whitespace, or trailing whitespace after a valid entry, must not make
+ * the parser read past the end of the buffer.
+ *
+ * Each input is copied into a buffer sized to exactly its length so that
+ * a read past the end is out of bounds.
+ */
+static void
+parse_nl(const char *text, int type)
+{
+ size_t len = strlen(text);
+ char *buf = malloc(len);
+ struct archive_acl acl;
+
+ assert(buf != NULL);
+ memcpy(buf, text, len);
+ memset(&acl, 0, sizeof(acl));
+ /* Must not read buf[len]. */
+ archive_acl_from_text_nl(&acl, buf, len, type, NULL);
+ archive_acl_clear(&acl);
+ free(buf);
+}
+
+DEFINE_TEST(test_acl_from_text_overread)
+{
+ static const char *whitespace[] = {
+ " ", " ", "\t", "\n\n", "user::rwx, "
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(whitespace) / sizeof(whitespace[0]); i++) {
+ parse_nl(whitespace[i], ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ parse_nl(whitespace[i], ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ }
+
+ /*
+ * Trailing whitespace after a valid entry must not change the parse:
+ * the count matches the same text without the trailing spaces.
+ */
+ {
+ static const char clean[] = "user:100:rwx";
+ static const char padded[] = "user:100:rwx ";
+ struct archive_acl acl1, acl2;
+ char *b1 = malloc(sizeof(clean) - 1);
+ char *b2 = malloc(sizeof(padded) - 1);
+
+ assert(b1 != NULL);
+ assert(b2 != NULL);
+ memcpy(b1, clean, sizeof(clean) - 1);
+ memcpy(b2, padded, sizeof(padded) - 1);
+ memset(&acl1, 0, sizeof(acl1));
+ memset(&acl2, 0, sizeof(acl2));
+ archive_acl_from_text_nl(&acl1, b1, sizeof(clean) - 1,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, NULL);
+ archive_acl_from_text_nl(&acl2, b2, sizeof(padded) - 1,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, NULL);
+ assertEqualInt(
+ archive_acl_count(&acl1, ARCHIVE_ENTRY_ACL_TYPE_ACCESS),
+ archive_acl_count(&acl2, ARCHIVE_ENTRY_ACL_TYPE_ACCESS));
+ assert(archive_acl_count(&acl2,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0);
+ archive_acl_clear(&acl1);
+ archive_acl_clear(&acl2);
+ free(b1);
+ free(b2);
+ }
+}
diff --git a/libarchive/test/test_archive_pathmatch_oob.c b/libarchive/test/test_archive_pathmatch_oob.c
index fc8f4a5ca178..a3885e44447d 100644
--- a/libarchive/test/test_archive_pathmatch_oob.c
+++ b/libarchive/test/test_archive_pathmatch_oob.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
+#include <wchar.h>
/*
* Regression test: malformed bracket expressions in patterns caused
@@ -37,8 +38,10 @@
* redzones detect overreads.
*/
-/* Declare the internal function we want to test. */
+/* Declare the internal functions we want to test. */
int __archive_pathmatch(const char *pattern, const char *string, int flags);
+int __archive_pathmatch_w(const wchar_t *pattern, const wchar_t *string,
+ int flags);
/*
* Replay a fuzzer repro file through __archive_pathmatch().
@@ -115,3 +118,39 @@ DEFINE_TEST(test_archive_pathmatch_oob_strchr)
{
replay_pathmatch_repro("test_archive_pathmatch_oob_strchr.bin");
}
+
+/*
+ * Wide-character twin of the bracket over-read. pm_w() matched a [...]
+ * class without first checking whether the subject string was already
+ * exhausted, so a negated class ([!...]/[^...]) at end-of-string reported
+ * a match and advanced the subject pointer past its terminator. Allocate
+ * tight wide buffers so ASan's redzones catch the over-read.
+ */
+static void
+check_pathmatch_w_oob(const wchar_t *pat, const wchar_t *str, int flags)
+{
+ size_t plen = wcslen(pat) + 1;
+ size_t slen = wcslen(str) + 1;
+ wchar_t *pattern = (wchar_t *)malloc(plen * sizeof(wchar_t));
+ wchar_t *string = (wchar_t *)malloc(slen * sizeof(wchar_t));
+
+ if (assert(pattern != NULL) && assert(string != NULL)) {
+ wmemcpy(pattern, pat, plen);
+ wmemcpy(string, str, slen);
+ /* This must not read past the allocated buffers. */
+ __archive_pathmatch_w(pattern, string, flags);
+ }
+ free(pattern);
+ free(string);
+}
+
+DEFINE_TEST(test_archive_pathmatch_oob_bracket_w)
+{
+ /* PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END, as used by
+ * archive_match() exclusion patterns. */
+ int flags = 1 | 2;
+
+ check_pathmatch_w_oob(L"abc[!x]", L"abc", flags);
+ check_pathmatch_w_oob(L"[!a]", L"", flags);
+ check_pathmatch_w_oob(L"[^a]", L"", flags);
+}
diff --git a/libarchive/test/test_archive_read_close_twice.c b/libarchive/test/test_archive_read_close_twice.c
index 4320ae670565..5f5beb01db9b 100644
--- a/libarchive/test/test_archive_read_close_twice.c
+++ b/libarchive/test/test_archive_read_close_twice.c
@@ -39,3 +39,45 @@ DEFINE_TEST(test_archive_read_close_twice)
archive_read_free(a);
}
+
+DEFINE_TEST(test_archive_read_close_twice_open_fd)
+{
+ struct archive* a = archive_read_new();
+
+ assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_open_fd(a, 0, 0));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_archive_read_close_twice_open_filename)
+{
+ const char *filename = "empty.file";
+ struct archive* a = archive_read_new();
+
+ assertMakeFile(filename, 0644, "");
+ assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_open_filename(a, filename, 0));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(0, archive_errno(a));
+ assertEqualString(NULL, archive_error_string(a));
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_archive_read_set_filter_option.c b/libarchive/test/test_archive_read_set_filter_option.c
deleted file mode 100644
index 68494abbc1e9..000000000000
--- a/libarchive/test/test_archive_read_set_filter_option.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*-
- * Copyright (c) 2011 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"
-
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_read_set_filter_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
-{
- struct archive* a = archive_read_new();
-
- if (!pristine)
- archive_read_support_filter_all(a);
-
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
-
- should(a, ARCHIVE_FAILED, NULL, "fubar", NULL);
- should(a, ARCHIVE_FAILED, NULL, "fubar", "snafu");
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
-
- archive_read_free(a);
-}
-
-DEFINE_TEST(test_archive_read_set_filter_option)
-{
- test(1);
- test(0);
-}
diff --git a/libarchive/test/test_archive_read_set_option.c b/libarchive/test/test_archive_read_set_option.c
deleted file mode 100644
index 195c1eaa3e13..000000000000
--- a/libarchive/test/test_archive_read_set_option.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*-
- * Copyright (c) 2011 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"
-
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_read_set_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
-{
- struct archive* a = archive_read_new();
- int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
-
- if (!pristine) {
- archive_read_support_filter_all(a);
- archive_read_support_format_all(a);
- }
-
- /* NULL and "" denote `no option', so they're ok no matter
- * what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
- should(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
-
- /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
-
- archive_read_free(a);
-}
-
-DEFINE_TEST(test_archive_read_set_option)
-{
- test(1);
- test(0);
-}
diff --git a/libarchive/test/test_archive_read_set_options.c b/libarchive/test/test_archive_read_set_options.c
index 2f1f344eaad3..4405be8f96eb 100644
--- a/libarchive/test/test_archive_read_set_options.c
+++ b/libarchive/test/test_archive_read_set_options.c
@@ -25,11 +25,121 @@
#include "test.h"
-#define should(__a, __code, __opts) \
+#define should_read_set_filter_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_read_set_filter_option(__a, __m, __o, __v))
+
+static void
+read_set_filter_option_test(int pristine)
+{
+ struct archive* a = archive_read_new();
+
+ if (!pristine)
+ archive_read_support_filter_all(a);
+
+ should_read_set_filter_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_read_set_filter_option(a, ARCHIVE_OK, "", "", "");
+
+ should_read_set_filter_option(a, ARCHIVE_FAILED, NULL, "fubar", NULL);
+ should_read_set_filter_option(a, ARCHIVE_FAILED, NULL, "fubar", "snafu");
+ should_read_set_filter_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_read_set_filter_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ archive_read_free(a);
+}
+
+DEFINE_TEST(test_archive_read_set_filter_option)
+{
+ read_set_filter_option_test(1);
+ read_set_filter_option_test(0);
+}
+
+#define should_read_set_format_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_read_set_format_option(__a, __m, __o, __v))
+
+static void
+read_set_format_option_test(int pristine)
+{
+ struct archive* a = archive_read_new();
+ int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
+
+ if (!pristine)
+ archive_read_support_format_all(a);
+
+ /* NULL and "" denote `no option', so they're ok no matter
+ * what, if any, formats are registered */
+ should_read_set_format_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_read_set_format_option(a, ARCHIVE_OK, "", "", "");
+
+ /* unknown modules and options */
+ should_read_set_format_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_read_set_format_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ /* unknown modules and options */
+ should_read_set_format_option(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
+ should_read_set_format_option(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
+
+ /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
+ should_read_set_format_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_read_set_format_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_read_set_format_option(a, known_option_rv, NULL, "joliet", NULL);
+ should_read_set_format_option(a, known_option_rv, NULL, "joliet", NULL);
+
+ archive_read_free(a);
+}
+
+DEFINE_TEST(test_archive_read_set_format_option)
+{
+ read_set_format_option_test(1);
+ read_set_format_option_test(0);
+}
+
+#define should_read_set_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_read_set_option(__a, __m, __o, __v))
+
+static void
+read_set_option_test(int pristine)
+{
+ struct archive* a = archive_read_new();
+ int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
+
+ if (!pristine) {
+ archive_read_support_filter_all(a);
+ archive_read_support_format_all(a);
+ }
+
+ /* NULL and "" denote `no option', so they're ok no matter
+ * what, if any, formats are registered */
+ should_read_set_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_read_set_option(a, ARCHIVE_OK, "", "", "");
+
+ /* unknown modules and options */
+ should_read_set_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_read_set_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ /* unknown modules and options */
+ should_read_set_option(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
+ should_read_set_option(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
+
+ /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
+ should_read_set_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_read_set_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_read_set_option(a, known_option_rv, NULL, "joliet", NULL);
+ should_read_set_option(a, known_option_rv, NULL, "joliet", NULL);
+
+ archive_read_free(a);
+}
+
+DEFINE_TEST(test_archive_read_set_option)
+{
+ read_set_option_test(1);
+ read_set_option_test(0);
+}
+
+#define should_read_set_options(__a, __code, __opts) \
assertEqualInt(__code, archive_read_set_options(__a, __opts))
static void
-test(int pristine)
+read_set_options_test(int pristine)
{
struct archive* a = archive_read_new();
int halfempty_options_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
@@ -42,62 +152,62 @@ test(int pristine)
/* NULL and "" denote `no option', so they're ok no matter
* what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL);
- should(a, ARCHIVE_OK, "");
+ should_read_set_options(a, ARCHIVE_OK, NULL);
+ should_read_set_options(a, ARCHIVE_OK, "");
/* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar:snafu");
+ should_read_set_options(a, ARCHIVE_FAILED, "fubar:snafu");
assertEqualString("Unknown module name: `fubar'",
archive_error_string(a));
- should(a, ARCHIVE_FAILED, "fubar:snafu=betcha");
+ should_read_set_options(a, ARCHIVE_FAILED, "fubar:snafu=betcha");
assertEqualString("Unknown module name: `fubar'",
archive_error_string(a));
/* unknown modules and options */
- should(a, ARCHIVE_FAILED, "snafu");
+ should_read_set_options(a, ARCHIVE_FAILED, "snafu");
assertEqualString("Undefined option: `snafu'",
archive_error_string(a));
- should(a, ARCHIVE_FAILED, "snafu=betcha");
+ should_read_set_options(a, ARCHIVE_FAILED, "snafu=betcha");
assertEqualString("Undefined option: `snafu'",
archive_error_string(a));
/* ARCHIVE_OK with iso9660 loaded, ARCHIVE_FAILED otherwise */
- should(a, known_option_rv, "iso9660:joliet");
+ should_read_set_options(a, known_option_rv, "iso9660:joliet");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
}
- should(a, known_option_rv, "iso9660:joliet");
+ should_read_set_options(a, known_option_rv, "iso9660:joliet");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
}
- should(a, known_option_rv, "joliet");
+ should_read_set_options(a, known_option_rv, "joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, known_option_rv, "!joliet");
+ should_read_set_options(a, known_option_rv, "!joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, ARCHIVE_OK, ",");
- should(a, ARCHIVE_OK, ",,");
+ should_read_set_options(a, ARCHIVE_OK, ",");
+ should_read_set_options(a, ARCHIVE_OK, ",,");
- should(a, halfempty_options_rv, ",joliet");
+ should_read_set_options(a, halfempty_options_rv, ",joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, halfempty_options_rv, "joliet,");
+ should_read_set_options(a, halfempty_options_rv, "joliet,");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, ARCHIVE_FAILED, "joliet,snafu");
+ should_read_set_options(a, ARCHIVE_FAILED, "joliet,snafu");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
@@ -106,7 +216,7 @@ test(int pristine)
archive_error_string(a));
}
- should(a, ARCHIVE_FAILED, "iso9660:snafu");
+ should_read_set_options(a, ARCHIVE_FAILED, "iso9660:snafu");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
@@ -120,6 +230,6 @@ test(int pristine)
DEFINE_TEST(test_archive_read_set_options)
{
- test(1);
- test(0);
+ read_set_options_test(1);
+ read_set_options_test(0);
}
diff --git a/libarchive/test/test_archive_seek_data_unsupported.c b/libarchive/test/test_archive_seek_data_unsupported.c
new file mode 100644
index 000000000000..e10e811fcb4c
--- /dev/null
+++ b/libarchive/test/test_archive_seek_data_unsupported.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2026 tbontb-iaq
+ * 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"
+
+/*
+ * archive_seek_data() returns ARCHIVE_FAILED when the active format
+ * does not implement a seek_data hook. Such "no seeking for this
+ * format" is a capability gap, not stream corruption, and the archive
+ * must remain usable for subsequent reads. See #3323.
+ */
+
+DEFINE_TEST(test_archive_seek_data_unsupported)
+{
+ char buff[8192];
+ size_t used = 0;
+ struct archive *a;
+ struct archive_entry *ae;
+ char rbuf[64];
+ la_ssize_t rd;
+ la_int64_t probe;
+
+ /* Build a small ustar archive in memory. ustar has no seek_data. */
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "hello.txt");
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ archive_entry_set_size(ae, 11);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assertEqualIntA(a, 11, archive_write_data(a, "hello world", 11));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ /* Read it back. */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("hello.txt", archive_entry_pathname(ae));
+
+ /*
+ * Capability probe. Must report the missing capability with
+ * ARCHIVE_FAILED and set an error string. 3.8.7 and 3.8.8 both
+ * returned ARCHIVE_FATAL here; ARCHIVE_FAILED is the new
+ * contract, because a missing hook is recoverable.
+ */
+ probe = archive_seek_data(a, 0, SEEK_CUR);
+ failure("archive_seek_data() on a format without a seek_data hook "
+ "must report ARCHIVE_FAILED (return value contract)");
+ assertEqualInt(ARCHIVE_FAILED, probe);
+ failure("archive_seek_data() must set an error string when "
+ "the format has no seek_data hook");
+ assert(archive_error_string(a) != NULL);
+
+ /*
+ * The probe must NOT have poisoned the archive. Subsequent reads
+ * from the current entry must still succeed and return the real
+ * entry content. This is the 3.8.8 regression.
+ */
+ rd = archive_read_data(a, rbuf, sizeof(rbuf));
+ failure("archive_read_data() after a non-fatal capability probe "
+ "must still return the entry data (regression in 3.8.8, "
+ "see GitHub issue #3323)");
+ assertEqualInt(11, rd);
+ assertEqualMem(rbuf, "hello world", 11);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_archive_string_conversion_overread.c b/libarchive/test/test_archive_string_conversion_overread.c
new file mode 100644
index 000000000000..cb7a49123f8a
--- /dev/null
+++ b/libarchive/test/test_archive_string_conversion_overread.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2026 Kaif Khan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+#define __LIBARCHIVE_TEST
+#include "archive_string.h"
+
+/*
+ * archive_strncpy_l() passes a pointer and a byte count to the string
+ * conversion converter and does not require the buffer to be NUL
+ * terminated: the readers hand it a __archive_read_ahead() window sized
+ * exactly to the field length (mbsnbytes() returns that length when no
+ * NUL is present). The best-effort converter must therefore stop after
+ * the declared number of bytes.
+ *
+ * Requesting a conversion from a charset iconv cannot open selects
+ * best_effort_strncat_in_locale() with sc->same == 0. If a platform's
+ * iconv unexpectedly accepts the name the conversion still succeeds and
+ * the assertions hold, so this never yields a false failure.
+ */
+DEFINE_TEST(test_archive_string_conversion_overread)
+{
+ struct archive *a;
+ struct archive_string_conv *sc;
+
+ assert((a = archive_read_new()) != NULL);
+ sc = archive_string_conversion_from_charset(a, "NO-SUCH-CHARSET-8859", 1);
+ assert(sc != NULL);
+
+ /*
+ * The buffer declares 8 bytes but is followed by 4 more non-NUL
+ * bytes and then a NUL. A converter that honors the declared length
+ * copies 8 bytes; the over-read walks on to the NUL and copies 12.
+ */
+ {
+ struct archive_string as;
+ static const char data[] = "AAAAAAAABBBB"; /* 8 + 4, then NUL */
+
+ archive_string_init(&as);
+ assertEqualInt(0, archive_strncpy_l(&as, data, 8, sc));
+ assertEqualInt(8, (int)as.length);
+ assertEqualMem(as.s, "AAAAAAAA", 8);
+ archive_string_free(&as);
+ }
+
+ /*
+ * Exact-length heap buffer with no trailing NUL: any read past the
+ * end is out of bounds and observable to AddressSanitizer.
+ */
+ {
+ struct archive_string as;
+ size_t n = 8;
+ char *buf = malloc(n);
+
+ assert(buf != NULL);
+ memset(buf, 'A', n);
+ archive_string_init(&as);
+ assertEqualInt(0, archive_strncpy_l(&as, buf, n, sc)); /* must not read buf[n] */
+ assertEqualInt(8, (int)as.length);
+ archive_string_free(&as);
+ free(buf);
+ }
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_archive_write_set_format_option.c b/libarchive/test/test_archive_write_set_format_option.c
deleted file mode 100644
index 38f83aba2ede..000000000000
--- a/libarchive/test/test_archive_write_set_format_option.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-
- * Copyright (c) 2011 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"
-
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_write_set_format_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
-{
- struct archive* a = archive_write_new();
- int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
-
- if (!pristine)
- archive_write_set_format_iso9660(a);
-
- /* NULL and "" denote `no option', so they're ok no matter
- * what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
- should(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
-
- /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
-
- archive_write_free(a);
-}
-
-DEFINE_TEST(test_archive_write_set_format_option)
-{
- test(1);
- test(0);
-}
diff --git a/libarchive/test/test_archive_write_set_option.c b/libarchive/test/test_archive_write_set_option.c
deleted file mode 100644
index aa44edad653d..000000000000
--- a/libarchive/test/test_archive_write_set_option.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*-
- * Copyright (c) 2011 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"
-
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_write_set_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
-{
- struct archive* a = archive_write_new();
- int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
-
- if (!pristine) {
- archive_write_add_filter_gzip(a);
- archive_write_set_format_iso9660(a);
- }
-
- /* NULL and "" denote `no option', so they're ok no matter
- * what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
-
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
- should(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
-
- /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
-
- archive_write_free(a);
-}
-
-DEFINE_TEST(test_archive_write_set_option)
-{
- test(1);
- test(0);
-}
diff --git a/libarchive/test/test_archive_write_set_options.c b/libarchive/test/test_archive_write_set_options.c
index 6a0198de35e1..c35c87185041 100644
--- a/libarchive/test/test_archive_write_set_options.c
+++ b/libarchive/test/test_archive_write_set_options.c
@@ -25,11 +25,121 @@
#include "test.h"
-#define should(__a, __code, __opts) \
+#define should_write_set_filter_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_write_set_filter_option(__a, __m, __o, __v))
+
+static void
+write_set_filter_option_test(int pristine)
+{
+ struct archive* a = archive_write_new();
+
+ if (!pristine)
+ archive_write_add_filter_gzip(a);
+
+ should_write_set_filter_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_write_set_filter_option(a, ARCHIVE_OK, "", "", "");
+
+ should_write_set_filter_option(a, ARCHIVE_FAILED, NULL, "fubar", NULL);
+ should_write_set_filter_option(a, ARCHIVE_FAILED, NULL, "fubar", "snafu");
+ should_write_set_filter_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_write_set_filter_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ archive_write_free(a);
+}
+
+DEFINE_TEST(test_archive_write_set_filter_option)
+{
+ write_set_filter_option_test(1);
+ write_set_filter_option_test(0);
+}
+
+#define should_write_set_format_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_write_set_format_option(__a, __m, __o, __v))
+
+static void
+write_set_format_option_test(int pristine)
+{
+ struct archive* a = archive_write_new();
+ int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
+
+ if (!pristine)
+ archive_write_set_format_iso9660(a);
+
+ /* NULL and "" denote `no option', so they're ok no matter
+ * what, if any, formats are registered */
+ should_write_set_format_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_write_set_format_option(a, ARCHIVE_OK, "", "", "");
+
+ /* unknown modules and options */
+ should_write_set_format_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_write_set_format_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ /* unknown modules and options */
+ should_write_set_format_option(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
+ should_write_set_format_option(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
+
+ /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
+ should_write_set_format_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_write_set_format_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_write_set_format_option(a, known_option_rv, NULL, "joliet", NULL);
+ should_write_set_format_option(a, known_option_rv, NULL, "joliet", NULL);
+
+ archive_write_free(a);
+}
+
+DEFINE_TEST(test_archive_write_set_format_option)
+{
+ write_set_format_option_test(1);
+ write_set_format_option_test(0);
+}
+
+#define should_write_set_option(__a, __code, __m, __o, __v) \
+assertEqualInt(__code, archive_write_set_option(__a, __m, __o, __v))
+
+static void
+write_set_option_test(int pristine)
+{
+ struct archive* a = archive_write_new();
+ int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
+
+ if (!pristine) {
+ archive_write_add_filter_gzip(a);
+ archive_write_set_format_iso9660(a);
+ }
+
+ /* NULL and "" denote `no option', so they're ok no matter
+ * what, if any, formats are registered */
+ should_write_set_option(a, ARCHIVE_OK, NULL, NULL, NULL);
+ should_write_set_option(a, ARCHIVE_OK, "", "", "");
+
+ /* unknown modules and options */
+ should_write_set_option(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
+ should_write_set_option(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+
+ /* unknown modules and options */
+ should_write_set_option(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
+ should_write_set_option(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
+
+ /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
+ should_write_set_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_write_set_option(a, known_option_rv, "iso9660", "joliet", NULL);
+ should_write_set_option(a, known_option_rv, NULL, "joliet", NULL);
+ should_write_set_option(a, known_option_rv, NULL, "joliet", NULL);
+
+ archive_write_free(a);
+}
+
+DEFINE_TEST(test_archive_write_set_option)
+{
+ write_set_option_test(1);
+ write_set_option_test(0);
+}
+
+#define should_write_set_options(__a, __code, __opts) \
assertEqualInt(__code, archive_write_set_options(__a, __opts))
static void
-test(int pristine)
+write_set_options_test(int pristine)
{
struct archive* a = archive_write_new();
int halfempty_options_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
@@ -42,62 +152,62 @@ test(int pristine)
/* NULL and "" denote `no option', so they're ok no matter
* what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL);
- should(a, ARCHIVE_OK, "");
+ should_write_set_options(a, ARCHIVE_OK, NULL);
+ should_write_set_options(a, ARCHIVE_OK, "");
/* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar:snafu");
+ should_write_set_options(a, ARCHIVE_FAILED, "fubar:snafu");
assertEqualString("Unknown module name: `fubar'",
archive_error_string(a));
- should(a, ARCHIVE_FAILED, "fubar:snafu=betcha");
+ should_write_set_options(a, ARCHIVE_FAILED, "fubar:snafu=betcha");
assertEqualString("Unknown module name: `fubar'",
archive_error_string(a));
/* unknown modules and options */
- should(a, ARCHIVE_FAILED, "snafu");
+ should_write_set_options(a, ARCHIVE_FAILED, "snafu");
assertEqualString("Undefined option: `snafu'",
archive_error_string(a));
- should(a, ARCHIVE_FAILED, "snafu=betcha");
+ should_write_set_options(a, ARCHIVE_FAILED, "snafu=betcha");
assertEqualString("Undefined option: `snafu'",
archive_error_string(a));
/* ARCHIVE_OK with iso9660 loaded, ARCHIVE_FAILED otherwise */
- should(a, known_option_rv, "iso9660:joliet");
+ should_write_set_options(a, known_option_rv, "iso9660:joliet");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
}
- should(a, known_option_rv, "iso9660:joliet");
+ should_write_set_options(a, known_option_rv, "iso9660:joliet");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
}
- should(a, known_option_rv, "joliet");
+ should_write_set_options(a, known_option_rv, "joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, known_option_rv, "!joliet");
+ should_write_set_options(a, known_option_rv, "!joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, ARCHIVE_OK, ",");
- should(a, ARCHIVE_OK, ",,");
+ should_write_set_options(a, ARCHIVE_OK, ",");
+ should_write_set_options(a, ARCHIVE_OK, ",,");
- should(a, halfempty_options_rv, ",joliet");
+ should_write_set_options(a, halfempty_options_rv, ",joliet");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, halfempty_options_rv, "joliet,");
+ should_write_set_options(a, halfempty_options_rv, "joliet,");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
}
- should(a, ARCHIVE_FAILED, "joliet,snafu");
+ should_write_set_options(a, ARCHIVE_FAILED, "joliet,snafu");
if (pristine) {
assertEqualString("Undefined option: `joliet'",
archive_error_string(a));
@@ -106,7 +216,7 @@ test(int pristine)
archive_error_string(a));
}
- should(a, ARCHIVE_FAILED, "iso9660:snafu");
+ should_write_set_options(a, ARCHIVE_FAILED, "iso9660:snafu");
if (pristine) {
assertEqualString("Unknown module name: `iso9660'",
archive_error_string(a));
@@ -120,6 +230,6 @@ test(int pristine)
DEFINE_TEST(test_archive_write_set_options)
{
- test(1);
- test(0);
+ write_set_options_test(1);
+ write_set_options_test(0);
}
diff --git a/libarchive/test/test_entry.c b/libarchive/test/test_entry.c
index cff9c5c86efc..d852b9c1fde6 100644
--- a/libarchive/test/test_entry.c
+++ b/libarchive/test/test_entry.c
@@ -988,3 +988,31 @@ DEFINE_TEST(test_entry)
/* Release the experimental entry. */
archive_entry_free(e);
}
+
+DEFINE_TEST(test_entry_mac_metadata_self_copy)
+{
+ static const unsigned char metadata[64] = {
+ 0x41, 0x42, 0x43, 0x44
+ };
+ struct archive_entry *entry;
+ const void *copy;
+ size_t copy_size;
+
+ assert((entry = archive_entry_new()) != NULL);
+ archive_entry_copy_mac_metadata(entry, metadata, sizeof(metadata));
+ copy = archive_entry_mac_metadata(entry, &copy_size);
+ assertEqualInt(sizeof(metadata), copy_size);
+
+ archive_entry_copy_mac_metadata(entry, copy, copy_size);
+ copy = archive_entry_mac_metadata(entry, &copy_size);
+ assertEqualInt(sizeof(metadata), copy_size);
+ assertEqualMem(metadata, copy, sizeof(metadata));
+
+ archive_entry_copy_mac_metadata(entry,
+ (const unsigned char *)copy + 1, copy_size - 1);
+ copy = archive_entry_mac_metadata(entry, &copy_size);
+ assertEqualInt(sizeof(metadata) - 1, copy_size);
+ assertEqualMem(metadata + 1, copy, sizeof(metadata) - 1);
+
+ archive_entry_free(entry);
+}
diff --git a/libarchive/test/test_fuzz.c b/libarchive/test/test_fuzz.c
index dcb4f73ca372..ff6b9d9b70b8 100644
--- a/libarchive/test/test_fuzz.c
+++ b/libarchive/test/test_fuzz.c
@@ -31,7 +31,7 @@
*
* This test simply reads each archive image into memory, pokes
* random values into it and runs it through libarchive. It tries
- * to damage about 1% of each file and repeats the exercise 100 times
+ * to damage about 1% of each file and repeats the exercise 1000 times
* with each file.
*
* Unlike most other tests, this test does not verify libarchive's
diff --git a/libarchive/test/test_fuzz_consumer.h b/libarchive/test/test_fuzz_consumer.h
index 4033253e7a84..450b97273eb9 100644
--- a/libarchive/test/test_fuzz_consumer.h
+++ b/libarchive/test/test_fuzz_consumer.h
@@ -99,14 +99,14 @@ fuzz_consume_u32(struct fuzz_consumer *c)
FUZZ_UNUSED static int64_t
fuzz_consume_i64(struct fuzz_consumer *c)
{
- int64_t val = 0;
+ uint64_t val = 0;
if (c->pos + 8 <= c->size) {
int i;
for (i = 0; i < 8; i++)
- val |= (int64_t)c->data[c->pos + i] << (8 * i);
+ val |= (uint64_t)c->data[c->pos + i] << (8 * i);
c->pos += 8;
}
- return val;
+ return (int64_t)val;
}
FUZZ_UNUSED static const char *
diff --git a/libarchive/test/test_gnutar_filename_encoding.c b/libarchive/test/test_gnutar_filename_encoding.c
index 830b3884f829..f53523a198f8 100644
--- a/libarchive/test/test_gnutar_filename_encoding.c
+++ b/libarchive/test/test_gnutar_filename_encoding.c
@@ -157,7 +157,7 @@ DEFINE_TEST(test_gnutar_filename_encoding_CP1251_UTF8)
if (NULL == setlocale(LC_ALL, "Russian_Russia") &&
NULL == setlocale(LC_ALL, "ru_RU.CP1251")) {
- skipping("KOI8-R locale not available on this system.");
+ skipping("CP1251 locale not available on this system.");
return;
}
@@ -169,7 +169,7 @@ DEFINE_TEST(test_gnutar_filename_encoding_CP1251_UTF8)
assertEqualInt(ARCHIVE_OK, archive_write_set_format_gnutar(a));
if (archive_write_set_options(a, "hdrcharset=UTF-8") != ARCHIVE_OK) {
skipping("This system cannot convert character-set"
- " from KOI8-R to UTF-8.");
+ " from CP1251 to UTF-8.");
archive_write_free(a);
return;
}
@@ -177,7 +177,7 @@ DEFINE_TEST(test_gnutar_filename_encoding_CP1251_UTF8)
archive_write_open_memory(a, buff, sizeof(buff), &used));
entry = archive_entry_new2(a);
- /* Set a KOI8-R filename. */
+ /* Set a CP1251 filename. */
archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_size(entry, 0);
@@ -201,7 +201,7 @@ DEFINE_TEST(test_gnutar_filename_encoding_ru_RU_CP1251)
size_t used;
if (NULL == setlocale(LC_ALL, "ru_RU.CP1251")) {
- skipping("KOI8-R locale not available on this system.");
+ skipping("CP1251 locale not available on this system.");
return;
}
@@ -215,7 +215,7 @@ DEFINE_TEST(test_gnutar_filename_encoding_ru_RU_CP1251)
archive_write_open_memory(a, buff, sizeof(buff), &used));
entry = archive_entry_new2(a);
- /* Set a KOI8-R filename. */
+ /* Set a CP1251 filename. */
archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_size(entry, 0);
@@ -389,6 +389,55 @@ DEFINE_TEST(test_gnutar_filename_encoding_CP932_UTF8)
assertEqualMem(buff, "\xE8\xA1\xA8.txt", 7);
}
+/*
+ * A group name whose converted byte length exceeds the 32-byte gname
+ * field used to clamp on strlen() of the converted string instead of its
+ * byte length. With a header charset that embeds NUL bytes (UTF-16),
+ * strlen() stops at the first NUL while the memcpy still used the full
+ * byte length, writing past the 512-byte header buffer.
+ */
+DEFINE_TEST(test_gnutar_filename_encoding_long_gname_utf16)
+{
+ struct archive *a;
+ struct archive_entry *entry;
+ char buff[4096];
+ char gname[256];
+ size_t used;
+
+ a = archive_write_new();
+ assertEqualInt(ARCHIVE_OK, archive_write_set_format_gnutar(a));
+ if (archive_write_set_options(a, "hdrcharset=UTF-16LE") != ARCHIVE_OK) {
+ skipping("This system cannot convert character-set"
+ " to UTF-16LE.");
+ archive_write_free(a);
+ return;
+ }
+ assertEqualInt(ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ memset(gname, 'A', sizeof(gname) - 1);
+ gname[sizeof(gname) - 1] = '\0';
+
+ entry = archive_entry_new2(a);
+ archive_entry_set_pathname(entry, "file");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ archive_entry_set_gname(entry, gname);
+ assertEqualInt(ARCHIVE_OK, archive_write_header(a, entry));
+ archive_entry_free(entry);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ /* The archive is still well-formed; the gname field is truncated to
+ * the 32-byte field, which reads back as "A" (the first UTF-16 unit
+ * before the embedded NUL). */
+ a = archive_read_new();
+ assertEqualInt(ARCHIVE_OK, archive_read_support_format_tar(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+ assertEqualInt(ARCHIVE_OK, archive_read_next_header(a, &entry));
+ assertEqualString("A", archive_entry_gname(entry));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_gnutar_filename_encoding_UTF16_win)
{
#if !defined(_WIN32) || defined(__CYGWIN__)
diff --git a/libarchive/test/test_pax_filename_encoding.c b/libarchive/test/test_pax_filename_encoding.c
index 27e10eaffc6c..db506c95f7eb 100644
--- a/libarchive/test/test_pax_filename_encoding.c
+++ b/libarchive/test/test_pax_filename_encoding.c
@@ -389,7 +389,7 @@ DEFINE_TEST(test_pax_filename_encoding_CP1251)
if (NULL == setlocale(LC_ALL, "Russian_Russia") &&
NULL == setlocale(LC_ALL, "ru_RU.CP1251")) {
- skipping("KOI8-R locale not available on this system.");
+ skipping("CP1251 locale not available on this system.");
return;
}
@@ -398,7 +398,7 @@ DEFINE_TEST(test_pax_filename_encoding_CP1251)
assertEqualInt(ARCHIVE_OK, archive_write_set_format_pax(a));
if (archive_write_set_options(a, "hdrcharset=UTF-8") != ARCHIVE_OK) {
skipping("This system cannot convert character-set"
- " from KOI8-R to UTF-8.");
+ " from CP1251 to UTF-8.");
archive_write_free(a);
return;
}
@@ -419,7 +419,7 @@ DEFINE_TEST(test_pax_filename_encoding_CP1251)
archive_entry_free(entry);
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
- /* Above three characters in KOI8-R should translate to the following
+ /* Above three characters in CP1251 should translate to the following
* three characters (two bytes each) in UTF-8. */
assertEqualMem(buff + 512, "15 path=\xD0\xBF\xD1\x80\xD0\xB8\x0A", 15);
}
@@ -483,7 +483,7 @@ DEFINE_TEST(test_pax_filename_encoding_CP932)
if (NULL == setlocale(LC_ALL, "Japanese_Japan") &&
NULL == setlocale(LC_ALL, "ja_JP.SJIS")) {
- skipping("eucJP locale not available on this system.");
+ skipping("CP932/SJIS locale not available on this system.");
return;
}
diff --git a/libarchive/test/test_read_data_into_fd_size_exceeds_declared.c b/libarchive/test/test_read_data_into_fd_size_exceeds_declared.c
new file mode 100644
index 000000000000..a711a380dd0c
--- /dev/null
+++ b/libarchive/test/test_read_data_into_fd_size_exceeds_declared.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2026 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"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define open _open
+#define close _close
+#endif
+
+/*
+ * archive_read_data_into_fd() writes archive_read_data_block() output
+ * straight to an fd with no notion of the entry's declared size -- it's
+ * used directly by callers like bsdtar's "-O" (stdout) extraction and
+ * bsdcat, which bypass archive_write_disk (and the size-truncation
+ * safeguard archive_write_disk already has). This is a format-agnostic
+ * backstop for that gap: it must never write more than the entry's
+ * declared size, regardless of which format reader produced the data
+ * or whether that reader independently enforces its own size boundary.
+ *
+ * The reference archives are crafted ZIP files (a stored entry whose
+ * physical body is longer than its declared size, and a deflate entry
+ * whose real inflated output exceeds 256KiB so it can't complete within
+ * a single internal decode call -- see zip_read_data_deflate()'s
+ * internal output buffer). Each has two entries: "first" declares
+ * uncompressed_size=10 but its real content is longer, and "second" is
+ * a well-formed 11-byte "hello world" file, read right after "first"
+ * fails to confirm the mis-declared entry doesn't disrupt the rest of
+ * the archive. This test is deliberately independent of the ZIP reader
+ * itself enforcing this boundary -- archive_read_data_into_fd() must
+ * cap its own output regardless of whether the format reader does.
+ */
+static void
+verify(const char *refname)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char tmpfilename[] = "data_into_fd_test";
+ int fd;
+ char buff[32];
+ size_t n;
+ FILE *f;
+
+ extract_reference_file(refname);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("first", archive_entry_pathname(ae));
+ assertEqualInt(10, archive_entry_size(ae));
+
+ fd = open(tmpfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ assert(fd >= 0);
+ failure("archive_read_data_into_fd() must report an error when the "
+ "entry's actual data exceeds its declared size");
+ assert(archive_read_data_into_fd(a, fd) != ARCHIVE_OK);
+ close(fd);
+
+ f = fopen(tmpfilename, "rb");
+ assert(f != NULL);
+ n = fread(buff, 1, sizeof(buff), f);
+ fclose(f);
+ failure("archive_read_data_into_fd() must never write more bytes "
+ "than the entry's declared size");
+ assert(n <= 10);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(11, archive_entry_size(ae));
+
+ fd = open(tmpfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ assert(fd >= 0);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_into_fd(a, fd));
+ close(fd);
+
+ f = fopen(tmpfilename, "rb");
+ assert(f != NULL);
+ n = fread(buff, 1, sizeof(buff), f);
+ fclose(f);
+ assertEqualInt(11, (int)n);
+ assertEqualMem(buff, "hello world", 11);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_data_into_fd_size_exceeds_declared_stored)
+{
+ verify("test_read_data_into_fd_size_exceeds_declared_stored.zip");
+}
+
+DEFINE_TEST(test_read_data_into_fd_size_exceeds_declared_deflate)
+{
+ verify("test_read_data_into_fd_size_exceeds_declared_deflate.zip");
+}
diff --git a/libarchive/test/test_read_data_into_fd_size_exceeds_declared_deflate.zip.uu b/libarchive/test/test_read_data_into_fd_size_exceeds_declared_deflate.zip.uu
new file mode 100644
index 000000000000..12f633e45b6a
--- /dev/null
+++ b/libarchive/test/test_read_data_into_fd_size_exceeds_declared_deflate.zip.uu
@@ -0,0 +1,23 @@
+begin 644 test_read_data_into_fd_size_exceeds_declared_deflate.zip
+M4$L#!!0````(``````#"V8Q_D`(```H````%````9FER<W3MR#$!```(P*!P
+MLW\F+TT!)W7FY9QSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCG7`E!+`P04````````````A1%*#0L````+````!@``
+M`'-E8V]N9&AE;&QO('=O<FQD4$L!`A0`%`````@``````,+9C'^0`@``"@``
+M``4``````````````````````&9I<G-T4$L!`A0`%````````````(412@T+
+M````"P````8`````````````````LP(``'-E8V]N9%!+!08``````@`"`&<`
+(``#B`@``````
+`
+end
diff --git a/libarchive/test/test_read_data_into_fd_size_exceeds_declared_stored.zip.uu b/libarchive/test/test_read_data_into_fd_size_exceeds_declared_stored.zip.uu
new file mode 100644
index 000000000000..228ca983f0b6
--- /dev/null
+++ b/libarchive/test/test_read_data_into_fd_size_exceeds_declared_stored.zip.uu
@@ -0,0 +1,9 @@
+begin 644 test_read_data_into_fd_size_exceeds_declared_stored.zip
+M4$L#!!0```````````#-F1X3%`````H````%````9FER<W1!04%!04%!04%!
+M0D)"0D)"0D)"0E!+`P04````````````A1%*#0L````+````!@```'-E8V]N
+M9&AE;&QO('=O<FQD4$L!`A0`%````````````,V9'A,4````"@````4`````
+M`````````````````&9I<G-T4$L!`A0`%````````````(412@T+````"P``
+M``8`````````````````-P```'-E8V]N9%!+!08``````@`"`&<```!F````
+"````
+`
+end
diff --git a/libarchive/test/test_read_data_large.c b/libarchive/test/test_read_data_large.c
index 59cbfd496ca9..37182349cc42 100644
--- a/libarchive/test/test_read_data_large.c
+++ b/libarchive/test/test_read_data_large.c
@@ -35,6 +35,7 @@
#if defined(_WIN32) && !defined(__CYGWIN__)
#define open _open
#define close _close
+#define ftruncate _chsize
#endif
static char buff1[11000000];
@@ -108,3 +109,94 @@ DEFINE_TEST(test_read_data_large)
fclose(f);
assertEqualMem(buff2, buff3, sizeof(buff3));
}
+
+#define FILE_SIZE 0x90000 /* 576 KiB total logical size */
+
+static char sparse_buff[0x10000]; /* Holds the (small) stored archive. */
+static char sparse_data[FILE_SIZE];
+static char sparse_expected[FILE_SIZE];
+
+/*
+ * Regression test for archive_read_data_into_fd() extracting a sparse
+ * file that ends in a hole. For a sparse entry libarchive seeks the
+ * destination descriptor forward to recreate holes. The trailing hole
+ * is created from the EOF handling after the last data block.
+ */
+DEFINE_TEST(test_read_data_into_fd_sparse)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ size_t archive_size = 0;
+ const char *skip_sparse_tests;
+ int fd;
+
+ skip_sparse_tests = getenv("SKIP_TEST_SPARSE");
+ if (skip_sparse_tests != NULL) {
+ skipping("Skipping sparse tests due to SKIP_TEST_SPARSE "
+ "environment variable");
+ return;
+ }
+
+ /*
+ * The logical file is all 'a', and the sparse map marks two data
+ * regions, everything else (including the tail) is a hole that
+ * must read back as zero. The last data region ends at 0x81000
+ * while the file size is 0x90000, so the file ends in a hole.
+ */
+ memset(sparse_data, 'a', sizeof(sparse_data));
+ memset(sparse_expected, 0, sizeof(sparse_expected));
+ memset(sparse_expected + 0x10000, 'a', 0x1000);
+ memset(sparse_expected + 0x80000, 'a', 0x1000);
+
+ /* Create a sparse pax archive in memory. */
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, sparse_buff,
+ sizeof(sparse_buff), &archive_size));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "file");
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ archive_entry_set_size(ae, FILE_SIZE);
+ archive_entry_sparse_add_entry(ae, 0x10000, 0x1000);
+ archive_entry_sparse_add_entry(ae, 0x80000, 0x1000);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assertEqualIntA(a, FILE_SIZE,
+ archive_write_data(a, sparse_data, sizeof(sparse_data)));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ /* Read it back and extract it into a real file descriptor. */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, sparse_buff, archive_size));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualInt(FILE_SIZE, archive_entry_size(ae));
+
+ fd = open("file", O_WRONLY | O_CREAT | O_BINARY, 0644);
+ assert(fd >= 0);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_into_fd(a, fd));
+
+ /*
+ * archive_read_data_into_fd() seeks over the trailing hole but, like
+ * a regular extraction, leaves the on-disk size short. Truncate up
+ * to the full size to realize the final hole, as a consumer does.
+ */
+ assert(ftruncate(fd, FILE_SIZE) != -1);
+ close(fd);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /*
+ * The reconstructed file must match byte for byte: data regions
+ * are 'a', every hole (including the trailing one) is zero.
+ */
+ assertFileSize("file", FILE_SIZE);
+ assertFileContents(sparse_expected, FILE_SIZE, "file");
+}
diff --git a/libarchive/test/test_read_filter_program.c b/libarchive/test/test_read_filter_program.c
index 18b42121cc22..bd584f5086f7 100644
--- a/libarchive/test/test_read_filter_program.c
+++ b/libarchive/test/test_read_filter_program.c
@@ -77,6 +77,8 @@ DEFINE_TEST(test_read_filter_program)
archive_read_next_header(a, &ae));
assertEqualInt(archive_filter_code(a, 0), ARCHIVE_FILTER_PROGRAM);
assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR);
+ assertEqualString("Program: gzip -d", archive_filter_name(a, 0));
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualString("Program: gzip -d", archive_filter_name(a, 0));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
diff --git a/libarchive/test/test_read_format_7zip.c b/libarchive/test/test_read_format_7zip.c
index 6f5ae3671a8c..66b5930e4319 100644
--- a/libarchive/test/test_read_format_7zip.c
+++ b/libarchive/test/test_read_format_7zip.c
@@ -160,6 +160,32 @@ test_empty_file(void)
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+DEFINE_TEST(test_read_format_7zip_archive_properties)
+{
+ const char *refname = "test_read_format_7zip_archive_properties.7z";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ extract_reference_file(refname);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("empty", archive_entry_pathname(ae));
+ assertEqualInt(0, archive_entry_size(ae));
+ assertEqualInt(1, archive_file_count(a));
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
/*
* Extract an encoded file.
* The header of the 7z archive files is not encoded.
@@ -1383,6 +1409,47 @@ DEFINE_TEST(test_read_format_7zip_sfx_elf)
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+static void
+test_sfx_boundary_empty_7z(const char *refname)
+{
+ struct archive_entry *ae = NULL;
+ struct archive *a;
+ const int bs = 10240;
+
+ extract_reference_file(refname);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, bs));
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(0, archive_file_count(a));
+ assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_7zip_sfx_boundary_pe)
+{
+ /*
+ * The embedded empty 7z archive starts at the final exact
+ * 32-byte scan window after the PE overlay offset.
+ */
+ test_sfx_boundary_empty_7z(
+ "test_read_format_7zip_sfx_boundary_pe.exe");
+}
+
+DEFINE_TEST(test_read_format_7zip_sfx_boundary_elf)
+{
+ /*
+ * The ELF section string table contains "\0.data\0", so
+ * ".data\0" starts at the final valid string-table position.
+ */
+ test_sfx_boundary_empty_7z(
+ "test_read_format_7zip_sfx_boundary_elf.elf");
+}
+
/*
* A truncated ELF 64-bit MSB file.
*/
diff --git a/libarchive/test/test_read_format_7zip_archive_properties.7z.uu b/libarchive/test/test_read_format_7zip_archive_properties.7z.uu
new file mode 100644
index 000000000000..b5759d932401
--- /dev/null
+++ b/libarchive/test/test_read_format_7zip_archive_properties.7z.uu
@@ -0,0 +1,5 @@
+begin 644 test_read_format_7zip_archive_properties.7z
+M-WJ\KR<< ,=YJHC S '@@J#,! @$!J@ % 0X!@ \!
+F@!$- &4 ;0!P '0 >0 !0* 0" UD J+*= 14& 0 @@*2!
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_folders_oom.7z.uu b/libarchive/test/test_read_format_7zip_folders_oom.7z.uu
new file mode 100644
index 000000000000..682ffb8ce422
--- /dev/null
+++ b/libarchive/test/test_read_format_7zip_folders_oom.7z.uu
@@ -0,0 +1,8 @@
+begin 644 test_read_format_7zip_folders_oom.7z
+M-WJ\KR<< *^Y_3?$ !E &/C(9T! -A86$* $ V)B
+M8@H 00& ()" @* 5\J+KLX07WL <+XR&= 0 #86%A"@ ! -B8F(* $$
+M!@ ""0@( @ !(2$!%@$A,@$6# 0$"@&57?ASX1\F3 ( % A,9 &$ +@!T
+M '@ = #_ !X '0 4 0 #80 &/C(9T! -A86$* $
+0 V+<MM4!@)N8]-RVU0$
+
+end
diff --git a/libarchive/test/test_archive_read_close_twice_open_filename.c b/libarchive/test/test_read_format_7zip_folders_oom.c
index 0be64091d77d..53342dfc0375 100644
--- a/libarchive/test/test_archive_read_close_twice_open_filename.c
+++ b/libarchive/test/test_read_format_7zip_folders_oom.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011 Tim Kientzle
+ * Copyright (c) 2026 Kaif Khan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,27 +22,29 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
#include "test.h"
-DEFINE_TEST(test_archive_read_close_twice_open_filename)
+/*
+ * The NumFolders field in a 7-Zip StreamsInfo/CodersInfo header is used to
+ * size the folders allocation before any folder is read. A crafted archive
+ * that declares a huge NumFolders must be rejected instead of attempting a
+ * multi-gigabyte allocation.
+ */
+DEFINE_TEST(test_read_format_7zip_folders_oom)
{
- const char *filename = "empty.file";
- struct archive* a = archive_read_new();
+ const char *refname = "test_read_format_7zip_folders_oom.7z";
+ struct archive *a;
+ struct archive_entry *ae;
- assertMakeFile(filename, 0644, "");
- assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a));
- assertEqualInt(ARCHIVE_OK, archive_read_open_filename(a, filename, 0));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ extract_reference_file(refname);
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
diff --git a/libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu b/libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu
new file mode 100644
index 000000000000..1d396baf8a44
--- /dev/null
+++ b/libarchive/test/test_read_format_7zip_sfx_boundary_elf.elf.uu
@@ -0,0 +1,22 @@
+begin 644 test_read_format_7zip_sfx_boundary_elf.elf
+M?T5,1@(!`0````````````(`/@`!```````````````````````````"````
+M`````````$```````$```P`!```N9&%T80``````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````,`````
+M````````````````````0``````````'````````````````````````````
+M``````````````$````!```````````````````````````#````````(```
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````-WJ\KR<<``2-F]4/````````````````````````````````````````
+6````````````````````````````````
+`
+end
diff --git a/libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu b/libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu
new file mode 100644
index 000000000000..a4895655b281
--- /dev/null
+++ b/libarchive/test/test_read_format_7zip_sfx_boundary_pe.exe.uu
@@ -0,0 +1,22 @@
+begin 644 test_read_format_7zip_sfx_boundary_pe.exe
+M35H`````````````````````````````````````````````````````````
+M````````````````````0````%!%``!,`0$`````````````````X``"````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````+G1E
+M>'0````````````````!`````@``````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````04%!04%!04%!04%!04%!04%!04%!04%!04%!04%!04$W>KRO)QP`!(V;
+6U0\`````````````````````````````
+`
+end
diff --git a/libarchive/test/test_archive_read_close_twice_open_fd.c b/libarchive/test/test_read_format_cab_lzx_16bit.c
index 852038dde3f4..6efc3801fc1c 100644
--- a/libarchive/test/test_archive_read_close_twice_open_fd.c
+++ b/libarchive/test/test_read_format_cab_lzx_16bit.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011 Tim Kientzle
+ * Copyright (c) 2026 Tobias Stoeckmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,25 +22,31 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
#include "test.h"
-DEFINE_TEST(test_archive_read_close_twice_open_fd)
+DEFINE_TEST(test_read_format_cab_lzx_16bit)
{
- struct archive* a = archive_read_new();
+ const char *refname = "test_read_format_cab_lzx_16bit.cab";
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[16];
- assertEqualInt(ARCHIVE_OK, archive_read_support_format_empty(a));
- assertEqualInt(ARCHIVE_OK, archive_read_open_fd(a, 0, 0));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ extract_reference_file(refname);
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(0, archive_errno(a));
- assertEqualString(NULL, archive_error_string(a));
+ /* Read entry. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "lzx16.bin");
+ assertEqualInt(archive_entry_size(ae), 16);
+ assertEqualIntA(a, sizeof(buff),
+ archive_read_data(a, buff, sizeof(buff)));
+ assertEqualMem(buff, "ABABABABABABABAB", 16);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
diff --git a/libarchive/test/test_read_format_cab_lzx_16bit.cab.uu b/libarchive/test/test_read_format_cab_lzx_16bit.cab.uu
new file mode 100644
index 000000000000..c1e6330a47ee
--- /dev/null
+++ b/libarchive/test/test_read_format_cab_lzx_16bit.cab.uu
@@ -0,0 +1,6 @@
+begin 644 test_read_format_cab_lzx_16bit.cab
+M35-#1@````""`````````"P``````````P$!``$`````````1@````$``P\0
+M````````````(0```"``;'IX,38N8FEN```````T`!```!`#`6=%JXGOS0+P
+H"R(%[??[`.@``````````'^(__\`X`````````(`_R'__ZK*`*@`````
+`
+end
diff --git a/libarchive/test/test_archive_read_set_format_option.c b/libarchive/test/test_read_format_cab_lzx_e8.c
index e32d8112445c..8ebe09033438 100644
--- a/libarchive/test/test_archive_read_set_format_option.c
+++ b/libarchive/test/test_read_format_cab_lzx_e8.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011 Tim Kientzle
+ * Copyright (c) 2026 Tobias Stoeckmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,45 +22,38 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
#include "test.h"
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_read_set_format_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
+DEFINE_TEST(test_read_format_cab_lzx_e8)
{
- struct archive* a = archive_read_new();
- int known_option_rv = pristine ? ARCHIVE_FAILED : ARCHIVE_OK;
-
- if (!pristine)
- archive_read_support_format_all(a);
+ const char *refname = "test_read_format_cab_lzx_e8.cab";
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[30];
- /* NULL and "" denote `no option', so they're ok no matter
- * what, if any, formats are registered */
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
+ extract_reference_file(refname);
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
- /* unknown modules and options */
- should(a, ARCHIVE_FAILED, NULL, "snafu", NULL);
- should(a, ARCHIVE_FAILED, NULL, "snafu", "betcha");
+ /* Read first entry. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "e8.bin");
+ assertEqualInt(archive_entry_size(ae), 30);
- /* ARCHIVE_OK with iso9660 loaded, ARCHIVE_WARN otherwise */
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, "iso9660", "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
- should(a, known_option_rv, NULL, "joliet", NULL);
+ /* Verify that E8 translation works. */
+ assertEqualIntA(a, sizeof(buff),
+ archive_read_data(a, buff, sizeof(buff)));
+ assertEqualMem(buff, "\xE8\x00\x00\x00\x00", 5);
+ assertEqualMem(buff + 5, "\xE8\x00\x00\x00\x80", 5);
+ assertEqualMem(buff + 10, "\xE8\xFF\xFF\xFF\x7F", 5);
+ assertEqualMem(buff + 15, "\xE8\xFF\x00\x00\x00", 5);
+ assertEqualMem(buff + 20, "\xE8\x12\x34\x56\x78", 5);
+ assertEqualMem(buff + 25, "\xE8\x87\x65\x43\x21", 5);
- archive_read_free(a);
-}
-
-DEFINE_TEST(test_archive_read_set_format_option)
-{
- test(1);
- test(0);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
diff --git a/libarchive/test/test_read_format_cab_lzx_e8.cab.uu b/libarchive/test/test_read_format_cab_lzx_e8.cab.uu
new file mode 100644
index 000000000000..f619dac36330
--- /dev/null
+++ b/libarchive/test/test_read_format_cab_lzx_e8.cab.uu
@@ -0,0 +1,6 @@
+begin 644 test_read_format_cab_lzx_e8.cab.uu
+M35-#1@````!]`````````"P``````````P$!``$`````````0P````$``P\>
+M````````````XUQ,BB``93@N8FEN`,J?6J@R`!X`6X"`C0`PX`$/`````0``
+C``4```#H`````.@```"`Z/___W_H#@$``.@2-%9XZ(=E0R$`
+`
+end
diff --git a/libarchive/test/test_read_format_cpio_symlink_trailer.c b/libarchive/test/test_read_format_cpio_symlink_trailer.c
new file mode 100644
index 000000000000..5c520a783bb3
--- /dev/null
+++ b/libarchive/test/test_read_format_cpio_symlink_trailer.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2026 Kaif Khan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+/*
+ * The cpio reader reads an entry's name into a read-ahead buffer and holds
+ * that pointer to compare the name against "TRAILER!!!" (end of archive).
+ * For a symlink entry the link body is read first, and a large body can grow
+ * (and free) the read-ahead buffer, leaving the name pointer dangling before
+ * the trailer comparison.
+ *
+ * The reference archive is a single newc entry: a symlink named "TRAILER!!!"
+ * with a 128 KiB body. Reading it in small blocks assembles the name in the
+ * reader's copy buffer, which the symlink body read then reallocates; the
+ * trailer must still be detected without touching freed memory.
+ */
+DEFINE_TEST(test_read_format_cpio_symlink_trailer)
+{
+ const char *name = "test_read_format_cpio_symlink_trailer.cpio";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ extract_reference_file(name);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cpio(a));
+ /* Small block size so the name is assembled in the reader's copy
+ * buffer, which the symlink body read then reallocates. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 13));
+
+ /* The "TRAILER!!!" entry ends the archive. */
+ 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_cpio_symlink_trailer.cpio.uu b/libarchive/test/test_read_format_cpio_symlink_trailer.cpio.uu
new file mode 100644
index 000000000000..1ac44473d3f2
--- /dev/null
+++ b/libarchive/test/test_read_format_cpio_symlink_trailer.cpio.uu
@@ -0,0 +1,2919 @@
+begin 644 test_read_format_cpio_symlink_trailer.cpio
+M,#<P-S`Q,#`P,#`P,#$P,#`P03%&1C`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,3`P,#`P,#`P,#`P,C`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#!",#`P,#`P,#!44D%)3$52(2$A`````'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+M>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+5>'AX>'AX>'AX>'AX>'AX>'AX>'AX
+`
+end
diff --git a/libarchive/test/test_read_format_gtar_sparse.c b/libarchive/test/test_read_format_gtar_sparse.c
index fdb4be31a170..06fcffef4057 100644
--- a/libarchive/test/test_read_format_gtar_sparse.c
+++ b/libarchive/test/test_read_format_gtar_sparse.c
@@ -169,6 +169,58 @@ static struct archive_contents {
{ NULL, NULL }
};
+/* Reuse one callback buffer so the sparse extension overwrites the main header. */
+struct sparse_reuse_data {
+ FILE *file;
+ unsigned char block[512];
+};
+
+static la_ssize_t
+sparse_reuse_read(struct archive *a, void *client_data, const void **buffer)
+{
+ struct sparse_reuse_data *data = client_data;
+ size_t size;
+
+ (void)a;
+ size = fread(data->block, 1, sizeof(data->block), data->file);
+ if (size == 0 && ferror(data->file))
+ return (-1);
+ *buffer = data->block;
+ return ((la_ssize_t)size);
+}
+
+static void
+verify_sparse_header_reuse(void)
+{
+ const char *name = "test_read_format_gtar_sparse_reuse.tar";
+ struct sparse_reuse_data data;
+ struct archive_entry *ae;
+ struct archive *a;
+
+ extract_reference_file(name);
+ memset(&data, 0, sizeof(data));
+ data.file = fopen(name, "rb");
+ if (!assert(data.file != NULL))
+ return;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open(a, &data, NULL, sparse_reuse_read, NULL));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+
+ assertEqualString("sparse", archive_entry_pathname(ae));
+ assertEqualInt(0644, archive_entry_perm(ae));
+ assertEqualInt(123, archive_entry_uid(ae));
+ assertEqualInt(456, archive_entry_gid(ae));
+ assertEqualInt(123456789, archive_entry_mtime(ae));
+
+ 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));
+ assertEqualInt(0, fclose(data.file));
+}
+
static void
verify_archive_file(const char *name, struct archive_contents *ac)
{
@@ -262,6 +314,8 @@ verify_archive_file(const char *name, struct archive_contents *ac)
DEFINE_TEST(test_read_format_gtar_sparse)
{
+ verify_sparse_header_reuse();
+
/* Two archives that use the "GNU tar sparse format". */
verify_archive_file("test_read_format_gtar_sparse_1_13.tar", files);
verify_archive_file("test_read_format_gtar_sparse_1_17.tar", files);
diff --git a/libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu b/libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu
new file mode 100644
index 000000000000..72df7c720d15
--- /dev/null
+++ b/libarchive/test/test_read_format_gtar_sparse_reuse.tar.uu
@@ -0,0 +1,48 @@
+begin 644 test_read_format_gtar_sparse_reuse.tar
+M<W!A<G-E````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V-#0`,#`P,#$W,P`P,#`P-S$P`#`P,#`P,#`P,#`P
+M`#`P-S(V-S0V-#(U`#`R,#8S,0`@4P``````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<B`@`'5S97(`
+M````````````````````````````````````9W)O=7``````````````````
+M```````````````````P,#`P,#`P`#`P,#`P,#``,#`P,#`P,#`P,#``,#`P
+M,#`P,#`P,#``,#`P,#`P,#`P,#`````````P,#`P,#`P,#`P,``P,#`P,#`P
+M,#`P,```````````````````````````````````````````````````````
+M```````````````````````````````````````````!,#`P,#`P,#`P,#``
+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````````````````````````````````````````````````````````````
+7````````````````````````````````
+end
diff --git a/libarchive/test/test_read_format_iso_rockridge_zf_overflow.c b/libarchive/test/test_read_format_iso_rockridge_zf_overflow.c
new file mode 100644
index 000000000000..3768669e6ddf
--- /dev/null
+++ b/libarchive/test/test_read_format_iso_rockridge_zf_overflow.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2026 Kaif Khan
+ * 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"
+
+/*
+ * parse_rockridge_ZF1() reads data[0] and data[1] before checking that the
+ * "ZF" entry is long enough (the data_length == 12 test was the last term of
+ * the condition). A "ZF" entry with a length byte of 5 yields data_length 1,
+ * so data[1] reads one byte past the SUSP area validated by parse_rockridge().
+ *
+ * The reference image carries the "ZF" entry in a "CE" continuation block that
+ * is the final logical block of the image, so the over-read lands one byte
+ * past the end of the buffer that holds the block. Reading the image from an
+ * exactly-sized memory buffer makes the over-read observable to AddressSanitizer
+ * (a padded buffer would hide it). With the fix the short entry is rejected and
+ * the read completes.
+ */
+DEFINE_TEST(test_read_format_iso_rockridge_zf_overflow)
+{
+ const char *refname = "test_read_format_iso_rockridge_zf_overflow.iso";
+ struct archive *a;
+ struct archive_entry *ae;
+ char *buff, *exact;
+ size_t used;
+ int r;
+
+ extract_reference_file(refname);
+
+ /* slurpfile() over-allocates by one byte; copy into an exactly-sized
+ * buffer so a single-byte over-read is not masked. */
+ buff = slurpfile(&used, "%s", refname);
+ assert(buff != NULL);
+ assert(used > 0);
+ assert((exact = malloc(used)) != NULL);
+ memcpy(exact, buff, used);
+ free(buff);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, exact, used));
+
+ /* Walk the image to completion. The malformed "ZF" entry is parsed
+ * while reading the root directory's continuation block; before the
+ * fix this over-reads the block buffer. */
+ while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK ||
+ r == ARCHIVE_WARN)
+ archive_read_data_skip(a);
+ assert(r == ARCHIVE_EOF || r == ARCHIVE_FATAL);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ free(exact);
+}
diff --git a/libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu b/libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu
new file mode 100644
index 000000000000..1ca39dfe7859
--- /dev/null
+++ b/libarchive/test/test_read_format_iso_rockridge_zf_overflow.iso.uu
@@ -0,0 +1,1096 @@
+begin 664 test_read_format_iso_rockridge_zf_overflow.iso
+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````````````````````````````````````````````````````````````
+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````````````````````````````````````````````````````````````
+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````````````````````````````````````````````````````````````
+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```````````!0T0P,#$!`"`@("`@("`@("`@("`@("`@("`@("`@("`@("`@
+M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("```````````!@`
+M```````8```````````````````````````````````````````!```!`0``
+M`0`("```"``````(`!(````````````````````B`!8````````6``@`````
+M"`!Z`0$``````@```0```0$`````````````````````````````````````
+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`````````````````````````````````````````/]#1#`P,0$`````````
+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````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````N`!8````````6``@`````"`!Z`0$``````@```0```0$`
+M4U`'`;[O`%!$!0$`(@`6````````%@`(``````@`>@$!``````(```$```$!
+M`3X`%P```````!<``````````'H!`0`````"```!```!`4%#11P!%P``````
+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`````````````````````````````````````````````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+M"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$"0$``````%!$
+,"0$``````%I&!0%P
+`
+end
diff --git a/libarchive/test/test_read_format_isojoliet_bz2.c b/libarchive/test/test_read_format_isojoliet_compress.c
index 68a9486e7aac..9e509fc62b9c 100644
--- a/libarchive/test/test_read_format_isojoliet_bz2.c
+++ b/libarchive/test/test_read_format_isojoliet_compress.c
@@ -2,7 +2,7 @@
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
- * Based on libarchive/test/test_read_format_isorr_bz2.c with
+ * Based on libarchive/test/test_read_format_isorr_compress.c with
* bugs introduced by Andreas Henriksson <andreas@fatal.se> for
* testing ISO9660 image with Joliet extension.
*
@@ -30,7 +30,7 @@
/*
Execute the following to rebuild the data for this program:
- tail -n +35 test_read_format_isojoliet_bz2.c | /bin/sh
+ tail -n +35 test_read_format_isojoliet_compress.c | /bin/sh
rm -rf /tmp/iso
mkdir /tmp/iso
@@ -51,7 +51,7 @@ uuencode $F $F > $F.uu
exit 1
*/
-DEFINE_TEST(test_read_format_isojoliet_bz2)
+DEFINE_TEST(test_read_format_isojoliet_compress)
{
const char *refname = "test_read_format_iso_joliet.iso.Z";
struct archive_entry *ae;
diff --git a/libarchive/test/test_read_format_isorr_bz2.c b/libarchive/test/test_read_format_isorr_compress.c
index c1accea78792..f1164bce1ae4 100644
--- a/libarchive/test/test_read_format_isorr_bz2.c
+++ b/libarchive/test/test_read_format_isorr_compress.c
@@ -29,7 +29,7 @@ PLEASE use old cdrtools; mkisofs version is 2.01.
This version mkisofs made wrong "SL" System Use Entry of RRIP.
Execute the following command to rebuild the data for this program:
- tail -n +34 test_read_format_isorr_bz2.c | /bin/sh
+ tail -n +34 test_read_format_isorr_compress.c | /bin/sh
rm -rf /tmp/iso
mkdir /tmp/iso
@@ -51,7 +51,7 @@ uuencode $F $F > $F.uu
exit 1
*/
-DEFINE_TEST(test_read_format_isorr_bz2)
+DEFINE_TEST(test_read_format_isorr_compress)
{
const char *refname = "test_read_format_iso_rockridge.iso.Z";
struct archive_entry *ae;
@@ -68,7 +68,7 @@ DEFINE_TEST(test_read_format_isorr_bz2)
assertEqualInt(ARCHIVE_OK,
archive_read_open_filename(a, refname, 10240));
- /* Retrieve each of the 8 files on the ISO image and
+ /* Retrieve each of the 10 entries on the ISO image and
* verify that each one is what we expect. */
for (i = 0; i < 10; ++i) {
assertEqualInt(0, archive_read_next_header(a, &ae));
diff --git a/libarchive/test/test_read_format_isorr_new_bz2.c b/libarchive/test/test_read_format_isorr_new_compress.c
index 787c1e6301be..43aea982ff48 100644
--- a/libarchive/test/test_read_format_isorr_new_bz2.c
+++ b/libarchive/test/test_read_format_isorr_new_compress.c
@@ -29,7 +29,7 @@ PLEASE use latest cdrtools at least mkisofs version is 2.01.01a63 or later.
Old version mkisofs made wrong "SL" System Use Entry of RRIP.
Execute the following command to rebuild the data for this program:
- tail -n +34 test_read_format_isorr_new_bz2.c | /bin/sh
+ tail -n +34 test_read_format_isorr_new_compress.c | /bin/sh
rm -rf /tmp/iso
mkdir /tmp/iso
@@ -51,7 +51,7 @@ uuencode $F $F > $F.uu
exit 1
*/
-DEFINE_TEST(test_read_format_isorr_new_bz2)
+DEFINE_TEST(test_read_format_isorr_new_compress)
{
const char *refname = "test_read_format_iso_rockridge_new.iso.Z";
struct archive_entry *ae;
@@ -68,7 +68,7 @@ DEFINE_TEST(test_read_format_isorr_new_bz2)
assertEqualInt(ARCHIVE_OK,
archive_read_open_filename(a, refname, 10240));
- /* Retrieve each of the 8 files on the ISO image and
+ /* Retrieve each of the 10 entries on the ISO image and
* verify that each one is what we expect. */
for (i = 0; i < 10; ++i) {
assertEqualInt(0, archive_read_next_header(a, &ae));
diff --git a/libarchive/test/test_read_format_isozisofs_bz2.c b/libarchive/test/test_read_format_isozisofs_compress.c
index 3f059fde08b5..2ca3ff52e21f 100644
--- a/libarchive/test/test_read_format_isozisofs_bz2.c
+++ b/libarchive/test/test_read_format_isozisofs_compress.c
@@ -26,7 +26,7 @@
/*
Execute the following command to rebuild the data for this program:
- tail -n +32 test_read_format_isozisofs_bz2.c | /bin/sh
+ tail -n +32 test_read_format_isozisofs_compress.c | /bin/sh
rm -rf /tmp/iso /tmp/ziso
mkdir /tmp/iso
@@ -50,7 +50,7 @@ exit 1
*/
-DEFINE_TEST(test_read_format_isozisofs_bz2)
+DEFINE_TEST(test_read_format_isozisofs_compress)
{
const char *refname = "test_read_format_iso_zisofs.iso.Z";
struct archive_entry *ae;
diff --git a/libarchive/test/test_read_format_lha_symlink_missing_target.c b/libarchive/test/test_read_format_lha_symlink_missing_target.c
new file mode 100644
index 000000000000..4ac38b7a18d0
--- /dev/null
+++ b/libarchive/test/test_read_format_lha_symlink_missing_target.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 2026 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"
+#include <fcntl.h>
+
+/*
+ * A malformed symlink entry (one whose pathname lacks the required '|'
+ * separator should FAIL the entry but the client should be able to recover
+ * and read the following entry.
+ *
+ * This was an existing behavior of the LHA reader that was incorrectly
+ * handled by the read core (it got translated into a FATAL archive condition).
+ * Recent fixes to the read core enabled this to work as intended.
+ */
+
+static struct archive *
+open_fixture(const char *refname)
+{
+ struct archive *a;
+
+ extract_reference_file(refname);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 4096));
+ return a;
+}
+
+static void
+test_recovery(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ char buff[32];
+
+ failure("LHA's own ARCHIVE_FAILED from read_header must propagate, "
+ "not be silently swallowed or crash");
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+
+ failure("archive_read_next_header() must still be able to advance "
+ "past a failed entry to the next one");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(11, (int)archive_read_data(a, buff, sizeof(buff)));
+ assertEqualMem(buff, "hello world", 11);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_read_data(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ char buff[32];
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+
+ failure("archive_read_data() must refuse content from a failed "
+ "entry, not return real (or garbage) bytes");
+ assert(archive_read_data(a, buff, sizeof(buff)) < 0);
+
+ failure("the archive must stay unusable after that misuse, not "
+ "silently recover");
+ assert(archive_read_next_header(a, &ae) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_read_data_block(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ failure("archive_read_data_block() must refuse a failed entry");
+ assert(archive_read_data_block(a, &buff, &size, &offset) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_seek_data(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ failure("archive_seek_data() must refuse a failed entry");
+ assert(archive_seek_data(a, 0, SEEK_SET) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_read_data_into_fd(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ int fd;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ fd = open("data_into_fd_lha.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ assert(fd >= 0);
+ failure("archive_read_data_into_fd() must refuse a failed entry");
+ assert(archive_read_data_into_fd(a, fd) < 0);
+ close(fd);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_explicit_skip(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_skip(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_lha_symlink_missing_target)
+{
+ const char *refname =
+ "test_read_format_lha_symlink_missing_target.lzh";
+
+ test_recovery(refname);
+ test_illegal_read_data(refname);
+ test_illegal_read_data_block(refname);
+ test_illegal_seek_data(refname);
+ test_illegal_read_data_into_fd(refname);
+ test_explicit_skip(refname);
+}
diff --git a/libarchive/test/test_read_format_lha_symlink_missing_target.lzh.uu b/libarchive/test/test_read_format_lha_symlink_missing_target.lzh.uu
new file mode 100644
index 000000000000..9213c54704f0
--- /dev/null
+++ b/libarchive/test/test_read_format_lha_symlink_missing_target.lzh.uu
@@ -0,0 +1,11 @@
+Level-0 LHA archive with two entries: "bad_symlink" is a symlink
+(Unix-extended mode field, S_IFLNK) whose pathname has no '|' separator,
+so lha_parse_linkname() can't extract a link target and read_header()
+returns ARCHIVE_FAILED; "second" is a normal stored file. See
+test_read_format_lha_symlink_missing_target.c.
+
+begin 644 test_read_format_lha_symlink_missing_target.lzh
+M+>LM;&@P+0``````````````````"V)A9%]S>6UL:6YK``!5``````#_H0``
+J```:]BUL:#`M"P````L````````````&<V5C;VYD:&5L;&\@=V]R;&0`
+`
+end
diff --git a/libarchive/test/test_read_format_mtree.c b/libarchive/test/test_read_format_mtree.c
index e95bdabc3923..a38b27038a43 100644
--- a/libarchive/test/test_read_format_mtree.c
+++ b/libarchive/test/test_read_format_mtree.c
@@ -878,3 +878,35 @@ DEFINE_TEST(test_read_format_mtree_nano)
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+
+/*
+ * A /set line whose value-less keyword (no '=') repeats made
+ * process_global_set() compute a bogus length and over-read the option
+ * buffer in remove_option(). Parsing must stay in bounds and succeed.
+ */
+DEFINE_TEST(test_read_format_mtree_global_set_no_value)
+{
+ static char archive[] =
+ "#mtree\n"
+ "/set nochange nochange\n"
+ "a type=file\n";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, archive, sizeof(archive)));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "a");
+ assertEqualInt(archive_entry_filetype(ae), AE_IFREG);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(1, archive_file_count(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_rar.c b/libarchive/test/test_read_format_rar.c
index f8e4b324fcb9..b4b16c66ec9c 100644
--- a/libarchive/test/test_read_format_rar.c
+++ b/libarchive/test/test_read_format_rar.c
@@ -3918,6 +3918,28 @@ DEFINE_TEST(test_read_format_rar_ppmd_use_after_free2)
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+DEFINE_TEST(test_read_format_rar_seek_data_cursor0)
+{
+ const char* reffile = "test_read_format_rar_seek_data_cursor0.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));
+
+ failure("RAR seek should reject an invalid cursor state");
+ assert(archive_seek_data(a, -1, SEEK_END) < 0);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_read_format_rar_newsub_huge)
{
#if SIZE_MAX == UINT64_MAX
diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c
index 57d49ad2fb54..01881c1dd3d6 100644
--- a/libarchive/test/test_read_format_rar5.c
+++ b/libarchive/test/test_read_format_rar5.c
@@ -1572,3 +1572,45 @@ DEFINE_TEST(test_read_format_rar5_bytes_remaining_underflow)
EPILOGUE();
}
+
+DEFINE_TEST(test_read_format_rar5_unpacked_size_exceeds_declared)
+{
+ PROLOGUE("test_read_format_rar5_unpacked_size_exceeds_declared.rar");
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("seed", archive_entry_pathname(ae));
+ assertEqualInt(109, archive_entry_size(ae));
+
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+ assertA(archive_error_string(a) != NULL);
+
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * RAR5 is a streaming unpacker and never implements seeking.
+ * archive_seek_data() on a RAR5 entry must report unsupported via
+ * ARCHIVE_FAILED without poisoning the archive state. See #3323.
+ */
+DEFINE_TEST(test_read_format_rar5_seek_data_unsupported)
+{
+ const int DATA_SIZE = 1200;
+ uint8_t buff[1200];
+
+ PROLOGUE("test_read_format_rar5_compressed.rar");
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("test.bin", archive_entry_pathname(ae));
+
+ /* Capability probe. */
+ assertEqualIntA(a, ARCHIVE_FAILED,
+ archive_seek_data(a, 0, SEEK_CUR));
+ assertA(archive_error_string(a) != NULL);
+
+ /* Subsequent read must still return the real entry content. */
+ assertA(DATA_SIZE == archive_read_data(a, buff, DATA_SIZE));
+ assertA(ARCHIVE_EOF == archive_read_next_header(a, &ae));
+ assertA(1 == verify_data(buff, 0, DATA_SIZE));
+
+ EPILOGUE();
+}
diff --git a/libarchive/test/test_read_format_rar5_unpacked_size_exceeds_declared.rar.uu b/libarchive/test/test_read_format_rar5_unpacked_size_exceeds_declared.rar.uu
new file mode 100644
index 000000000000..df8db2169789
--- /dev/null
+++ b/libarchive/test/test_read_format_rar5_unpacked_size_exceeds_declared.rar.uu
@@ -0,0 +1,10 @@
+begin 644 test_read_format_rar5_unpacked_size_exceeds_declared.rar
+M4F%R(1H'`0!S'-%&"@$%+`8`!0$!@(``PJ.']B("`POG``3M`*2#`E0DF'B`
+M`P$$<V5E9`H#$V0JGU[VYTHYP?]D-50S+Y,[YA3P`1@)46%!68"P&$$H2)$8
+M,V_"7`<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'!P<'
+M!P<0R!KGS?_Y1ZGJ/]5[V/WGQV3QS(RALJQ[H%.N$.-9EH-4R%#&),\=!'V$
+MXR:QWL0$Z0'%XQY*.<$4(I44Z>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GI
+MZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GIZ>GI
+;Z>GIZ>G)%78/_6H4^?O2+65;@!UW5E$#!00`
+`
+end
diff --git a/libarchive/test/test_read_format_rar_seek_data_cursor0.rar.uu b/libarchive/test/test_read_format_rar_seek_data_cursor0.rar.uu
new file mode 100644
index 000000000000..53dfcc6f0d06
--- /dev/null
+++ b/libarchive/test/test_read_format_rar_seek_data_cursor0.rar.uu
@@ -0,0 +1,164 @@
+begin 600 test_read_format_rar_seek_data_cursor0.rar
+M4F%R(1H'`%)A<B$:!P`9>G,1``T`````````R^IT(Y!#`)4;``"/3@```V!_
+M2MWS8R%`%#`9`*2!``!,:6)A<F-H:79E061D:6YG5&5S="YH=&ULP,SS8R%`
+MNTPU03`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P
+M6FYS$0$-`````````*>#="*03`",,P``CTX```/V6-3"NTPU010P(@"D@0``
+L=&5S=&1I<EQ,:6)A<F-H:79E061D:6YG5&5S=#(N:'1M;(#,,E\[0E%?.T(`
+`
+end
diff --git a/libarchive/test/test_read_format_tar_empty_pax.c b/libarchive/test/test_read_format_tar_empty_pax.c
index 3de9c6719c63..d80b86bacf52 100644
--- a/libarchive/test/test_read_format_tar_empty_pax.c
+++ b/libarchive/test/test_read_format_tar_empty_pax.c
@@ -24,6 +24,71 @@
*/
#include "test.h"
+#define TAR_BLOCK_SIZE 512
+
+static void
+format_octal(char *p, size_t s, unsigned long long v)
+{
+ memset(p, 0, s);
+ snprintf(p, s, "%0*llo", (int)s - 1, v);
+}
+
+static void
+build_tar_header(char *p, const char *name, char typeflag,
+ unsigned long long size)
+{
+ unsigned int checksum;
+ size_t i;
+
+ memset(p, 0, TAR_BLOCK_SIZE);
+ snprintf(p, 100, "%s", name);
+ format_octal(p + 100, 8, 0644);
+ format_octal(p + 108, 8, 0);
+ format_octal(p + 116, 8, 0);
+ format_octal(p + 124, 12, size);
+ format_octal(p + 136, 12, 0);
+ memset(p + 148, ' ', 8);
+ p[156] = typeflag;
+ memcpy(p + 257, "ustar", 5);
+ memcpy(p + 263, "00", 2);
+
+ checksum = 0;
+ for (i = 0; i < TAR_BLOCK_SIZE; ++i)
+ checksum += (unsigned char)p[i];
+ snprintf(p + 148, 8, "%06o", checksum);
+ p[154] = '\0';
+ p[155] = ' ';
+}
+
+static void
+build_empty_pax_archive(char *buff, char typeflag)
+{
+ memset(buff, 0, TAR_BLOCK_SIZE * 4);
+ build_tar_header(buff,
+ typeflag == 'g' ? "pax_global_header" : "./PaxHeaders/file",
+ typeflag, 0);
+ build_tar_header(buff + TAR_BLOCK_SIZE, "file", '0', 0);
+}
+
+static void
+assert_empty_pax_rejected(char typeflag, const char *error)
+{
+ char archive[TAR_BLOCK_SIZE * 4];
+ struct archive_entry *ae;
+ struct archive *a;
+
+ build_empty_pax_archive(archive, typeflag);
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, archive, sizeof(archive)));
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+ assertEqualString(error, archive_error_string(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
/*
* A "usual" empty tar archive contains only zero bytes
* and gets handled by the 'empty' format, not by the 'tar'
@@ -54,4 +119,8 @@ DEFINE_TEST(test_read_format_tar_empty_pax)
assertEqualInt(ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_format(a));
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* Empty pax extension headers are invalid: they contain no records. */
+ assert_empty_pax_rejected('x', "Invalid empty pax extended header");
+ assert_empty_pax_rejected('g', "Invalid empty pax global extended header");
}
diff --git a/libarchive/test/test_read_format_tar_pax_sun_holesdata.c b/libarchive/test/test_read_format_tar_pax_sun_holesdata.c
new file mode 100644
index 000000000000..b2f24fe2536a
--- /dev/null
+++ b/libarchive/test/test_read_format_tar_pax_sun_holesdata.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 2026 Kaif Khan
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+/*
+ * A valid SUN.holesdata sparse map ends right after its final digit, so
+ * pax_attribute_SUN_holesdata() must stop once it has consumed all
+ * value_length bytes rather than inspecting the byte that follows.
+ * The archive holds a 1536-byte file with map " 0 512 1024 1536":
+ * data at [0,512) and [1024,1536) with a hole between.
+ */
+static void
+test_sun_holesdata(void)
+{
+ const char *name = "test_read_format_tar_pax_sun_holesdata.tar";
+ struct archive_entry *ae;
+ struct archive *a;
+ int64_t offset, length;
+
+ 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));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("hole", archive_entry_pathname(ae));
+ assertEqualInt(1536, archive_entry_size(ae));
+
+ /* Two data blocks separated by a single hole. */
+ failure("The final map offset ends in a digit and must still parse");
+ assertEqualInt(2, archive_entry_sparse_reset(ae));
+ assertEqualInt(ARCHIVE_OK,
+ archive_entry_sparse_next(ae, &offset, &length));
+ assertEqualInt(0, offset);
+ assertEqualInt(512, length);
+ assertEqualInt(ARCHIVE_OK,
+ archive_entry_sparse_next(ae, &offset, &length));
+ assertEqualInt(1024, offset);
+ assertEqualInt(512, length);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+ assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_tar_pax_sun_holesdata)
+{
+ test_sun_holesdata();
+}
diff --git a/libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu b/libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu
new file mode 100644
index 000000000000..79d3cdff73fb
--- /dev/null
+++ b/libarchive/test/test_read_format_tar_pax_sun_holesdata.tar.uu
@@ -0,0 +1,95 @@
+begin 644 test_read_format_tar_pax_sun_holesdata.tar
+M4&%X2&5A9&5R<R]H;VQE````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#`V-#0`,#`P,#$T-0`P,#`P,#$R`#`P,#`P,#`P,#0R
+M`#$Q-C`V-C0U-C0S`#`Q,C4U-P`@>```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,&-U90``
+M````````````````````````````````````<W1A9F8`````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````S-"!354XN:&]L97-D871A/2`P(#4Q,B`Q,#(T
+M(#$U,S8*````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````&AO;&4`````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`P-C0T`#`P,#`Q-#4`,#`P,#`Q,@`P,#`P,#`P,S`P,``Q,38P-C8T-38T
+M,P`P,3`T,C``(#``````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!C=64`````````````````
+M`````````````````````'-T869F````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+M86%A86%A86%A86%A86%A86%A86%A86$`````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````````````&-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C8V-C
+M8V-C8V-C8V-C8V-C````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+!````
+`
+end
diff --git a/libarchive/test/test_read_format_warc.c b/libarchive/test/test_read_format_warc.c
index 745aabffa93b..be14b88b1a29 100644
--- a/libarchive/test/test_read_format_warc.c
+++ b/libarchive/test/test_read_format_warc.c
@@ -102,3 +102,103 @@ DEFINE_TEST(test_read_format_warc_incomplete)
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+
+DEFINE_TEST(test_read_format_warc_skip_after_extract)
+{
+#define WARC_APPEND(s) do { \
+ memcpy(p, s, sizeof(s) - 1); \
+ p += sizeof(s) - 1; \
+} while (0)
+ const size_t body_size = 200000;
+ char *warc;
+ char *p;
+ char second[6];
+ size_t used;
+ int fd;
+ struct archive_entry *ae;
+ struct archive *a;
+
+ warc = malloc(body_size + 1024);
+ assert(warc != NULL);
+ p = warc;
+
+ WARC_APPEND("WARC/1.0\r\n"
+ "WARC-Type: resource\r\n"
+ "WARC-Date: 2014-06-10T10:10:10Z\r\n"
+ "WARC-Target-URI: file://first.txt\r\n"
+ "Content-Length: 200000\r\n"
+ "\r\n");
+ memset(p, 'A', body_size);
+ p += body_size;
+ WARC_APPEND("\r\n\r\n"
+ "WARC/1.0\r\n"
+ "WARC-Type: resource\r\n"
+ "WARC-Date: 2014-06-10T10:10:11Z\r\n"
+ "WARC-Target-URI: file://second.txt\r\n"
+ "Content-Length: 6\r\n"
+ "\r\n"
+ "SECOND"
+ "\r\n\r\n");
+ used = (size_t)(p - warc);
+
+ 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_memory2(a, warc, used, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("first.txt", archive_entry_pathname(ae));
+
+ fd = open("warc.out", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ assert(fd >= 0);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_into_fd(a, fd));
+ assertEqualInt(0, close(fd));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second.txt", archive_entry_pathname(ae));
+ assertEqualInt(6, archive_entry_size(ae));
+ assertEqualInt(6, archive_read_data(a, second, sizeof(second)));
+ assertEqualMem(second, "SECOND", 6);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ free(warc);
+#undef WARC_APPEND
+}
+
+DEFINE_TEST(test_read_format_warc_truncated_body)
+{
+ static const char warc[] =
+ "WARC/1.0\r\n"
+ "WARC-Type: resource\r\n"
+ "WARC-Date: 2014-06-10T10:10:10Z\r\n"
+ "WARC-Target-URI: file://test.txt\r\n"
+ "Content-Length: 10\r\n"
+ "\r\n"
+ "ABC";
+ char body[3];
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, warc, sizeof(warc) - 1U));
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_next_header(a, &ae));
+ assertEqualInt(3,
+ archive_read_data(a, body, sizeof(body)));
+ assertEqualMem(body, "ABC", sizeof(body));
+ assertEqualIntA(a, ARCHIVE_FATAL,
+ archive_read_data(a, body, sizeof(body)));
+ assertEqualString("Truncated WARC file data",
+ archive_error_string(a));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_xar.c b/libarchive/test/test_read_format_xar.c
index d70282778869..e04cdf313fd8 100644
--- a/libarchive/test/test_read_format_xar.c
+++ b/libarchive/test/test_read_format_xar.c
@@ -969,3 +969,109 @@ DEFINE_TEST(test_read_format_xar_large_mode)
assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+
+/*
+ * A TOC with an odd-length base64-encoded <name> used to make
+ * strappend_base64() read one byte past the (non-NUL-terminated) expat
+ * character-data buffer and then underflow its size_t length counter.
+ * The trailing incomplete base64 character must be dropped; the rest of
+ * the name still decodes ("YWJjA" -> "abc").
+ */
+DEFINE_TEST(test_read_format_xar_base64_oob)
+{
+ const char *reffile = "test_read_format_xar_base64_oob.xar";
+ struct archive_entry *ae;
+ struct archive *a;
+ int r;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+
+ r = archive_read_support_format_xar(a);
+ if (r == ARCHIVE_WARN) {
+ skipping("xar reading not fully supported on this platform");
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ return;
+ }
+
+ assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("abc", archive_entry_pathname(ae));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * A numeric TOC field (here <inode>) is parsed by atou64(). Under the
+ * expat backend that function is handed the raw, non-NUL-terminated
+ * character-data buffer (libxml2 masks this by passing a strlen'd copy).
+ * The loop fetched the next digit with *++p before checking the remaining
+ * count, so an all-digit field whose content ends at the parser buffer
+ * read one byte past the supplied range. The parsed value is unchanged;
+ * only the stray read is gone. Build with ASan to catch the over-read.
+ */
+DEFINE_TEST(test_read_format_xar_atou64_overread)
+{
+ const char *reffile = "test_read_format_xar_atou64_overread.xar";
+ struct archive_entry *ae;
+ struct archive *a;
+ int r;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+
+ r = archive_read_support_format_xar(a);
+ if (r == ARCHIVE_WARN) {
+ skipping("xar reading not fully supported on this platform");
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ return;
+ }
+
+ assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("num", archive_entry_pathname(ae));
+ assertEqualInt(1234567890, archive_entry_ino64(ae));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/*
+ * parse_time() reads a fixed 20-byte timestamp and handed atou64() a width
+ * of 4 for every field. The trailing seconds field sits at offset 17, so a
+ * value whose last three bytes are all digits (here "...00:00:123") made
+ * atou64() read one byte past the 20-byte value. This archive carries such
+ * an <mtime>; reading it must stay within the value.
+ */
+DEFINE_TEST(test_read_format_xar_parse_time_overread)
+{
+ const char *reffile = "test_read_format_xar_parse_time_overread.xar";
+ struct archive_entry *ae;
+ struct archive *a;
+ int r;
+
+ extract_reference_file(reffile);
+ assert((a = archive_read_new()) != NULL);
+ assertA(0 == archive_read_support_filter_all(a));
+
+ r = archive_read_support_format_xar(a);
+ if (r == ARCHIVE_WARN) {
+ skipping("xar reading not fully supported on this platform");
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ return;
+ }
+
+ assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+ assertA(0 == archive_read_next_header(a, &ae));
+ assertEqualString("num", archive_entry_pathname(ae));
+ assertEqualInt(1234567890, archive_entry_ino64(ae));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/libarchive/test/test_read_format_xar_atou64_overread.xar.uu b/libarchive/test/test_read_format_xar_atou64_overread.xar.uu
new file mode 100644
index 000000000000..dc055589e13c
--- /dev/null
+++ b/libarchive/test/test_read_format_xar_atou64_overread.xar.uu
@@ -0,0 +1,7 @@
+begin 644 test_read_format_xar_atou64_overread.xar
+M>&%R(0`<``$`````````AP````````"R`````'C:-8Y+#H,P#$3WG"+RGAKH
+MCTHF['J"]@"(N%4DXE245O3VS4>L_/0THS'UJYO4E^>W]=)!O:M`L8S>6'EV
+M<+]=RQ9Z7=`ZS+I0BA8_QAOH82=6UH0.9!.<#(ZU?!QAHDTOOQ?KF"=,N'DK
+<WK"NF_WA>#JWEXHPFSR`L9%&,:T2IB?^<Y@PE```
+`
+end
diff --git a/libarchive/test/test_read_format_xar_base64_oob.xar.uu b/libarchive/test/test_read_format_xar_base64_oob.xar.uu
new file mode 100644
index 000000000000..c58671cf2778
--- /dev/null
+++ b/libarchive/test/test_read_format_xar_base64_oob.xar.uu
@@ -0,0 +1,7 @@
+begin 644 test_read_format_xar_base64_oob.xar
+M>&%R(0`<``$`````````@@````````"E`````'C:L[&OR,U1*$LM*L[,S[-5
+M,M0S4%)(S4O.3\G,2[=5"@UQT[50LK?CLJE(+++C4E"P*<E/!M%`5EIF3JI"
+M9@I0CQ)$!"B6EYB;"M)>4EF0:JN4E%B<:F:B9!<9[I7E:*,/DH2K!*FP`QEA
+7HP]F0LS4!XF`[=$'6V2C#[87`%#3+F\`
+`
+end
diff --git a/libarchive/test/test_read_format_xar_parse_time_overread.xar.uu b/libarchive/test/test_read_format_xar_parse_time_overread.xar.uu
new file mode 100644
index 000000000000..bef099078820
--- /dev/null
+++ b/libarchive/test/test_read_format_xar_parse_time_overread.xar.uu
@@ -0,0 +1,8 @@
+begin 644 test_read_format_xar_parse_time_overread.xar
+M>&%R(0 < $ G #< 'C:-8Y="H,P$(3?/47(N]W5
+M_MFRQK>>P!Y 3%H")BG6%GO[K@F!@1V^W5F&NM5-XFOFMPV^E=4.I3!^#-KZ
+M9ROO_:UL9*<*6H=9%4+0$L9MLGO8R0BK.2,38>8'9Y3_.(+H,EY^+Z.V>X)H
+M,[<^:*.J>G\XGL[-!0D2R7NW6/Y2(V*)%:M'O+(X0)!VJ0ILOV,]B/T(8MT_
+$+RDZM0
+`
+end
diff --git a/libarchive/test/test_read_format_zip.c b/libarchive/test/test_read_format_zip.c
index 2c18387bd459..04e6732c09d5 100644
--- a/libarchive/test/test_read_format_zip.c
+++ b/libarchive/test/test_read_format_zip.c
@@ -272,6 +272,69 @@ test_info_zip_ux(void)
}
/*
+ * Read PKWARE Unix Extra Field 0x000d.
+ */
+static void
+verify_pkware_unix(struct archive *a, int seek_checks)
+{
+ struct archive_entry *ae;
+ char buff[8];
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("file1", archive_entry_pathname(ae));
+ assertEqualInt(1300668680, archive_entry_mtime(ae));
+ assertEqualInt(1300668670, archive_entry_atime(ae));
+ assertEqualInt(6, archive_entry_size(ae));
+ assertEqualInt(archive_entry_is_encrypted(ae), 0);
+ assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0);
+ if (seek_checks)
+ assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae));
+ failure("zip reader should read PKWARE Unix Extra Field");
+ assertEqualInt(1001, archive_entry_uid(ae));
+ assertEqualInt(1002, archive_entry_gid(ae));
+ assertEqualInt(6, archive_read_data(a, buff, sizeof(buff)));
+ assertEqualMem(buff, "hello\n", 6);
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+
+ /* Verify the number of files read. */
+ failure("the archive file has just one file");
+ assertEqualInt(1, archive_file_count(a));
+
+ assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0));
+ assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_pkware_unix(void)
+{
+ const char *refname = "test_read_format_zip_pkware_unix.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, 10240));
+ verify_pkware_unix(a, 1);
+
+ /* Verify with streaming reader. */
+ p = slurpfile(&s, "%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_pkware_unix(a, 0);
+ free(p);
+}
+
+/*
* Verify that test_read_extract correctly works with
* Zip entries that use length-at-end.
*/
@@ -377,6 +440,7 @@ DEFINE_TEST(test_read_format_zip)
{
test_basic();
test_info_zip_ux();
+ test_pkware_unix();
test_extract_length_at_end();
test_symlink();
}
diff --git a/libarchive/test/test_read_format_zip_7075_utf8_paths.c b/libarchive/test/test_read_format_zip_7075_utf8_paths.c
index 66650030e449..136ddaa5388e 100644
--- a/libarchive/test/test_read_format_zip_7075_utf8_paths.c
+++ b/libarchive/test/test_read_format_zip_7075_utf8_paths.c
@@ -96,6 +96,6 @@ DEFINE_TEST(test_read_format_zip_utf8_paths)
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));
+ assertEqualInt(ARCHIVE_OK, archive_free(a));
free(p);
}
diff --git a/libarchive/test/test_read_format_zip_filename.c b/libarchive/test/test_read_format_zip_filename.c
index 21702d2f62f3..4f8c95581ef8 100644
--- a/libarchive/test/test_read_format_zip_filename.c
+++ b/libarchive/test/test_read_format_zip_filename.c
@@ -176,7 +176,7 @@ DEFINE_TEST(test_read_format_zip_filename_UTF8_eucJP)
* Bit 11 of its general purpose bit flag is set.
*/
if (NULL == setlocale(LC_ALL, "ja_JP.eucJP")) {
- skipping("ja_JP.eucJP locale not availablefilename_ on "
+ skipping("ja_JP.eucJP locale not available on "
"this system.");
return;
}
diff --git a/libarchive/test/test_read_format_zip_pkware_unix.zip.uu b/libarchive/test/test_read_format_zip_pkware_unix.zip.uu
new file mode 100644
index 000000000000..ac47a4ff5046
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_pkware_unix.zip.uu
@@ -0,0 +1,7 @@
+begin 644 test_read_format_zip_pkware_unix.zip
+M4$L#!!0 =3X@,#HV!@ 8 % ! 9FEL93$- P _J"&30BA
+MADWI ^H#:&5L;&\*4$L! A0#% !U/B P.C8& !@ 4 $```
+M *2! &9I;&4Q#0 , /Z@ADT(H89-Z0/J U!+!08 0 !
++ $, Y````````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.zipx.uu b/libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.zipx.uu
new file mode 100644
index 000000000000..20d985fb5af0
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_ppmd8_aes256_streaming.zipx.uu
@@ -0,0 +1,16 @@
+begin 644 test_read_format_zip_ppmd8_aes256_streaming.zipx
+M4$L#!#\`"0!C`+:\T%P````````````````2``L`96YC<GEP=&5D7V9I;&4N
+M='AT`9D'``(`044#8@!0DU<O)`#\`^#5`+;![K',-BB=Y1/1$;^7ZX-Z>LH8
+MT/WJTW6S)$X63"#%E92J_MH4!H:N8BQR;/??F]NDA!],NJ07_%G39I2[AOO)
+MYVZ#VPVEDCRXSI217JP'Y5PF\GZUC'Z0\:2YWQ%@E`R4]($CP5)2^!8BFGSV
+M%7)I0I\5+7./\#\(K@D&[QFV49JSSRFT+'=%<EWYK'Z!?]H)\O_ZA#),OLL[
+MI9V1VN;\2<,&*+O)OUCF`M)<?BWXN=/A7G6*X\@[X-A5S`*RLV5V"JN@V]75
+ML`D296YS&G>/`U%6A=SSF`$C`-&F8B[77W4P?*7N.-*`4'TYPI,H?X)Q8^+2
+MP5KY,(.%H:+XGUL@GS'E21\)U^/?G5Y',@X&98JH@1Z=4FLZ";,H6@I%\%?Q
+MA6LR]Y$MP7@AJEH]5-QAOF:OX*)*7C/H23QFQ\--HU#7^K<J?3:?<SB9EMF8
+M;))S,7%<T"+;Y@I8-61DVLX;J/%+!5!+`0(_`S\``0!C`+:\T%P`````<`$`
+M`/\!```2`"\`````````(("D@0````!E;F-R>7!T961?9FEL92YT>'0*`"``
+M``````$`&`!TGW-<V/W<`>UY<US8_=P!=)]S7-C]W`$!F0<``@!!10-B`%!+
+4!08``````0`!`&\```"K`0``````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_size_exceeds_declared.c b/libarchive/test/test_read_format_zip_size_exceeds_declared.c
new file mode 100644
index 000000000000..d17e72d58d60
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_size_exceeds_declared.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2026 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"
+
+/*
+ * Each reference archive has two entries. The first entry's header
+ * declares uncompressed_size=10, but the entry actually contains more
+ * data than that:
+ *
+ * _stored: method STORED, compressed_size (which bounds the physical
+ * read for STORED) set to the true 20-byte body. STORED has no
+ * compressed-stream end marker of its own, so nothing but the
+ * declared size stands between the reader and those extra 10 bytes.
+ *
+ * _deflate: method DEFLATE, a real compressed stream that inflates to
+ * 256KiB+6 bytes. This has to exceed the 256KiB internal output
+ * buffer used by zip_read_data_deflate(): a stream that fully
+ * inflates within a single internal decode call also reaches its
+ * own Z_STREAM_END in that same call, so any final-size check runs
+ * before data is handed back to the caller and nothing leaks. Once
+ * decoding needs a second internal call, the first call's full
+ * buffer is handed back as an ordinary chunk before end-of-stream is
+ * ever seen.
+ *
+ * The second entry ("second", 11 bytes, "hello world") is well-formed.
+ * Reading it after the first entry has failed confirms that a
+ * mis-declared entry doesn't prevent correctly reading the rest of the
+ * archive.
+ */
+
+static void
+verify(const char *refname)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[8];
+ la_ssize_t n;
+ int64_t total;
+
+ /* Seeking reader. */
+ extract_reference_file(refname);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("first", archive_entry_pathname(ae));
+ assertEqualInt(10, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0) {
+ total += n;
+ failure("archive_read_data() must never return more than "
+ "the declared entry size");
+ assert(total <= 10);
+ }
+ assertEqualInt(ARCHIVE_FAILED, n);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(11, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0)
+ total += n;
+ assertEqualInt(0, n);
+ assertEqualInt(11, total);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* Streaming reader: same checks, fed through a non-seekable
+ * memory stream so the source can't be re-read or skipped via
+ * offsets the way a seekable file can. */
+ {
+ size_t s;
+ char *p = slurpfile(&s, "%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, 7));
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_next_header(a, &ae));
+ assertEqualString("first", archive_entry_pathname(ae));
+ assertEqualInt(10, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0) {
+ total += n;
+ failure("archive_read_data() must never return "
+ "more than the declared entry size");
+ assert(total <= 10);
+ }
+ assertEqualInt(ARCHIVE_FAILED, n);
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(11, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0)
+ total += n;
+ assertEqualInt(0, n);
+ assertEqualInt(11, total);
+
+ assertEqualIntA(a, ARCHIVE_EOF,
+ archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ free(p);
+ }
+}
+
+DEFINE_TEST(test_read_format_zip_size_exceeds_declared_stored)
+{
+ verify("test_read_format_zip_size_exceeds_declared_stored.zip");
+}
+
+DEFINE_TEST(test_read_format_zip_size_exceeds_declared_deflate)
+{
+ verify("test_read_format_zip_size_exceeds_declared_deflate.zip");
+}
diff --git a/libarchive/test/test_read_format_zip_size_exceeds_declared_deflate.zip.uu b/libarchive/test/test_read_format_zip_size_exceeds_declared_deflate.zip.uu
new file mode 100644
index 000000000000..65959d9ee2d9
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_size_exceeds_declared_deflate.zip.uu
@@ -0,0 +1,23 @@
+begin 644 test_read_format_zip_size_exceeds_declared_deflate.zip
+M4$L#!!0````(``````#"V8Q_D`(```H````%````9FER<W3MR#$!```(P*!P
+MLW\F+TT!)W7FY9QSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCG7`E!+`P04````````````A1%*#0L````+````!@``
+M`'-E8V]N9&AE;&QO('=O<FQD4$L!`A0`%`````@``````,+9C'^0`@``"@``
+M``4``````````````````````&9I<G-T4$L!`A0`%````````````(412@T+
+M````"P````8`````````````````LP(``'-E8V]N9%!+!08``````@`"`&<`
+(``#B`@``````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_size_exceeds_declared_stored.zip.uu b/libarchive/test/test_read_format_zip_size_exceeds_declared_stored.zip.uu
new file mode 100644
index 000000000000..35f405f22aed
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_size_exceeds_declared_stored.zip.uu
@@ -0,0 +1,9 @@
+begin 644 test_read_format_zip_size_exceeds_declared_stored.zip
+M4$L#!!0```````````#-F1X3%`````H````%````9FER<W1!04%!04%!04%!
+M0D)"0D)"0D)"0E!+`P04````````````A1%*#0L````+````!@```'-E8V]N
+M9&AE;&QO('=O<FQD4$L!`A0`%````````````,V9'A,4````"@````4`````
+M`````````````````&9I<G-T4$L!`A0`%````````````(412@T+````"P``
+M``8`````````````````-P```'-E8V]N9%!+!08``````@`"`&<```!F````
+"````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_symlink_unsupported_compression.c b/libarchive/test/test_read_format_zip_symlink_unsupported_compression.c
new file mode 100644
index 000000000000..dc9ea2bf9d72
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_symlink_unsupported_compression.c
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 2026 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"
+#include <fcntl.h>
+
+/*
+ * A symlink entry with an unrecognized compression format should FAIL
+ * (that individual entry is unsupported) without preventing the rest of
+ * the archive from being read.
+ *
+ * The Zip reader previously FAILED the entry but the archive read core
+ * translated that to FATAL, preventing the client from continuing. Updates
+ * to the read core allow us to finally handle that correctly.
+ */
+
+static struct archive *
+open_fixture(const char *refname)
+{
+ struct archive *a;
+
+ extract_reference_file(refname);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 4096));
+ return a;
+}
+
+/* A well-behaved client that never tries to read data from the failed
+ * entry must be able to recover and read the rest of the archive. */
+static void
+test_recovery(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ char buff[32];
+
+ failure("a format's ARCHIVE_FAILED from read_header must propagate, "
+ "not be silently swallowed or crash");
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+
+ failure("archive_read_next_header() must still be able to advance "
+ "past a failed entry to the next one");
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(11, (int)archive_read_data(a, buff, sizeof(buff)));
+ assertEqualMem(buff, "hello world", 11);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/* A client that instead tries to read real content from the failed entry
+ * must be refused (not handed garbage, not crash) -- and, matching every
+ * other API-misuse case in libarchive, the archive object is left
+ * unusable afterward rather than silently continuing. */
+static void
+test_illegal_read_data(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ char buff[32];
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+
+ failure("archive_read_data() must refuse content from a failed "
+ "entry, not return real (or garbage) bytes");
+ assert(archive_read_data(a, buff, sizeof(buff)) < 0);
+
+ failure("the archive must stay unusable after that misuse, not "
+ "silently recover");
+ assert(archive_read_next_header(a, &ae) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_read_data_block(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ failure("archive_read_data_block() must refuse a failed entry");
+ assert(archive_read_data_block(a, &buff, &size, &offset) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_seek_data(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ failure("archive_seek_data() must refuse a failed entry");
+ assert(archive_seek_data(a, 0, SEEK_SET) < 0);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_illegal_read_data_into_fd(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+ int fd;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ fd = open("data_into_fd.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ assert(fd >= 0);
+ failure("archive_read_data_into_fd() must refuse a failed entry");
+ assert(archive_read_data_into_fd(a, fd) < 0);
+ close(fd);
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+/* Explicitly calling archive_read_data_skip() (rather than relying on the
+ * implicit skip inside archive_read_next_header()) is a legal way to
+ * recover, too. */
+static void
+test_explicit_skip(const char *refname)
+{
+ struct archive *a = open_fixture(refname);
+ struct archive_entry *ae;
+
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_data_skip(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("second", archive_entry_pathname(ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_zip_symlink_unsupported_compression)
+{
+ const char *refname =
+ "test_read_format_zip_symlink_unsupported_compression.zip";
+
+ test_recovery(refname);
+ test_illegal_read_data(refname);
+ test_illegal_read_data_block(refname);
+ test_illegal_seek_data(refname);
+ test_illegal_read_data_into_fd(refname);
+ test_explicit_skip(refname);
+}
diff --git a/libarchive/test/test_read_format_zip_symlink_unsupported_compression.zip.uu b/libarchive/test/test_read_format_zip_symlink_unsupported_compression.zip.uu
new file mode 100644
index 000000000000..7c778b318a33
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_symlink_unsupported_compression.zip.uu
@@ -0,0 +1,16 @@
+ZIP archive with two entries: "bad_symlink" is a symlink (Unix mode
+S_IFLNK) whose compression method (200) isn't one of the three the ZIP
+reader's symlink-target decompression understands (stored/deflate/LZMA
+alone), triggering ARCHIVE_FAILED from read_header(); "second" is a
+normal stored file. See
+test_read_format_zip_symlink_unsupported_compression.c.
+
+begin 644 test_read_format_zip_symlink_unsupported_compression.zip
+M4$L#!!0```#(```````^CAE/"0````D````+````8F%D7W-Y;6QI;FMS;VUE
+M=VAE<G502P,$%````````````(412@T+````"P````8```!S96-O;F1H96QL
+M;R!W;W)L9%!+`0(4`Q0```#(```````^CAE/"0````D````+````````````
+M``#_H0````!B861?<WEM;&EN:U!+`0(4`Q0```````````"%$4H-"P````L`
+M```&``````````````"D@3(```!S96-O;F102P4&``````(``@!M````80``
+#````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c b/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c
new file mode 100644
index 000000000000..7ea5d18e76d0
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2026 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"
+
+/*
+ * The reference archive has one STORED entry, "hello.txt", holding the 5
+ * real bytes "hello". Both 32-bit size fields are the zip64 sentinel
+ * (0xffffffff); the zip64 extra field is spec-valid and resolves
+ * compressed_size to the true 5, but resolves uncompressed_size to
+ * 2^32+5 (4294967301) -- a lie that differs from the truth by an exact
+ * multiple of 2^32.
+ *
+ * archive_read_format_zip_read_data()'s end-of-entry check compared
+ * declared vs. actual uncompressed size masked to the low 32 bits, so
+ * this lie went undetected: the entry reports a ~4GiB size via
+ * archive_entry_size() (unchanged by this fix -- that's a header-time
+ * concern, not this one), but silently completes extraction having
+ * delivered only 5 bytes. The fix compares the full 64-bit values, so
+ * extraction now fails once the size mismatch is discovered at
+ * end-of-entry.
+ */
+static void
+verify(const char *refname)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[16];
+ la_ssize_t n;
+ int64_t total;
+
+ /* Seeking reader. */
+ extract_reference_file(refname);
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_filename(a, refname, 10240));
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("hello.txt", archive_entry_pathname(ae));
+ assertEqualInt(4294967301LL, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0)
+ total += n;
+ failure("must never report success after silently delivering fewer "
+ "bytes than the declared size");
+ assertEqualInt(ARCHIVE_FAILED, n);
+ assert(total <= 4294967301LL);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* Streaming reader. */
+ {
+ size_t s;
+ char *p = slurpfile(&s, "%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, 7));
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_next_header(a, &ae));
+ assertEqualString("hello.txt", archive_entry_pathname(ae));
+ assertEqualInt(4294967301LL, archive_entry_size(ae));
+ total = 0;
+ while ((n = archive_read_data(a, buff, sizeof(buff))) > 0)
+ total += n;
+ assertEqualInt(ARCHIVE_FAILED, n);
+ assert(total <= 4294967301LL);
+
+ assertEqualIntA(a, ARCHIVE_EOF,
+ archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ free(p);
+ }
+}
+
+DEFINE_TEST(test_read_format_zip_uncompressed_size_off_by_4gib)
+{
+ verify("test_read_format_zip_uncompressed_size_off_by_4gib.zip");
+}
diff --git a/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.zip.uu b/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.zip.uu
new file mode 100644
index 000000000000..f9caf6d65e7b
--- /dev/null
+++ b/libarchive/test/test_read_format_zip_uncompressed_size_off_by_4gib.zip.uu
@@ -0,0 +1,7 @@
+begin 644 test_read_format_zip_uncompressed_size_off_by_4gib.zip
+M4$L#!"T```````````"&IA`V__________\)`!0`:&5L;&\N='AT`0`0``4`
+M```!````!0````````!H96QL;U!+`0(M`"T```````````"&IA`V________
+M__\)`!0```````````````````!H96QL;RYT>'0!`!``!0````$````%````
+:`````%!+!08``````0`!`$L```!`````````
+`
+end
diff --git a/libarchive/test/test_read_format_zip_zipx_encrypted.c b/libarchive/test/test_read_format_zip_zipx_encrypted.c
index e27848ce4bc1..f07e1e5c3129 100644
--- a/libarchive/test/test_read_format_zip_zipx_encrypted.c
+++ b/libarchive/test/test_read_format_zip_zipx_encrypted.c
@@ -37,9 +37,26 @@ static const char file_data[] =
"discotheques provide jukeboxes. Crazy Frederick bought many very\n"
"exquisite opal jewels. We promptly judged antique ivory buckles\n"
"for the next prize. Sixty zippers were quickly picked from the bag.\n";
+static const size_t file_size = sizeof(file_data) - 1;
+
+struct streaming_buffer {
+ const char *data;
+ la_ssize_t len;
+ size_t pos;
+ int served;
+};
+
+static la_ssize_t read_streaming_buffer(struct archive *a, void *_client_data, const void **buffer) {
+ (void)a;
+ struct streaming_buffer *buff = _client_data;
+ if (buff->served) return 0;
+ *buffer = buff->data;
+ buff->served = 1;
+ return buff->len;
+}
static void
-validate_entry_read(struct archive *a, const char* msg)
+validate_entry_read(struct archive *a, const int streaming_reader)
{
struct archive_entry *ae;
size_t total_read;
@@ -48,8 +65,10 @@ validate_entry_read(struct archive *a, const char* msg)
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
- assertEqualInt((int)(sizeof(file_data) - 1),
- archive_entry_size(ae));
+ if (!streaming_reader)
+ assertEqualInt((int)file_size, (int)archive_entry_size(ae));
+ else
+ assertEqualInt(archive_entry_size(ae), 0);
assertEqualInt(1, archive_entry_is_data_encrypted(ae));
total_read = 0;
@@ -57,7 +76,8 @@ validate_entry_read(struct archive *a, const char* msg)
r = archive_read_data(a, readbuf, sizeof(readbuf));
if (r <= 0) {
if (r < 0) {
- failure(msg, (int) r, archive_error_string(a));
+ failure("archive_read_data returned %d: %s",
+ (int)r, archive_error_string(a));
assertEqualInt(r > 0, 1);
}
break;
@@ -65,18 +85,79 @@ validate_entry_read(struct archive *a, const char* msg)
assertEqualMem(readbuf, file_data + total_read, (size_t)r);
total_read += (size_t)r;
}
- assertEqualInt((int)total_read, (int)(sizeof(file_data) - 1));
+ assertEqualInt((int)total_read, (int)file_size);
+}
+
+static void
+test_encrypted_zipx_read_mem(char* buff, size_t used)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char readbuf[1024];
+
+ /* ---- Read back without password — should fail ---- */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
+ assertEqualInt(1, archive_entry_is_data_encrypted(ae));
+ assertEqualInt(0, archive_entry_is_metadata_encrypted(ae));
+ assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, readbuf, sizeof(readbuf)));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* ---- Read back with correct password ---- */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
+
+ validate_entry_read(a, 0);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /* ---- Seeking reader with correct password ---- */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
+ assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
+
+ validate_entry_read(a, 0);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+static void
+test_encrypted_zipx_read_callback(const char* buff, const size_t used)
+{
+ struct archive *a;
+ struct streaming_buffer stream_buffer = { buff, used, 0, 0 };
+
+ /* ---- Read back with correct password ---- */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
+ /* NOTE: archive_read_open2 with read callback only -> NON-seekable. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_open2(a, &stream_buffer, NULL, read_streaming_buffer, NULL, NULL));
+ validate_entry_read(a, 1);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
static void
-test_encrypted_zipx(const char *compression_name)
+test_encrypted_zipx(const char *compression_name, const int streaming_entry)
{
struct archive *a;
struct archive_entry *ae;
size_t used;
size_t buffsize = 1000000;
char *buff;
- char readbuf[1024];
buff = malloc(buffsize);
assert(buff != NULL);
@@ -109,74 +190,79 @@ test_encrypted_zipx(const char *compression_name)
archive_entry_set_mtime(ae, 1, 0);
archive_entry_copy_pathname(ae, "encrypted_file.txt");
archive_entry_set_mode(ae, AE_IFREG | 0644);
- archive_entry_set_size(ae, sizeof(file_data) - 1);
+ if (!streaming_entry)
+ archive_entry_set_size(ae, file_size);
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
- assertEqualInt(sizeof(file_data) - 1, archive_write_data(a, file_data, sizeof(file_data) - 1));
+ assertEqualInt(file_size, archive_write_data(a, file_data, file_size));
archive_entry_free(ae);
assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
- /* ---- Read back without password — should fail ---- */
- assert((a = archive_read_new()) != NULL);
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
- assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
- assertEqualInt(1, archive_entry_is_data_encrypted(ae));
- assertEqualInt(0, archive_entry_is_metadata_encrypted(ae));
- assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, readbuf, sizeof(readbuf)));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(ARCHIVE_OK, archive_read_free(a));
-
- /* ---- Read back with correct password ---- */
- assert((a = archive_read_new()) != NULL);
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
-
- validate_entry_read(a, "archive_read_data returned %d: %s");
-
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(ARCHIVE_OK, archive_read_free(a));
-
- /* ---- Seeking reader with correct password ---- */
- assert((a = archive_read_new()) != NULL);
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
- assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
- assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
-
- validate_entry_read(a, "seek: archive_read_data returned %d: %s");
-
- assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
- assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+ test_encrypted_zipx_read_mem(buff, used);
+ test_encrypted_zipx_read_callback(buff, used);
free(buff);
}
DEFINE_TEST(test_read_format_zip_deflate_encrypted)
{
- test_encrypted_zipx("zip:compression=deflate");
+ test_encrypted_zipx("zip:compression=deflate", 0);
}
DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted)
{
- test_encrypted_zipx("zip:compression=bzip2");
+ test_encrypted_zipx("zip:compression=bzip2", 0);
}
DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted)
{
- test_encrypted_zipx("zip:compression=lzma");
+ test_encrypted_zipx("zip:compression=lzma", 0);
}
DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted)
{
- test_encrypted_zipx("zip:compression=xz");
+ test_encrypted_zipx("zip:compression=xz", 0);
}
DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted)
{
- test_encrypted_zipx("zip:compression=zstd");
+ test_encrypted_zipx("zip:compression=zstd", 0);
+}
+
+DEFINE_TEST(test_read_format_zip_deflate_encrypted_streaming)
+{
+ test_encrypted_zipx("zip:compression=deflate", 1);
+}
+
+DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted_streaming)
+{
+ test_encrypted_zipx("zip:compression=bzip2", 1);
+}
+
+DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted_streaming)
+{
+ test_encrypted_zipx("zip:compression=lzma", 1);
+}
+
+DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted_streaming)
+{
+ test_encrypted_zipx("zip:compression=xz", 1);
+}
+
+DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted_streaming)
+{
+ test_encrypted_zipx("zip:compression=zstd", 1);
+}
+
+DEFINE_TEST(test_read_format_zip_ppmd8_aes256_streaming)
+{
+ const char *refname = "test_read_format_zip_ppmd8_aes256_streaming.zipx";
+ size_t used;
+ char buff[600];
+ extract_reference_file(refname);
+ FILE *f = fopen(refname, "rb");
+ used = fread(buff, 1, sizeof(buff), f);
+ fclose(f);
+ test_encrypted_zipx_read_mem(buff, used);
+ test_encrypted_zipx_read_callback(buff, used);
}
diff --git a/libarchive/test/test_sparse_basic.c b/libarchive/test/test_sparse_basic.c
index d4405ddae48e..288987c2354d 100644
--- a/libarchive/test/test_sparse_basic.c
+++ b/libarchive/test/test_sparse_basic.c
@@ -73,10 +73,16 @@ static void create_sparse_file(const char *, const struct sparse *);
/* A few data points:
* = ZFS on FreeBSD needs this to be at least 200kB
* = macOS APFS needs this to be at least 4096x4097 bytes
+ * = Linux tmpfs on 16KB-page architectures (like LoongArch64) uses
+ * 32MiB Transparent Huge Pages (THP). If a hole is exactly the
+ * size of a THP, the data blocks on either side can end up in
+ * adjacent physical folios, causing SEEK_HOLE to report the range
+ * as contiguous data.
*
- * 32MiB here is bigger than either of the above.
+ * 64MiB here is enough to ensure a hole exists between THP folios on all
+ * common architectures.
*/
-#define MIN_HOLE (32 * 1024UL * 1024UL)
+#define MIN_HOLE (64 * 1024UL * 1024UL)
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <winioctl.h>
@@ -364,7 +370,9 @@ verify_sparse_file(struct archive *a, const char *path,
/* Block that overlaps beginning of data */
if (expected_offset < offset
&& expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
- const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
+ /* Avoid forming an intermediate pointer before buff. */
+ const char *end = (const char *)buff
+ + ((expected_offset - offset) + (int64_t)sparse->size);
#if DEBUG
fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
#endif
@@ -379,7 +387,8 @@ verify_sparse_file(struct archive *a, const char *path,
}
/* Blocks completely contained in data we just read. */
while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) {
- const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size;
+ const char *end = (const char *)buff
+ + ((expected_offset - offset) + (int64_t)sparse->size);
if (sparse->type == HOLE) {
#if DEBUG
fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size);
diff --git a/libarchive/test/test_ustar_filename_encoding.c b/libarchive/test/test_ustar_filename_encoding.c
index 440d1b99276e..d230eeddd4d3 100644
--- a/libarchive/test/test_ustar_filename_encoding.c
+++ b/libarchive/test/test_ustar_filename_encoding.c
@@ -158,7 +158,7 @@ DEFINE_TEST(test_ustar_filename_encoding_CP1251_UTF8)
if (NULL == setlocale(LC_ALL, "Russian_Russia") &&
NULL == setlocale(LC_ALL, "ru_RU.CP1251")) {
- skipping("KOI8-R locale not available on this system.");
+ skipping("CP1251 locale not available on this system.");
return;
}
@@ -170,7 +170,7 @@ DEFINE_TEST(test_ustar_filename_encoding_CP1251_UTF8)
assertEqualInt(ARCHIVE_OK, archive_write_set_format_ustar(a));
if (archive_write_set_options(a, "hdrcharset=UTF-8") != ARCHIVE_OK) {
skipping("This system cannot convert character-set"
- " from KOI8-R to UTF-8.");
+ " from CP1251 to UTF-8.");
archive_write_free(a);
return;
}
@@ -178,7 +178,7 @@ DEFINE_TEST(test_ustar_filename_encoding_CP1251_UTF8)
archive_write_open_memory(a, buff, sizeof(buff), &used));
entry = archive_entry_new2(a);
- /* Set a KOI8-R filename. */
+ /* Set a CP1251 filename. */
archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_size(entry, 0);
@@ -202,7 +202,7 @@ DEFINE_TEST(test_ustar_filename_encoding_ru_RU_CP1251)
size_t used;
if (NULL == setlocale(LC_ALL, "ru_RU.CP1251")) {
- skipping("KOI8-R locale not available on this system.");
+ skipping("CP1251 locale not available on this system.");
return;
}
@@ -216,7 +216,7 @@ DEFINE_TEST(test_ustar_filename_encoding_ru_RU_CP1251)
archive_write_open_memory(a, buff, sizeof(buff), &used));
entry = archive_entry_new2(a);
- /* Set a KOI8-R filename. */
+ /* Set a CP1251 filename. */
archive_entry_set_pathname(entry, "\xEF\xF0\xE8");
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_size(entry, 0);
diff --git a/libarchive/test/test_write_disk_secure.c b/libarchive/test/test_write_disk_secure.c
index ddaf95d7497c..4c4e8d00bd47 100644
--- a/libarchive/test/test_write_disk_secure.c
+++ b/libarchive/test/test_write_disk_secure.c
@@ -26,6 +26,10 @@
#define UMASK 022
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#endif
+
/*
* Exercise security checks that should prevent certain
* writes.
@@ -33,17 +37,18 @@
DEFINE_TEST(test_write_disk_secure)
{
-#if defined(_WIN32) && !defined(__CYGWIN__)
- skipping("archive_write_disk security checks not supported on Windows");
-#else
struct archive *a;
struct archive_entry *ae;
- struct stat st;
#if defined(HAVE_LCHMOD) && defined(HAVE_SYMLINK) && \
defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
int working_lchmod;
#endif
+ if (!canSymlink()) {
+ skipping("Can't test symlinks on this filesystem");
+ return;
+ }
+
/* Start with a known umask. */
assertUmask(UMASK);
@@ -63,6 +68,7 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_copy_pathname(ae, "link_to_dir");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -87,11 +93,14 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_free(ae);
assert(0 == archive_write_finish_entry(a));
+ /* These tests hardcode the location of /tmp. skip them on Windows for now. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
/* Write an absolute symlink to /tmp. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_symlink");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "/tmp");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -107,12 +116,14 @@ DEFINE_TEST(test_write_disk_secure)
assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink"));
unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
+#endif
/* Create another link. */
assert((ae = archive_entry_new()) != NULL);
archive_entry_copy_pathname(ae, "link_to_dir2");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -134,6 +145,7 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_copy_pathname(ae, "dir/nested_link_to_dir");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "../dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -157,6 +169,7 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_copy_pathname(ae, "link_to_dir3");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -167,8 +180,7 @@ DEFINE_TEST(test_write_disk_secure)
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
/* Verify link was followed. */
- assertEqualInt(0, lstat("link_to_dir3", &st));
- assert(S_ISLNK(st.st_mode));
+ assertIsSymlink("link_to_dir3", "dir", 1);
archive_entry_free(ae);
/*
@@ -179,6 +191,7 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_copy_pathname(ae, "link_to_dir4");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "nonexistent_dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -189,8 +202,7 @@ DEFINE_TEST(test_write_disk_secure)
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
/* Verify link was replaced. */
- assertEqualInt(0, lstat("link_to_dir4", &st));
- assert(S_ISDIR(st.st_mode));
+ assertIsDir("link_to_dir4", -1);
archive_entry_free(ae);
/*
@@ -207,6 +219,7 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_copy_pathname(ae, "link_to_dir5");
archive_entry_set_mode(ae, S_IFLNK | 0777);
archive_entry_set_symlink(ae, "non_dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_FILE);
archive_write_disk_set_options(a, 0);
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
@@ -217,10 +230,11 @@ DEFINE_TEST(test_write_disk_secure)
assert(0 == archive_write_header(a, ae));
assert(0 == archive_write_finish_entry(a));
/* Verify link was replaced. */
- assertEqualInt(0, lstat("link_to_dir5", &st));
- assert(S_ISDIR(st.st_mode));
+ assertIsDir("link_to_dir5", -1);
archive_entry_free(ae);
+ /* These tests hardcode the location of /tmp. skip them on Windows for now. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
/*
* Without security checks, we should be able to
* extract an absolute path.
@@ -243,17 +257,14 @@ DEFINE_TEST(test_write_disk_secure)
archive_entry_free(ae);
assert(0 == archive_write_finish_entry(a));
assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
+#endif
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
/* Test the entries on disk. */
- assert(0 == lstat("dir", &st));
- failure("dir: st.st_mode=%o", st.st_mode);
- assert((st.st_mode & 0777) == 0755);
+ assertIsDir("dir", 0755);
+ assertIsSymlink("link_to_dir", "dir", 1);
- assert(0 == lstat("link_to_dir", &st));
- failure("link_to_dir: st.st_mode=%o", st.st_mode);
- assert(S_ISLNK(st.st_mode));
#if defined(HAVE_SYMLINK) && defined(HAVE_LCHMOD) && \
defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
/* Verify if we are able to lchmod() */
@@ -277,30 +288,245 @@ DEFINE_TEST(test_write_disk_secure)
working_lchmod = 0;
if (working_lchmod) {
+ struct stat st;
+ assert(0 == lstat("link_to_dir", &st));
failure("link_to_dir: st.st_mode=%o", st.st_mode);
assert((st.st_mode & 07777) == 0755);
}
#endif
- assert(0 == lstat("dir/filea", &st));
- failure("dir/filea: st.st_mode=%o", st.st_mode);
- assert((st.st_mode & 07777) == 0755);
+ assertIsReg("dir/filea", 0755);
+ assertFileNotExists("dir/fileb");
+ assertIsDir("link_to_dir2", 0755);
+ assertIsReg("link_to_dir2/filec", 0755);
+ assertFileNotExists("dir/filed");
+}
+
+/*
+ * This is a simplified variant of the above test which never turns off secure
+ * symlinks. It is designed to test quirks in the Windows implementation of
+ * archive_write_disk; however, its behavior under test should not be exclusive
+ * to Windows.
+ */
+DEFINE_TEST(test_write_disk_secure_symlinks_only)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ const int default_options = ARCHIVE_EXTRACT_SECURE_SYMLINKS;
- failure("dir/fileb: This file should not have been created");
- assert(0 != lstat("dir/fileb", &st));
+ if (!canSymlink()) {
+ skipping("Can't test symlinks on this filesystem");
+ return;
+ }
- assert(0 == lstat("link_to_dir2", &st));
- failure("link_to_dir2 should have been re-created as a true dir");
- assert(S_ISDIR(st.st_mode));
- failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
- assert((st.st_mode & 0777) == 0755);
+ /* Start with a known umask. */
+ assertUmask(UMASK);
- assert(0 == lstat("link_to_dir2/filec", &st));
- assert(S_ISREG(st.st_mode));
- failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode);
- assert((st.st_mode & 07777) == 0755);
+ /* Create an archive_write_disk object. */
+ assert((a = archive_write_disk_new()) != NULL);
+ archive_write_disk_set_options(a, default_options);
- failure("dir/filed: This file should not have been created");
- assert(0 != lstat("dir/filed", &st));
-#endif
+ /* Write a regular dir to it. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "dir");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assert(0 == archive_write_finish_entry(a));
+ assertIsDir("dir", -1);
+
+ /* Write a symlink to the dir above. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir", "dir", 1);
+
+ /* With security checks enabled, this should fail. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir/fileb");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ failure("Extracting a file through a symlink should fail here.");
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assert(0 == archive_write_finish_entry(a));
+ assertFileNotExists("dir/fileb");
+
+ /* Create another link. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir2");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir2", "dir", 1);
+
+ /*
+ * With symlink check and unlink option, it should remove
+ * the link and create the dir.
+ */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir2/filec");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ archive_write_disk_set_options(a, default_options | ARCHIVE_EXTRACT_UNLINK);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assert(0 == archive_write_finish_entry(a));
+ assertIsDir("link_to_dir2", -1);
+ assertIsReg("link_to_dir2/filec", -1);
+
+ /*
+ * Restore the prior security mode.
+ */
+ archive_write_disk_set_options(a, default_options);
+
+ /* Create a nested symlink. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "dir/nested_link_to_dir");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "../dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("dir/nested_link_to_dir", "../dir", -1);
+
+ /* With security checks enabled, this should fail. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "dir/nested_link_to_dir/filed");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ failure("Extracting a file through a symlink should fail here.");
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assert(0 == archive_write_finish_entry(a));
+ assertFileNotExists("dir/filed");
+
+ /* Create a symlink to a dir. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir3");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir3", "dir", 1);
+ /* Extract a dir whose name matches the symlink. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir3");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ archive_entry_free(ae);
+ assertIsDir("link_to_dir3", -1);
+
+ /*
+ * As above, but a broken link, so the link should get replaced.
+ */
+
+ /* Create a symlink to a dir. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir4");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "nonexistent_dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir4", "nonexistent_dir", 1);
+ /* Extract a dir whose name matches the symlink. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir4");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ archive_entry_free(ae);
+ assertIsDir("link_to_dir4", -1);
+
+ /*
+ * As above, but a link to a non-dir, so the link should get replaced.
+ * (file is named "link_to_dir" because we are transforming a link into a dir,)
+ */
+ /* Create a regular file and a symlink to it */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "non_dir");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsReg("non_dir", -1);
+ /* Create symlink to the file. */
+ archive_entry_copy_pathname(ae, "link_to_dir5");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "non_dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_FILE);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir5", "non_dir", 0);
+ /* Extract a dir whose name matches the symlink. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir5");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ archive_entry_free(ae);
+ assertIsDir("link_to_dir5", -1);
+
+ /*
+ * Create a link to a (technically safe) directory, then replace it, then write through it.
+ * Exercises the safety cache to ensure that it does not treat new entries as safe.
+ */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir6");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir6", "dir", 1);
+ /* Replace it. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir6");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir6", "dir", 1);
+ /* Extract through it. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir6/filee");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assertFileNotExists("dir/filee");
+
+ /*
+ * Create an empty directory, then replace it, then write through it.
+ * Exercises the safety cache to ensure that it does not treat new entries as safe.
+ */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir7");
+ archive_entry_set_mode(ae, S_IFDIR | 0777);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsDir("link_to_dir7", -1);
+ /* Replace it. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir7");
+ archive_entry_set_mode(ae, S_IFLNK | 0777);
+ archive_entry_set_symlink(ae, "dir");
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
+ assert(0 == archive_write_header(a, ae));
+ assert(0 == archive_write_finish_entry(a));
+ assertIsSymlink("link_to_dir7", "dir", 1);
+ /* Extract through it. */
+ assert(archive_entry_clear(ae) != NULL);
+ archive_entry_copy_pathname(ae, "link_to_dir7/filef");
+ archive_entry_set_mode(ae, S_IFREG | 0777);
+ assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ assertFileNotExists("dir/filef");
+
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
}
diff --git a/libarchive/test/test_write_disk_secure744.c b/libarchive/test/test_write_disk_secure744.c
index 35824658ce65..69ca5a455782 100644
--- a/libarchive/test/test_write_disk_secure744.c
+++ b/libarchive/test/test_write_disk_secure744.c
@@ -33,9 +33,6 @@
DEFINE_TEST(test_write_disk_secure744)
{
-#if defined(_WIN32) && !defined(__CYGWIN__)
- skipping("archive_write_disk security checks not supported on Windows");
-#else
struct archive *a;
struct archive_entry *ae;
size_t buff_size = 8192;
@@ -46,6 +43,12 @@ DEFINE_TEST(test_write_disk_secure744)
assert(buff != NULL);
+ if (!canSymlink()) {
+ free(buff);
+ skipping("Can't test symlinks on this filesystem");
+ return;
+ }
+
/* Start with a known umask. */
assertUmask(UMASK);
@@ -70,6 +73,7 @@ DEFINE_TEST(test_write_disk_secure744)
archive_entry_copy_pathname(ae, buff);
archive_entry_set_mode(ae, S_IFREG | 0777);
archive_entry_copy_symlink(ae, testworkdir);
+ archive_entry_set_symlink_type(ae, AE_SYMLINK_TYPE_DIRECTORY);
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
archive_entry_free(ae);
@@ -90,5 +94,4 @@ DEFINE_TEST(test_write_disk_secure744)
}
archive_free(a);
free(buff);
-#endif
}
diff --git a/libarchive/test/test_write_disk_secure745.c b/libarchive/test/test_write_disk_secure745.c
index 570a8356a9f2..b219c4e1897b 100644
--- a/libarchive/test/test_write_disk_secure745.c
+++ b/libarchive/test/test_write_disk_secure745.c
@@ -35,6 +35,7 @@
DEFINE_TEST(test_write_disk_secure745)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Specifically, file permission checks. */
skipping("archive_write_disk security checks not supported on Windows");
#else
struct archive *a;
diff --git a/libarchive/test/test_write_disk_secure746.c b/libarchive/test/test_write_disk_secure746.c
index 3f0588914707..81f608e78fee 100644
--- a/libarchive/test/test_write_disk_secure746.c
+++ b/libarchive/test/test_write_disk_secure746.c
@@ -79,12 +79,14 @@ DEFINE_TEST(test_write_disk_secure746a)
*/
DEFINE_TEST(test_write_disk_secure746b)
{
-#if defined(_WIN32) && !defined(__CYGWIN__)
- skipping("archive_write_disk security checks not supported on Windows");
-#else
struct archive *a;
struct archive_entry *ae;
+ if (!canSymlink()) {
+ skipping("Can't test symlinks on this filesystem");
+ return;
+ }
+
/* Start with a known umask. */
assertUmask(UMASK);
@@ -123,5 +125,4 @@ DEFINE_TEST(test_write_disk_secure746b)
assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a));
archive_write_free(a);
-#endif
}
diff --git a/libarchive/test/test_write_format_7zip.c b/libarchive/test/test_write_format_7zip.c
index ef5b40906d16..43a86bb70de2 100644
--- a/libarchive/test/test_write_format_7zip.c
+++ b/libarchive/test/test_write_format_7zip.c
@@ -26,6 +26,9 @@
#include "test.h"
+#define __LIBARCHIVE_TEST
+#include "archive_write_private.h"
+
static void
test_basic(const char *compression_type)
{
@@ -573,3 +576,39 @@ DEFINE_TEST(test_write_format_7zip_basic_zstd)
/* Test that making a 7-Zip archive file with zstandard compression. */
test_basic("zstd");
}
+
+DEFINE_TEST(test_write_format_7zip_unregister)
+{
+ struct archive *a;
+ struct archive_write *aw;
+
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_7zip(a));
+
+ aw = (struct archive_write *)a;
+ assert(aw->format_data != NULL);
+ assert(aw->format_free != NULL);
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ __archive_write_unregister_format(aw));
+
+ assert(aw->format_data == NULL);
+ assert(aw->format_name == NULL);
+ assert(aw->format_init == NULL);
+ assert(aw->format_options == NULL);
+ assert(aw->format_finish_entry == NULL);
+ assert(aw->format_write_header == NULL);
+ assert(aw->format_write_data == NULL);
+ assert(aw->format_close == NULL);
+ assert(aw->format_free == NULL);
+ assertEqualInt(0, aw->archive.archive_format);
+ assert(aw->archive.archive_format_name == NULL);
+
+ assertEqualIntA(a, ARCHIVE_FAILED,
+ archive_write_set_format_option(a, NULL, "compression",
+ "store"));
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ __archive_write_unregister_format(aw));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
diff --git a/libarchive/test/test_write_format_gnutar.c b/libarchive/test/test_write_format_gnutar.c
index 213efe3a6086..00dc516d7db0 100644
--- a/libarchive/test/test_write_format_gnutar.c
+++ b/libarchive/test/test_write_format_gnutar.c
@@ -278,3 +278,14 @@ DEFINE_TEST(test_write_format_gnutar)
free(buff);
}
+
+DEFINE_TEST(test_write_format_gnutar_switch)
+{
+ struct archive *a;
+
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_7zip(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_gnutar(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_gnutar(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
diff --git a/libarchive/test/test_archive_write_set_filter_option.c b/libarchive/test/test_write_format_gnutar_huge.c
index 6b90a831b469..c0a4b7e00a92 100644
--- a/libarchive/test/test_archive_write_set_filter_option.c
+++ b/libarchive/test/test_write_format_gnutar_huge.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011 Tim Kientzle
+ * Copyright (c) 2026 Tobias Stoeckmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,33 +22,36 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
+#include "archive.h"
#include "test.h"
-#define should(__a, __code, __m, __o, __v) \
-assertEqualInt(__code, archive_write_set_filter_option(__a, __m, __o, __v))
-
-static void
-test(int pristine)
+DEFINE_TEST(test_write_format_gnutar_huge)
{
- struct archive* a = archive_write_new();
+ struct archive_entry *ae;
+ struct archive *a;
+ size_t buffsize = 8192;
+ size_t used;
+ char *buff;
- if (!pristine)
- archive_write_add_filter_gzip(a);
+ /* Create a new archive in memory. */
+ assert((buff = malloc(buffsize)) != NULL);
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_gnutar(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, buffsize, &used));
- should(a, ARCHIVE_OK, NULL, NULL, NULL);
- should(a, ARCHIVE_OK, "", "", "");
+ /* Create an entry with INT64_MAX bytes. */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_copy_pathname(ae, "file");
+ archive_entry_set_size(ae, INT64_MAX);
+ archive_entry_set_filetype(ae, AE_IFREG);
- should(a, ARCHIVE_FAILED, NULL, "fubar", NULL);
- should(a, ARCHIVE_FAILED, NULL, "fubar", "snafu");
- should(a, ARCHIVE_FAILED, "fubar", "snafu", NULL);
- should(a, ARCHIVE_FAILED, "fubar", "snafu", "betcha");
+ /* Try to write entry into archive. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_write_finish_entry(a));
- archive_write_free(a);
-}
-
-DEFINE_TEST(test_archive_write_set_filter_option)
-{
- test(1);
- test(0);
+ archive_entry_free(ae);
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+ free(buff);
}
diff --git a/libarchive/test/test_write_format_iso9660_rockridge_overflow.c b/libarchive/test/test_write_format_iso9660_rockridge_overflow.c
new file mode 100644
index 000000000000..f4272206989d
--- /dev/null
+++ b/libarchive/test/test_write_format_iso9660_rockridge_overflow.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2026 Microsoft Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+DEFINE_TEST(test_write_format_iso9660_rockridge_overflow)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ unsigned char *buff;
+ char name[706];
+ size_t buffsize = 256 * 2048;
+ size_t used;
+
+ buff = malloc(buffsize);
+ assert(buff != NULL);
+ if (buff == NULL)
+ return;
+
+ assert((a = archive_write_new()) != NULL);
+ assertA(0 == archive_write_set_format_iso9660(a));
+ assertA(0 == archive_write_add_filter_none(a));
+ assertA(0 == archive_write_set_bytes_per_block(a, 1));
+ assertA(0 == archive_write_set_bytes_in_last_block(a, 1));
+ assertA(0 == archive_write_open_memory(a, buff, buffsize, &used));
+
+
+ name[0] = '.';
+ name[1] = '/';
+ /* Empirically, it seems it requires at least 7 iterations to line up the RR blocks and trigger the ASAN failure */
+ for (int i = 0; i < 10; ++i) {
+ /* AAAA... BBBB... CCCC... */
+ memset(&name[2], 0x41 + i, 703);
+ name[705] = '\0';
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_atime(ae, 2, 0);
+ archive_entry_set_ctime(ae, 4, 0);
+ archive_entry_set_mtime(ae, 5, 0);
+ archive_entry_copy_pathname(ae, name);
+ archive_entry_set_mode(ae, S_IFREG | 0755);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ }
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+
+ /* Close out the archive. */
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ free(buff);
+}
diff --git a/libarchive/test/test_write_format_iso9660_zisofs_overflow.c b/libarchive/test/test_write_format_iso9660_zisofs_overflow.c
new file mode 100644
index 000000000000..12be811a72d9
--- /dev/null
+++ b/libarchive/test/test_write_format_iso9660_zisofs_overflow.c
@@ -0,0 +1,127 @@
+/*-SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Dustin L. Howett
+ * 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"
+
+/*
+ * Writing a file to zisofs which is near the 4 GiB limit triggered an
+ * overflow and wraparound in calculating how much space we needed to
+ * store compressed block pointers, which led to an out-of-bounds write
+ * to the heap.
+ *
+ * This test can only fail under ASan.
+ */
+DEFINE_TEST(test_write_format_iso9660_zisofs_overflow)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ unsigned char *inbuff, *arcbuff;
+ size_t buffsize = 2048 * 288, writesize = 1048576, entrysize = UINT32_MAX - 32000, used;
+ int r;
+
+ /* ISO9660 format: Create a new archive in memory. */
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, 0, archive_write_set_format_iso9660(a));
+ assertEqualIntA(a, 0, archive_write_add_filter_none(a));
+ r = archive_write_set_option(a, NULL, "zisofs", "1");
+ if (r == ARCHIVE_FATAL) {
+ skipping("zisofs option not supported on this platform");
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+ return;
+ }
+
+ arcbuff = malloc(buffsize);
+ assert(arcbuff != NULL);
+ if (arcbuff == NULL)
+ return;
+
+ inbuff = calloc(writesize, 1);
+ assert(inbuff != NULL);
+ if (inbuff == NULL) {
+ free(arcbuff);
+ return;
+ }
+
+ assertEqualIntA(a, 0, archive_write_set_option(a, NULL, "pad", NULL));
+ assertEqualIntA(a, 0, archive_write_open_memory(a, arcbuff, buffsize, &used));
+
+ /*
+ * "file1" is almost exactly 4 GiB; it's enough to trigger a block pointer counting
+ * overflow in zisofs.
+ */
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_atime(ae, 2, 20);
+ archive_entry_set_birthtime(ae, 3, 30);
+ archive_entry_set_ctime(ae, 4, 40);
+ archive_entry_set_mtime(ae, 5, 50);
+ archive_entry_copy_pathname(ae, "file1");
+ archive_entry_set_mode(ae, S_IFREG | 0755);
+ archive_entry_set_size(ae, entrysize);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+ while (entrysize > writesize) {
+ assertEqualIntA(a, writesize, archive_write_data(a, inbuff, writesize));
+ entrysize -= writesize;
+ }
+ /* remainder */
+ if (0 != (writesize = entrysize))
+ assertEqualIntA(a, writesize, archive_write_data(a, inbuff, writesize));
+
+ /* Close out the archive. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ failure("The ISO image size should be 589824 bytes.");
+ assertEqualInt(used, 2048 * 288);
+
+ /*
+ * Read ISO image (basic sanity check).
+ */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, 0, archive_read_support_format_all(a));
+ assertEqualIntA(a, 0, archive_read_support_filter_all(a));
+ assertEqualIntA(a, 0, archive_read_open_memory(a, arcbuff, used));
+
+ /*
+ * Skip the root directory
+ */
+ assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+
+ /*
+ * file1
+ */
+ assertEqualIntA(a, 0, archive_read_next_header(a, &ae));
+
+ /*
+ * Verify the end of the archive.
+ */
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ free(arcbuff);
+ free(inbuff);
+}
diff --git a/libarchive/test/test_write_format_pax.c b/libarchive/test/test_write_format_pax.c
index a84fb8250521..44cce2f8d2b4 100644
--- a/libarchive/test/test_write_format_pax.c
+++ b/libarchive/test/test_write_format_pax.c
@@ -36,6 +36,7 @@ DEFINE_TEST(test_write_format_pax)
int i;
char nulls[1024];
int64_t offset, length;
+ char long_slashes[300 + 1];
buff = malloc(buffsize); /* million bytes of work area */
assert(buff != NULL);
@@ -126,6 +127,23 @@ DEFINE_TEST(test_write_format_pax)
assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9));
/*
+ * A pathname made entirely of '/' characters used to trigger an
+ * out-of-bounds read in the pax writer when the pathname was long
+ * enough to require USTAR name splitting.
+ */
+ memset(long_slashes, '/', 300);
+ long_slashes[300] = '\0';
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_atime(ae, 2, 20);
+ archive_entry_set_ctime(ae, 4, 40);
+ archive_entry_set_mtime(ae, 5, 50);
+ archive_entry_copy_pathname(ae, long_slashes);
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ archive_entry_set_size(ae, 0);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ /*
* XXX TODO XXX Archive directory, other file types.
* Archive extended attributes, ACLs, other metadata.
* Verify they get read back correctly.
@@ -244,6 +262,19 @@ DEFINE_TEST(test_write_format_pax)
assertEqualMem(buff2, "12345678", 8);
/*
+ * Read the all-slash pathname entry.
+ */
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(long_slashes, archive_entry_pathname(ae));
+ assertEqualInt(2, archive_entry_atime(ae));
+ assertEqualInt(20, archive_entry_atime_nsec(ae));
+ assertEqualInt(4, archive_entry_ctime(ae));
+ assertEqualInt(40, archive_entry_ctime_nsec(ae));
+ assertEqualInt(5, archive_entry_mtime(ae));
+ assertEqualInt(50, archive_entry_mtime_nsec(ae));
+ assertEqualInt(0, archive_entry_size(ae));
+
+ /*
* Verify the end of the archive.
*/
assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
diff --git a/libarchive/test/test_write_format_pax_align.c b/libarchive/test/test_write_format_pax_align.c
new file mode 100644
index 000000000000..0ca0c81062a4
--- /dev/null
+++ b/libarchive/test/test_write_format_pax_align.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2024 libarchive contributors
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+/*
+ * The pax "align" option must start each big-enough regular file's data on
+ * an alignment boundary in the archive stream, so it can be reflinked out.
+ */
+
+#define ALIGN 4096
+
+struct file {
+ const char *name;
+ size_t size;
+ char fill;
+ int add_xattr; /* force a "natural" pax extended header */
+};
+
+/*
+ * A mix of entries: small/large regular files, a directory, long-name and
+ * xattr entries (which force a natural pax header), and an exactly-align file.
+ */
+static const struct file files[] = {
+ { "small", 100, 'a', 0 },
+ { "big1", 5000, 'b', 0 },
+ { "adir/", 0, 0, 0 },
+ { "big2", ALIGN, 'c', 0 },
+ { "tiny", 1, 'd', 0 },
+ { "big3", 100000, 'e', 0 },
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/longname", 8192, 'f', 0 },
+ { "withxattr", 9000, 'g', 1 },
+ { NULL, 0, 0, 0 }
+};
+
+static void
+verify_align_limit(void)
+{
+ struct archive *a;
+
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax_restricted(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_set_options(a, "align=1048576"));
+ assertEqualIntA(a, ARCHIVE_FAILED,
+ archive_write_set_options(a, "align=2097152"));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+static int
+memory_contains(const char *memory, size_t memory_size, const char *needle)
+{
+ size_t needle_size = strlen(needle);
+ size_t i;
+
+ for (i = 0; i + needle_size <= memory_size; i++) {
+ if (memcmp(memory + i, needle, needle_size) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+static void
+verify_sparse_not_padded(void)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char *buff, *data;
+ size_t buffsize = 65536, used;
+
+ buff = malloc(buffsize);
+ data = calloc(1, 8192);
+ assert(buff != NULL);
+ assert(data != NULL);
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax_restricted(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_set_options(a, "align=4096"));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, buffsize, &used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_pathname(ae, "sparse");
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ archive_entry_set_size(ae, 8192);
+ archive_entry_sparse_add_entry(ae, 4096, 4096);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ assertEqualIntA(a, 8192, archive_write_data(a, data, 8192));
+ archive_entry_free(ae);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+
+ failure("Sparse entries must not get misleading alignment padding");
+ assert(!memory_contains(buff, used, "LIBARCHIVE.pad"));
+ free(data);
+ free(buff);
+}
+
+static void
+write_archive(char *buff, size_t buffsize, size_t *used, int gzip)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ const struct file *f;
+ char *data;
+
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_pax_restricted(a));
+ if (gzip)
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_gzip(a));
+ else
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ /* Small blocks so the writer doesn't tail-pad past our offsets. */
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 512));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_set_options(a, "align=4096"));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, buffsize, used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ for (f = files; f->name != NULL; f++) {
+ archive_entry_clear(ae);
+ archive_entry_set_pathname(ae, f->name);
+ archive_entry_set_mtime(ae, 5, 0);
+ if (f->name[strlen(f->name) - 1] == '/') {
+ archive_entry_set_mode(ae, S_IFDIR | 0755);
+ } else {
+ archive_entry_set_mode(ae, S_IFREG | 0644);
+ archive_entry_set_size(ae, f->size);
+ }
+ if (f->add_xattr)
+ archive_entry_xattr_add_entry(ae, "user.test",
+ "value", 5);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ if (f->size > 0) {
+ data = malloc(f->size);
+ assert(data != NULL);
+ memset(data, f->fill, f->size);
+ assertEqualIntA(a, f->size,
+ archive_write_data(a, data, f->size));
+ free(data);
+ }
+ }
+ archive_entry_free(ae);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+static void
+verify_archive(const char *buff, size_t used, int gzip)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ const struct file *f;
+
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_tar(a));
+ if (gzip)
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_gzip(a));
+ else
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, buff, used));
+
+ for (f = files; f->name != NULL; f++) {
+ int64_t off, size;
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualStringA(a, f->name, archive_entry_pathname(ae));
+ size = archive_entry_size(ae);
+ assertEqualIntA(a, f->size, size);
+ /*
+ * archive_filter_bytes(a, 0) is the offset of the data in
+ * the *decompressed* archive stream, i.e. exactly where a
+ * reflink would read from an uncompressed archive.
+ */
+ off = archive_filter_bytes(a, 0);
+ if (archive_entry_filetype(ae) == AE_IFREG && size >= ALIGN) {
+ failure("data for '%s' (size %jd) must start on a "
+ "%d-byte boundary but starts at %jd",
+ f->name, (intmax_t)size, ALIGN, (intmax_t)off);
+ assertEqualInt(0, (int)(off % ALIGN));
+ }
+ /* Data must still round-trip intact through the padding. */
+ if (size > 0) {
+ char *data = malloc((size_t)size);
+ assert(data != NULL);
+ assertEqualIntA(a, size,
+ archive_read_data(a, data, (size_t)size));
+ assertMemoryFilledWith(data, (size_t)size, f->fill);
+ free(data);
+ }
+ }
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_write_format_pax_align)
+{
+ size_t buffsize = 2000000;
+ char *buff;
+ size_t used;
+
+ verify_align_limit();
+ verify_sparse_not_padded();
+
+ buff = malloc(buffsize);
+ assert(buff != NULL);
+
+ /* Uncompressed: the archive itself is aligned on disk. */
+ write_archive(buff, buffsize, &used, 0);
+ verify_archive(buff, used, 0);
+
+ /* Compressed: the *decompressed* stream is still aligned, which is
+ * what a reflink-from-decompressed-copy relies on. */
+ if (canGzip()) {
+ write_archive(buff, buffsize, &used, 1);
+ verify_archive(buff, used, 1);
+ } else {
+ skipping("gzip unavailable; skipped compressed alignment check");
+ }
+
+ free(buff);
+}
diff --git a/libarchive/test/test_write_format_tar_ustar.c b/libarchive/test/test_write_format_tar_ustar.c
index d86749fd41b0..480418d8a6f1 100644
--- a/libarchive/test/test_write_format_tar_ustar.c
+++ b/libarchive/test/test_write_format_tar_ustar.c
@@ -216,7 +216,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "010034\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -237,7 +237,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "010707\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -256,7 +256,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "007747\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "5", 1); /* typeflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -275,7 +275,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "011446\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "2", 1); /* linkflag */
myAssertEqualMem(e + 157, "file", 5); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -294,7 +294,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "034242\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -313,7 +313,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "026230\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
@@ -332,7 +332,7 @@ DEFINE_TEST(test_write_format_tar_ustar)
myAssertEqualMem(e + 148, "055570\0 ", 8); /* checksum */
myAssertEqualMem(e + 156, "0", 1); /* linkflag */
myAssertEqualMem(e + 157, "", 1); /* linkname */
- myAssertEqualMem(e + 257, "ustar\000000", 8); /* signature/version */
+ myAssertEqualMem(e + 257, "ustar\00000", 8); /* signature/version */
myAssertEqualMem(e + 265, "", 1); /* uname */
myAssertEqualMem(e + 297, "", 1); /* gname */
myAssertEqualMem(e + 329, "000000 ", 8); /* devmajor */
diff --git a/libarchive/test/test_write_format_warc.c b/libarchive/test/test_write_format_warc.c
index bb9f2388095b..295efed49774 100644
--- a/libarchive/test/test_write_format_warc.c
+++ b/libarchive/test/test_write_format_warc.c
@@ -108,6 +108,7 @@ DEFINE_TEST(test_write_format_warc)
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
archive_entry_free(ae);
assertEqualIntA(a, 9, archive_write_data(a, "12345678", 9));
+ assertEqualIntA(a, 0, archive_write_data(a, "overflow", 8));
assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
@@ -143,3 +144,108 @@ DEFINE_TEST(test_write_format_warc)
free(buff);
}
+
+DEFINE_TEST(test_write_format_warc_size)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[2048];
+ size_t used;
+
+ memset(buff, 0, sizeof(buff));
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_warc(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_pathname(ae, "test");
+ archive_entry_set_filetype(ae, AE_IFREG);
+ assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
+ assertEqualString("Size required", archive_error_string(a));
+ archive_entry_free(ae);
+
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+DEFINE_TEST(test_write_format_warc_incomplete)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[2048];
+ size_t used;
+
+ memset(buff, 0, sizeof(buff));
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_warc(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_pathname(ae, "test");
+ archive_entry_set_filetype(ae, AE_IFREG);
+ archive_entry_set_size(ae, 9);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ assertEqualIntA(a, 8, archive_write_data(a, "12345678", 8));
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_write_close(a));
+ assertEqualString("WARC entry is shorter than Content-Length",
+ archive_error_string(a));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+DEFINE_TEST(test_write_format_warc_warcinfo_output_error)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[1];
+ size_t used;
+
+ memset(buff, 0, sizeof(buff));
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_warc(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_pathname(ae, "test");
+ archive_entry_set_filetype(ae, AE_IFREG);
+ archive_entry_set_size(ae, 1);
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
+
+DEFINE_TEST(test_write_format_warc_resource_output_error)
+{
+ struct archive *a;
+ struct archive_entry *ae;
+ char buff[1];
+ size_t used;
+
+ memset(buff, 0, sizeof(buff));
+ assert((a = archive_write_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_warc(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_set_format_option(a, NULL, "omit-warcinfo", NULL));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
+ assertEqualIntA(a, ARCHIVE_OK, archive_write_set_bytes_per_block(a, 1));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_write_open_memory(a, buff, sizeof(buff), &used));
+
+ assert((ae = archive_entry_new()) != NULL);
+ archive_entry_set_pathname(ae, "test");
+ archive_entry_set_filetype(ae, AE_IFREG);
+ archive_entry_set_size(ae, 1);
+ assertEqualIntA(a, ARCHIVE_FATAL, archive_write_header(a, ae));
+ archive_entry_free(ae);
+
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
diff --git a/libarchive/test/test_write_format_xar.c b/libarchive/test/test_write_format_xar.c
index 495400a34f92..40d42291a2ac 100644
--- a/libarchive/test/test_write_format_xar.c
+++ b/libarchive/test/test_write_format_xar.c
@@ -25,13 +25,16 @@
*/
#include "test.h"
+#define __LIBARCHIVE_TEST
+#include "archive_write_private.h"
+
#include <locale.h>
static void
test_xar(const char *option)
{
char buff2[64];
- size_t buffsize = 1500;
+ size_t buffsize = 4096;
char *buff;
struct archive_entry *ae;
struct archive *a;
@@ -318,18 +321,42 @@ DEFINE_TEST(test_write_format_xar)
/* Disable TOC checksum. */
test_xar("!toc-checksum");
test_xar("toc-checksum=none");
+#ifdef ARCHIVE_HAS_SHA1
/* Specify TOC checksum type to sha1. */
test_xar("toc-checksum=sha1");
+#endif
+#ifdef ARCHIVE_HAS_MD5
/* Specify TOC checksum type to md5. */
test_xar("toc-checksum=md5");
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ /* Specify TOC checksum type to sha256. */
+ test_xar("toc-checksum=sha256");
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ /* Specify TOC checksum type to sha512. */
+ test_xar("toc-checksum=sha512");
+#endif
/* Disable file checksum. */
test_xar("!checksum");
test_xar("checksum=none");
+#ifdef ARCHIVE_HAS_SHA1
/* Specify file checksum type to sha1. */
test_xar("checksum=sha1");
+#endif
+#ifdef ARCHIVE_HAS_MD5
/* Specify file checksum type to md5. */
test_xar("checksum=md5");
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ /* Specify file checksum type to sha256. */
+ test_xar("checksum=sha256");
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ /* Specify file checksum type to sha512. */
+ test_xar("checksum=sha512");
+#endif
/* Disable compression. */
test_xar("!compression");
@@ -380,3 +407,43 @@ DEFINE_TEST(test_write_format_xar_entry_trunc)
assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
}
+
+DEFINE_TEST(test_write_format_xar_unregister)
+{
+ struct archive *a;
+ struct archive_write *aw;
+
+ assert((a = archive_write_new()) != NULL);
+ if (archive_write_set_format_xar(a) != ARCHIVE_OK) {
+ skipping("xar is not supported on this platform");
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+ return;
+ }
+
+ aw = (struct archive_write *)a;
+ assert(aw->format_data != NULL);
+ assert(aw->format_free != NULL);
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ __archive_write_unregister_format(aw));
+
+ assert(aw->format_data == NULL);
+ assert(aw->format_name == NULL);
+ assert(aw->format_init == NULL);
+ assert(aw->format_options == NULL);
+ assert(aw->format_finish_entry == NULL);
+ assert(aw->format_write_header == NULL);
+ assert(aw->format_write_data == NULL);
+ assert(aw->format_close == NULL);
+ assert(aw->format_free == NULL);
+ assertEqualInt(0, aw->archive.archive_format);
+ assert(aw->archive.archive_format_name == NULL);
+
+ assertEqualIntA(a, ARCHIVE_FAILED,
+ archive_write_set_format_option(a, NULL, "compression",
+ "gzip"));
+
+ assertEqualIntA(a, ARCHIVE_OK,
+ __archive_write_unregister_format(aw));
+ assertEqualInt(ARCHIVE_OK, archive_write_free(a));
+}
diff --git a/libarchive/test/test_write_format_zip.c b/libarchive/test/test_write_format_zip.c
index 569b54da5e22..857d0f3fd3a0 100644
--- a/libarchive/test/test_write_format_zip.c
+++ b/libarchive/test/test_write_format_zip.c
@@ -428,7 +428,7 @@ verify_contents(struct archive *a, int seeking, int content)
if (content) {
assertEqualIntA(a, 5,
archive_read_data(a, filedata, sizeof(filedata)));
- assertEqualMem(filedata, "ghijk", 4);
+ assertEqualMem(filedata, "ghijk", 5);
}
/* Read symlink. */
@@ -520,7 +520,7 @@ verify_contents(struct archive *a, int seeking, int content)
if (content) {
assertEqualIntA(a, 5,
archive_read_data(a, filedata, sizeof(filedata)));
- assertEqualMem(filedata, "ijklm", 4);
+ assertEqualMem(filedata, "ijklm", 5);
}
/* Read symlink. */
diff --git a/libarchive/test/test_write_format_zip_compression_lzmaxz.c b/libarchive/test/test_write_format_zip_compression_lzma_xz.c
index f627b1930a1b..fc7d011f8923 100644
--- a/libarchive/test/test_write_format_zip_compression_lzmaxz.c
+++ b/libarchive/test/test_write_format_zip_compression_lzma_xz.c
@@ -320,7 +320,7 @@ static void verify_lzma_contents(const char *buff, size_t used)
}
#endif /* HAVE_LZMA_H */
-DEFINE_TEST(test_write_format_zip_compression_lzmaxz)
+DEFINE_TEST(test_write_format_zip_compression_lzma_xz)
{
#ifndef HAVE_LZMA_H
skipping("lzma is not fully supported on this platform");
diff --git a/libarchive/test/test_write_read_format_zip.c b/libarchive/test/test_write_read_format_zip.c
index 1be282f67354..5a316d004b44 100644
--- a/libarchive/test/test_write_read_format_zip.c
+++ b/libarchive/test/test_write_read_format_zip.c
@@ -436,7 +436,7 @@ verify_contents(struct archive *a, int seeking, int improved_streaming)
}
assertEqualIntA(a, 5,
archive_read_data(a, filedata, sizeof(filedata)));
- assertEqualMem(filedata, "ghijk", 4);
+ assertEqualMem(filedata, "ghijk", 5);
/* Read symlink. */
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -534,7 +534,7 @@ verify_contents(struct archive *a, int seeking, int improved_streaming)
}
assertEqualIntA(a, 5,
archive_read_data(a, filedata, sizeof(filedata)));
- assertEqualMem(filedata, "ijklm", 4);
+ assertEqualMem(filedata, "ijklm", 5);
/* Read symlink. */
assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
diff --git a/tar/CMakeLists.txt b/tar/CMakeLists.txt
index c082c66e4b05..39600360776b 100644
--- a/tar/CMakeLists.txt
+++ b/tar/CMakeLists.txt
@@ -30,6 +30,15 @@ IF(ENABLE_TAR)
IF(WIN32 AND NOT CYGWIN)
LIST(APPEND bsdtar_SOURCES bsdtar_windows.c)
LIST(APPEND bsdtar_SOURCES bsdtar_windows.h)
+ IF(NOT MSVC)
+ # The GNU toolchains (MinGW/Clang) do not generate an application
+ # manifest, so we embed one through a resource script.
+ ENABLE_LANGUAGE(RC)
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+ LIST(APPEND bsdtar_SOURCES bsdtar.rc)
+ SET_SOURCE_FILES_PROPERTIES(bsdtar.rc PROPERTIES
+ OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bsdtar.exe.manifest)
+ ENDIF(NOT MSVC)
ENDIF(WIN32 AND NOT CYGWIN)
# bsdtar documentation
@@ -44,6 +53,10 @@ IF(ENABLE_TAR)
SET_TARGET_PROPERTIES(bsdtar PROPERTIES COMPILE_DEFINITIONS
LIBARCHIVE_STATIC)
ENDIF(ENABLE_TAR_SHARED)
+ IF(WIN32 AND NOT CYGWIN AND MSVC)
+ # MSVC merges .manifest sources into the linker's default manifest
+ TARGET_SOURCES(bsdtar PRIVATE bsdtar.exe.manifest)
+ ENDIF(WIN32 AND NOT CYGWIN AND MSVC)
# Installation rules
IF(ENABLE_INSTALL)
diff --git a/tar/bsdtar.exe.manifest b/tar/bsdtar.exe.manifest
new file mode 100644
index 000000000000..32142e951c7b
--- /dev/null
+++ b/tar/bsdtar.exe.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
+ <ws2:longPathAware>true</ws2:longPathAware>
+ </windowsSettings>
+ </application>
+</assembly>
diff --git a/tar/bsdtar.h b/tar/bsdtar.h
index 692b800bf811..29c67fc08295 100644
--- a/tar/bsdtar.h
+++ b/tar/bsdtar.h
@@ -196,7 +196,6 @@ int need_report(void);
int pathcmp(const char *a, const char *b);
void safe_fprintf(FILE * ARCHIVE_RESTRICT, const char * ARCHIVE_RESTRICT fmt, ...) __LA_PRINTF(2, 3);
void set_chdir(struct bsdtar *, const char *newdir);
-const char *tar_i64toa(int64_t);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
diff --git a/tar/bsdtar.rc b/tar/bsdtar.rc
new file mode 100644
index 000000000000..d12da1eacc00
--- /dev/null
+++ b/tar/bsdtar.rc
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Embed an application manifest that marks bsdtar as long-path aware.
+ *
+ * bsdtar implements -C/--directory on Windows with SetCurrentDirectoryW(),
+ * whose argument is limited to MAX_PATH (260) characters unless the process
+ * opts in to long paths via a "longPathAware" manifest. A system-wide
+ * LongPathsEnabled registry value is not sufficient on its own; the executable
+ * must also carry this manifest. Without it, extracting into deeply nested
+ * destination directories fails with "could not chdir to ...".
+ *
+ * See:
+ * https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation
+ */
+
+#define RT_MANIFEST 24
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "bsdtar.exe.manifest"
diff --git a/tar/read.c b/tar/read.c
index 57ae5200cf0d..a50c7747d87a 100644
--- a/tar/read.c
+++ b/tar/read.c
@@ -121,16 +121,16 @@ progress_func(void *cookie)
else
compression = (int)((uncomp - comp) * 100 / uncomp);
fprintf(stderr,
- "In: %s bytes, compression %d%%;",
- tar_i64toa(comp), compression);
- fprintf(stderr, " Out: %d files, %s bytes\n",
- archive_file_count(a), tar_i64toa(uncomp));
+ "In: %ju bytes, compression %d%%;",
+ (uintmax_t)comp, compression);
+ fprintf(stderr, " Out: %d files, %ju bytes\n",
+ archive_file_count(a), (uintmax_t)uncomp);
}
if (entry != NULL) {
safe_fprintf(stderr, "Current: %s",
archive_entry_pathname(entry));
- fprintf(stderr, " (%s bytes)\n",
- tar_i64toa(archive_entry_size(entry)));
+ fprintf(stderr, " (%jd bytes)\n",
+ (intmax_t)archive_entry_size(entry));
}
}
diff --git a/tar/subst.c b/tar/subst.c
index 6acf8cd15f31..62191a6304a0 100644
--- a/tar/subst.c
+++ b/tar/subst.c
@@ -264,6 +264,8 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result,
++i;
c = rule->result[i];
+ if (c == '\0')
+ break;
switch (c) {
case '~':
case '\\':
diff --git a/tar/test/CMakeLists.txt b/tar/test/CMakeLists.txt
index 5318c24c077e..5edb07074b92 100644
--- a/tar/test/CMakeLists.txt
+++ b/tar/test/CMakeLists.txt
@@ -70,6 +70,7 @@ IF(ENABLE_TAR AND ENABLE_TEST)
test_option_r.c
test_option_s.c
test_option_safe_writes.c
+ test_option_stdout_size_exceeds_declared.c
test_option_uid_uname.c
test_option_uuencode.c
test_option_xattrs.c
diff --git a/tar/test/test_option_stdout_size_exceeds_declared.c b/tar/test/test_option_stdout_size_exceeds_declared.c
new file mode 100644
index 000000000000..6c9242f12fde
--- /dev/null
+++ b/tar/test/test_option_stdout_size_exceeds_declared.c
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Tim Kientzle
+ * All rights reserved.
+ */
+#include "test.h"
+
+/*
+ * `bsdtar -x` writing to real files is protected by archive_write_disk,
+ * which already truncates output at an entry's declared size. `bsdtar
+ * -xO` (stdout) bypasses archive_write_disk entirely and writes
+ * archive_read_data_into_fd()'s output straight to fd 1 -- this test
+ * exercises that path's own backstop (added directly to
+ * archive_read_data_into_fd(), so it applies no matter which archive
+ * format is being read) at the bsdtar CLI level.
+ *
+ * The reference archives are crafted ZIP files with two entries: "first"
+ * declares uncompressed_size=10 but its true content is longer
+ * ("AAAAAAAAAABBBBBBBBBB" for _stored, 256KiB+6 bytes of
+ * "CCCCCCCCCCDDDDDDDDDD..." for _deflate), and "second" is a normal
+ * 11-byte "hello world" file, extracted right after "first" fails to
+ * confirm the mis-declared entry doesn't disrupt the rest of the
+ * archive.
+ *
+ * The ZIP reader itself also rejects "first" outright (a separate,
+ * format-level fix -- see test_read_format_zip_size_exceeds_declared),
+ * so archive_read_data_block() never hands this backstop any bytes for
+ * "first" to (partially) pass through: stdout ends up with none of
+ * "first"'s data at all, followed directly by "second". That's still
+ * "never more than declared" -- just fewer bytes than the boundary
+ * allows, since the format reader was already more conservative here.
+ */
+static void
+verify(const char *refname)
+{
+ extract_reference_file(refname);
+ failure("bsdtar -xO must report an error for the mis-declared entry");
+ assert(systemf("%s -xOf %s >test.out 2>test.err", testprog, refname)
+ != 0);
+
+ assertFileContents("hello world", 11, "test.out");
+ assertNonEmptyFile("test.err");
+}
+
+DEFINE_TEST(test_option_stdout_size_exceeds_declared)
+{
+ verify("test_option_stdout_size_exceeds_declared_stored.zip");
+ verify("test_option_stdout_size_exceeds_declared_deflate.zip");
+}
diff --git a/tar/test/test_option_stdout_size_exceeds_declared_deflate.zip.uu b/tar/test/test_option_stdout_size_exceeds_declared_deflate.zip.uu
new file mode 100644
index 000000000000..d01212b3ab48
--- /dev/null
+++ b/tar/test/test_option_stdout_size_exceeds_declared_deflate.zip.uu
@@ -0,0 +1,23 @@
+begin 644 test_option_stdout_size_exceeds_declared_deflate.zip
+M4$L#!!0````(``````#"V8Q_D`(```H````%````9FER<W3MR#$!```(P*!P
+MLW\F+TT!)W7FY9QSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGGG'/..>><<\XYYYQSSCGG
+MG'/..>><<\XYYYQSSCG7`E!+`P04````````````A1%*#0L````+````!@``
+M`'-E8V]N9&AE;&QO('=O<FQD4$L!`A0`%`````@``````,+9C'^0`@``"@``
+M``4``````````````````````&9I<G-T4$L!`A0`%````````````(412@T+
+M````"P````8`````````````````LP(``'-E8V]N9%!+!08``````@`"`&<`
+(``#B`@``````
+`
+end
diff --git a/tar/test/test_option_stdout_size_exceeds_declared_stored.zip.uu b/tar/test/test_option_stdout_size_exceeds_declared_stored.zip.uu
new file mode 100644
index 000000000000..ee5197727257
--- /dev/null
+++ b/tar/test/test_option_stdout_size_exceeds_declared_stored.zip.uu
@@ -0,0 +1,9 @@
+begin 644 test_option_stdout_size_exceeds_declared_stored.zip
+M4$L#!!0```````````#-F1X3%`````H````%````9FER<W1!04%!04%!04%!
+M0D)"0D)"0D)"0E!+`P04````````````A1%*#0L````+````!@```'-E8V]N
+M9&AE;&QO('=O<FQD4$L!`A0`%````````````,V9'A,4````"@````4`````
+M`````````````````&9I<G-T4$L!`A0`%````````````(412@T+````"P``
+M``8`````````````````-P```'-E8V]N9%!+!08``````@`"`&<```!F````
+"````
+`
+end
diff --git a/tar/test/test_stdio.c b/tar/test/test_stdio.c
index 239f3815fb71..e8b6558930ca 100644
--- a/tar/test/test_stdio.c
+++ b/tar/test/test_stdio.c
@@ -8,7 +8,7 @@
/*
* This data would fit onto one line, but it's easier to understand when it's
- * on mulitple lines (and thus matches the output files).
+ * on multiple lines (and thus matches the output files).
*/
static const char *cvf_err =
"a f\n"
diff --git a/tar/util.c b/tar/util.c
index 3fb0f9b02055..730a9adb2ea2 100644
--- a/tar/util.c
+++ b/tar/util.c
@@ -585,27 +585,6 @@ edit_mtime(struct bsdtar *bsdtar, struct archive_entry *entry)
}
/*
- * It would be nice to just use printf() for formatting large numbers,
- * but the compatibility problems are quite a headache. Hence the
- * following simple utility function.
- */
-const char *
-tar_i64toa(int64_t n0)
-{
- static char buff[24];
- uint64_t n = n0 < 0 ? -n0 : n0;
- char *p = buff + sizeof(buff);
-
- *--p = '\0';
- do {
- *--p = '0' + (int)(n % 10);
- } while (n /= 10);
- if (n0 < 0)
- *--p = '-';
- return p;
-}
-
-/*
* Like strcmp(), but try to be a little more aware of the fact that
* we're comparing two paths. Right now, it just handles leading
* "./" and trailing '/' specially, so that "a/b/" == "./a/b"
@@ -748,7 +727,9 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
(unsigned long)archive_entry_rdevminor(entry));
p = tmp;
} else {
- p = tar_i64toa(archive_entry_size(entry));
+ snprintf(tmp, sizeof(tmp), "%jd",
+ (intmax_t)archive_entry_size(entry));
+ p = tmp;
}
if (w + strlen(p) >= bsdtar->gs_width)
bsdtar->gs_width = w + strlen(p) + 1;
diff --git a/tar/write.c b/tar/write.c
index 302df020ad3a..13ff4eac766e 100644
--- a/tar/write.c
+++ b/tar/write.c
@@ -450,8 +450,11 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
/* Choose a suitable copy buffer size */
bsdtar->buff_size = 64 * 1024;
- while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block)
- bsdtar->buff_size *= 2;
+ while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block) {
+ bsdtar->buff_size *= 2;
+ if (bsdtar->buff_size < 64 * 1024)
+ lafe_errc(1, 0, "cannot allocate memory");
+ }
/* Try to compensate for space we'll lose to alignment. */
bsdtar->buff_size += 16 * 1024;
@@ -560,6 +563,8 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
* without failure.
*/
entry2 = archive_entry_new();
+ if (entry2 == NULL)
+ lafe_errc(1, errno, "Out of memory");
r = archive_read_next_header2(disk, entry2);
archive_entry_free(entry2);
if (r != ARCHIVE_OK) {
@@ -593,8 +598,8 @@ cleanup:
bsdtar->diskreader = NULL;
if (bsdtar->flags & OPTFLAG_TOTALS) {
- fprintf(stderr, "Total bytes written: %s\n",
- tar_i64toa(archive_filter_bytes(a, -1)));
+ fprintf(stderr, "Total bytes written: %jd\n",
+ (intmax_t)archive_filter_bytes(a, -1));
}
archive_write_free(a);
@@ -1038,21 +1043,20 @@ report_write(struct bsdtar *bsdtar, struct archive *a,
fprintf(stderr, "\n");
comp = archive_filter_bytes(a, -1);
uncomp = archive_filter_bytes(a, 0);
- fprintf(stderr, "In: %d files, %s bytes;",
- archive_file_count(a), tar_i64toa(uncomp));
+ fprintf(stderr, "In: %d files, %ju bytes;",
+ archive_file_count(a), (uintmax_t)uncomp);
if (comp >= uncomp)
compression = 0;
else
compression = (int)((uncomp - comp) * 100 / uncomp);
fprintf(stderr,
- " Out: %s bytes, compression %d%%\n",
- tar_i64toa(comp), compression);
- /* Can't have two calls to tar_i64toa() pending, so split the output. */
- safe_fprintf(stderr, "Current: %s (%s",
+ " Out: %ju bytes, compression %d%%\n",
+ (uintmax_t)comp, compression);
+ safe_fprintf(stderr, "Current: %s (%jd",
archive_entry_pathname(entry),
- tar_i64toa(progress));
- fprintf(stderr, "/%s bytes)\n",
- tar_i64toa(archive_entry_size(entry)));
+ (intmax_t)progress);
+ fprintf(stderr, "/%jd bytes)\n",
+ (intmax_t)archive_entry_size(entry));
}
static void
diff --git a/test_utils/test_main.c b/test_utils/test_main.c
index f5de428f2890..3238feed46c7 100644
--- a/test_utils/test_main.c
+++ b/test_utils/test_main.c
@@ -153,9 +153,6 @@ extern char **environ;
#if !defined(__BORLANDC__)
#define getcwd _getcwd
#endif
-#define lstat stat
-/*#define lstat _stat64*/
-/*#define stat _stat64*/
#define rmdir _rmdir
#if !defined(__BORLANDC__)
#define strdup _strdup
@@ -358,10 +355,28 @@ my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi)
{
HANDLE h;
int r;
+ const char *p;
+ char *s, *pn;
+
+ /* Replace slashes with backslashes in path */
+ pn = malloc(strlen(path) + 1);
+ if (pn == NULL) {
+ return (0);
+ }
+
+ for (p = path, s = pn; *p != '\0'; p++, s++) {
+ if (*p == '/')
+ *s = '\\';
+ else
+ *s = *p;
+ }
+ *s = '\0';
memset(bhfi, 0, sizeof(*bhfi));
- h = CreateFileA(path, FILE_READ_ATTRIBUTES, 0, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ h = CreateFileA(pn, FILE_READ_ATTRIBUTES, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+ free(pn);
if (h == INVALID_HANDLE_VALUE)
return (0);
r = GetFileInformationByHandle(h, bhfi);
@@ -1508,7 +1523,8 @@ assertion_file_time(const char *file, int line,
* a directory file. If not, CreateFile() will fail when
* the pathname is a directory. */
h = CreateFileA(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (h == INVALID_HANDLE_VALUE) {
failure_start(file, line, "Can't access %s\n", pathname);
failure_finish(NULL);
@@ -1733,13 +1749,28 @@ assertion_file_size(const char *file, int line, const char *pathname, long size)
int
assertion_is_dir(const char *file, int line, const char *pathname, int mode)
{
- struct stat st;
int r;
+ assertion_count(file, line);
+
#if defined(_WIN32) && !defined(__CYGWIN__)
+ BY_HANDLE_FILE_INFORMATION st;
(void)mode; /* UNUSED */
-#endif
- assertion_count(file, line);
+ r = my_GetFileInformationByName(pathname, &st);
+ if (r == 0) {
+ failure_start(file, line, "Dir should exist: %s", pathname);
+ failure_finish(NULL);
+ return (0);
+ }
+ if ((st.dwFileAttributes &
+ (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) !=
+ FILE_ATTRIBUTE_DIRECTORY) {
+ failure_start(file, line, "%s is not a dir", pathname);
+ failure_finish(NULL);
+ return (0);
+ }
+#else
+ struct stat st;
r = lstat(pathname, &st);
if (r != 0) {
failure_start(file, line, "Dir should exist: %s", pathname);
@@ -1751,7 +1782,6 @@ assertion_is_dir(const char *file, int line, const char *pathname, int mode)
failure_finish(NULL);
return (0);
}
-#if !defined(_WIN32) || defined(__CYGWIN__)
/* Windows doesn't handle permissions the same way as POSIX,
* so just ignore the mode tests. */
/* TODO: Can we do better here? */
@@ -1771,20 +1801,35 @@ assertion_is_dir(const char *file, int line, const char *pathname, int mode)
int
assertion_is_reg(const char *file, int line, const char *pathname, int mode)
{
- struct stat st;
int r;
+ assertion_count(file, line);
+
#if defined(_WIN32) && !defined(__CYGWIN__)
+ BY_HANDLE_FILE_INFORMATION st;
(void)mode; /* UNUSED */
-#endif
- assertion_count(file, line);
+ r = my_GetFileInformationByName(pathname, &st);
+ if (r == 0) {
+ failure_start(file, line, "File should exist: %s", pathname);
+ failure_finish(NULL);
+ return (0);
+ }
+ if ((st.dwFileAttributes &
+ (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) !=
+ 0) {
+ failure_start(file, line, "%s is not a regular file", pathname);
+ failure_finish(NULL);
+ return (0);
+ }
+#else
+ struct stat st;
r = lstat(pathname, &st);
if (r != 0 || !S_ISREG(st.st_mode)) {
failure_start(file, line, "File should exist: %s", pathname);
failure_finish(NULL);
return (0);
}
-#if !defined(_WIN32) || defined(__CYGWIN__)
+
/* Windows doesn't handle permissions the same way as POSIX,
* so just ignore the mode tests. */
/* TODO: Can we do better here? */
@@ -2160,7 +2205,7 @@ assertion_utimes(const char *file, int line, const char *pathname,
assertion_count(file, line);
h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (h == INVALID_HANDLE_VALUE) {
failure_start(file, line, "Can't access %s\n", pathname);
failure_finish(NULL);
@@ -3082,7 +3127,7 @@ systemf(const char *fmt, ...)
r = vsnprintf(buff + off, avail, fmt, ap);
va_end(ap);
- if (r < 0 || (size_t)r > avail) {
+ if (r < 0 || (size_t)r >= avail) {
return (-1);
}
diff --git a/unzip/CMakeLists.txt b/unzip/CMakeLists.txt
index 10582b70760b..496b9b368810 100644
--- a/unzip/CMakeLists.txt
+++ b/unzip/CMakeLists.txt
@@ -25,6 +25,19 @@ IF(ENABLE_UNZIP)
../libarchive_fe/passphrase.h
)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../libarchive_fe)
+ IF(WIN32 AND NOT CYGWIN)
+ LIST(APPEND bsdunzip_SOURCES bsdunzip_windows.c)
+ LIST(APPEND bsdunzip_SOURCES bsdunzip_windows.h)
+ IF(NOT MSVC)
+ # The GNU toolchains (MinGW/Clang) do not generate an application
+ # manifest, so we embed one through a resource script.
+ ENABLE_LANGUAGE(RC)
+ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+ LIST(APPEND bsdunzip_SOURCES bsdunzip.rc)
+ SET_SOURCE_FILES_PROPERTIES(bsdunzip.rc PROPERTIES
+ OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bsdunzip.exe.manifest)
+ ENDIF(NOT MSVC)
+ ENDIF(WIN32 AND NOT CYGWIN)
# bsdunzip documentation
SET(bsdunzip_MANS bsdunzip.1)
@@ -38,6 +51,10 @@ IF(ENABLE_UNZIP)
SET_TARGET_PROPERTIES(bsdunzip PROPERTIES COMPILE_DEFINITIONS
LIBARCHIVE_STATIC)
ENDIF(ENABLE_UNZIP_SHARED)
+ IF(WIN32 AND NOT CYGWIN AND MSVC)
+ # MSVC merges .manifest sources into the linker's default manifest
+ TARGET_SOURCES(bsdunzip PRIVATE bsdunzip.exe.manifest)
+ ENDIF(WIN32 AND NOT CYGWIN AND MSVC)
# Installation rules
IF(ENABLE_INSTALL)
diff --git a/unzip/bsdunzip.c b/unzip/bsdunzip.c
index 454d7f7bade2..ba63e41ef0c3 100644
--- a/unzip/bsdunzip.c
+++ b/unzip/bsdunzip.c
@@ -145,6 +145,7 @@ errorx(const char *fmt, ...)
exit(EXIT_FAILURE);
}
+#if defined(HAVE_LCHMOD) || defined(HAVE_UTIMENSAT) || defined(HAVE_LUTIMES)
/* non-fatal error message + errno */
static void
warning(const char *fmt, ...)
@@ -160,6 +161,7 @@ warning(const char *fmt, ...)
va_end(ap);
fprintf(stderr, ": %s\n", strerror(errno));
}
+#endif
/* non-fatal error message, no errno */
static void
@@ -326,9 +328,33 @@ accept_pathname(const char *pathname)
return (1);
}
+/* System call to create a directory. */
+static int
+system_mkdir(const char *pathname, int mode)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ (void)mode; /* UNUSED */
+ return _mkdir(pathname);
+#else
+ return mkdir(pathname, mode);
+#endif
+}
+
+static void
+system_unlink(const char *pathname) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (unlink(pathname) == -1) {
+ /* Windows treats directory symbolic links specially. */
+ rmdir(pathname);
+ }
+#else
+ (void)unlink(pathname);
+#endif
+}
+
/*
* Create the specified directory with the specified mode, taking certain
- * precautions on they way.
+ * precautions on the way.
*/
static void
make_dir(const char *path, int mode)
@@ -346,14 +372,9 @@ make_dir(const char *path, int mode)
* even compromise (if this non-directory happens to be a
* symlink to somewhere unsafe), so we don't.
*/
-
- /*
- * Don't check unlink() result; failure will cause mkdir()
- * to fail later, which we will catch.
- */
- (void)unlink(path);
+ system_unlink(path);
}
- if (mkdir(path, (mode_t)mode) != 0 && errno != EEXIST)
+ if (system_mkdir(path, (mode_t)mode) != 0 && errno != EEXIST)
error("mkdir('%s')", path);
}
@@ -378,10 +399,10 @@ make_parent(char *path)
*sep = '/';
return;
}
- unlink(path);
+ system_unlink(path);
}
make_parent(path);
- mkdir(path, 0755);
+ system_mkdir(path, 0755);
*sep = '/';
#if 0
@@ -458,7 +479,7 @@ handle_existing_file(char **path)
/* FALLTHROUGH */
case 'y':
case 'Y':
- (void)unlink(*path);
+ system_unlink(*path);
return 1;
case 'N':
n_opt = 1;
@@ -658,10 +679,10 @@ recheck:
#endif
))
return;
- (void)unlink(*path);
+ system_unlink(*path);
} else if (o_opt) {
/* overwrite */
- (void)unlink(*path);
+ system_unlink(*path);
} else if (n_opt) {
/* do not overwrite */
return;
@@ -738,6 +759,17 @@ recheck:
error("close('%s')", *path);
}
+static int
+pathname_is_insecure(const char* pathname)
+{
+ size_t len = strlen(pathname);
+ return (pathname[0] == '/' ||
+ strcmp(pathname, "..") == 0 ||
+ strncmp(pathname, "../", 3) == 0 ||
+ strstr(pathname, "/../") != NULL ||
+ (len >= 3 && strcmp(pathname + len - 3, "/..") == 0));
+}
+
/*
* Extract a zipfile entry: first perform some sanity checks to ensure
* that it is either a directory or a regular file and that the path is
@@ -757,6 +789,7 @@ static void
extract(struct archive *a, struct archive_entry *e)
{
char *pathname, *realpathname;
+ const char *linktarget;
mode_t filetype;
char *p, *q;
@@ -768,15 +801,22 @@ extract(struct archive *a, struct archive_entry *e)
filetype = archive_entry_filetype(e);
/* sanity checks */
- if (pathname[0] == '/' ||
- strncmp(pathname, "../", 3) == 0 ||
- strstr(pathname, "/../") != NULL) {
+ if (pathname_is_insecure(pathname)) {
warningx("skipping insecure entry '%s'", pathname);
ac(archive_read_data_skip(a));
free(pathname);
return;
}
+ if (S_ISLNK(filetype) &&
+ ((linktarget = archive_entry_symlink(e)) != NULL) &&
+ pathname_is_insecure(linktarget)) {
+ warningx("skipping insecure symlink '%s' to '%s'", pathname, linktarget);
+ ac(archive_read_data_skip(a));
+ free(pathname);
+ return;
+ }
+
/* I don't think this can happen in a zipfile.. */
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
warningx("skipping non-regular entry '%s'", pathname);
@@ -878,9 +918,9 @@ list(struct archive *a, struct archive_entry *e)
mtime = archive_entry_mtime(e);
tm = localtime(&mtime);
if (*y_str)
- strftime(buf, sizeof(buf), "%m-%d-%Y %R", tm);
+ strftime(buf, sizeof(buf), "%m-%d-%Y %H:%M", tm);
else
- strftime(buf, sizeof(buf), "%m-%d-%y %R", tm);
+ strftime(buf, sizeof(buf), "%m-%d-%y %H:%M", tm);
pathname = archive_entry_pathname(e);
if (!pathname)
diff --git a/unzip/bsdunzip.exe.manifest b/unzip/bsdunzip.exe.manifest
new file mode 100644
index 000000000000..32142e951c7b
--- /dev/null
+++ b/unzip/bsdunzip.exe.manifest
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
+ <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
+ <ws2:longPathAware>true</ws2:longPathAware>
+ </windowsSettings>
+ </application>
+</assembly>
diff --git a/unzip/bsdunzip.rc b/unzip/bsdunzip.rc
new file mode 100644
index 000000000000..6b4d9b502a43
--- /dev/null
+++ b/unzip/bsdunzip.rc
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Embed an application manifest that marks bsdunzip as long-path aware.
+ *
+ * bsdunzip's file name handling is limited to MAX_PATH (260) characters unless
+ * the process opts in to long paths via a "longPathAware" manifest.
+ * A system-wide LongPathsEnabled registry value is not sufficient on its own;
+ * the executable must also carry this manifest. Without it, extracting into
+ * deeply nested destination directories fails.
+ *
+ * See:
+ * https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation
+ */
+
+#define RT_MANIFEST 24
+#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "bsdunzip.exe.manifest"
diff --git a/unzip/bsdunzip_platform.h b/unzip/bsdunzip_platform.h
index f00d57981db2..f8753d2201ee 100644
--- a/unzip/bsdunzip_platform.h
+++ b/unzip/bsdunzip_platform.h
@@ -22,6 +22,10 @@
#include "config.h"
#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include "bsdunzip_windows.h"
+#endif
+
#ifdef HAVE_LIBARCHIVE
/* If we're using the platform libarchive, include system headers. */
#include <archive.h>
diff --git a/unzip/bsdunzip_windows.c b/unzip/bsdunzip_windows.c
new file mode 100644
index 000000000000..9ce87924d2f1
--- /dev/null
+++ b/unzip/bsdunzip_windows.c
@@ -0,0 +1,728 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2003-2007 Kees Zeelenberg
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2026 Tobias Stoeckmann
+ * All rights reserved.
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#include "bsdunzip_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#include <winnt.h>
+
+#ifndef S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif
+
+#define _S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC)
+#define _S_IXUSR _S_IEXEC /* read permission, user */
+#define _S_IWUSR _S_IWRITE /* write permission, user */
+#define _S_IRUSR _S_IREAD /* execute/search permission, user */
+#define _S_IRWXG (_S_IRWXU >> 3)
+#define _S_IXGRP (_S_IXUSR >> 3) /* read permission, group */
+#define _S_IWGRP (_S_IWUSR >> 3) /* write permission, group */
+#define _S_IRGRP (_S_IRUSR >> 3) /* execute/search permission, group */
+#define _S_IRWXO (_S_IRWXG >> 3)
+#define _S_IXOTH (_S_IXGRP >> 3) /* read permission, other */
+#define _S_IWOTH (_S_IWGRP >> 3) /* write permission, other */
+#define _S_IROTH (_S_IRGRP >> 3) /* execute/search permission, other */
+
+#ifndef S_IRWXU
+#define S_IRWXU _S_IRWXU
+#define S_IXUSR _S_IXUSR
+#define S_IWUSR _S_IWUSR
+#define S_IRUSR _S_IRUSR
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG _S_IRWXG
+#define S_IXGRP _S_IXGRP
+#define S_IWGRP _S_IWGRP
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP _S_IRGRP
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO _S_IRWXO
+#define S_IXOTH _S_IXOTH
+#define S_IWOTH _S_IWOTH
+#define S_IROTH _S_IROTH
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#endif
+
+#define NTFS_EPOC_TIME 11644473600ULL
+#define NTFS_TICKS 10000000ULL
+#define NTFS_EPOC_TICKS (NTFS_EPOC_TIME * NTFS_TICKS)
+#define WINTIME(sec, usec) (((sec * 10000000LL) + NTFS_EPOC_TICKS) + (usec * 10))
+
+static void unzip_dosmaperr(unsigned long);
+
+/*
+ * Convert the path name to unicode.
+ */
+static wchar_t *
+unzip_strtowcs(const char *name)
+{
+ wchar_t *wn;
+ DWORD l, len;
+
+ len = (DWORD)strlen(name);
+ wn = malloc((len + 1) * sizeof(wchar_t));
+ if (wn == NULL)
+ return (NULL);
+ l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
+ if (l == 0) {
+ free(wn);
+ errno = EILSEQ;
+ return (NULL);
+ }
+ wn[l] = L'\0';
+
+ return wn;
+}
+
+static wchar_t *
+unzip_win_permissive_name_w(const wchar_t *wname)
+{
+ wchar_t *wn, *wnp;
+ wchar_t *ws, *wsp;
+ DWORD l, len, slen;
+ int unc;
+
+ /* Get a full-pathname. */
+ l = GetFullPathNameW(wname, 0, NULL, NULL);
+ if (l == 0)
+ return (NULL);
+ /* NOTE: GetFullPathNameW has a bug that if the length of the file
+ * name is just 1 then it returns incomplete buffer size. Thus, we
+ * have to add three to the size to allocate a sufficient buffer
+ * size for the full-pathname of the file name. */
+ l += 3;
+ wnp = malloc(l * sizeof(wchar_t));
+ if (wnp == NULL)
+ return (NULL);
+ len = GetFullPathNameW(wname, l, wnp, NULL);
+ wn = wnp;
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'?' && wnp[3] == L'\\')
+ /* We have already a permissive name. */
+ return (wn);
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'.' && wnp[3] == L'\\') {
+ /* This is a device name */
+ if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+ (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+ wnp[5] == L':' && wnp[6] == L'\\')
+ wnp[2] = L'?';/* Not device name. */
+ return (wn);
+ }
+
+ unc = 0;
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+ wchar_t *p = &wnp[2];
+
+ /* Skip server-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\') {
+ wchar_t *rp = ++p;
+ /* Skip share-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\' && p != rp) {
+ /* Now, match patterns such as
+ * "\\server-name\share-name\" */
+ wnp += 2;
+ len -= 2;
+ unc = 1;
+ }
+ }
+ }
+
+ slen = 4 + (unc * 4) + len + 1;
+ ws = wsp = malloc(slen * sizeof(wchar_t));
+ if (ws == NULL) {
+ free(wn);
+ return (NULL);
+ }
+ /* prepend "\\?\" */
+ wcsncpy(wsp, L"\\\\?\\", 4);
+ wsp += 4;
+ slen -= 4;
+ if (unc) {
+ /* append "UNC\" ---> "\\?\UNC\" */
+ wcsncpy(wsp, L"UNC\\", 4);
+ wsp += 4;
+ slen -= 4;
+ }
+ wcsncpy(wsp, wnp, slen);
+ wsp[slen - 1] = L'\0'; /* Ensure null termination. */
+ free(wn);
+ return (ws);
+}
+
+/* Windows FILETIME to unix time. */
+static uint64_t
+unzip_FILETIME_to_unix(const FILETIME *filetime)
+{
+ ULARGE_INTEGER utc;
+ uint64_t ntfs, unix;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ ntfs = utc.QuadPart;
+
+ if (ntfs > INT64_MAX) {
+ ntfs -= NTFS_EPOC_TICKS;
+ unix = ntfs / NTFS_TICKS;
+ } else {
+ lldiv_t tdiv;
+ int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS;
+
+ tdiv = lldiv(value, NTFS_TICKS);
+ unix = tdiv.quot;
+ }
+ return unix;
+}
+
+static HANDLE
+unzip_get_handle(const wchar_t *path, int access, int open_link, int *is_link)
+{
+ HANDLE h;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+ WIN32_FIND_DATAW findData;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ h = FindFirstFileW(path, &findData);
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = unzip_win_permissive_name_w(path);
+ h = FindFirstFileW(full, &findData);
+ free(full);
+ }
+ if (h == INVALID_HANDLE_VALUE)
+ return (h);
+ FindClose(h);
+
+ /* Is symlink file? */
+ if (((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))) {
+ if (open_link)
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+ *is_link = 1;
+ } else
+ *is_link = 0;
+
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, access, 0, OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = unzip_win_permissive_name_w(path);
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ h = CreateFile2(full, access, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(full, access, 0, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ free(full);
+ }
+
+ return (h);
+}
+
+static int
+unzip_stat_w(const wchar_t *path, int stat_link, struct stat *st)
+{
+ HANDLE h;
+ int is_link, r;
+ DWORD ftype;
+ BY_HANDLE_FILE_INFORMATION info;
+ ULARGE_INTEGER ino64;
+
+ h = unzip_get_handle(path, 0, stat_link, &is_link);
+ if (h == INVALID_HANDLE_VALUE) {
+ unzip_dosmaperr(GetLastError());
+ return (-1);
+ }
+ ZeroMemory(&info, sizeof(info));
+ r = GetFileInformationByHandle(h, &info);
+
+ switch (ftype = GetFileType(h)) {
+ case FILE_TYPE_UNKNOWN:
+ errno = EBADF;
+ return (-1);
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_PIPE:
+ if (ftype == FILE_TYPE_CHAR) {
+ st->st_mode = S_IFCHR;
+ st->st_size = 0;
+ } else {
+ DWORD avail;
+
+ st->st_mode = S_IFIFO;
+ if (PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL))
+ st->st_size = avail;
+ else
+ st->st_size = 0;
+ }
+ st->st_atime = 0;
+ st->st_mtime = 0;
+ st->st_ctime = 0;
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ st->st_dev = 0;
+ return (0);
+ case FILE_TYPE_DISK:
+ break;
+ default:
+ /* This ftype is undocumented type. */
+ unzip_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ CloseHandle(h);
+ if (r == 0) {
+ unzip_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ st->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ st->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && is_link)
+ st->st_mode |= S_IFLNK;
+ else if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else {
+ const wchar_t *p;
+
+ st->st_mode |= S_IFREG;
+ p = wcsrchr(path, L'.');
+ if (p != NULL && wcslen(p) == 4) {
+ switch (p[1]) {
+ case L'B': case L'b':
+ if ((p[2] == L'A' || p[2] == L'a' ) &&
+ (p[3] == L'T' || p[3] == L't' ))
+ st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'C': case L'c':
+ if (((p[2] == L'M' || p[2] == L'm' ) &&
+ (p[3] == L'D' || p[3] == L'd' )))
+ st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'E': case L'e':
+ if ((p[2] == L'X' || p[2] == L'x' ) &&
+ (p[3] == L'E' || p[3] == L'e' ))
+ st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ st->st_atime = unzip_FILETIME_to_unix(&info.ftLastAccessTime);
+ st->st_mtime = unzip_FILETIME_to_unix(&info.ftLastWriteTime);
+ st->st_ctime = unzip_FILETIME_to_unix(&info.ftCreationTime);
+ st->st_size =
+ ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
+ + (int64_t)(info.nFileSizeLow);
+#ifdef SIMULATE_WIN_STAT
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_dev = 0;
+#else
+ /* Getting FileIndex as i-node. We should remove a sequence which
+ * is high-16-bits of nFileIndexHigh. */
+ ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
+ ino64.LowPart = info.nFileIndexLow;
+ st->st_ino = ino64.QuadPart;
+ st->st_nlink = info.nNumberOfLinks;
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ++st->st_nlink;/* Add parent directory. */
+ st->st_dev = info.dwVolumeSerialNumber;
+#endif
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+
+ return (0);
+}
+
+int
+lstat(const char *path, struct stat *st)
+{
+ wchar_t *wcs;
+ int r;
+
+ wcs = unzip_strtowcs(path);
+ if (wcs == NULL)
+ return (-1);
+
+ r = unzip_stat_w(wcs, 1, st);
+ free(wcs);
+ return r == 0 ? 0 : -1;
+}
+
+static int
+unzip_unlink(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wunlink(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = unzip_win_permissive_name_w(path);
+ r = _wunlink(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+static int
+unzip_rmdir(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wrmdir(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = unzip_win_permissive_name_w(path);
+ r = _wrmdir(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+/*
+ * Create file or directory symbolic link
+ *
+ * Guess linktype from the link target
+ */
+static int
+unzip_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) {
+ BOOL ret = 0;
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA ||\
+ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ (void)linkname; /* UNUSED */
+ (void)target; /* UNUSED */
+#else
+ struct stat st;
+ wchar_t *ttarget, *p;
+ size_t len;
+ DWORD attrs = 0;
+ DWORD flags = 0;
+ DWORD newflags = 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';
+
+ /* Check target type if available. */
+ if (unzip_stat_w(ttarget, 0, &st) == 0) {
+ if (S_ISDIR(st.st_mode)) {
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+ }
+ } else {
+ /*
+ * We guess symlink type 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 (*(p - 1) == L'\\' || (*(p - 1) == L'.' && (
+ len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && (
+ len == 2 || *(p - 3) == L'\\'))))) {
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+ }
+ }
+
+#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)
+ unzip_rmdir(linkname);
+ else
+ unzip_unlink(linkname);
+ }
+
+ ret = CreateSymbolicLinkW(linkname, ttarget, newflags);
+ /*
+ * Prior to Windows 10 calling CreateSymbolicLinkW() will fail
+ * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+ */
+ if (!ret) {
+ ret = CreateSymbolicLinkW(linkname, ttarget, flags);
+ }
+ free(ttarget);
+#endif
+ return (ret);
+}
+
+int
+symlink(const char *target, const char *source)
+{
+ wchar_t *wcsource, *wctarget;
+ int r;
+
+ wcsource = unzip_strtowcs(source);
+ wctarget = unzip_strtowcs(target);
+
+ if (wcsource != NULL && wctarget != NULL) {
+ r = unzip_CreateSymbolicLinkW(wcsource, wctarget);
+ if (r == 0)
+ unzip_dosmaperr(GetLastError());
+ } else {
+ errno = ENOMEM;
+ r = 0;
+ }
+ free(wcsource);
+ free(wctarget);
+ return r == 0 ? -1 : 0;
+}
+
+static int
+unzip_set_times(HANDLE handle, const struct timeval *times)
+{
+ ULARGE_INTEGER wintm;
+ FILETIME fatime, fmtime;
+
+ wintm.QuadPart = WINTIME(times[0].tv_sec, times[0].tv_usec);
+ fatime.dwLowDateTime = wintm.LowPart;
+ fatime.dwHighDateTime = wintm.HighPart;
+
+ wintm.QuadPart = WINTIME(times[1].tv_sec, times[1].tv_usec);
+ fmtime.dwLowDateTime = wintm.LowPart;
+ fmtime.dwHighDateTime = wintm.HighPart;
+
+ if (SetFileTime(handle, NULL, &fatime, &fmtime) == 0) {
+ unzip_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+futimes(int fd, const struct timeval *times)
+{
+ return (unzip_set_times((HANDLE)_get_osfhandle(fd), times));
+}
+
+#ifndef HAVE_GETTIMEOFDAY
+int
+gettimeofday(struct timeval *tv, struct timezone *tz) {
+ FILETIME ft;
+ uint64_t time;
+
+ (void)tz; /* UNUSED */
+
+ GetSystemTimePreciseAsFileTime(&ft);
+ time = unzip_FILETIME_to_unix(&ft);
+
+ tv->tv_sec = (long)(time / NTFS_TICKS);
+ tv->tv_usec = (long)((time % NTFS_TICKS) / 10ULL);
+
+ return (0);
+}
+#endif
+
+int
+lutimes(const char *path, const struct timeval *times)
+{
+ HANDLE h;
+ wchar_t *p;
+ int link, ret;
+
+ p = unzip_strtowcs(path);
+ if (p == NULL)
+ return (-1);
+
+ h = unzip_get_handle(p, FILE_WRITE_ATTRIBUTES, 1, &link);
+ ret = unzip_set_times(h, times);
+
+ free(p);
+ CloseHandle(h);
+
+ return (ret);
+}
+
+/*
+ * The following function was modified from PostgreSQL sources and is
+ * subject to the copyright below.
+ */
+/*-------------------------------------------------------------------------
+ *
+ * win32error.c
+ * Map win32 error codes to errno values
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*/
+
+static const struct {
+ DWORD winerr;
+ int doserr;
+} doserrors[] =
+{
+ { ERROR_INVALID_FUNCTION, EINVAL },
+ { ERROR_FILE_NOT_FOUND, ENOENT },
+ { ERROR_PATH_NOT_FOUND, ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
+ { ERROR_ACCESS_DENIED, EACCES },
+ { ERROR_INVALID_HANDLE, EBADF },
+ { ERROR_ARENA_TRASHED, ENOMEM },
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
+ { ERROR_INVALID_BLOCK, ENOMEM },
+ { ERROR_BAD_ENVIRONMENT, E2BIG },
+ { ERROR_BAD_FORMAT, ENOEXEC },
+ { ERROR_INVALID_ACCESS, EINVAL },
+ { ERROR_INVALID_DATA, EINVAL },
+ { ERROR_INVALID_DRIVE, ENOENT },
+ { ERROR_CURRENT_DIRECTORY, EACCES },
+ { ERROR_NOT_SAME_DEVICE, EXDEV },
+ { ERROR_NO_MORE_FILES, ENOENT },
+ { ERROR_LOCK_VIOLATION, EACCES },
+ { ERROR_SHARING_VIOLATION, EACCES },
+ { ERROR_BAD_NETPATH, ENOENT },
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES },
+ { ERROR_BAD_NET_NAME, ENOENT },
+ { ERROR_FILE_EXISTS, EEXIST },
+ { ERROR_CANNOT_MAKE, EACCES },
+ { ERROR_FAIL_I24, EACCES },
+ { ERROR_INVALID_PARAMETER, EINVAL },
+ { ERROR_NO_PROC_SLOTS, EAGAIN },
+ { ERROR_DRIVE_LOCKED, EACCES },
+ { ERROR_BROKEN_PIPE, EPIPE },
+ { ERROR_DISK_FULL, ENOSPC },
+ { ERROR_INVALID_TARGET_HANDLE, EBADF },
+ { ERROR_INVALID_HANDLE, EINVAL },
+ { ERROR_WAIT_NO_CHILDREN, ECHILD },
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD },
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
+ { ERROR_NEGATIVE_SEEK, EINVAL },
+ { ERROR_SEEK_ON_DEVICE, EACCES },
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
+ { ERROR_NOT_LOCKED, EACCES },
+ { ERROR_BAD_PATHNAME, ENOENT },
+ { ERROR_MAX_THRDS_REACHED, EAGAIN },
+ { ERROR_LOCK_FAILED, EACCES },
+ { ERROR_ALREADY_EXISTS, EEXIST },
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT },
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
+ { ERROR_PRIVILEGE_NOT_HELD, EPERM },
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
+};
+
+static void
+unzip_dosmaperr(unsigned long e)
+{
+ int i;
+
+ if (e == 0)
+ {
+ errno = 0;
+ return;
+ }
+
+ for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
+ {
+ if (doserrors[i].winerr == e)
+ {
+ errno = doserrors[i].doserr;
+ return;
+ }
+ }
+
+ /* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
+ errno = EINVAL;
+ return;
+}
+
+#endif
diff --git a/unzip/bsdunzip_windows.h b/unzip/bsdunzip_windows.h
new file mode 100644
index 000000000000..a524496db321
--- /dev/null
+++ b/unzip/bsdunzip_windows.h
@@ -0,0 +1,61 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2003-2007 Kees Zeelenberg
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2026 Tobias Stoeckmann
+ * All rights reserved.
+ */
+#ifndef BSDUNZIP_WINDOWS_H
+#define BSDUNZIP_WINDOWS_H 1
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#ifndef _S_IFLNK
+#define _S_IFLNK 0120000 /* symbolic link */
+#endif
+#ifndef S_IFLNK
+#define S_IFLNK _S_IFLNK
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* directory */
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */
+#endif
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* symbolic link */
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#define HAVE_FUTIMES 1
+#define HAVE_LUTIMES 1
+
+#ifndef HAVE_GETTIMEOFDAY
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+int gettimeofday(struct timeval *tv, struct timezone *tz);
+#endif
+
+int futimes(int fd, const struct timeval *times);
+int lstat(const char *path, struct stat *st);
+int lutimes(const char *path, const struct timeval *times);
+int symlink(const char *target, const char *source);
+
+#endif /* !BSDUNZIP_WINDOWS_H */
diff --git a/unzip/test/CMakeLists.txt b/unzip/test/CMakeLists.txt
index c2bbfa1b27bf..a90a1e16b29d 100644
--- a/unzip/test/CMakeLists.txt
+++ b/unzip/test/CMakeLists.txt
@@ -15,9 +15,12 @@ IF(ENABLE_UNZIP AND ENABLE_TEST)
test_glob.c
test_not_exist.c
test_singlefile.c
+ test_symlink.c
+ test_symlink_dotdot.c
test_C.c
test_p.c
test_d.c
+ test_f.c
test_j.c
test_L.c
test_n.c
@@ -25,6 +28,7 @@ IF(ENABLE_UNZIP AND ENABLE_TEST)
test_q.c
test_t.c
test_t_bad.c
+ test_u.c
test_version.c
test_x.c
test_Z1.c
diff --git a/unzip/test/test_I.c b/unzip/test/test_I.c
index 2a656012f0a8..e3721402f956 100644
--- a/unzip/test/test_I.c
+++ b/unzip/test/test_I.c
@@ -13,13 +13,10 @@
/* Test I arg - file name encoding */
DEFINE_TEST(test_I)
{
- const char *reffile = "test_I.zip";
#if !defined(_WIN32) || defined(__CYGWIN__)
+ const char *reffile = "test_I.zip";
const char *envstr = "env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 "
"LC_CTYPE=en_US.UTF-8";
-#else
- const char *envstr = "";
-#endif
int r;
#if HAVE_SETLOCALE
@@ -39,4 +36,7 @@ DEFINE_TEST(test_I)
assertEmptyFile("test.err");
assertTextFileContents("Hello, World!\n", "Γειά σου Κόσμε.txt");
+#else
+ skipping("Windows not supported");
+#endif
}
diff --git a/unzip/test/test_f.c b/unzip/test/test_f.c
new file mode 100644
index 000000000000..2b71955b634c
--- /dev/null
+++ b/unzip/test/test_f.c
@@ -0,0 +1,39 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Tobias Stoeckmann
+ * All rights reserved.
+ */
+#include "test.h"
+
+/* Test f arg - don't create new files or overwrite newer files */
+DEFINE_TEST(test_f)
+{
+ const char *reffile1 = "test_f_new.zip";
+ const char *reffile2 = "test_f_old.zip";
+ int r;
+
+ extract_reference_file(reffile1);
+ extract_reference_file(reffile2);
+
+ /* First run with -f should not extract: test.txt does not exist */
+ r = systemf("%s -f %s >test.out 2>test.err", testprog, reffile1);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertFileNotExists("test.txt");
+
+ /* Extract archive */
+ r = systemf("%s %s >test.out 2>test.err", testprog, reffile1);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertTextFileContents("world\n", "test.txt");
+
+ /* Second run with -f should not overwrite newer file */
+ r = systemf("%s -f %s >test.out 2>test.err", testprog, reffile2);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertTextFileContents("world\n", "test.txt");
+}
diff --git a/unzip/test/test_f_new.zip.uu b/unzip/test/test_f_new.zip.uu
new file mode 100644
index 000000000000..b092714094d4
--- /dev/null
+++ b/unzip/test/test_f_new.zip.uu
@@ -0,0 +1,8 @@
+begin 644 test_f_new.zip
+M4$L#!`H`"````(6KUEP````````````````(`"``=&5S="YT>'1U>`L``03H
+M`P``!.@#``!55`T`!\N,.6K+C#EJRXPY:G=O<FQD"E!+!PBH83C=!@````8`
+M``!02P$""@,*``@```"%J]9<J&$XW08````&````"``8````````````I($`
+M````=&5S="YT>'1U>`L``03H`P``!.@#``!55`4``<N,.6I02P4&``````$`
+,`0!.````7```````
+`
+end
diff --git a/unzip/test/test_f_old.zip.uu b/unzip/test/test_f_old.zip.uu
new file mode 100644
index 000000000000..34cdd9338521
--- /dev/null
+++ b/unzip/test/test_f_old.zip.uu
@@ -0,0 +1,8 @@
+begin 644 test_f_old.zip
+M4$L#!`H`"````'&KUEP````````````````(`"``=&5S="YT>'1U>`L``03H
+M`P``!.@#``!55`T`!Z>,.6JGC#EJIXPY:FAE;&QO"E!+!P@@,#HV!@````8`
+M``!02P$""@,*``@```!QJ]9<(#`Z-@8````&````"``8````````````I($`
+M````=&5S="YT>'1U>`L``03H`P``!.@#``!55`4``:>,.6I02P4&``````$`
+,`0!.````7```````
+`
+end
diff --git a/unzip/test/test_symlink.c b/unzip/test/test_symlink.c
new file mode 100644
index 000000000000..5afcf9d610a3
--- /dev/null
+++ b/unzip/test/test_symlink.c
@@ -0,0 +1,53 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Tobias Stoeckmann
+ * All rights reserved.
+ */
+#include "test.h"
+
+/* Test symlinks */
+DEFINE_TEST(test_symlink)
+{
+ const char *reffile1 = "test_symlink_1.zip";
+ const char *reffile2 = "test_symlink_2.zip";
+ int r;
+
+ if (!canSymlink()) {
+ skipping("System cannot create symlinks.");
+ return;
+ }
+
+ assertUmask(0);
+ extract_reference_file(reffile1);
+ extract_reference_file(reffile2);
+
+ /* Extract first archive */
+ r = systemf("%s %s >test.out 2>test.err", testprog, reffile1);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertNonEmptyFile("test.err");
+ assertIsDir("dir", 0755);
+ assertIsSymlink("dlink", "dir", 1);
+ assertIsReg("file.txt", 0644);
+ assertIsSymlink("flink", "file.txt", 0);
+ assertFileNotExists("link.insecure");
+
+ /* Create dangling symlinks by deleting targets */
+ rmdir("dir");
+ unlink("file.txt");
+ assertFileNotExists("dir");
+ assertFileNotExists("file.txt");
+ assertIsSymlink("dlink", "dir", 1);
+ assertIsSymlink("flink", "file.txt", 0);
+
+ /* Extract second archive, overwriting dangling symlinks */
+ r = systemf("%s -o %s >test.out 2>test.err", testprog, reffile2);
+ assertEqualInt(0, r);
+ assertEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertFileNotExists("dir");
+ assertIsDir("dlink", 0755);
+ assertTextFileContents("hello world\n", "dlink/file.txt");
+ assertIsReg("flink", 0644);
+}
diff --git a/unzip/test/test_symlink_1.zip.uu b/unzip/test/test_symlink_1.zip.uu
new file mode 100644
index 000000000000..60b9a69c57fc
--- /dev/null
+++ b/unzip/test/test_symlink_1.zip.uu
@@ -0,0 +1,21 @@
+begin 644 test_symlink_1.zip
+M4$L#!!0``````)&NUEP````````````````$`"``9&ER+W5X"P`!!.@#```$
+MZ`,``%54#0`'@I(Y:H*2.6J"DCEJ4$L#!!0``````)"NUEP0>JNZ`P````,`
+M```%`"``9&QI;FMU>`L``03H`P``!.@#``!55`T`!X"2.6J`DCEJ@)(Y:F1I
+M<E!+`P0*``@```"5KM9<````````````````"``@`&9I;&4N='AT=7@+``$$
+MZ`,```3H`P``550-``>+DCEJBY(Y:HN2.6I02P<(````````````````4$L#
+M!!0``````)>NUEPE%O?@"`````@````%`"``9FQI;FMU>`L``03H`P``!.@#
+M``!55`T`!X^2.6J/DCEJCY(Y:F9I;&4N='AT4$L#!!0``````(RNUEP&4/WP
+M"@````H````-`"``:6YS96-U<F4N;&EN:W5X"P`!!.@#```$Z`,``%54#0`'
+M>9(Y:GF2.6IYDCEJ+BXO;W5T<VED95!+`0(4`Q0``````)&NUEP`````````
+M```````$`!@```````````#M00````!D:7(O=7@+``$$Z`,```3H`P``550%
+M``&"DCEJ4$L!`A0#%```````D*[67!!ZJ[H#`````P````4`&```````````
+M`/^A0@```&1L:6YK=7@+``$$Z`,```3H`P``550%``&`DCEJ4$L!`@H#"@`(
+M````E:[67`````````````````@`&````````````*2!B````&9I;&4N='AT
+M=7@+``$$Z`,```3H`P``550%``&+DCEJ4$L!`A0#%```````EZ[67"46]^`(
+M````"`````4`&````````````/^AW@```&9L:6YK=7@+``$$Z`,```3H`P``
+M550%``&/DCEJ4$L!`A0#%```````C*[67`90_?`*````"@````T`&```````
+M`````/^A*0$``&EN<V5C=7)E+FQI;FMU>`L``03H`P``!.@#``!55`4``7F2
+8.6I02P4&``````4`!0"!`0``?@$`````
+`
+end
diff --git a/unzip/test/test_symlink_2.zip.uu b/unzip/test/test_symlink_2.zip.uu
new file mode 100644
index 000000000000..1cc92d70ab4e
--- /dev/null
+++ b/unzip/test/test_symlink_2.zip.uu
@@ -0,0 +1,12 @@
+begin 644 test_symlink_2.zip
+M4$L#!`H`"````,NNUEP````````````````.`"``9&QI;FLO9FEL92YT>'1U
+M>`L``03H`P``!.@#``!55`T`!^^2.6KODCEJ[Y(Y:FAE;&QO('=O<FQD"E!+
+M!P@M.PBO#`````P```!02P,$"@`(````V*[67`````````````````4`(`!F
+M;&EN:W5X"P`!!.@#```$Z`,``%54#0`'"9,Y:A>3.6H)DSEJ8V]N=&5N="!F
+M;&EN:PI02P<(%P(`(PX````.````4$L!`@H#"@`(````RZ[67"T["*\,````
+M#`````X`&````````````*2!`````&1L:6YK+V9I;&4N='AT=7@+``$$Z`,`
+M``3H`P``550%``'ODCEJ4$L!`@H#"@`(````V*[67!<"`",.````#@````4`
+M&````````````*2!:````&9L:6YK=7@+``$$Z`,```3H`P``550%``$)DSEJ
+64$L%!@`````"``(`GP```,D`````````
+`
+end
diff --git a/unzip/test/test_symlink_dotdot.c b/unzip/test/test_symlink_dotdot.c
new file mode 100644
index 000000000000..2e975220699f
--- /dev/null
+++ b/unzip/test/test_symlink_dotdot.c
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Kaif Khan
+ * All rights reserved.
+ */
+#include "test.h"
+
+/*
+ * A symlink whose target is a bare ".." (or a path ending in "/..")
+ * escapes the extraction directory just like "../" and "/../" do, so it
+ * must be rejected as insecure.
+ */
+DEFINE_TEST(test_symlink_dotdot)
+{
+ const char *reffile = "test_symlink_dotdot.zip";
+ int r;
+
+ if (!canSymlink()) {
+ skipping("System cannot create symlinks.");
+ return;
+ }
+
+ assertUmask(0);
+ extract_reference_file(reffile);
+
+ r = systemf("%s %s >test.out 2>test.err", testprog, reffile);
+ assertEqualInt(0, r);
+
+ /* The two insecure symlinks must be skipped, not created. */
+ assertFileNotExists("dotdot");
+ assertFileNotExists("traildot");
+
+ /* A regular entry in the same archive is still extracted. */
+ assertIsReg("safe.txt", 0644);
+}
diff --git a/unzip/test/test_symlink_dotdot.zip.uu b/unzip/test/test_symlink_dotdot.zip.uu
new file mode 100644
index 000000000000..fd7fa27ff114
--- /dev/null
+++ b/unzip/test/test_symlink_dotdot.zip.uu
@@ -0,0 +1,10 @@
+begin 644 test_symlink_dotdot.zip
+M4$L#!!0`````````(0`<%@B6`@````(````&````9&]T9&]T+BY02P,$%```
+M```````A`,6+J7@&````!@````@```!T<F%I;&1O='-U8B\N+E!+`P04````
+M`````"$`?0X6V@,````#````"````'-A9F4N='AT;VL*4$L!`A0#%```````
+M```A`!P6")8"`````@````8``````````````/^A`````&1O=&1O=%!+`0(4
+M`Q0`````````(0#%BZEX!@````8````(``````````````#_H28```!T<F%I
+M;&1O=%!+`0(4`Q0`````````(0!]#A;:`P````,````(``````````````"D
+C@5(```!S869E+G1X=%!+!08``````P`#`*````![````````
+`
+end
diff --git a/unzip/test/test_u.c b/unzip/test/test_u.c
new file mode 100644
index 000000000000..7aca312cafd8
--- /dev/null
+++ b/unzip/test/test_u.c
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Tobias Stoeckmann
+ * All rights reserved.
+ */
+#include "test.h"
+
+/* Test u arg - don't overwrite newer files */
+DEFINE_TEST(test_u)
+{
+ const char *reffile1 = "test_u_new.zip";
+ const char *reffile2 = "test_u_old.zip";
+ int r;
+
+ extract_reference_file(reffile1);
+ extract_reference_file(reffile2);
+
+ /* First run with -u should create file */
+ r = systemf("%s -u %s >test.out 2>test.err", testprog, reffile1);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertTextFileContents("world\n", "test.txt");
+
+ /* Second run with -u should not overwrite newer file */
+ r = systemf("%s -u %s >test.out 2>test.err", testprog, reffile2);
+ assertEqualInt(0, r);
+ assertNonEmptyFile("test.out");
+ assertEmptyFile("test.err");
+ assertTextFileContents("world\n", "test.txt");
+}
diff --git a/unzip/test/test_u_new.zip.uu b/unzip/test/test_u_new.zip.uu
new file mode 100644
index 000000000000..b092714094d4
--- /dev/null
+++ b/unzip/test/test_u_new.zip.uu
@@ -0,0 +1,8 @@
+begin 644 test_f_new.zip
+M4$L#!`H`"````(6KUEP````````````````(`"``=&5S="YT>'1U>`L``03H
+M`P``!.@#``!55`T`!\N,.6K+C#EJRXPY:G=O<FQD"E!+!PBH83C=!@````8`
+M``!02P$""@,*``@```"%J]9<J&$XW08````&````"``8````````````I($`
+M````=&5S="YT>'1U>`L``03H`P``!.@#``!55`4``<N,.6I02P4&``````$`
+,`0!.````7```````
+`
+end
diff --git a/unzip/test/test_u_old.zip.uu b/unzip/test/test_u_old.zip.uu
new file mode 100644
index 000000000000..34cdd9338521
--- /dev/null
+++ b/unzip/test/test_u_old.zip.uu
@@ -0,0 +1,8 @@
+begin 644 test_f_old.zip
+M4$L#!`H`"````'&KUEP````````````````(`"``=&5S="YT>'1U>`L``03H
+M`P``!.@#``!55`T`!Z>,.6JGC#EJIXPY:FAE;&QO"E!+!P@@,#HV!@````8`
+M``!02P$""@,*``@```!QJ]9<(#`Z-@8````&````"``8````````````I($`
+M````=&5S="YT>'1U>`L``03H`P``!.@#``!55`4``:>,.6I02P4&``````$`
+,`0!.````7```````
+`
+end