diff --git a/.cirrus.yml b/.cirrus.yml index d87e48983e8e..25c256113e62 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -10,9 +10,9 @@ FreeBSD_task: BS: cmake matrix: freebsd_instance: - image: freebsd-12-0-release-amd64 + image_family: freebsd-12-1 freebsd_instance: - image: freebsd-11-2-release-amd64 + image: freebsd-11-3-stable-amd64-v20190801 prepare_script: - ./build/ci/cirrus_ci/ci.sh prepare configure_script: @@ -26,28 +26,6 @@ FreeBSD_task: install_script: - ./build/ci/build.sh -a install -MacOS_task: - matrix: - env: - BS: autotools - env: - BS: cmake - matrix: - osx_instance: - image: mojave-xcode-10.2 - prepare_script: - - ./build/ci/cirrus_ci/ci.sh prepare - configure_script: - - ./build/ci/build.sh -a autogen - - ./build/ci/build.sh -a configure - build_script: - - ./build/ci/build.sh -a build - test_script: - - ./build/ci/build.sh -a test - - ./build/ci/cirrus_ci/ci.sh test - install_script: - - ./build/ci/build.sh -a install - Fedora_30_task: container: dockerfile: build/ci/cirrus_ci/Dockerfile.fc30 @@ -66,51 +44,6 @@ Fedora_30_task: install_script: - ./build/ci/build.sh -a install -Fedora_30_distcheck_task: - container: - dockerfile: build/ci/cirrus_ci/Dockerfile.fc30.distcheck - env: - BS: autotools - configure_script: - - ./build/ci/build.sh -a autogen - - ./build/ci/build.sh -a configure - distcheck_script: - - ./build/ci/build.sh -a distcheck - -Windows_MSVC_task: - windows_container: - dockerfile: build/ci/cirrus_ci/Dockerfile.msvc - os_version: 2019 - env: - BE: msvc - configure_script: - - build\ci\cirrus_ci\ci.cmd configure - build_script: - - build\ci\cirrus_ci\ci.cmd build - test_script: - - build\ci\cirrus_ci\ci.cmd test - instal_script: - - build\ci\cirrus_ci\ci.cmd install - -Windows_MinGW_task: - windows_container: - image: cirrusci/windowsservercore:2019 - os_version: 2019 - env: - BE: mingw-gcc - prepare_script: - - build\ci\cirrus_ci\ci.cmd prepare - deplibs_script: - - build\ci\cirrus_ci\ci.cmd deplibs - configure_script: - - build\ci\cirrus_ci\ci.cmd configure - build_script: - - build\ci\cirrus_ci\ci.cmd build - test_script: - - build\ci\cirrus_ci\ci.cmd test - install_script: - - build\ci\cirrus_ci\ci.cmd install - Windows_Cygwin_task: windows_container: image: cirrusci/windowsservercore:2019 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11fa1b6b8464..7d5ba4fa4d80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,44 @@ -name: Ubuntu +name: CI on: [push, pull_request] jobs: - Build-and-test: - - runs-on: ubuntu-latest - + MacOS: + runs-on: macos-latest + strategy: + matrix: + bs: [autotools, cmake] + steps: + - uses: actions/checkout@master + - name: Install dependencies + run: ./build/ci/github_actions/macos.sh prepare + - name: Autogen + run: ./build/ci/build.sh -a autogen + env: + BS: ${{ matrix.bs }} + - name: Configure + run: ./build/ci/build.sh -a configure + env: + BS: ${{ matrix.bs }} + - name: Build + run: ./build/ci/build.sh -a build + env: + BS: ${{ matrix.bs }} + - name: Test + run: ./build/ci/build.sh -a test + env: + BS: ${{ matrix.bs }} + SKIP_OPEN_FD_ERR_TEST: 1 + - name: Install + run: ./build/ci/build.sh -a install + env: + BS: ${{ matrix.bs }} + + Ubuntu: + runs-on: ubuntu-latest strategy: matrix: bs: [autotools, cmake] - steps: - uses: actions/checkout@master - name: Install dependencies @@ -31,7 +59,56 @@ jobs: run: ./build/ci/build.sh -a test env: BS: ${{ matrix.bs }} + SKIP_OPEN_FD_ERR_TEST: 1 - name: Install run: ./build/ci/build.sh -a install env: BS: ${{ matrix.bs }} + + Ubuntu-distcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install dependencies + run: sudo apt-get install -y build-essential cmake libssl-dev libacl1-dev libbz2-dev liblzma-dev libzip-dev liblz4-dev libzstd-dev lzop groff ghostscript + - name: Autogen + run: ./build/ci/build.sh -a autogen + - name: Configure + run: ./build/ci/build.sh -a configure + - name: Distcheck + run: ./build/ci/build.sh -a distcheck + env: + SKIP_OPEN_FD_ERR_TEST: 1 + + Windows: + runs-on: windows-latest + strategy: + matrix: + be: [mingw-gcc, msvc] + steps: + - uses: actions/checkout@master + - name: Install dependencies + run: ./build/ci/github_actions/ci.cmd deplibs + shell: cmd + env: + BE: ${{ matrix.be }} + - name: Configure + run: ./build/ci/github_actions/ci.cmd configure + shell: cmd + env: + BE: ${{ matrix.be }} + - name: Build + run: ./build/ci/github_actions/ci.cmd build + shell: cmd + env: + BE: ${{ matrix.be }} + - name: Test + run: ./build/ci/github_actions/ci.cmd test + shell: cmd + env: + BE: ${{ matrix.be }} + - name: Install + run: ./build/ci/github_actions/ci.cmd install + shell: cmd + env: + BE: ${{ matrix.be }} diff --git a/Makefile.am b/Makefile.am index 20eb5312e275..781bbf726c2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -429,6 +429,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_open_file.c \ 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_large.c \ libarchive/test/test_read_disk.c \ libarchive/test/test_read_disk_directory_traversals.c \ @@ -448,6 +449,7 @@ 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_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 \ @@ -488,6 +490,7 @@ libarchive_test_SOURCES= \ 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_mtree.c \ libarchive/test/test_read_format_mtree_crash747.c \ libarchive/test/test_read_format_pax_bz2.c \ @@ -701,6 +704,9 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_fuzz.lzh.uu \ libarchive/test/test_fuzz_1.iso.Z.uu \ libarchive/test/test_pax_filename_encoding.tar.uu \ + libarchive/test/test_pax_xattr_header_all.tar.uu \ + libarchive/test/test_pax_xattr_header_libarchive.tar.uu \ + libarchive/test/test_pax_xattr_header_schily.tar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part1.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part2.rar.uu \ libarchive/test/test_rar_multivolume_multiple_files.part3.rar.uu \ @@ -757,6 +763,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_7zip_lzma2.7z.uu \ libarchive/test/test_read_format_7zip_malformed.7z.uu \ libarchive/test/test_read_format_7zip_malformed2.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_symbolic_name.7z.uu \ libarchive/test/test_read_format_ar.ar.uu \ @@ -798,6 +805,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_iso_zisofs.iso.Z.uu \ libarchive/test/test_read_format_lha_bugfix_0.lzh.uu \ libarchive/test/test_read_format_lha_filename_cp932.lzh.uu \ + libarchive/test/test_read_format_lha_filename_utf16.lzh.uu \ libarchive/test/test_read_format_lha_header0.lzh.uu \ libarchive/test/test_read_format_lha_header1.lzh.uu \ libarchive/test/test_read_format_lha_header2.lzh.uu \ @@ -835,6 +843,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_blake2.rar.uu \ libarchive/test/test_read_format_rar5_compressed.rar.uu \ libarchive/test/test_read_format_rar5_different_window_size.rar.uu \ + libarchive/test/test_read_format_rar5_different_solid_window_size.rar.uu \ libarchive/test/test_read_format_rar5_distance_overflow.rar.uu \ libarchive/test/test_read_format_rar5_extra_field_version.rar.uu \ libarchive/test/test_read_format_rar5_fileattr.rar.uu \ @@ -866,6 +875,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_truncated_huff.rar.uu \ libarchive/test/test_read_format_rar5_win32.rar.uu \ libarchive/test/test_read_format_rar5_arm_filter_on_window_boundary.rar.uu \ + libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.gz.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ diff --git a/build/ci/cirrus_ci/Dockerfile.fc30.distcheck b/build/ci/cirrus_ci/Dockerfile.fc30.distcheck index b59044be37a8..0129ec44e92a 100644 --- a/build/ci/cirrus_ci/Dockerfile.fc30.distcheck +++ b/build/ci/cirrus_ci/Dockerfile.fc30.distcheck @@ -1,3 +1,3 @@ FROM fedora:30 -RUN dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison sharutils pkgconf libacl-devel libasan librichacl-devel bzip2-devel libzip-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel groff ghostscript +RUN dnf -y install make cmake gcc gcc-c++ kernel-devel automake libtool bison sharutils pkgconf libacl-devel libasan librichacl-devel bzip2-devel libzip-devel zlib-devel xz-devel lz4-devel libzstd-devel openssl-devel groff ghostscript xz zip diff --git a/build/ci/github_actions/ci.cmd b/build/ci/github_actions/ci.cmd new file mode 100755 index 000000000000..30626d5c2a40 --- /dev/null +++ b/build/ci/github_actions/ci.cmd @@ -0,0 +1,79 @@ +@ECHO OFF +SET ZLIB_VERSION=1.2.11 +IF NOT "%BE%"=="mingw-gcc" ( + IF NOT "%BE%"=="msvc" ( + ECHO Environment variable BE must be mingw-gcc or msvc + EXIT /b 1 + ) +) + +IF "%1"=="deplibs" ( + 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 + ) + CD zlib-%ZLIB_VERSION% + IF "%BE%"=="mingw-gcc" ( + SET PATH=C:\Program Files\cmake\bin;C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin + 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 16 2019" . || 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%"=="mingw-gcc" ( + SET 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" ( + MKDIR build_ci\cmake + CD build_ci\cmake + cmake -G "Visual Studio 16 2019" -D CMAKE_BUILD_TYPE="Release" ..\.. || EXIT /b 1 + ) +) ELSE IF "%1%"=="build" ( + IF "%BE%"=="mingw-gcc" ( + SET 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" ( + CD build_ci\cmake + cmake --build . --target ALL_BUILD --config Release + ) +) ELSE IF "%1%"=="test" ( + IF "%BE%"=="mingw-gcc" ( + SET 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" ( + 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%"=="mingw-gcc" ( + SET 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" ( + cmake --build . --target INSTALL --config Release + ) +) ELSE ( + ECHO "Usage: %0% deplibs|configure|build|test|install" + @EXIT /b 0 +) +@EXIT /b 0 diff --git a/build/ci/github_actions/macos.sh b/build/ci/github_actions/macos.sh new file mode 100755 index 000000000000..e51574cb6dfa --- /dev/null +++ b/build/ci/github_actions/macos.sh @@ -0,0 +1,10 @@ +#!/bin/sh +if [ "$1" = "prepare" ] +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 +fi diff --git a/configure.ac b/configure.ac index d77143e3fbc4..c7b1a5943e1e 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ AC_CONFIG_AUX_DIR([build/autoconf]) # M4 scripts AC_CONFIG_MACRO_DIR([build/autoconf]) # Must follow AC_CONFIG macros above... -AM_INIT_AUTOMAKE() +AM_INIT_AUTOMAKE([1.11 dist-xz dist-zip]) AM_MAINTAINER_MODE([enable]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/examples/minitar/minitar.c b/examples/minitar/minitar.c index 0b4b4147ca99..881f94b2d33e 100644 --- a/examples/minitar/minitar.c +++ b/examples/minitar/minitar.c @@ -398,6 +398,9 @@ extract(const char *filename, int do_extract, int flags) } archive_read_close(a); archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); exit(0); } diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index a1989ffa80f8..b7b3498a2035 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -524,9 +524,6 @@ __LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type __LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */, int * /* type */, int * /* permset */, int * /* tag */, int * /* qual */, const char ** /* name */); -__LA_DECL int archive_entry_acl_next_w(struct archive_entry *, int /* want_type */, - int * /* type */, int * /* permset */, int * /* tag */, - int * /* qual */, const wchar_t ** /* name */); /* * Construct a text-format ACL. The flags argument is a bitmask that diff --git a/libarchive/archive_entry_acl.3 b/libarchive/archive_entry_acl.3 index 7dcc5854ce10..50dd642c20c6 100644 --- a/libarchive/archive_entry_acl.3 +++ b/libarchive/archive_entry_acl.3 @@ -34,7 +34,6 @@ .Nm archive_entry_acl_from_text , .Nm archive_entry_acl_from_text_w , .Nm archive_entry_acl_next , -.Nm archive_entry_acl_next_w , .Nm archive_entry_acl_reset , .Nm archive_entry_acl_to_text , .Nm archive_entry_acl_to_text_w , @@ -89,16 +88,6 @@ Streaming Archive Library (libarchive, -larchive) .Fa "const char **ret_name" .Fc .Ft int -.Fo archive_entry_acl_next_w -.Fa "struct archive_entry *a" -.Fa "int type" -.Fa "int *ret_type" -.Fa "int *ret_permset" -.Fa "int *ret_tag" -.Fa "int *ret_qual" -.Fa "const wchar_t **ret_name" -.Fc -.Ft int .Fn archive_entry_acl_reset "struct archive_entry *a" "int type" .Ft char * .Fo archive_entry_acl_to_text @@ -349,8 +338,6 @@ character are skipped. .Pp .Fn archive_entry_acl_next -and -.Fn archive_entry_acl_next_w return the next entry of the ACL list. This functions may only be called after .Fn archive_entry_acl_reset @@ -358,9 +345,7 @@ has indicated the presence of extended ACL entries. .Pp .Fn archive_entry_acl_reset prepare reading the list of ACL entries with -.Fn archive_entry_acl_next -or -.Fn archive_entry_acl_next_w . +.Fn archive_entry_acl_next . The function returns 0 if no non-extended ACLs are found. In this case, the access permissions should be obtained by .Xr archive_entry_mode 3 @@ -447,9 +432,7 @@ if all entries were successfully parsed and if one or more entries were invalid or non-parseable. .Pp .Fn archive_entry_acl_next -and -.Fn archive_entry_acl_next_w -return +returns .Dv ARCHIVE_OK on success, .Dv ARCHIVE_EOF diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c index 392916de5b7b..7c626df6e1f1 100644 --- a/libarchive/archive_hmac.c +++ b/libarchive/archive_hmac.c @@ -83,7 +83,9 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) static int __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) { +#ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" +#endif BCRYPT_ALG_HANDLE hAlg; BCRYPT_HASH_HANDLE hHash; DWORD hash_len; diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 45417e9ac763..2a8cec8d1178 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -249,11 +249,11 @@ archive_read_disk_entry_from_file(struct archive *_a, #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { - size_t linkbuffer_len = st->st_size + 1; + size_t linkbuffer_len = st->st_size; char *linkbuffer; int lnklen; - linkbuffer = malloc(linkbuffer_len); + linkbuffer = malloc(linkbuffer_len + 1); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); @@ -280,7 +280,7 @@ archive_read_disk_entry_from_file(struct archive *_a, free(linkbuffer); return (ARCHIVE_FAILED); } - linkbuffer[lnklen] = 0; + linkbuffer[lnklen] = '\0'; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 87963c3c7c28..183ca1e8790d 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -694,6 +694,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, struct tree *t = a->tree; int r; ssize_t bytes; + int64_t sparse_bytes; size_t buffbytes; int empty_sparse_region = 0; @@ -792,9 +793,9 @@ _archive_read_data_block(struct archive *_a, const void **buff, a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } - bytes = t->current_sparse->offset - t->entry_total; - t->entry_remaining_bytes -= bytes; - t->entry_total += bytes; + sparse_bytes = t->current_sparse->offset - t->entry_total; + t->entry_remaining_bytes -= sparse_bytes; + t->entry_total += sparse_bytes; } /* @@ -2172,7 +2173,7 @@ tree_reopen(struct tree *t, const char *path, int restore_time) #elif defined(O_SEARCH) /* SunOS */ const int o_flag = O_SEARCH; -#elif defined(O_EXEC) +#elif defined(__FreeBSD__) && defined(O_EXEC) /* FreeBSD */ const int o_flag = O_EXEC; #endif @@ -2198,7 +2199,8 @@ tree_reopen(struct tree *t, const char *path, int restore_time) t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); -#if defined(O_PATH) || defined(O_SEARCH) || defined(O_EXEC) +#if defined(O_PATH) || defined(O_SEARCH) || \ + (defined(__FreeBSD__) && defined(O_EXEC)) /* * Most likely reason to fail opening "." is that it's not readable, * so try again for execute. The consequences of not opening this are diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 8ca422ec0669..87c6c5272197 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -1787,7 +1787,7 @@ read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) return (0); } - if (*p != kSize) + if (*p != kCRC) return (-1); if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index 95c99bb1f31e..35405bcdd97f 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -175,7 +175,9 @@ struct lha { struct archive_string gname; uint16_t header_crc; uint16_t crc; - struct archive_string_conv *sconv; + /* dirname and filename could be in different codepages */ + struct archive_string_conv *sconv_dir; + struct archive_string_conv *sconv_fname; struct archive_string_conv *opt_sconv; struct archive_string dirname; @@ -232,8 +234,8 @@ static time_t lha_dos_time(const unsigned char *); static time_t lha_win_time(uint64_t, long *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); -static int lha_parse_linkname(struct archive_string *, - struct archive_string *); +static int lha_parse_linkname(struct archive_wstring *, + struct archive_wstring *); static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, @@ -473,13 +475,15 @@ static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { - struct archive_string linkname; - struct archive_string pathname; + struct archive_wstring linkname; + struct archive_wstring pathname; struct lha *lha; const unsigned char *p; const char *signature; int err; - + struct archive_mstring conv_buffer; + const wchar_t *conv_buffer_p; + lha_crc16_init(); a->archive.archive_format = ARCHIVE_FORMAT_LHA; @@ -561,10 +565,13 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; - if (lha->opt_sconv != NULL) - lha->sconv = lha->opt_sconv; - else - lha->sconv = NULL; + if (lha->opt_sconv != NULL) { + lha->sconv_dir = lha->opt_sconv; + lha->sconv_fname = lha->opt_sconv; + } else { + lha->sconv_dir = NULL; + lha->sconv_fname = NULL; + } switch (p[H_LEVEL_OFFSET]) { case 0: @@ -594,12 +601,54 @@ archive_read_format_lha_read_header(struct archive_read *a, return (truncated_error(a)); /* - * Make a pathname from a dirname and a filename. - */ - archive_string_concat(&lha->dirname, &lha->filename); + * Make a pathname from a dirname and a filename, after converting to Unicode. + * This is because codepages might differ between dirname and filename. + */ archive_string_init(&pathname); archive_string_init(&linkname); - archive_string_copy(&pathname, &lha->dirname); + archive_string_init(&conv_buffer.aes_mbs); + archive_string_init(&conv_buffer.aes_mbs_in_locale); + archive_string_init(&conv_buffer.aes_utf8); + archive_string_init(&conv_buffer.aes_wcs); + if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname cannot be converted " + "from %s to Unicode.", + archive_string_conversion_charset_name(lha->sconv_dir)); + err = ARCHIVE_FATAL; + } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) + err = ARCHIVE_FATAL; + if (err == ARCHIVE_FATAL) { + archive_mstring_clean(&conv_buffer); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); + return (err); + } + archive_wstring_copy(&pathname, &conv_buffer.aes_wcs); + + archive_string_empty(&conv_buffer.aes_mbs); + archive_string_empty(&conv_buffer.aes_mbs_in_locale); + archive_string_empty(&conv_buffer.aes_utf8); + archive_wstring_empty(&conv_buffer.aes_wcs); + if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Pathname cannot be converted " + "from %s to Unicode.", + archive_string_conversion_charset_name(lha->sconv_fname)); + err = ARCHIVE_FATAL; + } + else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p)) + err = ARCHIVE_FATAL; + if (err == ARCHIVE_FATAL) { + archive_mstring_clean(&conv_buffer); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); + return (err); + } + archive_wstring_concat(&pathname, &conv_buffer.aes_wcs); + archive_mstring_clean(&conv_buffer); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* @@ -610,8 +659,8 @@ archive_read_format_lha_read_header(struct archive_read *a, archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown symlink-name"); - archive_string_free(&pathname); - archive_string_free(&linkname); + archive_wstring_free(&pathname); + archive_wstring_free(&linkname); return (ARCHIVE_FAILED); } } else { @@ -629,39 +678,13 @@ archive_read_format_lha_read_header(struct archive_read *a, /* * Set basic file parameters. */ - if (archive_entry_copy_pathname_l(entry, pathname.s, - pathname.length, lha->sconv) != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Pathname"); - return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Pathname cannot be converted " - "from %s to current locale.", - archive_string_conversion_charset_name(lha->sconv)); - err = ARCHIVE_WARN; - } - archive_string_free(&pathname); + archive_entry_copy_pathname_w(entry, pathname.s); + archive_wstring_free(&pathname); if (archive_strlen(&linkname) > 0) { - if (archive_entry_copy_symlink_l(entry, linkname.s, - linkname.length, lha->sconv) != 0) { - if (errno == ENOMEM) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate memory for Linkname"); - return (ARCHIVE_FATAL); - } - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Linkname cannot be converted " - "from %s to current locale.", - archive_string_conversion_charset_name(lha->sconv)); - err = ARCHIVE_WARN; - } + archive_entry_copy_symlink_w(entry, linkname.s); } else archive_entry_set_symlink(entry, NULL); - archive_string_free(&linkname); + archive_wstring_free(&linkname); /* * When a header level is 0, there is a possibility that * a pathname and a symlink has '\' character, a directory @@ -1208,6 +1231,26 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; + case EXT_UTF16_FILENAME: + if (datasize == 0) { + /* maybe directory header */ + archive_string_empty(&lha->filename); + break; + } else if (datasize & 1) { + /* UTF-16 characters take always 2 or 4 bytes */ + goto invalid; + } + if (extdheader[0] == '\0') + goto invalid; + archive_string_empty(&lha->filename); + archive_array_append(&lha->filename, + (const char *)extdheader, datasize); + /* Setup a string conversion for a filename. */ + lha->sconv_fname = archive_string_conversion_from_charset( + &a->archive, "UTF-16LE", 1); + if (lha->sconv_fname == NULL) + return (ARCHIVE_FATAL); + break; case EXT_DIRECTORY: if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ @@ -1228,6 +1271,36 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, /* invalid directory data */ goto invalid; break; + case EXT_UTF16_DIRECTORY: + /* UTF-16 characters take always 2 or 4 bytes */ + if (datasize == 0 || (datasize & 1) || extdheader[0] == '\0') + /* no directory name data. exit this case. */ + goto invalid; + + archive_string_empty(&lha->dirname); + archive_array_append(&lha->dirname, + (const char *)extdheader, datasize); + lha->sconv_dir = archive_string_conversion_from_charset( + &a->archive, "UTF-16LE", 1); + if (lha->sconv_dir == NULL) + return (ARCHIVE_FATAL); + else { + /* + * Convert directory delimiter from 0xFF + * to '/' for local system. + */ + /* UTF-16LE character */ + uint16_t *utf16name = (uint16_t *)lha->dirname.s; + for (i = 0; i < lha->dirname.length / 2; i++) { + if (utf16name[i] == 0xFFFF) + utf16name[i] = L'/'; + } + /* Is last character directory separator? */ + if (utf16name[lha->dirname.length / 2 - 1] != L'/') + /* invalid directory data */ + goto invalid; + } + break; case EXT_DOS_ATTR: if (datasize == 2) lha->dos_attr = (unsigned char) @@ -1276,11 +1349,16 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, charset = cp.s; break; } - lha->sconv = + lha->sconv_dir = + archive_string_conversion_from_charset( + &(a->archive), charset, 1); + lha->sconv_fname = archive_string_conversion_from_charset( &(a->archive), charset, 1); archive_string_free(&cp); - if (lha->sconv == NULL) + if (lha->sconv_dir == NULL) + return (ARCHIVE_FATAL); + if (lha->sconv_fname == NULL) return (ARCHIVE_FATAL); } break; @@ -1336,8 +1414,7 @@ lha_read_file_extended_header(struct archive_read *a, struct lha *lha, } break; case EXT_TIMEZONE: /* Not supported */ - case EXT_UTF16_FILENAME: /* Not supported */ - case EXT_UTF16_DIRECTORY: /* Not supported */ + break; default: break; } @@ -1600,19 +1677,19 @@ archive_read_format_lha_cleanup(struct archive_read *a) * then a archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int -lha_parse_linkname(struct archive_string *linkname, - struct archive_string *pathname) +lha_parse_linkname(struct archive_wstring *linkname, + struct archive_wstring *pathname) { - char * linkptr; + wchar_t * linkptr; size_t symlen; - linkptr = strchr(pathname->s, '|'); + linkptr = wcschr(pathname->s, L'|'); if (linkptr != NULL) { - symlen = strlen(linkptr + 1); - archive_strncpy(linkname, linkptr+1, symlen); + symlen = wcslen(linkptr + 1); + archive_wstrncpy(linkname, linkptr+1, symlen); *linkptr = 0; - pathname->length = strlen(pathname->s); + pathname->length = wcslen(pathname->s); return (1); } diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index e58cbbf6d829..ce38b1fc990f 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -63,6 +63,7 @@ #if defined DEBUG #define DEBUG_CODE if(1) +#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0) #else #define DEBUG_CODE if(0) #endif @@ -115,6 +116,8 @@ struct file_header { /* Optional redir fields */ uint64_t redir_type; uint64_t redir_flags; + + ssize_t solid_window_size; /* Used in file format check. */ }; enum EXTRA { @@ -1177,7 +1180,7 @@ static int process_main_locator_extra_block(struct archive_read* a, static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, ssize_t* extra_data_size) { - size_t hash_type; + size_t hash_type = 0; size_t value_len; if(!read_var_sized(a, &hash_type, &value_len)) @@ -1303,7 +1306,7 @@ static int parse_file_extra_htime(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { char unix_time = 0; - size_t flags; + size_t flags = 0; size_t value_len; enum HTIME_FLAGS { @@ -1665,6 +1668,17 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, 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; + + /* Archives which declare solid files without initializing the window + * buffer first are invalid. */ + + if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Declared solid file, but no window buffer " + "initialized yet."); + return ARCHIVE_FATAL; + } /* Check if window_size is a sane value. Also, if the file is not * declared as a directory, disallow window_size == 0. */ @@ -1676,12 +1690,36 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, return ARCHIVE_FATAL; } - /* Values up to 64M should fit into ssize_t on every - * architecture. */ - rar->cstate.window_size = (ssize_t) window_size; + if(rar->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) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Window size for this solid file doesn't match " + "the window size used in previous solid file. "); + return ARCHIVE_FATAL; + } + } + + /* If we're currently switching volumes, ignore the new definition of + * window_size. */ + if(rar->cstate.switch_multivolume == 0) { + /* Values up to 64M should fit into ssize_t on every + * architecture. */ + rar->cstate.window_size = (ssize_t) window_size; + } + + if(rar->file.solid > 0 && rar->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; + } + init_window_mask(rar); - rar->file.solid = (compression_info & SOLID) > 0; rar->file.service = 0; if(!read_var_sized(a, &host_os, NULL)) diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 9934bf1504dc..6581ca0acf6d 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -1797,6 +1797,23 @@ zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, "lzma data error (error %d)", (int) lz_ret); return (ARCHIVE_FATAL); + /* 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: + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + if((int64_t) zip->zipx_lzma_stream.total_in != + zip->entry_bytes_remaining) + { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "lzma alone premature end of stream"); + return (ARCHIVE_FATAL); + } + + zip->end_of_entry = 1; + break; + case LZMA_OK: break; diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 979a418b6779..399299ea631f 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -75,6 +75,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33 #define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) #endif +#undef max +#define max(a, b) ((a)>(b)?(a):(b)) + struct archive_string_conv { struct archive_string_conv *next; char *from_charset; @@ -591,7 +594,7 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ - size_t wcs_length = len; + // size_t wcs_length = len; size_t mbs_length = len; const char *mbs = p; wchar_t *wcs; @@ -600,7 +603,11 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, memset(&shift_state, 0, sizeof(shift_state)); #endif - if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1)) + /* + * As we decided to have wcs_length == mbs_length == len + * we can use len here instead of wcs_length + */ + if (NULL == archive_wstring_ensure(dest, dest->length + len + 1)) return (-1); wcs = dest->s + dest->length; /* @@ -609,6 +616,12 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, * multi bytes. */ while (*mbs && mbs_length > 0) { + /* + * The buffer we allocated is always big enough. + * Keep this code path in a comment if we decide to choose + * smaller wcs_length in the future + */ +/* if (wcs_length == 0) { dest->length = wcs - dest->s; dest->s[dest->length] = L'\0'; @@ -618,24 +631,20 @@ archive_wstring_append_from_mbs(struct archive_wstring *dest, return (-1); wcs = dest->s + dest->length; } +*/ #if HAVE_MBRTOWC - r = mbrtowc(wcs, mbs, wcs_length, &shift_state); + r = mbrtowc(wcs, mbs, mbs_length, &shift_state); #else - r = mbtowc(wcs, mbs, wcs_length); + r = mbtowc(wcs, mbs, mbs_length); #endif if (r == (size_t)-1 || r == (size_t)-2) { ret_val = -1; - if (errno == EILSEQ) { - ++mbs; - --mbs_length; - continue; - } else - break; + break; } if (r == 0 || r > mbs_length) break; wcs++; - wcs_length--; + // wcs_length--; mbs += r; mbs_length -= r; } @@ -798,7 +807,8 @@ archive_string_append_from_wcs(struct archive_string *as, as->s[as->length] = '\0'; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, - as->length + len * 2 + 1) == NULL) + as->length + max(len * 2, + (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; @@ -3440,7 +3450,8 @@ strncat_from_utf8_libarchive2(struct archive_string *as, as->length = p - as->s; /* Re-allocate buffer for MBS. */ if (archive_string_ensure(as, - as->length + len * 2 + 1) == NULL) + as->length + max(len * 2, + (size_t)MB_CUR_MAX) + 1) == NULL) return (-1); p = as->s + as->length; end = as->s + as->buffer_length - MB_CUR_MAX -1; diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index e8daf530d260..e7a973ae41a7 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -212,6 +212,7 @@ __archive_write_allocate_filter(struct archive *_a) f = calloc(1, sizeof(*f)); f->archive = _a; + f->state = ARCHIVE_WRITE_FILTER_STATE_NEW; if (a->filter_first == NULL) a->filter_first = f; else @@ -228,6 +229,9 @@ __archive_write_filter(struct archive_write_filter *f, const void *buff, size_t length) { int r; + /* Never write to non-open filters */ + if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN) + return(ARCHIVE_FATAL); if (length == 0) return(ARCHIVE_OK); if (f->write == NULL) @@ -240,27 +244,70 @@ __archive_write_filter(struct archive_write_filter *f, } /* - * Open a filter. + * Recursive function for opening the filter chain + * Last filter is opened first */ -int +static int __archive_write_open_filter(struct archive_write_filter *f) { - if (f->open == NULL) + int ret; + + ret = ARCHIVE_OK; + if (f->next_filter != NULL) + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW) + return (ARCHIVE_FATAL); + if (f->open == NULL) { + f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; return (ARCHIVE_OK); - return (f->open)(f); + } + ret = (f->open)(f); + if (ret == ARCHIVE_OK) + f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN; + else + f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL; + return (ret); } /* - * Close a filter. + * Open all filters */ -int -__archive_write_close_filter(struct archive_write_filter *f) +static int +__archive_write_filters_open(struct archive_write *a) { - if (f->close != NULL) - return (f->close)(f); - if (f->next_filter != NULL) - return (__archive_write_close_filter(f->next_filter)); - return (ARCHIVE_OK); + return (__archive_write_open_filter(a->filter_first)); +} + +/* + * Close all filtes + */ +static int +__archive_write_filters_close(struct archive_write *a) +{ + struct archive_write_filter *f; + int ret, ret1; + ret = ARCHIVE_OK; + for (f = a->filter_first; f != NULL; f = f->next_filter) { + /* Do not close filters that are not open */ + if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) { + if (f->close != NULL) { + ret1 = (f->close)(f); + if (ret1 < ret) + ret = ret1; + if (ret1 == ARCHIVE_OK) { + f->state = + ARCHIVE_WRITE_FILTER_STATE_CLOSED; + } else { + f->state = + ARCHIVE_WRITE_FILTER_STATE_FATAL; + } + } else + f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; + } + } + return (ret); } int @@ -440,7 +487,7 @@ archive_write_client_close(struct archive_write_filter *f) free(state->buffer); free(state); /* Clear the close handler myself not to be called again. */ - f->close = NULL; + f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED; a->client_data = NULL; /* Clear passphrase. */ if (a->passphrase != NULL) { @@ -477,9 +524,10 @@ archive_write_open(struct archive *_a, void *client_data, client_filter->write = archive_write_client_write; client_filter->close = archive_write_client_close; - ret = __archive_write_open_filter(a->filter_first); + ret = __archive_write_filters_open(a); if (ret < ARCHIVE_WARN) { - r1 = __archive_write_close_filter(a->filter_first); + r1 = __archive_write_filters_close(a); + __archive_write_filters_free(_a); return (r1 < ret ? r1 : ret); } @@ -521,7 +569,7 @@ _archive_write_close(struct archive *_a) } /* Finish the compression and close the stream. */ - r1 = __archive_write_close_filter(a->filter_first); + r1 = __archive_write_filters_close(a); if (r1 < r) r = r1; diff --git a/libarchive/archive_write_add_filter_b64encode.c b/libarchive/archive_write_add_filter_b64encode.c index b46b19a0c74d..87fdb73ecb09 100644 --- a/libarchive/archive_write_add_filter_b64encode.c +++ b/libarchive/archive_write_add_filter_b64encode.c @@ -149,11 +149,6 @@ archive_filter_b64encode_open(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; size_t bs = 65536, bpb; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the of bytes @@ -266,7 +261,6 @@ static int archive_filter_b64encode_close(struct archive_write_filter *f) { struct private_b64encode *state = (struct private_b64encode *)f->data; - int ret, ret2; /* Flush remaining bytes. */ if (state->hold_len != 0) @@ -274,12 +268,8 @@ archive_filter_b64encode_close(struct archive_write_filter *f) archive_string_sprintf(&state->encoded_buff, "====\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); - ret = __archive_write_filter(f->next_filter, + return __archive_write_filter(f->next_filter, state->encoded_buff.s, archive_strlen(&state->encoded_buff)); - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - return (ret); } static int diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 68ed9579b027..7001e9c6b309 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -167,10 +167,6 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) struct private_data *data = (struct private_data *)f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != 0) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -262,7 +258,7 @@ static int archive_compressor_bzip2_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle. */ ret = drive_compressor(f, data, 1); @@ -281,9 +277,7 @@ archive_compressor_bzip2_close(struct archive_write_filter *f) "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } - - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/libarchive/archive_write_add_filter_compress.c b/libarchive/archive_write_add_filter_compress.c index 26fcef4d42bc..d404fae7dba4 100644 --- a/libarchive/archive_write_add_filter_compress.c +++ b/libarchive/archive_write_add_filter_compress.c @@ -146,17 +146,12 @@ archive_write_add_filter_compress(struct archive *_a) static int archive_compressor_compress_open(struct archive_write_filter *f) { - int ret; struct private_data *state; size_t bs = 65536, bpb; f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - state = (struct private_data *)calloc(1, sizeof(*state)); if (state == NULL) { archive_set_error(f->archive, ENOMEM, @@ -426,30 +421,27 @@ static int archive_compressor_compress_close(struct archive_write_filter *f) { struct private_data *state = (struct private_data *)f->data; - int ret, ret2; + int ret; ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) - goto cleanup; + return ret; ret = output_flush(f); if (ret != ARCHIVE_OK) - goto cleanup; + return ret; /* Write the last block */ ret = __archive_write_filter(f->next_filter, state->compressed, state->compressed_offset); -cleanup: - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - free(state->compressed); - free(state); return (ret); } static int archive_compressor_compress_free(struct archive_write_filter *f) { - (void)f; /* UNUSED */ + struct private_data *state = (struct private_data *)f->data; + + free(state->compressed); + free(state); return (ARCHIVE_OK); } diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c index e4b3435e4206..8670d5ca7403 100644 --- a/libarchive/archive_write_add_filter_gzip.c +++ b/libarchive/archive_write_add_filter_gzip.c @@ -184,10 +184,6 @@ archive_compressor_gzip_open(struct archive_write_filter *f) struct private_data *data = (struct private_data *)f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -307,7 +303,7 @@ archive_compressor_gzip_close(struct archive_write_filter *f) { unsigned char trailer[8]; struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle */ ret = drive_compressor(f, data, 1); @@ -338,8 +334,7 @@ archive_compressor_gzip_close(struct archive_write_filter *f) "Failed to clean up compressor"); ret = ARCHIVE_FATAL; } - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } /* diff --git a/libarchive/archive_write_add_filter_lz4.c b/libarchive/archive_write_add_filter_lz4.c index 15fd494a4197..cf19fadd5633 100644 --- a/libarchive/archive_write_add_filter_lz4.c +++ b/libarchive/archive_write_add_filter_lz4.c @@ -223,16 +223,11 @@ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret; size_t required_size; static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; - ret = __archive_write_open_filter(f->next_filter); - if (ret != 0) - return (ret); - if (data->block_maximum_size < 4) data->block_size = bkmap[0]; else @@ -343,7 +338,7 @@ static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; /* Finish compression cycle. */ ret = (int)lz4_write_one_block(f, NULL, 0); @@ -366,9 +361,7 @@ archive_filter_lz4_close(struct archive_write_filter *f) ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out - data->out_buffer); } - - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/libarchive/archive_write_add_filter_lzop.c b/libarchive/archive_write_add_filter_lzop.c index ad705c4a0687..3bd9062e4d32 100644 --- a/libarchive/archive_write_add_filter_lzop.c +++ b/libarchive/archive_write_add_filter_lzop.c @@ -228,11 +228,6 @@ static int archive_write_lzop_open(struct archive_write_filter *f) { struct write_lzop *data = (struct write_lzop *)f->data; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); switch (data->compression_level) { case 1: @@ -439,10 +434,7 @@ archive_write_lzop_close(struct archive_write_filter *f) } /* Write a zero uncompressed size as the end mark of the series of * compressed block. */ - r = __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); - if (r != ARCHIVE_OK) - return (r); - return (__archive_write_close_filter(f->next_filter)); + return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); } #else diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c index 660f693f29d0..a4bc1d90eda8 100644 --- a/libarchive/archive_write_add_filter_program.c +++ b/libarchive/archive_write_add_filter_program.c @@ -212,11 +212,6 @@ __archive_write_program_open(struct archive_write_filter *f, struct archive_write_program_data *data, const char *cmd) { pid_t child; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (data->child_buf == NULL) { data->child_buf_len = 65536; @@ -353,11 +348,11 @@ int __archive_write_program_close(struct archive_write_filter *f, struct archive_write_program_data *data) { - int ret, r1, status; + int ret, status; ssize_t bytes_read; if (data->child == 0) - return __archive_write_close_filter(f->next_filter); + return ARCHIVE_OK; ret = 0; close(data->child_stdin); @@ -409,7 +404,6 @@ __archive_write_program_close(struct archive_write_filter *f, "Error closing program: %s", data->program_name); ret = ARCHIVE_FATAL; } - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } diff --git a/libarchive/archive_write_add_filter_uuencode.c b/libarchive/archive_write_add_filter_uuencode.c index 23d9c150d17f..1ad458921928 100644 --- a/libarchive/archive_write_add_filter_uuencode.c +++ b/libarchive/archive_write_add_filter_uuencode.c @@ -138,11 +138,6 @@ archive_filter_uuencode_open(struct archive_write_filter *f) { struct private_uuencode *state = (struct private_uuencode *)f->data; size_t bs = 65536, bpb; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of the of bytes @@ -257,7 +252,6 @@ static int archive_filter_uuencode_close(struct archive_write_filter *f) { struct private_uuencode *state = (struct private_uuencode *)f->data; - int ret, ret2; /* Flush remaining bytes. */ if (state->hold_len != 0) @@ -265,12 +259,8 @@ archive_filter_uuencode_close(struct archive_write_filter *f) archive_string_sprintf(&state->encoded_buff, "`\nend\n"); /* Write the last block */ archive_write_set_bytes_in_last_block(f->archive, 1); - ret = __archive_write_filter(f->next_filter, + return __archive_write_filter(f->next_filter, state->encoded_buff.s, archive_strlen(&state->encoded_buff)); - ret2 = __archive_write_close_filter(f->next_filter); - if (ret > ret2) - ret = ret2; - return (ret); } static int diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c index 0f7c8cfc31a5..8c1ebb805b10 100644 --- a/libarchive/archive_write_add_filter_xz.c +++ b/libarchive/archive_write_add_filter_xz.c @@ -309,10 +309,6 @@ archive_compressor_xz_open(struct archive_write_filter *f) struct private_data *data = f->data; int ret; - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); - if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { @@ -448,7 +444,7 @@ static int archive_compressor_xz_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret, r1; + int ret; ret = drive_compressor(f, data, 1); if (ret == ARCHIVE_OK) { @@ -466,8 +462,7 @@ archive_compressor_xz_close(struct archive_write_filter *f) } } lzma_end(&(data->stream)); - r1 = __archive_write_close_filter(f->next_filter); - return (r1 < ret ? r1 : ret); + return ret; } static int diff --git a/libarchive/archive_write_add_filter_zstd.c b/libarchive/archive_write_add_filter_zstd.c index 671fc6affbaa..4c91551ed3e1 100644 --- a/libarchive/archive_write_add_filter_zstd.c +++ b/libarchive/archive_write_add_filter_zstd.c @@ -172,11 +172,6 @@ static int archive_compressor_zstd_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int ret; - - ret = __archive_write_open_filter(f->next_filter); - if (ret != ARCHIVE_OK) - return (ret); if (data->out.dst == NULL) { size_t bs = ZSTD_CStreamOutSize(), bpb; @@ -238,14 +233,9 @@ static int archive_compressor_zstd_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; - int r1, r2; /* Finish zstd frame */ - r1 = drive_compressor(f, data, 1, NULL, 0); - - r2 = __archive_write_close_filter(f->next_filter); - - return r1 < r2 ? r1 : r2; + return drive_compressor(f, data, 1, NULL, 0); } /* diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 6ae8a6a89bbf..df4b02f5efa1 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -419,7 +419,7 @@ la_opendirat(int fd, const char *path) { | O_PATH #elif defined(O_SEARCH) | O_SEARCH -#elif defined(O_EXEC) +#elif defined(__FreeBSD__) && defined(O_EXEC) | O_EXEC #else | O_RDONLY diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h index 0dfd1b1bca91..1c182f136801 100644 --- a/libarchive/archive_write_private.h +++ b/libarchive/archive_write_private.h @@ -38,6 +38,11 @@ #include "archive_string.h" #include "archive_private.h" +#define ARCHIVE_WRITE_FILTER_STATE_NEW 1U +#define ARCHIVE_WRITE_FILTER_STATE_OPEN 2U +#define ARCHIVE_WRITE_FILTER_STATE_CLOSED 4U +#define ARCHIVE_WRITE_FILTER_STATE_FATAL 0x8000U + struct archive_write; struct archive_write_filter { @@ -55,6 +60,7 @@ struct archive_write_filter { int code; int bytes_per_block; int bytes_in_last_block; + int state; }; #if ARCHIVE_VERSION < 4000000 @@ -66,8 +72,6 @@ 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_filter(struct archive_write_filter *, const void *, size_t); -int __archive_write_open_filter(struct archive_write_filter *); -int __archive_write_close_filter(struct archive_write_filter *); struct archive_write { struct archive archive; diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index cf2a1f959e34..7c5e63bb3a23 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -199,6 +199,28 @@ archive_write_pax_options(struct archive_write *a, const char *key, archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "pax: invalid charset name"); return (ret); + } else if (strcmp(key, "xattrheader") == 0) { + if (val == NULL || val[0] == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: xattrheader requires a value"); + } else if (strcmp(val, "ALL") == 0 || + strcmp(val, "all") == 0) { + pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR; + ret = ARCHIVE_OK; + } else if (strcmp(val, "SCHILY") == 0 || + strcmp(val, "schily") == 0) { + pax->flags |= WRITE_SCHILY_XATTR; + pax->flags &= ~WRITE_LIBARCHIVE_XATTR; + ret = ARCHIVE_OK; + } else if (strcmp(val, "LIBARCHIVE") == 0 || + strcmp(val, "libarchive") == 0) { + pax->flags |= WRITE_LIBARCHIVE_XATTR; + pax->flags &= ~WRITE_SCHILY_XATTR; + ret = ARCHIVE_OK; + } else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "pax: invalid xattr header name"); + return (ret); } /* Note: The "warn" return is just to inform the options diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index 7fcd1a07b3f5..f28a8c3a341f 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -1402,18 +1402,17 @@ 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); - if (type == AE_IFDIR && - (path[0] == '\0' || path[strlen(path) - 1] != '/')) { - return strlen(path) + 1; - } else { - return strlen(path); - } + len = strlen(path); + if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/')) + ++len; /* Space for the trailing / */ + return len; } static int @@ -1461,10 +1460,8 @@ copy_path(struct archive_entry *entry, unsigned char *p) memcpy(p, path, pathlen); /* Folders are recognized by a trailing slash. */ - if ((type == AE_IFDIR) & (path[pathlen - 1] != '/')) { + if ((type == AE_IFDIR) && (path[pathlen - 1] != '/')) p[pathlen] = '/'; - p[pathlen + 1] = '\0'; - } } diff --git a/libarchive/archive_write_set_options.3 b/libarchive/archive_write_set_options.3 index a9f70a664092..09eb95ea5aa9 100644 --- a/libarchive/archive_write_set_options.3 +++ b/libarchive/archive_write_set_options.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 27, 2019 +.Dd December 3, 2019 .Dt ARCHIVE_WRITE_OPTIONS 3 .Os .Sh NAME @@ -404,6 +404,32 @@ Specifies a filename that should not be compressed when using This option can be provided multiple times to suppress compression on many files. .El +.It Format pax +.Bl -tag -compact -width indent +.It Cm hdrcharset +This sets the character set used for filenames, uname and gname. +The value is one of +.Dq BINARY +or +.Dq UTF-8 . +With +.Dq BINARY +there is no character conversion, with +.Dq UTF-8 +names are converted to UTF-8. +.It Cm xattrheader +When storing extended attributes, this option configures which +headers should be written. The value is one of +.Dq all , +.Dq LIBARCHIVE , +or +.Dq SCHILY . +By default, both +.Dq LIBARCHIVE.xattr +and +.Dq SCHILY.xattr +headers are written. +.El .It Format 7zip .Bl -tag -compact -width indent .It Cm compression diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index 2f451d2a0649..df34d3e3200b 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -82,6 +82,7 @@ IF(ENABLE_TEST) test_open_file.c test_open_filename.c test_pax_filename_encoding.c + test_pax_xattr_header.c test_read_data_large.c test_read_disk.c test_read_disk_directory_traversals.c @@ -101,6 +102,7 @@ IF(ENABLE_TEST) test_read_format_7zip_encryption_header.c test_read_format_7zip_encryption_partially.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 @@ -141,6 +143,7 @@ IF(ENABLE_TEST) 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_mtree.c test_read_format_mtree_crash747.c test_read_format_pax_bz2.c diff --git a/libarchive/test/test_open_failure.c b/libarchive/test/test_open_failure.c index 845486cf9216..5316a872b6a8 100644 --- a/libarchive/test/test_open_failure.c +++ b/libarchive/test/test_open_failure.c @@ -160,11 +160,11 @@ DEFINE_TEST(test_open_failure) archive_write_open(a, &private, my_open, my_write, my_close)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); memset(&private, 0, sizeof(private)); private.magic = MAGIC; @@ -177,11 +177,11 @@ DEFINE_TEST(test_open_failure) archive_write_open(a, &private, my_open, my_write, my_close)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); memset(&private, 0, sizeof(private)); private.magic = MAGIC; @@ -193,11 +193,11 @@ DEFINE_TEST(test_open_failure) archive_write_open(a, &private, my_open, my_write, my_close)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); memset(&private, 0, sizeof(private)); private.magic = MAGIC; @@ -209,10 +209,10 @@ DEFINE_TEST(test_open_failure) archive_write_open(a, &private, my_open, my_write, my_close)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); assertEqualInt(1, private.open_called); assertEqualInt(0, private.write_called); - assertEqualInt(1, private.close_called); + assertEqualInt(0, private.close_called); } diff --git a/libarchive/test/test_open_fd.c b/libarchive/test/test_open_fd.c index ff5fab1ad45b..7da8b5ebcb56 100644 --- a/libarchive/test/test_open_fd.c +++ b/libarchive/test/test_open_fd.c @@ -42,6 +42,7 @@ DEFINE_TEST(test_open_fd) struct archive_entry *ae; struct archive *a; int fd; + const char *skip_open_fd_err_test; #if defined(__BORLANDC__) fd = open("test.tar", O_RDWR | O_CREAT | O_BINARY); @@ -116,16 +117,18 @@ DEFINE_TEST(test_open_fd) assertEqualInt(ARCHIVE_OK, archive_read_free(a)); close(fd); - - /* - * Verify some of the error handling. - */ - 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)); - /* FD 100 shouldn't be open. */ - assertEqualIntA(a, ARCHIVE_FATAL, - archive_read_open_fd(a, 100, 512)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + skip_open_fd_err_test = getenv("SKIP_OPEN_FD_ERR_TEST"); + if(skip_open_fd_err_test == NULL) { + /* + * Verify some of the error handling. + */ + 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)); + /* FD 100 shouldn't be open. */ + assertEqualIntA(a, ARCHIVE_FATAL, + archive_read_open_fd(a, 100, 512)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + } } diff --git a/libarchive/test/test_pax_xattr_header.c b/libarchive/test/test_pax_xattr_header.c new file mode 100644 index 000000000000..d0394aa09eda --- /dev/null +++ b/libarchive/test/test_pax_xattr_header.c @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2019 Martin Matuska + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static struct archive_entry* +create_archive_entry(void) { + struct archive_entry *ae; + + 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, "file"); + archive_entry_set_mode(ae, AE_IFREG | 0755); + archive_entry_set_nlink(ae, 2); + archive_entry_set_size(ae, 8); + archive_entry_xattr_add_entry(ae, "user.data1", "ABCDEFG", 7); + archive_entry_xattr_add_entry(ae, "user.data2", "XYZ", 3); + + return (ae); +} + +DEFINE_TEST(test_pax_xattr_header) +{ + static const char *reffiles[] = { + "test_pax_xattr_header_all.tar", + "test_pax_xattr_header_libarchive.tar", + "test_pax_xattr_header_schily.tar", + NULL + }; + struct archive *a; + struct archive_entry *ae; + + extract_reference_files(reffiles); + + /* First archive, no options */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_pax(a)); + assertEqualIntA(a, 0, archive_write_add_filter_none(a)); + assertEqualInt(0, + archive_write_open_filename(a, "test1.tar")); + ae = create_archive_entry(); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + assertEqualFile("test1.tar","test_pax_xattr_header_all.tar"); + + /* Second archive, xattrheader=SCHILY */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_pax(a)); + assertEqualIntA(a, 0, archive_write_add_filter_none(a)); + assertEqualIntA(a, 0, archive_write_set_options(a, + "xattrheader=SCHILY")); + assertEqualInt(0, + archive_write_open_filename(a, "test2.tar")); + + ae = create_archive_entry(); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + assertEqualFile("test2.tar","test_pax_xattr_header_schily.tar"); + + /* Third archive, xattrheader=LIBARCHIVE */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_pax(a)); + assertEqualIntA(a, 0, archive_write_add_filter_none(a)); + assertEqualIntA(a, 0, archive_write_set_options(a, + "xattrheader=LIBARCHIVE")); + assertEqualInt(0, + archive_write_open_filename(a, "test3.tar")); + + ae = create_archive_entry(); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + assertEqualFile("test3.tar","test_pax_xattr_header_libarchive.tar"); + + /* Fourth archive, xattrheader=ALL */ + assert((a = archive_write_new()) != NULL); + assertEqualIntA(a, 0, archive_write_set_format_pax(a)); + assertEqualIntA(a, 0, archive_write_add_filter_none(a)); + assertEqualIntA(a, 0, archive_write_set_options(a, "xattrheader=ALL")); + assertEqualInt(0, + archive_write_open_filename(a, "test4.tar")); + + ae = create_archive_entry(); + assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); + archive_entry_free(ae); + assertEqualIntA(a, 8, archive_write_data(a, "12345678", 9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_write_free(a)); + + assertEqualFile("test4.tar","test_pax_xattr_header_all.tar"); +} diff --git a/libarchive/test/test_pax_xattr_header_all.tar.uu b/libarchive/test/test_pax_xattr_header_all.tar.uu new file mode 100644 index 000000000000..086428e7144b --- /dev/null +++ b/libarchive/test/test_pax_xattr_header_all.tar.uu @@ -0,0 +1,72 @@ +begin 644 test_pax_xattr_header_all.tar +M4&%X2&5A9&5R+V9I;&4````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````#`P,#``````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````!U + +static void +test_read_format_lha_filename_UTF16_UTF8(const char *refname) +{ + struct archive *a; + struct archive_entry *ae; + + /* + * Read LHA filename in en_US.UTF-8. + */ + if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { + skipping("en_US.UTF-8 locale not available on this system."); + return; + } + /* + * Create a read object only for a test that platform support + * a character-set conversion because we can read a character-set + * of filenames from the header of an lha archive file and so we + * want to test that it works well. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + if (ARCHIVE_OK != archive_read_set_options(a, "hdrcharset=CP932")) { + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + skipping("This system cannot convert character-set" + " from CP932 to UTF-8."); + return; + } + if (ARCHIVE_OK != archive_read_set_options(a, "hdrcharset=UTF-16")) { + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + skipping("This system cannot convert character-set" + " from UTF-16 to UTF-8."); + return; + } + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* Note that usual Japanese filenames are tested in other cases */ +#if defined(__APPLE__) + /* NFD normalization */ + /* U:O:A:u:o:a: */ + #define UMLAUT_DIRNAME "\x55\xcc\x88\x4f\xcc\x88\x41\xcc\x88\x75\xcc\x88\x6f"\ + "\xcc\x88\x61\xcc\x88/" + /* a:o:u:A:O:U:.txt */ + #define UMLAUT_FNAME "\x61\xcc\x88\x6f\xcc\x88\x75\xcc\x88\x41\xcc\x88"\ + "\x4f\xcc\x88\x55\xcc\x88.txt" +#else + /* NFC normalization */ + /* U:O:A:u:o:a: */ + #define UMLAUT_DIRNAME "\xc3\x9c\xc3\x96\xc3\x84\xc3\xbc\xc3\xb6\xc3\xa4/" + /* a:o:u:A:O:U:.txt */ + #define UMLAUT_FNAME "\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x84\xc3\x96\xc3\x9c.txt" +#endif + +/* "Test" in Japanese Katakana */ +#define KATAKANA_FNAME "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt" +#define KATAKANA_DIRNAME "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88/" + + /* Verify regular file. U:O:A:u:o:a:/a:o:u:A:O:U:.txt */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(UMLAUT_DIRNAME UMLAUT_FNAME, archive_entry_pathname(ae)); + assertEqualInt(12, archive_entry_size(ae)); + + /* Verify directory. U:O:A:u:o:a:/ */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(UMLAUT_DIRNAME, archive_entry_pathname(ae)); + assertEqualInt(0, archive_entry_size(ae)); + + /* Verify regular file. U:O:A:u:o:a:/("Test" in Japanese).txt */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(UMLAUT_DIRNAME KATAKANA_FNAME, + archive_entry_pathname(ae)); + assertEqualInt(25, archive_entry_size(ae)); + + /* Verify regular file. ("Test" in Japanese)/a:o:u:A:O:U:.txt */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(KATAKANA_DIRNAME UMLAUT_FNAME, + archive_entry_pathname(ae)); + assertEqualInt(12, archive_entry_size(ae)); + + /* Verify directory. ("Test" in Japanese)/ */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(KATAKANA_DIRNAME, archive_entry_pathname(ae)); + assertEqualInt(0, archive_entry_size(ae)); + + /* Verify regular file. a:o:u:A:O:U:.txt */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString(UMLAUT_FNAME, archive_entry_pathname(ae)); + assertEqualInt(12, archive_entry_size(ae)); + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_FORMAT_LHA, archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_lha_filename_UTF16) +{ + /* A sample file was created with Unlha32.dll. */ + const char *refname = "test_read_format_lha_filename_utf16.lzh"; + extract_reference_file(refname); + + test_read_format_lha_filename_UTF16_UTF8(refname); +} + diff --git a/libarchive/test/test_read_format_lha_filename_utf16.lzh.uu b/libarchive/test/test_read_format_lha_filename_utf16.lzh.uu new file mode 100644 index 000000000000..ca5da7a64e59 --- /dev/null +++ b/libarchive/test/test_read_format_lha_filename_utf16.lzh.uu @@ -0,0 +1,19 @@ +begin 644 test_read_format_lha_filename_utf16.lzh +M@0`M;&@P+0P````,````L/5872`"IW%-!P!&I`,```T``5]?7U]?7RYT>'0* +M``)?7U]?7U__%P!$Y`#V`/P`Q`#6`-P`+@!T`'@`=``1`$7<`-8`Q`#\`/8` +MY`#__QL`078S9B"U0$H^DQ_F(+5 +M`2CZ3'^8@M4!!@``GW<'``!J`"UL:#`M&0```!D```!V)L1=(`*CNTT'`$:D +M`P``#0`!@V6#6(-G+G1X=`H``E]?7U]?7_\1`$7<`-8`Q`#\`/8`Y`#__QL` +M00IF#4J@E-4!L"5C?'65U0&P)6-\=975`08``$%^!P``5&5S="!I;B!*87!A +M;F5S92!+871A:V%N87L`+6QH,"T,````#````+#U6%T@`J=Q30<`1J0#```- +M``%?7U]?7U\N='AT"@`"@V6#6(-G_Q<`1.0`]@#\`,0`U@#<`"X`=`!X`'0` +M"P!%QC"Y,,@P__\;`$'D5O!-H)35`?4-F(*15=4!Y%;P3:"4U0$&``"A+0<` +M`,.DP[;#O,.$PY;#G%0`+6QH9"T``````````-3`PET@`@``30<`1J0#```# +M``$*``*#98-8@V?_!0!`$``;`$%%!\Y.H)35`3/_]%&@E-4!,__T4:"4U0$& +M``!>;@<``&8`+6QH,"T,````#````+#U6%T@`J=Q30<`1J0#```-``%?7U]? +M7U\N='AT%P!$Y`#V`/P`Q`#6`-P`+@!T`'@`=``;`$'D5O!-H)35`?4-F(*1 +@5=4!Y%;P3:"4U0$&``"M>`<``,.DP[;#O,.$PY;#G`#D +` +end diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c index b4ef29e464bf..bb94d4e34e25 100644 --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -969,13 +969,12 @@ DEFINE_TEST(test_read_format_rar5_readtables_overflow) PROLOGUE("test_read_format_rar5_readtables_overflow.rar"); - assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * buffer overflow errors during reading rar5 tables. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -986,13 +985,12 @@ DEFINE_TEST(test_read_format_rar5_leftshift1) PROLOGUE("test_read_format_rar5_leftshift1.rar"); - assertA(0 == archive_read_next_header(a, &ae)); /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -1003,14 +1001,12 @@ DEFINE_TEST(test_read_format_rar5_leftshift2) PROLOGUE("test_read_format_rar5_leftshift2.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -1021,14 +1017,12 @@ DEFINE_TEST(test_read_format_rar5_truncated_huff) PROLOGUE("test_read_format_rar5_truncated_huff.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - /* This archive is invalid. However, processing it shouldn't cause any * errors related to undefined operations when using -fsanitize. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -1058,14 +1052,12 @@ DEFINE_TEST(test_read_format_rar5_distance_overflow) PROLOGUE("test_read_format_rar5_distance_overflow.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - /* This archive is invalid. However, processing it shouldn't cause any * errors related to variable overflows when using -fsanitize. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -1076,14 +1068,12 @@ DEFINE_TEST(test_read_format_rar5_nonempty_dir_stream) PROLOGUE("test_read_format_rar5_nonempty_dir_stream.rar"); - assertA(0 == archive_read_next_header(a, &ae)); - /* This archive is invalid. However, processing it shouldn't cause any * errors related to buffer overflows when using -fsanitize. */ - assertA(archive_read_data(a, buf, sizeof(buf)) <= 0); - /* This test only cares about not returning success here. */ - assertA(ARCHIVE_OK != archive_read_next_header(a, &ae)); + (void) archive_read_next_header(a, &ae); + (void) archive_read_data(a, buf, sizeof(buf)); + (void) archive_read_next_header(a, &ae); EPILOGUE(); } @@ -1205,13 +1195,13 @@ DEFINE_TEST(test_read_format_rar5_different_window_size) * errors during processing. */ (void) archive_read_next_header(a, &ae); - while(0 != archive_read_data(a, buf, sizeof(buf))) {} + while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); - while(0 != archive_read_data(a, buf, sizeof(buf))) {} + while(0 < archive_read_data(a, buf, sizeof(buf))) {} (void) archive_read_next_header(a, &ae); - while(0 != archive_read_data(a, buf, sizeof(buf))) {} + while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } @@ -1226,7 +1216,43 @@ DEFINE_TEST(test_read_format_rar5_arm_filter_on_window_boundary) * errors during processing. */ (void) archive_read_next_header(a, &ae); - while(0 != archive_read_data(a, buf, sizeof(buf))) {} + while(0 < archive_read_data(a, buf, sizeof(buf))) {} + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_different_solid_window_size) +{ + char buf[4096]; + PROLOGUE("test_read_format_rar5_different_solid_window_size.rar"); + + /* Return codes of those calls are ignored, because this sample file + * is invalid. However, the unpacker shouldn't produce any SIGSEGV + * errors during processing. */ + + (void) archive_read_next_header(a, &ae); + while(0 < archive_read_data(a, buf, sizeof(buf))) {} + + (void) archive_read_next_header(a, &ae); + while(0 < archive_read_data(a, buf, sizeof(buf))) {} + + (void) archive_read_next_header(a, &ae); + while(0 < archive_read_data(a, buf, sizeof(buf))) {} + + EPILOGUE(); +} + +DEFINE_TEST(test_read_format_rar5_different_winsize_on_merge) +{ + char buf[4096]; + PROLOGUE("test_read_format_rar5_different_winsize_on_merge.rar"); + + /* Return codes of those calls are ignored, because this sample file + * is invalid. However, the unpacker shouldn't produce any SIGSEGV + * errors during processing. */ + + (void) archive_read_next_header(a, &ae); + while(0 < archive_read_data(a, buf, sizeof(buf))) {} EPILOGUE(); } diff --git a/libarchive/test/test_read_format_rar5_different_solid_window_size.rar.uu b/libarchive/test/test_read_format_rar5_different_solid_window_size.rar.uu new file mode 100644 index 000000000000..b131e6f00c8e --- /dev/null +++ b/libarchive/test/test_read_format_rar5_different_solid_window_size.rar.uu @@ -0,0 +1,73 @@ +begin 644 test_read_format_rar5_different_solid_window_size.rar +M4F%R(1H'`0"-[P+2``'#M#P\7P$'`0"-[P+2``7#````1H68F`#___\````` +M```0^OKZ^OJ%F)B8F)A)`)@"F-(%87)4`,.T/#Q?`0\"T@`"QP\`"7(A +MFC$!$AHCTBT``B@A4F%2(1H'&.D````!`(WO`M(`!<-%````1A?'#P`)\"T@`"QP\`"7(AFC$! +M$AHCTBT``B@A4F%2(1H'&.D````!`(WO`M(`!<-%````1A?'#P`)7!E/6P*+@HN"BYT+G0@='G^6_]P9\"T@`"TGX!M,-'4CQ2:7`)20`&`````````(WO`M(``0!R(8WO`M(``0!R +M(8WO`M(``0!R(8WO`M(`M`%?C>\"T@`"TGX!M,-'4CQ:!@!I`'`)20`````` +M`(WO`M(``0!R(8WO`M(``0!R(8WO`M(``0!R(8WO`M(`M`%?C>^NT@`"TGX! +MM%)A\"T@`"PP<<@`<`_O__T?___^@@JP#_Q00``B$<`0(`#@`` +M`0``_@C2(`$>____"```_?__$`#_W5A04)#_!&R&;;%DS,RWL0!)`(/__P#_ +M`/\`^?__\&3/)#)(L0!)`#J#+O_______REH%PHHV#!="0"N)$%SF_J;$___ +M_YP#%9,I````_________Q/_____________________________________ +M_RC_____`P````````#_____*/]!04%!0=U891/_9?]0/2[_______\I:!<* +M*-@P70D`KB1!\"T@`````````````` +` +end diff --git a/libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu b/libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu new file mode 100644 index 000000000000..85391fa4e1f6 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu @@ -0,0 +1,16 @@ +begin 644 test_read_format_rar5_different_winsize_on_merge.rar.uu +M4F%R(1H'`0"-[P+2``+''QP,!`H``"0`N)$#`0(H$"<"``X`/3Q/`0"V```` +MQ@$````V`/^%02`H^B7&,NX``"F&AK%M-50O\"T@`"_[6U,1"U +MM;6UM[BU45)AXM5%287*U +MM;6UM;2UM0``//_____W______________\`!F$`C>\"TM(``](?________ +M`+3#6@0&D0,!`B@0)P(`#@`]/$\!`+8```#&`0```#8`_X5!("CZ)<8R[@`` +M*8:&L6TU5"]S?E(````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````type != END) { if (s->type == HOLE) { @@ -282,7 +282,7 @@ create_sparse_file(const char *path, const struct sparse *s) { char buff[1024]; int fd; - size_t total_size = 0; + uint64_t total_size = 0; const struct sparse *cur = s; memset(buff, ' ', sizeof(buff)); @@ -555,6 +555,12 @@ DEFINE_TEST(test_sparse_basic) { HOLE, 1 }, { DATA, 10240 }, { END, 0 } }; + const struct sparse sparse_file4[] = { + { DATA, 4096 }, { HOLE, 0xc0000000 }, + /* This hole overflows the offset if stored in 32 bits. */ + { DATA, 4096 }, { HOLE, 0x50000000 }, + { END, 0 } + }; /* * Test for the case that sparse data indicates just the whole file @@ -596,6 +602,7 @@ DEFINE_TEST(test_sparse_basic) verify_sparse_file(a, "file2", sparse_file2, 20); /* Encoded non sparse; expect a data block but no sparse entries. */ verify_sparse_file(a, "file3", sparse_file3, 0); + verify_sparse_file(a, "file4", sparse_file4, 2); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); diff --git a/test_utils/test_main.c b/test_utils/test_main.c index 1b9af9a9c37b..1b44edf171d9 100644 --- a/test_utils/test_main.c +++ b/test_utils/test_main.c @@ -1863,7 +1863,7 @@ is_symlink(const char *file, int line, return (0); if (contents == NULL) return (1); - linklen = readlink(pathname, buff, sizeof(buff)); + linklen = readlink(pathname, buff, sizeof(buff) - 1); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL);